From c2a32de35f90c144edd5f9495943786f8b99340b Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 28 Feb 2022 14:07:47 -0600 Subject: [PATCH 001/238] TUN-5737: Support https protocol over unix socket origin --- ingress/ingress.go | 7 +++++-- ingress/ingress_test.go | 15 ++++++++++++++- ingress/origin_proxy.go | 2 +- ingress/origin_service.go | 3 ++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ingress/ingress.go b/ingress/ingress.go index 5e5f9655..801bc551 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -126,7 +126,7 @@ func parseSingleOriginService(c *cli.Context, allowURLFromArgs bool) (OriginServ if err != nil { return nil, errors.Wrap(err, "Error validating --unix-socket") } - return &unixSocketPath{path: path}, nil + return &unixSocketPath{path: path, scheme: "http"}, nil } u, err := url.Parse("http://localhost:8080") return &httpService{url: u}, err @@ -169,7 +169,10 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq if prefix := "unix:"; strings.HasPrefix(r.Service, prefix) { // No validation necessary for unix socket filepath services path := strings.TrimPrefix(r.Service, prefix) - service = &unixSocketPath{path: path} + service = &unixSocketPath{path: path, scheme: "http"} + } else if prefix := "unix+tls:"; strings.HasPrefix(r.Service, prefix) { + path := strings.TrimPrefix(r.Service, prefix) + service = &unixSocketPath{path: path, scheme: "https"} } else if prefix := "http_status:"; strings.HasPrefix(r.Service, prefix) { status, err := strconv.Atoi(strings.TrimPrefix(r.Service, prefix)) if err != nil { diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 9d09e8f8..1e999a4e 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -26,8 +26,21 @@ ingress: ` ing, err := ParseIngress(MustReadIngress(rawYAML)) require.NoError(t, err) - _, ok := ing.Rules[0].Service.(*unixSocketPath) + s, ok := ing.Rules[0].Service.(*unixSocketPath) require.True(t, ok) + require.Equal(t, "http", s.scheme) +} + +func TestParseUnixSocketTLS(t *testing.T) { + rawYAML := ` +ingress: +- service: unix+tls:/tmp/echo.sock +` + ing, err := ParseIngress(MustReadIngress(rawYAML)) + require.NoError(t, err) + s, ok := ing.Rules[0].Service.(*unixSocketPath) + require.True(t, ok) + require.Equal(t, "https", s.scheme) } func Test_parseIngress(t *testing.T) { diff --git a/ingress/origin_proxy.go b/ingress/origin_proxy.go index 63c10137..e99e002e 100644 --- a/ingress/origin_proxy.go +++ b/ingress/origin_proxy.go @@ -23,7 +23,7 @@ type StreamBasedOriginProxy interface { } func (o *unixSocketPath) RoundTrip(req *http.Request) (*http.Response, error) { - req.URL.Scheme = "http" + req.URL.Scheme = o.scheme return o.transport.RoundTrip(req) } diff --git a/ingress/origin_service.go b/ingress/origin_service.go index 116b77f0..c76c98a4 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -33,9 +33,10 @@ type OriginService interface { start(log *zerolog.Logger, shutdownC <-chan struct{}, cfg OriginRequestConfig) error } -// unixSocketPath is an OriginService representing a unix socket (which accepts HTTP) +// unixSocketPath is an OriginService representing a unix socket (which accepts HTTP or HTTPS) type unixSocketPath struct { path string + scheme string transport *http.Transport } From 706523389c83ad3bc9b950bd6cb712864e23f586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 23 Feb 2022 16:18:45 +0000 Subject: [PATCH 002/238] TUN-5679: Add support for service install using Tunnel Token --- cmd/cloudflared/common_service.go | 30 +++++ cmd/cloudflared/linux_service.go | 121 ++++++++------------- cmd/cloudflared/macos_service.go | 12 +- cmd/cloudflared/tunnel/subcommands.go | 4 +- cmd/cloudflared/tunnel/subcommands_test.go | 4 +- cmd/cloudflared/windows_service.go | 9 +- 6 files changed, 101 insertions(+), 79 deletions(-) create mode 100644 cmd/cloudflared/common_service.go diff --git a/cmd/cloudflared/common_service.go b/cmd/cloudflared/common_service.go new file mode 100644 index 00000000..db7338c0 --- /dev/null +++ b/cmd/cloudflared/common_service.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/rs/zerolog" + "github.com/urfave/cli/v2" + + "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" + "github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel" +) + +func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) { + token := c.Args().First() + if _, err := tunnel.ParseToken(token); err != nil { + return nil, cliutil.UsageError("Provided tunnel token is not valid (%s).", err) + } + + return []string{ + "tunnel", "run", "--token", token, + }, nil +} + +func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) { + if c.NArg() > 0 { + // currently, we only support extra args for token + return buildArgsForToken(c, log) + } else { + // empty extra args + return make([]string, 0), nil + } +} diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 85d195fa..2d3060e7 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -6,7 +6,6 @@ package main import ( "fmt" "os" - "path/filepath" "github.com/rs/zerolog" "github.com/urfave/cli/v2" @@ -26,12 +25,6 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) { Name: "install", Usage: "Install Cloudflare Tunnel as a system service", Action: cliutil.ConfiguredAction(installLinuxService), - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "legacy", - Usage: "Generate service file for non-named tunnels", - }, - }, }, { Name: "uninstall", @@ -62,7 +55,7 @@ After=network.target [Service] TimeoutStartSec=0 Type=notify -ExecStart={{ .Path }} --config /etc/cloudflared/config.yml --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} +ExecStart={{ .Path }} --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} Restart=on-failure RestartSec=5s @@ -112,7 +105,7 @@ var sysvTemplate = ServiceTemplate{ # Description: Cloudflare Tunnel agent ### END INIT INFO name=$(basename $(readlink -f $0)) -cmd="{{.Path}} --config /etc/cloudflared/config.yml --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}" +cmd="{{.Path}} --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}" pid_file="/var/run/$name.pid" stdout_log="/var/log/$name.log" stderr_log="/var/log/$name.err" @@ -191,27 +184,6 @@ func isSystemd() bool { return false } -func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, log *zerolog.Logger) error { - srcCredentialPath := filepath.Join(userConfigDir, userCredentialFile) - destCredentialPath := filepath.Join(serviceConfigDir, serviceCredentialFile) - if srcCredentialPath != destCredentialPath { - if err := copyCredential(srcCredentialPath, destCredentialPath); err != nil { - return err - } - } - - srcConfigPath := filepath.Join(userConfigDir, userConfigFile) - destConfigPath := filepath.Join(serviceConfigDir, serviceConfigFile) - if srcConfigPath != destConfigPath { - if err := copyConfig(srcConfigPath, destConfigPath); err != nil { - return err - } - log.Info().Msgf("Copied %s to %s", srcConfigPath, destConfigPath) - } - - return nil -} - func installLinuxService(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) @@ -223,52 +195,19 @@ func installLinuxService(c *cli.Context) error { Path: etPath, } - if err := ensureConfigDirExists(serviceConfigDir); err != nil { + var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error) + if c.NArg() == 0 { + extraArgsFunc = buildArgsForConfig + } else { + extraArgsFunc = buildArgsForToken + } + + extraArgs, err := extraArgsFunc(c, log) + if err != nil { return err } - if c.Bool("legacy") { - userConfigDir := filepath.Dir(c.String("config")) - userConfigFile := filepath.Base(c.String("config")) - userCredentialFile := config.DefaultCredentialFile - if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, log); err != nil { - log.Err(err).Msgf("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s", - serviceConfigDir, serviceCredentialFile, serviceConfigFile) - return err - } - templateArgs.ExtraArgs = []string{ - "--origincert", serviceConfigDir + "/" + serviceCredentialFile, - } - } else { - src, _, err := config.ReadConfigFile(c, log) - if err != nil { - return err - } - // can't use context because this command doesn't define "credentials-file" flag - configPresent := func(s string) bool { - val, err := src.String(s) - return err == nil && val != "" - } - if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) { - return fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials: -tunnel: TUNNEL-UUID -credentials-file: CREDENTIALS-FILE -`, src.Source()) - } - if src.Source() != serviceConfigPath { - if exists, err := config.FileExists(serviceConfigPath); err != nil || exists { - return fmt.Errorf("Possible conflicting configuration in %[1]s and %[2]s. Either remove %[2]s or run `cloudflared --config %[2]s service install`", src.Source(), serviceConfigPath) - } - - if err := copyFile(src.Source(), serviceConfigPath); err != nil { - return fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err) - } - } - - templateArgs.ExtraArgs = []string{ - "tunnel", "run", - } - } + templateArgs.ExtraArgs = extraArgs switch { case isSystemd(): @@ -280,6 +219,42 @@ credentials-file: CREDENTIALS-FILE } } +func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) { + if err := ensureConfigDirExists(serviceConfigDir); err != nil { + return nil, err + } + + src, _, err := config.ReadConfigFile(c, log) + if err != nil { + return nil, err + } + + // can't use context because this command doesn't define "credentials-file" flag + configPresent := func(s string) bool { + val, err := src.String(s) + return err == nil && val != "" + } + if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) { + return nil, fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials: +tunnel: TUNNEL-UUID +credentials-file: CREDENTIALS-FILE +`, src.Source()) + } + if src.Source() != serviceConfigPath { + if exists, err := config.FileExists(serviceConfigPath); err != nil || exists { + return nil, fmt.Errorf("Possible conflicting configuration in %[1]s and %[2]s. Either remove %[2]s or run `cloudflared --config %[2]s service install`", src.Source(), serviceConfigPath) + } + + if err := copyFile(src.Source(), serviceConfigPath); err != nil { + return nil, fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err) + } + } + + return []string{ + "--config", "/etc/cloudflared/config.yml", "tunnel", "run", + }, nil +} + func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { for _, serviceTemplate := range systemdTemplates { err := serviceTemplate.Generate(templateArgs) diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go index e987df87..542b8849 100644 --- a/cmd/cloudflared/macos_service.go +++ b/cmd/cloudflared/macos_service.go @@ -50,6 +50,9 @@ func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemp ProgramArguments {{ .Path }} + {{- range $i, $item := .ExtraArgs}} + {{ $item }} + {{- end}} RunAtLoad @@ -129,6 +132,13 @@ func installLaunchd(c *cli.Context) error { log.Err(err).Msg("Error determining install path") return errors.Wrap(err, "Error determining install path") } + extraArgs, err := getServiceExtraArgsFromCliArgs(c, log) + if err != nil { + errMsg := "Unable to determine extra arguments for launch daemon" + log.Err(err).Msg(errMsg) + return errors.Wrap(err, errMsg) + } + stdoutPath, err := stdoutPath() if err != nil { log.Err(err).Msg("error determining stdout path") @@ -140,7 +150,7 @@ func installLaunchd(c *cli.Context) error { return errors.Wrap(err, "error determining stderr path") } launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath) - templateArgs := ServiceTemplateArgs{Path: etPath} + templateArgs := ServiceTemplateArgs{Path: etPath, ExtraArgs: extraArgs} err = launchdTemplate.Generate(&templateArgs) if err != nil { log.Err(err).Msg("error generating launchd template") diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 8362d8f8..905c8ad6 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -644,7 +644,7 @@ func runCommand(c *cli.Context) error { // Check if token is provided and if not use default tunnelID flag method if tokenStr := c.String(TunnelTokenFlag); tokenStr != "" { - if token, err := parseToken(tokenStr); err == nil { + if token, err := ParseToken(tokenStr); err == nil { return sc.runWithCredentials(token.Credentials()) } @@ -663,7 +663,7 @@ func runCommand(c *cli.Context) error { } } -func parseToken(tokenStr string) (*connection.TunnelToken, error) { +func ParseToken(tokenStr string) (*connection.TunnelToken, error) { content, err := base64.StdEncoding.DecodeString(tokenStr) if err != nil { return nil, err diff --git a/cmd/cloudflared/tunnel/subcommands_test.go b/cmd/cloudflared/tunnel/subcommands_test.go index 81f542c7..2016fe6d 100644 --- a/cmd/cloudflared/tunnel/subcommands_test.go +++ b/cmd/cloudflared/tunnel/subcommands_test.go @@ -183,7 +183,7 @@ func Test_validateHostname(t *testing.T) { } func Test_TunnelToken(t *testing.T) { - token, err := parseToken("aabc") + token, err := ParseToken("aabc") require.Error(t, err) require.Nil(t, token) @@ -198,7 +198,7 @@ func Test_TunnelToken(t *testing.T) { token64 := base64.StdEncoding.EncodeToString(tokenJsonStr) - token, err = parseToken(token64) + token, err = ParseToken(token64) require.NoError(t, err) require.Equal(t, token, expectedToken) } diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 2eba4780..6006c0b0 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -193,8 +193,15 @@ func installWindowsService(c *cli.Context) error { s.Close() return fmt.Errorf("Service %s already exists", windowsServiceName) } + extraArgs, err := getServiceExtraArgsFromCliArgs(c, &log) + if err != nil { + errMsg := "Unable to determine extra arguments for windows service" + log.Err(err).Msg(errMsg) + return errors.Wrap(err, errMsg) + } + config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription} - s, err = m.CreateService(windowsServiceName, exepath, config) + s, err = m.CreateService(windowsServiceName, exepath, config, extraArgs...) if err != nil { return errors.Wrap(err, "Cannot install service") } From 5431e0ca126a3fbe0aad835f7fbbb6d9feb14456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 2 Mar 2022 15:56:32 +0000 Subject: [PATCH 003/238] TUN-5680: Adapt component tests for new service install based on token --- component-tests/README.md | 2 +- component-tests/config.py | 19 +++++++++++ component-tests/test_service.py | 60 +++++++++++++++++++++++++++------ component-tests/util.py | 14 ++++++-- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/component-tests/README.md b/component-tests/README.md index 27c34783..8a76eeda 100644 --- a/component-tests/README.md +++ b/component-tests/README.md @@ -14,7 +14,7 @@ classic_hostname: "classic-tunnel-component-tests.example.com" origincert: "/Users/tunnel/.cloudflared/cert.pem" ingress: - hostname: named-tunnel-component-tests.example.com - service: http_status:200 + service: hello_world - service: http_status:404 ``` diff --git a/component-tests/config.py b/component-tests/config.py index dc438149..f53732f2 100644 --- a/component-tests/config.py +++ b/component-tests/config.py @@ -1,5 +1,7 @@ #!/usr/bin/env python import copy +import json +import base64 from dataclasses import dataclass, InitVar @@ -61,6 +63,23 @@ class NamedTunnelConfig(NamedTunnelBaseConfig): def get_url(self): return "https://" + self.ingress[0]['hostname'] + def base_config(self): + config = self.full_config.copy() + + # removes the tunnel reference + del(config["tunnel"]) + del(config["credentials-file"]) + + return config + + def get_token(self): + with open(self.credentials_file) as json_file: + creds = json.load(json_file) + token_dict = {"a": creds["AccountTag"], "t": creds["TunnelID"], "s": creds["TunnelSecret"]} + token_json_str = json.dumps(token_dict) + + return base64.b64encode(token_json_str.encode('utf-8')) + @dataclass(frozen=True) class ClassicTunnelBaseConfig(BaseConfig): diff --git a/component-tests/test_service.py b/component-tests/test_service.py index efc3e242..2e02db6b 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import os +import pathlib import platform import subprocess from contextlib import contextmanager @@ -9,7 +10,7 @@ import pytest import test_logging from conftest import CfdModes -from util import start_cloudflared, wait_tunnel_ready +from util import start_cloudflared, wait_tunnel_ready, write_config def select_platform(plat): @@ -43,6 +44,21 @@ class TestServiceMode: self.launchd_service_scenario(config, assert_log_file) + @select_platform("Darwin") + @pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path") + def test_launchd_service_with_token(self, tmp_path, component_tests_config): + log_file = tmp_path / test_logging.default_log_file + additional_config = { + "logfile": str(log_file), + } + config = component_tests_config(additional_config=additional_config) + + # service install doesn't install the config file but in this case we want to use some default settings + # so we write the base config without the tunnel credentials and ID + write_config(pathlib.Path(default_config_dir()), config.base_config()) + + self.launchd_service_scenario(config, use_token=True) + @select_platform("Darwin") @pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path") def test_launchd_service_rotating_log(self, tmp_path, component_tests_config): @@ -60,12 +76,13 @@ class TestServiceMode: self.launchd_service_scenario(config, assert_rotating_log) - def launchd_service_scenario(self, config, extra_assertions): - with self.run_service(Path(default_config_dir()), config): + def launchd_service_scenario(self, config, extra_assertions=None, use_token=False): + with self.run_service(Path(default_config_dir()), config, use_token=use_token): self.launchctl_cmd("list") self.launchctl_cmd("start") wait_tunnel_ready(tunnel_url=config.get_url()) - extra_assertions() + if extra_assertions is not None: + extra_assertions() self.launchctl_cmd("stop") os.remove(default_config_file()) @@ -105,12 +122,30 @@ class TestServiceMode: self.sysv_service_scenario(config, tmp_path, assert_rotating_log) - def sysv_service_scenario(self, config, tmp_path, extra_assertions): - with self.run_service(tmp_path, config, root=True): + @select_platform("Linux") + @pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), + reason=f"There is already a config file in default path") + def test_sysv_service_with_token(self, tmp_path, component_tests_config): + additional_config = { + "loglevel": "debug", + } + + config = component_tests_config(additional_config=additional_config) + + # service install doesn't install the config file but in this case we want to use some default settings + # so we write the base config without the tunnel credentials and ID + config_path = write_config(tmp_path, config.base_config()) + subprocess.run(["sudo", "cp", config_path, "/etc/cloudflared/config.yml"], check=True) + + self.sysv_service_scenario(config, tmp_path, use_token=True) + + def sysv_service_scenario(self, config, tmp_path, extra_assertions=None, use_token=False): + with self.run_service(tmp_path, config, root=True, use_token=use_token): self.sysv_cmd("start") self.sysv_cmd("status") wait_tunnel_ready(tunnel_url=config.get_url()) - extra_assertions() + if extra_assertions is not None: + extra_assertions() self.sysv_cmd("stop") # Service install copies config file to /etc/cloudflared/config.yml @@ -118,14 +153,19 @@ class TestServiceMode: self.sysv_cmd("status", success=False) @contextmanager - def run_service(self, tmp_path, config, root=False): + def run_service(self, tmp_path, config, root=False, use_token=False): + args = ["service", "install"] + + if use_token: + args.append(config.get_token()) + try: service = start_cloudflared( - tmp_path, config, cfd_args=["service", "install"], cfd_pre_args=[], capture_output=False, root=root) + tmp_path, config, cfd_args=args, cfd_pre_args=[], capture_output=False, root=root, skip_config_flag=use_token) yield service finally: start_cloudflared( - tmp_path, config, cfd_args=["service", "uninstall"], cfd_pre_args=[], capture_output=False, root=root) + tmp_path, config, cfd_args=["service", "uninstall"], cfd_pre_args=[], capture_output=False, root=root, skip_config_flag=use_token) def launchctl_cmd(self, action, success=True): cmd = subprocess.run( diff --git a/component-tests/util.py b/component-tests/util.py index 3c42d2a7..db8f925d 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -21,9 +21,14 @@ def write_config(directory, config): def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel"], new_process=False, - allow_input=False, capture_output=True, root=False): - config_path = write_config(directory, config.full_config) + allow_input=False, capture_output=True, root=False, skip_config_flag=False): + + config_path = None + if not skip_config_flag: + config_path = write_config(directory, config.full_config) + cmd = cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root) + if new_process: return run_cloudflared_background(cmd, allow_input, capture_output) # By setting check=True, it will raise an exception if the process exits with non-zero exit code @@ -36,7 +41,10 @@ def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root): cmd += ["sudo"] cmd += [config.cloudflared_binary] cmd += cfd_pre_args - cmd += ["--config", str(config_path)] + + if config_path is not None: + cmd += ["--config", str(config_path)] + cmd += cfd_args LOGGER.info(f"Run cmd {cmd} with config {config}") return cmd From d17a61c15b12a0fa79758021566cd54b2805bb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 3 Mar 2022 20:50:35 +0000 Subject: [PATCH 004/238] Release 2022.3.0 --- RELEASE_NOTES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 153685ed..e3118e14 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,12 @@ +2022.3.0 +- 2022-03-02 TUN-5680: Adapt component tests for new service install based on token +- 2022-02-21 TUN-5682: Remove name field from credentials +- 2022-02-21 TUN-5681: Add support for running tunnel using Token +- 2022-02-28 TUN-5824: Update updater no-update-in-shell link +- 2022-02-28 TUN-5823: Warn about legacy flags that are ignored when ingress rules are used +- 2022-02-28 TUN-5737: Support https protocol over unix socket origin +- 2022-02-23 TUN-5679: Add support for service install using Tunnel Token + 2022.2.2 - 2022-02-22 TUN-5754: Allow ingress validate to take plaintext option - 2022-02-17 TUN-5678: Cloudflared uses typed tunnel API From 7220c2c2143f13081bf4ae4fd21b29c25c67540b Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 4 Mar 2022 09:31:40 +0000 Subject: [PATCH 005/238] TUN-5837: Log panic recovery in http2 logic with debug level log --- connection/http2.go | 13 ++++++++----- orchestration/orchestrator_test.go | 9 ++++++--- websocket/websocket.go | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/connection/http2.go b/connection/http2.go index d1e78c1f..b015117c 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -100,7 +100,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { connType := determineHTTP2Type(r) handleMissingRequestParts(connType, r) - respWriter, err := NewHTTP2RespWriter(r, w, connType) + respWriter, err := NewHTTP2RespWriter(r, w, connType, c.log) if err != nil { c.observer.log.Error().Msg(err.Error()) return @@ -163,14 +163,16 @@ type http2RespWriter struct { w http.ResponseWriter flusher http.Flusher shouldFlush bool + log *zerolog.Logger } -func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type) (*http2RespWriter, error) { +func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, log *zerolog.Logger) (*http2RespWriter, error) { flusher, isFlusher := w.(http.Flusher) if !isFlusher { respWriter := &http2RespWriter{ - r: r.Body, - w: w, + r: r.Body, + w: w, + log: log, } respWriter.WriteErrorResponse() return nil, fmt.Errorf("%T doesn't implement http.Flusher", w) @@ -181,6 +183,7 @@ func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type) ( w: w, flusher: flusher, shouldFlush: connType.shouldFlush(), + log: log, }, nil } @@ -239,7 +242,7 @@ func (rp *http2RespWriter) Write(p []byte) (n int, err error) { // Implementer of OriginClient should make sure it doesn't write to the connection after Proxy returns // Register a recover routine just in case. if r := recover(); r != nil { - println(fmt.Sprintf("Recover from http2 response writer panic, error %s", debug.Stack())) + rp.log.Debug().Msgf("Recover from http2 response writer panic, error %s", debug.Stack()) } }() n, err = rp.w.Write(p) diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index b4b19224..4fe6c7b1 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -332,7 +332,8 @@ func proxyHTTP(t *testing.T, originProxy connection.OriginProxy, hostname string require.NoError(t, err) w := httptest.NewRecorder() - respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeHTTP) + log := zerolog.Nop() + respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeHTTP, &log) require.NoError(t, err) err = originProxy.ProxyHTTP(respWriter, req, false) @@ -358,7 +359,8 @@ func proxyTCP(t *testing.T, originProxy connection.OriginProxy, originAddr strin req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", originAddr), reqBody) require.NoError(t, err) - respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeTCP) + log := zerolog.Nop() + respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeTCP, &log) require.NoError(t, err) tcpReq := &connection.TCPRequest{ @@ -578,7 +580,8 @@ func TestPersistentConnection(t *testing.T) { // ProxyHTTP will add Connection, Upgrade and Sec-Websocket-Version headers req.Header.Add("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==") - respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket) + log := zerolog.Nop() + respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket, &log) require.NoError(t, err) err = originProxy.ProxyHTTP(respWriter, req, true) diff --git a/websocket/websocket.go b/websocket/websocket.go index 67c8916b..7240e368 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "runtime/debug" "sync/atomic" "time" @@ -77,7 +78,7 @@ func unidirectionalStream(dst io.Writer, src io.Reader, dir string, status *bidi // exited. In such case, we stop a possible panic from propagating upstream. if r := recover(); r != nil { // We handle such unexpected errors only when we detect that one side of the streaming is done. - log.Debug().Msgf("Handled gracefully error %v in Streaming for %s", r, dir) + log.Debug().Msgf("Gracefully handled error %v in Streaming for %s, error %s", r, dir, debug.Stack()) } } }() From 5c6207debc89ada56a63dfa4e73bf875a91e132d Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 4 Mar 2022 11:35:57 +0000 Subject: [PATCH 006/238] TUN-5696: HTTP/2 Configuration Update --- connection/connection.go | 1 + connection/http2.go | 35 +++++++++++++++++++++++++++++++++++ connection/http2_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/connection/connection.go b/connection/connection.go index 525c1a6e..ad14a85c 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -80,6 +80,7 @@ const ( TypeTCP TypeControlStream TypeHTTP + TypeConfiguration ) // ShouldFlush returns whether this kind of connection should actively flush data diff --git a/connection/http2.go b/connection/http2.go index b015117c..55e73ad3 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -2,6 +2,7 @@ package connection import ( "context" + gojson "encoding/json" "fmt" "io" "net" @@ -23,6 +24,7 @@ const ( InternalTCPProxySrcHeader = "Cf-Cloudflared-Proxy-Src" WebsocketUpgrade = "websocket" ControlStreamUpgrade = "control-stream" + ConfigurationUpdate = "update-configuration" ) var errEdgeConnectionClosed = fmt.Errorf("connection with edge closed") @@ -120,6 +122,13 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { respWriter.WriteErrorResponse() } + case TypeConfiguration: + fmt.Println("TYPE CONFIGURATION?") + if err := c.handleConfigurationUpdate(respWriter, r); err != nil { + c.log.Error().Err(err) + respWriter.WriteErrorResponse() + } + case TypeWebsocket, TypeHTTP: stripWebsocketUpgradeHeader(r) if err := originProxy.ProxyHTTP(respWriter, r, connType == TypeWebsocket); err != nil { @@ -152,6 +161,26 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +// ConfigurationUpdateBody is the representation followed by the edge to send updates to cloudflared. +type ConfigurationUpdateBody struct { + Version int32 `json:"version"` + Config gojson.RawMessage `json:"config"` +} + +func (c *HTTP2Connection) handleConfigurationUpdate(respWriter *http2RespWriter, r *http.Request) error { + var configBody ConfigurationUpdateBody + if err := json.NewDecoder(r.Body).Decode(&configBody); err != nil { + return err + } + resp := c.orchestrator.UpdateConfig(configBody.Version, configBody.Config) + bdy, err := json.Marshal(resp) + if err != nil { + return err + } + _, err = respWriter.Write(bdy) + return err +} + func (c *HTTP2Connection) close() { // Wait for all serve HTTP handlers to return c.activeRequestsWG.Wait() @@ -258,6 +287,8 @@ func (rp *http2RespWriter) Close() error { func determineHTTP2Type(r *http.Request) Type { switch { + case isConfigurationUpdate(r): + return TypeConfiguration case isWebsocketUpgrade(r): return TypeWebsocket case IsTCPStream(r): @@ -291,6 +322,10 @@ func isWebsocketUpgrade(r *http.Request) bool { return r.Header.Get(InternalUpgradeHeader) == WebsocketUpgrade } +func isConfigurationUpdate(r *http.Request) bool { + return r.Header.Get(InternalUpgradeHeader) == ConfigurationUpdate +} + // IsTCPStream discerns if the connection request needs a tcp stream proxy. func IsTCPStream(r *http.Request) bool { return r.Header.Get(InternalTCPProxySrcHeader) != "" diff --git a/connection/http2_test.go b/connection/http2_test.go index c067229c..384d29fb 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -1,6 +1,7 @@ package connection import ( + "bytes" "context" "errors" "fmt" @@ -53,6 +54,41 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { ), edgeConn } +func TestHTTP2ConfigurationSet(t *testing.T) { + http2Conn, edgeConn := newTestHTTP2Connection() + + ctx, cancel := context.WithCancel(context.Background()) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + http2Conn.Serve(ctx) + }() + + edgeHTTP2Conn, err := testTransport.NewClientConn(edgeConn) + require.NoError(t, err) + + endpoint := fmt.Sprintf("http://localhost:8080/ok") + reqBody := []byte(`{ +"version": 2, +"config": {"warp-routing": {"enabled": true}, "originRequest" : {"connectTimeout": 10}, "ingress" : [ {"hostname": "test", "service": "https://localhost:8000" } , {"service": "http_status:404"} ]}} +`) + reader := bytes.NewReader(reqBody) + req, err := http.NewRequestWithContext(ctx, http.MethodPut, endpoint, reader) + require.NoError(t, err) + req.Header.Set(InternalUpgradeHeader, ConfigurationUpdate) + + resp, err := edgeHTTP2Conn.RoundTrip(req) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + bdy, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, `{"lastAppliedVersion":2,"err":null}`, string(bdy)) + cancel() + wg.Wait() + +} + func TestServeHTTP(t *testing.T) { tests := []testRequest{ { From 9d9627f6458b54dd6fa8b0476bc25d3c6cc3ab70 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 4 Mar 2022 16:22:17 +0000 Subject: [PATCH 007/238] TUN-5836: Avoid websocket#Stream function from crashing cloudflared with unexpected memory access --- websocket/websocket.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/websocket/websocket.go b/websocket/websocket.go index 7240e368..ee0bd225 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "github.com/getsentry/raven-go" "github.com/gorilla/websocket" "github.com/rs/zerolog" ) @@ -72,13 +73,25 @@ func unidirectionalStream(dst io.Writer, src io.Reader, dir string, status *bidi // close. In such case, if the other direction did not stop (due to application level stopping, e.g., if a // server/origin listens forever until closure), it may read/write from the underlying ReadWriter (backed by // the Edge<->cloudflared transport) in an unexpected state. - - if status.isAnyDone() { - // Because of this, we set this recover() logic, which kicks-in *only* if any stream is known to have - // exited. In such case, we stop a possible panic from propagating upstream. - if r := recover(); r != nil { + // Because of this, we set this recover() logic. + if r := recover(); r != nil { + if status.isAnyDone() { // We handle such unexpected errors only when we detect that one side of the streaming is done. log.Debug().Msgf("Gracefully handled error %v in Streaming for %s, error %s", r, dir, debug.Stack()) + } else { + // Otherwise, this is unexpected, but we prevent the program from crashing anyway. + log.Warn().Msgf("Gracefully handled unexpected error %v in Streaming for %s, error %s", r, dir, debug.Stack()) + + tags := make(map[string]string) + tags["root"] = "websocket.stream" + tags["dir"] = dir + switch rval := r.(type) { + case error: + raven.CaptureError(rval, tags) + default: + rvalStr := fmt.Sprint(rval) + raven.CaptureMessage(rvalStr, tags) + } } } }() From 3aebaaad012e7220e1120bfd17dc0613a57025e2 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Sat, 5 Mar 2022 18:05:07 +0000 Subject: [PATCH 008/238] TUN-5836: QUIC transport no longer sets body to nil in any condition Setting the body to nil was rendering cloudflared to crashing with a SIGSEGV in the odd case where the hostname accessed maps to a TCP origin (e.g. SSH/RDP/...) but the eyeball sends a plain HTTP request that does not go through cloudflared access (thus not wrapped in websocket as it should). Instead, QUIC transport now sets http.noBody in that condition, which deals with the situation gracefully. --- connection/quic.go | 2 +- connection/quic_test.go | 2 +- proxy/proxy_test.go | 27 +++++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 1b9f2e55..eb2529b8 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -342,7 +342,7 @@ func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadClose // * there is no transfer-encoding=chunked already set. // So, if transfer cannot be chunked and content length is 0, we dont set a request body. if !isWebsocket && !isTransferEncodingChunked(req) && req.ContentLength == 0 { - req.Body = nil + req.Body = http.NoBody } stripWebsocketUpgradeHeader(req) return req, err diff --git a/connection/quic_test.go b/connection/quic_test.go index 9763ae33..0f42b08d 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -345,7 +345,7 @@ func TestBuildHTTPRequest(t *testing.T) { }, ContentLength: 0, Host: "cf.host", - Body: nil, + Body: http.NoBody, }, body: io.NopCloser(&bytes.Buffer{}), }, diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index db8747f7..7bc3e21b 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -60,6 +60,11 @@ func (w *mockHTTPRespWriter) Read(data []byte) (int, error) { return 0, fmt.Errorf("mockHTTPRespWriter doesn't implement io.Reader") } +// respHeaders is a test function to read respHeaders +func (w *mockHTTPRespWriter) headers() http.Header { + return w.Header() +} + type mockWSRespWriter struct { *mockHTTPRespWriter writeNotification chan []byte @@ -554,6 +559,24 @@ func TestConnections(t *testing.T) { }, }, }, + { + // Send (unexpected) HTTP when origin expects WS (to unwrap for raw TCP) + name: "http-(ws)tcp proxy", + args: args{ + ingressServiceScheme: "tcp://", + originService: runEchoTCPService, + eyeballResponseWriter: newMockHTTPRespWriter(), + eyeballRequestBody: http.NoBody, + connectionType: connection.TypeHTTP, + requestHeaders: map[string][]string{ + "Cf-Cloudflared-Proxy-Src": {"non-blank-value"}, + }, + }, + want: want{ + message: []byte{}, + headers: map[string][]string{}, + }, + }, { name: "tcp-tcp proxy without warpRoutingService enabled", args: args{ @@ -650,8 +673,8 @@ func TestConnections(t *testing.T) { }() } if test.args.connectionType == connection.TypeTCP { - rws := connection.NewHTTPResponseReadWriterAcker(respWriter, req) - err = proxy.ProxyTCP(ctx, rws, &connection.TCPRequest{Dest: dest}) + rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) + err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { err = proxy.ProxyHTTP(respWriter, req, test.args.connectionType == connection.TypeWebsocket) } From f44e496dd92c7dfb2d68c2b93654892bd9680a94 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 7 Mar 2022 15:16:47 +0000 Subject: [PATCH 009/238] Release 2022.3.1 --- RELEASE_NOTES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index e3118e14..3cd05d6f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,9 @@ +2022.3.1 +- 2022-03-04 TUN-5837: Log panic recovery in http2 logic with debug level log +- 2022-03-04 TUN-5696: HTTP/2 Configuration Update +- 2022-03-04 TUN-5836: Avoid websocket#Stream function from crashing cloudflared with unexpected memory access +- 2022-03-05 TUN-5836: QUIC transport no longer sets body to nil in any condition + 2022.3.0 - 2022-03-02 TUN-5680: Adapt component tests for new service install based on token - 2022-02-21 TUN-5682: Remove name field from credentials From 0899d6a13619cba03bc0c933ef0c7032c2393311 Mon Sep 17 00:00:00 2001 From: emmanuel Date: Sat, 5 Feb 2022 15:39:29 +0000 Subject: [PATCH 010/238] CC-796: Remove dependency on unsupported version of go-oidc --- go.mod | 6 +- go.sum | 10 +- sshgen/sshgen.go | 18 +- sshgen/sshgen_test.go | 36 +- token/token.go | 17 +- vendor/github.com/coreos/go-oidc/LICENSE | 202 ----------- vendor/github.com/coreos/go-oidc/NOTICE | 5 - .../github.com/coreos/go-oidc/jose/claims.go | 126 ------- vendor/github.com/coreos/go-oidc/jose/doc.go | 2 - vendor/github.com/coreos/go-oidc/jose/jose.go | 112 ------ vendor/github.com/coreos/go-oidc/jose/jwk.go | 135 ------- vendor/github.com/coreos/go-oidc/jose/jws.go | 51 --- vendor/github.com/coreos/go-oidc/jose/jwt.go | 82 ----- vendor/github.com/coreos/go-oidc/jose/sig.go | 24 -- .../github.com/coreos/go-oidc/jose/sig_rsa.go | 67 ---- vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml | 6 +- vendor/gopkg.in/coreos/go-oidc.v2/oidc.go | 34 +- vendor/gopkg.in/coreos/go-oidc.v2/verify.go | 11 +- vendor/gopkg.in/square/go-jose.v2/.gitignore | 1 + vendor/gopkg.in/square/go-jose.v2/.travis.yml | 9 +- .../square/go-jose.v2/cipher/cbc_hmac.go | 2 +- vendor/gopkg.in/square/go-jose.v2/crypter.go | 1 + vendor/gopkg.in/square/go-jose.v2/encoding.go | 14 +- .../gopkg.in/square/go-jose.v2/json/decode.go | 52 ++- .../gopkg.in/square/go-jose.v2/json/stream.go | 7 +- vendor/gopkg.in/square/go-jose.v2/jwk.go | 174 ++++++++- vendor/gopkg.in/square/go-jose.v2/jws.go | 6 +- .../gopkg.in/square/go-jose.v2/jwt/builder.go | 334 ++++++++++++++++++ .../gopkg.in/square/go-jose.v2/jwt/claims.go | 121 +++++++ vendor/gopkg.in/square/go-jose.v2/jwt/doc.go | 22 ++ .../gopkg.in/square/go-jose.v2/jwt/errors.go | 53 +++ vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go | 169 +++++++++ .../square/go-jose.v2/jwt/validation.go | 114 ++++++ vendor/gopkg.in/square/go-jose.v2/opaque.go | 2 +- vendor/gopkg.in/square/go-jose.v2/shared.go | 6 +- vendor/gopkg.in/square/go-jose.v2/signing.go | 12 +- vendor/modules.txt | 8 +- 37 files changed, 1146 insertions(+), 905 deletions(-) delete mode 100644 vendor/github.com/coreos/go-oidc/LICENSE delete mode 100644 vendor/github.com/coreos/go-oidc/NOTICE delete mode 100644 vendor/github.com/coreos/go-oidc/jose/claims.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/doc.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jose.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jwk.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jws.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jwt.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/sig.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/sig_rsa.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/builder.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/claims.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/doc.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/errors.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/validation.go diff --git a/go.mod b/go.mod index 59b8ec7c..83328b33 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 - github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -51,14 +50,15 @@ require ( golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.43.0 // indirect - gopkg.in/coreos/go-oidc.v2 v2.1.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/square/go-jose.v2 v2.4.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect zombiezen.com/go/capnproto2 v2.18.0+incompatible ) +require gopkg.in/coreos/go-oidc.v2 v2.2.1 + require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect diff --git a/go.sum b/go.sum index ed534dcb..44c59257 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,6 @@ github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/coredns v1.8.7 h1:wVMjAnyFnY7Mc18AFO+9qbGD6ODPtdVUIlzoWrHr3hk= github.com/coredns/coredns v1.8.7/go.mod h1:bFmbgEfeRz5aizL2VsQ5LRlsvJuXWkgG/MWG9zxqjVM= -github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 h1:7CNPV0LWRCa1FNmqg700pbXhzvmoaXKyfxWRkjRym7Q= -github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1052,15 +1050,15 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/coreos/go-oidc.v2 v2.1.0 h1:E8PjVFdj/SLDKB0hvb70KTbMbYVHjqztiQdSkIg8E+I= -gopkg.in/coreos/go-oidc.v2 v2.1.0/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= +gopkg.in/coreos/go-oidc.v2 v2.2.1 h1:MY5SZClJ7vhjKfr64a4nHAOV/c3WH2gB9BMrR64J1Mc= +gopkg.in/coreos/go-oidc.v2 v2.2.1/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A= -gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/sshgen/sshgen.go b/sshgen/sshgen.go index 9fba2053..27e73715 100644 --- a/sshgen/sshgen.go +++ b/sshgen/sshgen.go @@ -15,10 +15,10 @@ import ( "net/url" "time" - "github.com/coreos/go-oidc/jose" homedir "github.com/mitchellh/go-homedir" "github.com/pkg/errors" gossh "golang.org/x/crypto/ssh" + "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -87,37 +87,33 @@ func SignCert(token, pubKey string) (string, error) { return "", errors.New("invalid token") } - jwt, err := jose.ParseJWT(token) + parsedToken, err := jwt.ParseSigned(token) if err != nil { return "", errors.Wrap(err, "failed to parse JWT") } - claims, err := jwt.Claims() + claims := jwt.Claims{} + err = parsedToken.UnsafeClaimsWithoutVerification(&claims) if err != nil { return "", errors.Wrap(err, "failed to retrieve JWT claims") } - issuer, _, err := claims.StringClaim("iss") - if err != nil { - return "", errors.Wrap(err, "failed to retrieve JWT iss") - } - buf, err := json.Marshal(&signPayload{ PublicKey: pubKey, JWT: token, - Issuer: issuer, + Issuer: claims.Issuer, }) if err != nil { return "", errors.Wrap(err, "failed to marshal signPayload") } var res *http.Response if mockRequest != nil { - res, err = mockRequest(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = mockRequest(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } else { client := http.Client{ Timeout: 10 * time.Second, } - res, err = client.Post(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = client.Post(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } if err != nil { diff --git a/sshgen/sshgen_test.go b/sshgen/sshgen_test.go index 99ac8021..c2716f59 100644 --- a/sshgen/sshgen_test.go +++ b/sshgen/sshgen_test.go @@ -4,8 +4,6 @@ package sshgen import ( - "crypto/rand" - "crypto/rsa" "encoding/json" "fmt" "io" @@ -18,8 +16,9 @@ import ( "testing" "time" - "github.com/coreos/go-oidc/jose" "github.com/stretchr/testify/assert" + "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -97,22 +96,25 @@ func TestCertGenSuccess(t *testing.T) { } func tokenGenerator() string { - iat := time.Now().Unix() - exp := time.Now().Add(time.Minute * 5).Unix() - claims := jose.Claims{} - claims.Add("aud", audTest) - claims.Add("iat", iat) - claims.Add("nonce", nonceTest) - claims.Add("exp", exp) + iat := time.Now() + exp := time.Now().Add(time.Minute * 5) - k, err := rsa.GenerateKey(rand.Reader, 512) + claims := jwt.Claims{ + Audience: jwt.Audience{audTest}, + IssuedAt: jwt.NewNumericDate(iat), + Expiry: jwt.NewNumericDate(exp), + } + + key := []byte("secret") + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT")) if err != nil { - return "" + panic(err) } - signer := jose.NewSignerRSA("asdf", *k) - token, terr := jose.NewSignedJWT(claims, signer) - if terr != nil { - return "" + + signedToken, err := jwt.Signed(signer).Claims(claims).CompactSerialize() + if err != nil { + panic(err) } - return token.Encode() + + return signedToken } diff --git a/token/token.go b/token/token.go index d4f237a9..abccf257 100644 --- a/token/token.go +++ b/token/token.go @@ -13,9 +13,9 @@ import ( "syscall" "time" - "github.com/coreos/go-oidc/jose" "github.com/pkg/errors" "github.com/rs/zerolog" + "gopkg.in/square/go-jose.v2" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/retry" @@ -342,7 +342,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.Payload, &payload) + err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) if err != nil { return "", err } @@ -351,7 +351,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { err := os.Remove(path) return "", err } - return token.Encode(), nil + return token.CompactSerialize() } func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { @@ -364,7 +364,7 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.Payload, &payload) + err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) if err != nil { return "", err } @@ -373,22 +373,21 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { err := os.Remove(path) return "", err } - return token.Encode(), nil + return token.CompactSerialize() } // GetTokenIfExists will return the token from local storage if it exists and not expired -func getTokenIfExists(path string) (*jose.JWT, error) { +func getTokenIfExists(path string) (*jose.JSONWebSignature, error) { content, err := ioutil.ReadFile(path) if err != nil { return nil, err } - token, err := jose.ParseJWT(string(content)) + token, err := jose.ParseSigned(string(content)) if err != nil { return nil, err } - - return &token, nil + return token, nil } // RemoveTokenIfExists removes the a token from local storage if it exists diff --git a/vendor/github.com/coreos/go-oidc/LICENSE b/vendor/github.com/coreos/go-oidc/LICENSE deleted file mode 100644 index e06d2081..00000000 --- a/vendor/github.com/coreos/go-oidc/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -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/coreos/go-oidc/NOTICE b/vendor/github.com/coreos/go-oidc/NOTICE deleted file mode 100644 index b39ddfa5..00000000 --- a/vendor/github.com/coreos/go-oidc/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2014 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-oidc/jose/claims.go b/vendor/github.com/coreos/go-oidc/jose/claims.go deleted file mode 100644 index 8b48bfd2..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/claims.go +++ /dev/null @@ -1,126 +0,0 @@ -package jose - -import ( - "encoding/json" - "fmt" - "math" - "time" -) - -type Claims map[string]interface{} - -func (c Claims) Add(name string, value interface{}) { - c[name] = value -} - -func (c Claims) StringClaim(name string) (string, bool, error) { - cl, ok := c[name] - if !ok { - return "", false, nil - } - - v, ok := cl.(string) - if !ok { - return "", false, fmt.Errorf("unable to parse claim as string: %v", name) - } - - return v, true, nil -} - -func (c Claims) StringsClaim(name string) ([]string, bool, error) { - cl, ok := c[name] - if !ok { - return nil, false, nil - } - - if v, ok := cl.([]string); ok { - return v, true, nil - } - - // When unmarshaled, []string will become []interface{}. - if v, ok := cl.([]interface{}); ok { - var ret []string - for _, vv := range v { - str, ok := vv.(string) - if !ok { - return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) - } - ret = append(ret, str) - } - return ret, true, nil - } - - return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) -} - -func (c Claims) Int64Claim(name string) (int64, bool, error) { - cl, ok := c[name] - if !ok { - return 0, false, nil - } - - v, ok := cl.(int64) - if !ok { - vf, ok := cl.(float64) - if !ok { - return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name) - } - v = int64(vf) - } - - return v, true, nil -} - -func (c Claims) Float64Claim(name string) (float64, bool, error) { - cl, ok := c[name] - if !ok { - return 0, false, nil - } - - v, ok := cl.(float64) - if !ok { - vi, ok := cl.(int64) - if !ok { - return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name) - } - v = float64(vi) - } - - return v, true, nil -} - -func (c Claims) TimeClaim(name string) (time.Time, bool, error) { - v, ok, err := c.Float64Claim(name) - if !ok || err != nil { - return time.Time{}, ok, err - } - - s := math.Trunc(v) - ns := (v - s) * math.Pow(10, 9) - return time.Unix(int64(s), int64(ns)).UTC(), true, nil -} - -func decodeClaims(payload []byte) (Claims, error) { - var c Claims - if err := json.Unmarshal(payload, &c); err != nil { - return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err) - } - return c, nil -} - -func marshalClaims(c Claims) ([]byte, error) { - b, err := json.Marshal(c) - if err != nil { - return nil, err - } - return b, nil -} - -func encodeClaims(c Claims) (string, error) { - b, err := marshalClaims(c) - if err != nil { - return "", err - } - - return encodeSegment(b), nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/doc.go b/vendor/github.com/coreos/go-oidc/jose/doc.go deleted file mode 100644 index b5e13217..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead. -package jose diff --git a/vendor/github.com/coreos/go-oidc/jose/jose.go b/vendor/github.com/coreos/go-oidc/jose/jose.go deleted file mode 100644 index 62099265..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jose.go +++ /dev/null @@ -1,112 +0,0 @@ -package jose - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "strings" -) - -const ( - HeaderMediaType = "typ" - HeaderKeyAlgorithm = "alg" - HeaderKeyID = "kid" -) - -const ( - // Encryption Algorithm Header Parameter Values for JWS - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6 - AlgHS256 = "HS256" - AlgHS384 = "HS384" - AlgHS512 = "HS512" - AlgRS256 = "RS256" - AlgRS384 = "RS384" - AlgRS512 = "RS512" - AlgES256 = "ES256" - AlgES384 = "ES384" - AlgES512 = "ES512" - AlgPS256 = "PS256" - AlgPS384 = "PS384" - AlgPS512 = "PS512" - AlgNone = "none" -) - -const ( - // Algorithm Header Parameter Values for JWE - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1 - AlgRSA15 = "RSA1_5" - AlgRSAOAEP = "RSA-OAEP" - AlgRSAOAEP256 = "RSA-OAEP-256" - AlgA128KW = "A128KW" - AlgA192KW = "A192KW" - AlgA256KW = "A256KW" - AlgDir = "dir" - AlgECDHES = "ECDH-ES" - AlgECDHESA128KW = "ECDH-ES+A128KW" - AlgECDHESA192KW = "ECDH-ES+A192KW" - AlgECDHESA256KW = "ECDH-ES+A256KW" - AlgA128GCMKW = "A128GCMKW" - AlgA192GCMKW = "A192GCMKW" - AlgA256GCMKW = "A256GCMKW" - AlgPBES2HS256A128KW = "PBES2-HS256+A128KW" - AlgPBES2HS384A192KW = "PBES2-HS384+A192KW" - AlgPBES2HS512A256KW = "PBES2-HS512+A256KW" -) - -const ( - // Encryption Algorithm Header Parameter Values for JWE - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22 - EncA128CBCHS256 = "A128CBC-HS256" - EncA128CBCHS384 = "A128CBC-HS384" - EncA256CBCHS512 = "A256CBC-HS512" - EncA128GCM = "A128GCM" - EncA192GCM = "A192GCM" - EncA256GCM = "A256GCM" -) - -type JOSEHeader map[string]string - -func (j JOSEHeader) Validate() error { - if _, exists := j[HeaderKeyAlgorithm]; !exists { - return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm) - } - - return nil -} - -func decodeHeader(seg string) (JOSEHeader, error) { - b, err := decodeSegment(seg) - if err != nil { - return nil, err - } - - var h JOSEHeader - err = json.Unmarshal(b, &h) - if err != nil { - return nil, err - } - - return h, nil -} - -func encodeHeader(h JOSEHeader) (string, error) { - b, err := json.Marshal(h) - if err != nil { - return "", err - } - - return encodeSegment(b), nil -} - -// Decode JWT specific base64url encoding with padding stripped -func decodeSegment(seg string) ([]byte, error) { - if l := len(seg) % 4; l != 0 { - seg += strings.Repeat("=", 4-l) - } - return base64.URLEncoding.DecodeString(seg) -} - -// Encode JWT specific base64url encoding with padding stripped -func encodeSegment(seg []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go deleted file mode 100644 index 119f073f..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jwk.go +++ /dev/null @@ -1,135 +0,0 @@ -package jose - -import ( - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/json" - "math/big" - "strings" -) - -// JSON Web Key -// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5 -type JWK struct { - ID string - Type string - Alg string - Use string - Exponent int - Modulus *big.Int - Secret []byte -} - -type jwkJSON struct { - ID string `json:"kid"` - Type string `json:"kty"` - Alg string `json:"alg"` - Use string `json:"use"` - Exponent string `json:"e"` - Modulus string `json:"n"` -} - -func (j *JWK) MarshalJSON() ([]byte, error) { - t := jwkJSON{ - ID: j.ID, - Type: j.Type, - Alg: j.Alg, - Use: j.Use, - Exponent: encodeExponent(j.Exponent), - Modulus: encodeModulus(j.Modulus), - } - - return json.Marshal(&t) -} - -func (j *JWK) UnmarshalJSON(data []byte) error { - var t jwkJSON - err := json.Unmarshal(data, &t) - if err != nil { - return err - } - - e, err := decodeExponent(t.Exponent) - if err != nil { - return err - } - - n, err := decodeModulus(t.Modulus) - if err != nil { - return err - } - - j.ID = t.ID - j.Type = t.Type - j.Alg = t.Alg - j.Use = t.Use - j.Exponent = e - j.Modulus = n - - return nil -} - -type JWKSet struct { - Keys []JWK `json:"keys"` -} - -func decodeExponent(e string) (int, error) { - decE, err := decodeBase64URLPaddingOptional(e) - if err != nil { - return 0, err - } - var eBytes []byte - if len(decE) < 8 { - eBytes = make([]byte, 8-len(decE), 8) - eBytes = append(eBytes, decE...) - } else { - eBytes = decE - } - eReader := bytes.NewReader(eBytes) - var E uint64 - err = binary.Read(eReader, binary.BigEndian, &E) - if err != nil { - return 0, err - } - return int(E), nil -} - -func encodeExponent(e int) string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(e)) - var idx int - for ; idx < 8; idx++ { - if b[idx] != 0x0 { - break - } - } - return base64.RawURLEncoding.EncodeToString(b[idx:]) -} - -// Turns a URL encoded modulus of a key into a big int. -func decodeModulus(n string) (*big.Int, error) { - decN, err := decodeBase64URLPaddingOptional(n) - if err != nil { - return nil, err - } - N := big.NewInt(0) - N.SetBytes(decN) - return N, nil -} - -func encodeModulus(n *big.Int) string { - return base64.RawURLEncoding.EncodeToString(n.Bytes()) -} - -// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not. -// The stdlib version currently doesn't handle this. -// We can get rid of this is if this bug: -// https://github.com/golang/go/issues/4237 -// ever closes. -func decodeBase64URLPaddingOptional(e string) ([]byte, error) { - if m := len(e) % 4; m != 0 { - e += strings.Repeat("=", 4-m) - } - return base64.URLEncoding.DecodeString(e) -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jws.go b/vendor/github.com/coreos/go-oidc/jose/jws.go deleted file mode 100644 index 1049ece8..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jws.go +++ /dev/null @@ -1,51 +0,0 @@ -package jose - -import ( - "fmt" - "strings" -) - -type JWS struct { - RawHeader string - Header JOSEHeader - RawPayload string - Payload []byte - Signature []byte -} - -// Given a raw encoded JWS token parses it and verifies the structure. -func ParseJWS(raw string) (JWS, error) { - parts := strings.Split(raw, ".") - if len(parts) != 3 { - return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts)) - } - - rawSig := parts[2] - jws := JWS{ - RawHeader: parts[0], - RawPayload: parts[1], - } - - header, err := decodeHeader(jws.RawHeader) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err) - } - if err = header.Validate(); err != nil { - return JWS{}, fmt.Errorf("malformed JWS, %s", err) - } - jws.Header = header - - payload, err := decodeSegment(jws.RawPayload) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err) - } - jws.Payload = payload - - sig, err := decodeSegment(rawSig) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err) - } - jws.Signature = sig - - return jws, nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt.go b/vendor/github.com/coreos/go-oidc/jose/jwt.go deleted file mode 100644 index 3b3e9634..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jwt.go +++ /dev/null @@ -1,82 +0,0 @@ -package jose - -import "strings" - -type JWT JWS - -func ParseJWT(token string) (jwt JWT, err error) { - jws, err := ParseJWS(token) - if err != nil { - return - } - - return JWT(jws), nil -} - -func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) { - jwt = JWT{} - - jwt.Header = header - jwt.Header[HeaderMediaType] = "JWT" - - claimBytes, err := marshalClaims(claims) - if err != nil { - return - } - jwt.Payload = claimBytes - - eh, err := encodeHeader(header) - if err != nil { - return - } - jwt.RawHeader = eh - - ec, err := encodeClaims(claims) - if err != nil { - return - } - jwt.RawPayload = ec - - return -} - -func (j *JWT) KeyID() (string, bool) { - kID, ok := j.Header[HeaderKeyID] - return kID, ok -} - -func (j *JWT) Claims() (Claims, error) { - return decodeClaims(j.Payload) -} - -// Encoded data part of the token which may be signed. -func (j *JWT) Data() string { - return strings.Join([]string{j.RawHeader, j.RawPayload}, ".") -} - -// Full encoded JWT token string in format: header.claims.signature -func (j *JWT) Encode() string { - d := j.Data() - s := encodeSegment(j.Signature) - return strings.Join([]string{d, s}, ".") -} - -func NewSignedJWT(claims Claims, s Signer) (*JWT, error) { - header := JOSEHeader{ - HeaderKeyAlgorithm: s.Alg(), - HeaderKeyID: s.ID(), - } - - jwt, err := NewJWT(header, claims) - if err != nil { - return nil, err - } - - sig, err := s.Sign([]byte(jwt.Data())) - if err != nil { - return nil, err - } - jwt.Signature = sig - - return &jwt, nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig.go b/vendor/github.com/coreos/go-oidc/jose/sig.go deleted file mode 100644 index 7b2b253c..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/sig.go +++ /dev/null @@ -1,24 +0,0 @@ -package jose - -import ( - "fmt" -) - -type Verifier interface { - ID() string - Alg() string - Verify(sig []byte, data []byte) error -} - -type Signer interface { - Verifier - Sign(data []byte) (sig []byte, err error) -} - -func NewVerifier(jwk JWK) (Verifier, error) { - if jwk.Type != "RSA" { - return nil, fmt.Errorf("unsupported key type %q", jwk.Type) - } - - return NewVerifierRSA(jwk) -} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go deleted file mode 100644 index 004e45dd..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go +++ /dev/null @@ -1,67 +0,0 @@ -package jose - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "fmt" -) - -type VerifierRSA struct { - KeyID string - Hash crypto.Hash - PublicKey rsa.PublicKey -} - -type SignerRSA struct { - PrivateKey rsa.PrivateKey - VerifierRSA -} - -func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) { - if jwk.Alg != "" && jwk.Alg != "RS256" { - return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg) - } - - v := VerifierRSA{ - KeyID: jwk.ID, - PublicKey: rsa.PublicKey{ - N: jwk.Modulus, - E: jwk.Exponent, - }, - Hash: crypto.SHA256, - } - - return &v, nil -} - -func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA { - return &SignerRSA{ - PrivateKey: key, - VerifierRSA: VerifierRSA{ - KeyID: kid, - PublicKey: key.PublicKey, - Hash: crypto.SHA256, - }, - } -} - -func (v *VerifierRSA) ID() string { - return v.KeyID -} - -func (v *VerifierRSA) Alg() string { - return "RS256" -} - -func (v *VerifierRSA) Verify(sig []byte, data []byte) error { - h := v.Hash.New() - h.Write(data) - return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig) -} - -func (s *SignerRSA) Sign(data []byte) ([]byte, error) { - h := s.Hash.New() - h.Write(data) - return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil)) -} diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml index 6ff9dd96..3fddaaac 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml +++ b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml @@ -1,13 +1,13 @@ language: go go: - - "1.9" - - "1.10" + - "1.12" + - "1.13" install: - go get -v -t github.com/coreos/go-oidc/... - go get golang.org/x/tools/cmd/cover - - go get github.com/golang/lint/golint + - go get golang.org/x/lint/golint script: - ./test diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go index 508b39d3..b39cb515 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go @@ -69,6 +69,7 @@ type Provider struct { authURL string tokenURL string userInfoURL string + algorithms []string // Raw claims returned by the server. rawClaims []byte @@ -82,11 +83,27 @@ type cachedKeys struct { } type providerJSON struct { - Issuer string `json:"issuer"` - AuthURL string `json:"authorization_endpoint"` - TokenURL string `json:"token_endpoint"` - JWKSURL string `json:"jwks_uri"` - UserInfoURL string `json:"userinfo_endpoint"` + Issuer string `json:"issuer"` + AuthURL string `json:"authorization_endpoint"` + TokenURL string `json:"token_endpoint"` + JWKSURL string `json:"jwks_uri"` + UserInfoURL string `json:"userinfo_endpoint"` + Algorithms []string `json:"id_token_signing_alg_values_supported"` +} + +// supportedAlgorithms is a list of algorithms explicitly supported by this +// package. If a provider supports other algorithms, such as HS256 or none, +// those values won't be passed to the IDTokenVerifier. +var supportedAlgorithms = map[string]bool{ + RS256: true, + RS384: true, + RS512: true, + ES256: true, + ES384: true, + ES512: true, + PS256: true, + PS384: true, + PS512: true, } // NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. @@ -123,11 +140,18 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { if p.Issuer != issuer { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } + var algs []string + for _, a := range p.Algorithms { + if supportedAlgorithms[a] { + algs = append(algs, a) + } + } return &Provider{ issuer: p.Issuer, authURL: p.AuthURL, tokenURL: p.TokenURL, userInfoURL: p.UserInfoURL, + algorithms: algs, rawClaims: body, remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), }, nil diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go index ff7555db..d43f0662 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go @@ -79,7 +79,9 @@ type Config struct { ClientID string // If specified, only this set of algorithms may be used to sign the JWT. // - // Since many providers only support RS256, SupportedSigningAlgs defaults to this value. + // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this + // defaults to the set of algorithms the provider supports. Otherwise this values + // defaults to RS256. SupportedSigningAlgs []string // If true, no ClientID check performed. Must be true if ClientID field is empty. @@ -105,6 +107,13 @@ type Config struct { // The returned IDTokenVerifier is tied to the Provider's context and its behavior is // undefined once the Provider's context is canceled. func (p *Provider) Verifier(config *Config) *IDTokenVerifier { + if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { + // Make a copy so we don't modify the config values. + cp := &Config{} + *cp = *config + cp.SupportedSigningAlgs = p.algorithms + config = cp + } return NewVerifier(p.issuer, p.remoteKeySet, config) } diff --git a/vendor/gopkg.in/square/go-jose.v2/.gitignore b/vendor/gopkg.in/square/go-jose.v2/.gitignore index 5b4d73b6..95a85158 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.gitignore +++ b/vendor/gopkg.in/square/go-jose.v2/.gitignore @@ -5,3 +5,4 @@ *.pem *.cov jose-util/jose-util +jose-util.t.err \ No newline at end of file diff --git a/vendor/gopkg.in/square/go-jose.v2/.travis.yml b/vendor/gopkg.in/square/go-jose.v2/.travis.yml index 9ab2abf6..391b99a4 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.travis.yml +++ b/vendor/gopkg.in/square/go-jose.v2/.travis.yml @@ -8,8 +8,8 @@ matrix: - go: tip go: -- '1.11.x' -- '1.12.x' +- '1.14.x' +- '1.15.x' - tip go_import_path: gopkg.in/square/go-jose.v2 @@ -26,6 +26,8 @@ before_install: - go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls - go get github.com/stretchr/testify/assert +- go get github.com/stretchr/testify/require +- go get github.com/google/go-cmp/cmp - go get golang.org/x/tools/cmd/cover || true - go get code.google.com/p/go.tools/cmd/cover || true - pip install cram --user @@ -35,10 +37,9 @@ script: - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov - go test ./json -v # no coverage for forked encoding/json package -- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t +- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util - cd .. after_success: - gocovmerge *.cov */*.cov > merged.coverprofile - $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci - diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go index 126b85ce..f6465c04 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go @@ -150,7 +150,7 @@ func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { return hmac.Sum(nil)[:ctx.authtagBytes] } -// resize ensures the the given slice has a capacity of at least n bytes. +// resize ensures that the given slice has a capacity of at least n bytes. // If the capacity of the slice is less than n, a new slice is allocated // and the existing data will be copied. func resize(in []byte, n uint64) (head, tail []byte) { diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go index d24cabf6..be7433e2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go @@ -216,6 +216,7 @@ func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *Encrypter if opts != nil { encrypter.compressionAlg = opts.Compression + encrypter.extraHeaders = opts.ExtraHeaders } for _, recipient := range rcpts { diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go index b9687c64..70f7385c 100644 --- a/vendor/gopkg.in/square/go-jose.v2/encoding.go +++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go @@ -23,13 +23,12 @@ import ( "encoding/binary" "io" "math/big" - "regexp" + "strings" + "unicode" "gopkg.in/square/go-jose.v2/json" ) -var stripWhitespaceRegex = regexp.MustCompile("\\s") - // Helper function to serialize known-good objects. // Precondition: value is not a nil pointer. func mustSerializeJSON(value interface{}) []byte { @@ -56,7 +55,14 @@ func mustSerializeJSON(value interface{}) []byte { // Strip all newlines and whitespace func stripWhitespace(data string) string { - return stripWhitespaceRegex.ReplaceAllString(data, "") + buf := strings.Builder{} + buf.Grow(len(data)) + for _, r := range data { + if !unicode.IsSpace(r) { + buf.WriteRune(r) + } + } + return buf.String() } // Perform compression based on algorithm diff --git a/vendor/gopkg.in/square/go-jose.v2/json/decode.go b/vendor/gopkg.in/square/go-jose.v2/json/decode.go index 37457e5a..4dbc4146 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/decode.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/decode.go @@ -13,6 +13,7 @@ import ( "encoding/base64" "errors" "fmt" + "math" "reflect" "runtime" "strconv" @@ -245,6 +246,18 @@ func isValidNumber(s string) bool { return s == "" } +type NumberUnmarshalType int + +const ( + // unmarshal a JSON number into an interface{} as a float64 + UnmarshalFloat NumberUnmarshalType = iota + // unmarshal a JSON number into an interface{} as a `json.Number` + UnmarshalJSONNumber + // unmarshal a JSON number into an interface{} as a int64 + // if value is an integer otherwise float64 + UnmarshalIntOrFloat +) + // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -252,7 +265,7 @@ type decodeState struct { scan scanner nextscan scanner // for calls to nextValue savedError error - useNumber bool + numberType NumberUnmarshalType } // errPhase is used for errors that should not happen unless @@ -723,17 +736,38 @@ func (d *decodeState) literal(v reflect.Value) { d.literalStore(d.data[start:d.off], v, false) } -// convertNumber converts the number literal s to a float64 or a Number -// depending on the setting of d.useNumber. +// convertNumber converts the number literal s to a float64, int64 or a Number +// depending on d.numberDecodeType. func (d *decodeState) convertNumber(s string) (interface{}, error) { - if d.useNumber { + switch d.numberType { + + case UnmarshalJSONNumber: return Number(s), nil + case UnmarshalIntOrFloat: + v, err := strconv.ParseInt(s, 10, 64) + if err == nil { + return v, nil + } + + // tries to parse integer number in scientific notation + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + + // if it has no decimal value use int64 + if fi, fd := math.Modf(f); fd == 0.0 { + return int64(fi), nil + } + return f, nil + default: + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil } - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - return f, nil + } var numberType = reflect.TypeOf(Number("")) diff --git a/vendor/gopkg.in/square/go-jose.v2/json/stream.go b/vendor/gopkg.in/square/go-jose.v2/json/stream.go index 8ddcf4d2..9b2b926b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/stream.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/stream.go @@ -31,9 +31,14 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } +// Deprecated: Use `SetNumberType` instead // UseNumber causes the Decoder to unmarshal a number into an interface{} as a // Number instead of as a float64. -func (dec *Decoder) UseNumber() { dec.d.useNumber = true } +func (dec *Decoder) UseNumber() { dec.d.numberType = UnmarshalJSONNumber } + +// SetNumberType causes the Decoder to unmarshal a number into an interface{} as a +// Number, float64 or int64 depending on `t` enum value. +func (dec *Decoder) SetNumberType(t NumberUnmarshalType) { dec.d.numberType = t } // Decode reads the next JSON-encoded value from its // input and stores it in the value pointed to by v. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk.go b/vendor/gopkg.in/square/go-jose.v2/jwk.go index 936a0202..222e260c 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk.go @@ -17,15 +17,20 @@ package jose import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/sha1" + "crypto/sha256" "crypto/x509" "encoding/base64" + "encoding/hex" "errors" "fmt" "math/big" + "net/url" "reflect" "strings" @@ -57,16 +62,31 @@ type rawJSONWebKey struct { Dq *byteBuffer `json:"dq,omitempty"` Qi *byteBuffer `json:"qi,omitempty"` // Certificates - X5c []string `json:"x5c,omitempty"` + X5c []string `json:"x5c,omitempty"` + X5u *url.URL `json:"x5u,omitempty"` + X5tSHA1 string `json:"x5t,omitempty"` + X5tSHA256 string `json:"x5t#S256,omitempty"` } // JSONWebKey represents a public or private key in JWK format. type JSONWebKey struct { - Key interface{} + // Cryptographic key, can be a symmetric or asymmetric key. + Key interface{} + // Key identifier, parsed from `kid` header. + KeyID string + // Key algorithm, parsed from `alg` header. + Algorithm string + // Key use, parsed from `use` header. + Use string + + // X.509 certificate chain, parsed from `x5c` header. Certificates []*x509.Certificate - KeyID string - Algorithm string - Use string + // X.509 certificate URL, parsed from `x5u` header. + CertificatesURL *url.URL + // X.509 certificate thumbprint (SHA-1), parsed from `x5t` header. + CertificateThumbprintSHA1 []byte + // X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header. + CertificateThumbprintSHA256 []byte } // MarshalJSON serializes the given key to its JSON representation. @@ -105,6 +125,39 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) } + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 { + if x5tSHA1Len != sha1.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) + } + raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) + } + if x5tSHA256Len > 0 { + if x5tSHA256Len != sha256.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) + } + raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) + } + + // If cert chain is attached (as opposed to being behind a URL), check the + // keys thumbprints to make sure they match what is expected. This is to + // ensure we don't accidentally produce a JWK with semantically inconsistent + // data in the headers. + if len(k.Certificates) > 0 { + expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) + expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { + return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") + } + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { + return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") + } + } + + raw.X5u = k.CertificatesURL + return json.Marshal(raw) } @@ -116,28 +169,61 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { return err } + certs, err := parseCertificateChain(raw.X5c) + if err != nil { + return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) + } + var key interface{} + var certPub interface{} + var keyPub interface{} + + if len(certs) > 0 { + // We need to check that leaf public key matches the key embedded in this + // JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise + // the JWK parsed could be semantically invalid. Technically, should also + // check key usage fields and other extensions on the cert here, but the + // standard doesn't exactly explain how they're supposed to map from the + // JWK representation to the X.509 extensions. + certPub = certs[0].PublicKey + } + switch raw.Kty { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() + if err == nil { + keyPub = key.(*ecdsa.PrivateKey).Public() + } } else { key, err = raw.ecPublicKey() + keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() + if err == nil { + keyPub = key.(*rsa.PrivateKey).Public() + } } else { key, err = raw.rsaPublicKey() + keyPub = key } case "oct": + if certPub != nil { + return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") + } key, err = raw.symmetricKey() case "OKP": if raw.Crv == "Ed25519" && raw.X != nil { if raw.D != nil { key, err = raw.edPrivateKey() + if err == nil { + keyPub = key.(ed25519.PrivateKey).Public() + } } else { key, err = raw.edPublicKey() + keyPub = key } } else { err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) @@ -146,12 +232,78 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) } - if err == nil { - *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} + if err != nil { + return + } - k.Certificates, err = parseCertificateChain(raw.X5c) + if certPub != nil && keyPub != nil { + if !reflect.DeepEqual(certPub, keyPub) { + return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match") + } + } + + *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} + + k.CertificatesURL = raw.X5u + + // x5t parameters are base64url-encoded SHA thumbprints + // See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8 + x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") + } + + // RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex, + // for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded + // checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll + // try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum. + if len(x5tSHA1bytes) == 2*sha1.Size { + hx, err := hex.DecodeString(string(x5tSHA1bytes)) if err != nil { - return fmt.Errorf("failed to unmarshal x5c field: %s", err) + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) + + } + x5tSHA1bytes = hx + } + + k.CertificateThumbprintSHA1 = x5tSHA1bytes + + x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") + } + + if len(x5tSHA256bytes) == 2*sha256.Size { + hx256, err := hex.DecodeString(string(x5tSHA256bytes)) + if err != nil { + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) + } + x5tSHA256bytes = hx256 + } + + k.CertificateThumbprintSHA256 = x5tSHA256bytes + + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { + return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") + } + if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") + } + + // If certificate chain *and* thumbprints are set, verify correctness. + if len(k.Certificates) > 0 { + leaf := k.Certificates[0] + sha1sum := sha1.Sum(leaf.Raw) + sha256sum := sha256.Sum256(leaf.Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") + } + + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") } } @@ -180,7 +332,7 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` -const edThumbprintTemplate = `{"crv":"%s","kty":"OKP",x":"%s"}` +const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}` func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { coordLength := curveSize(curve) @@ -254,7 +406,7 @@ func (k *JSONWebKey) IsPublic() bool { } } -// Public creates JSONWebKey with corresponding publik key if JWK represents asymmetric private key. +// Public creates JSONWebKey with corresponding public key if JWK represents asymmetric private key. func (k *JSONWebKey) Public() JSONWebKey { if k.IsPublic() { return *k diff --git a/vendor/gopkg.in/square/go-jose.v2/jws.go b/vendor/gopkg.in/square/go-jose.v2/jws.go index e52a4766..7e261f93 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws.go @@ -102,14 +102,14 @@ func (sig Signature) mergedHeaders() rawHeader { } // Compute data to be signed -func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte { +func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) { var authData bytes.Buffer protectedHeader := new(rawHeader) if signature.original != nil && signature.original.Protected != nil { if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil { - panic(err) + return nil, err } authData.WriteString(signature.original.Protected.base64()) } else if signature.protected != nil { @@ -134,7 +134,7 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature authData.Write(payload) } - return authData.Bytes() + return authData.Bytes(), nil } // parseSignedFull parses a message in full format. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go new file mode 100644 index 00000000..3afa9030 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go @@ -0,0 +1,334 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "bytes" + "reflect" + + "gopkg.in/square/go-jose.v2/json" + + "gopkg.in/square/go-jose.v2" +) + +// Builder is a utility for making JSON Web Tokens. Calls can be chained, and +// errors are accumulated until the final call to CompactSerialize/FullSerialize. +type Builder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) Builder + // Token builds a JSONWebToken from provided data. + Token() (*JSONWebToken, error) + // FullSerialize serializes a token using the full serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens. +// Calls can be chained, and errors are accumulated until final call to +// CompactSerialize/FullSerialize. +type NestedBuilder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) NestedBuilder + // Token builds a NestedJSONWebToken from provided data. + Token() (*NestedJSONWebToken, error) + // FullSerialize serializes a token using the full serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +type builder struct { + payload map[string]interface{} + err error +} + +type signedBuilder struct { + builder + sig jose.Signer +} + +type encryptedBuilder struct { + builder + enc jose.Encrypter +} + +type nestedBuilder struct { + builder + sig jose.Signer + enc jose.Encrypter +} + +// Signed creates builder for signed tokens. +func Signed(sig jose.Signer) Builder { + return &signedBuilder{ + sig: sig, + } +} + +// Encrypted creates builder for encrypted tokens. +func Encrypted(enc jose.Encrypter) Builder { + return &encryptedBuilder{ + enc: enc, + } +} + +// SignedAndEncrypted creates builder for signed-then-encrypted tokens. +// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type. +func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder { + if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" { + return &nestedBuilder{ + builder: builder{ + err: ErrInvalidContentType, + }, + } + } + return &nestedBuilder{ + sig: sig, + enc: enc, + } +} + +func (b builder) claims(i interface{}) builder { + if b.err != nil { + return b + } + + m, ok := i.(map[string]interface{}) + switch { + case ok: + return b.merge(m) + case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: + m, err := normalize(i) + if err != nil { + return builder{ + err: err, + } + } + return b.merge(m) + default: + return builder{ + err: ErrInvalidClaims, + } + } +} + +func normalize(i interface{}) (map[string]interface{}, error) { + m := make(map[string]interface{}) + + raw, err := json.Marshal(i) + if err != nil { + return nil, err + } + + d := json.NewDecoder(bytes.NewReader(raw)) + d.SetNumberType(json.UnmarshalJSONNumber) + + if err := d.Decode(&m); err != nil { + return nil, err + } + + return m, nil +} + +func (b *builder) merge(m map[string]interface{}) builder { + p := make(map[string]interface{}) + for k, v := range b.payload { + p[k] = v + } + for k, v := range m { + p[k] = v + } + + return builder{ + payload: p, + } +} + +func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { + return &JSONWebToken{ + payload: p, + Headers: h, + }, nil +} + +func (b *signedBuilder) Claims(i interface{}) Builder { + return &signedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + } +} + +func (b *signedBuilder) Token() (*JSONWebToken, error) { + sig, err := b.sign() + if err != nil { + return nil, err + } + + h := make([]jose.Header, len(sig.Signatures)) + for i, v := range sig.Signatures { + h[i] = v.Header + } + + return b.builder.token(sig.Verify, h) +} + +func (b *signedBuilder) CompactSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.CompactSerialize() +} + +func (b *signedBuilder) FullSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.FullSerialize(), nil +} + +func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.sig.Sign(p) +} + +func (b *encryptedBuilder) Claims(i interface{}) Builder { + return &encryptedBuilder{ + builder: b.builder.claims(i), + enc: b.enc, + } +} + +func (b *encryptedBuilder) CompactSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *encryptedBuilder) FullSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *encryptedBuilder) Token() (*JSONWebToken, error) { + enc, err := b.encrypt() + if err != nil { + return nil, err + } + + return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) +} + +func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.enc.Encrypt(p) +} + +func (b *nestedBuilder) Claims(i interface{}) NestedBuilder { + return &nestedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + enc: b.enc, + } +} + +func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return nil, err + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} + +func (b *nestedBuilder) CompactSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *nestedBuilder) FullSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + sig, err := b.sig.Sign(p) + if err != nil { + return nil, err + } + + p2, err := sig.CompactSerialize() + if err != nil { + return nil, err + } + + return b.enc.Encrypt([]byte(p2)) +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go new file mode 100644 index 00000000..5f40ef3b --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go @@ -0,0 +1,121 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "strconv" + "time" + + "gopkg.in/square/go-jose.v2/json" +) + +// Claims represents public claim values (as specified in RFC 7519). +type Claims struct { + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audience Audience `json:"aud,omitempty"` + Expiry *NumericDate `json:"exp,omitempty"` + NotBefore *NumericDate `json:"nbf,omitempty"` + IssuedAt *NumericDate `json:"iat,omitempty"` + ID string `json:"jti,omitempty"` +} + +// NumericDate represents date and time as the number of seconds since the +// epoch, ignoring leap seconds. Non-integer values can be represented +// in the serialized format, but we round to the nearest second. +// See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2 +type NumericDate int64 + +// NewNumericDate constructs NumericDate from time.Time value. +func NewNumericDate(t time.Time) *NumericDate { + if t.IsZero() { + return nil + } + + // While RFC 7519 technically states that NumericDate values may be + // non-integer values, we don't bother serializing timestamps in + // claims with sub-second accurancy and just round to the nearest + // second instead. Not convined sub-second accuracy is useful here. + out := NumericDate(t.Unix()) + return &out +} + +// MarshalJSON serializes the given NumericDate into its JSON representation. +func (n NumericDate) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(int64(n), 10)), nil +} + +// UnmarshalJSON reads a date from its JSON representation. +func (n *NumericDate) UnmarshalJSON(b []byte) error { + s := string(b) + + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return ErrUnmarshalNumericDate + } + + *n = NumericDate(f) + return nil +} + +// Time returns time.Time representation of NumericDate. +func (n *NumericDate) Time() time.Time { + if n == nil { + return time.Time{} + } + return time.Unix(int64(*n), 0) +} + +// Audience represents the recipients that the token is intended for. +type Audience []string + +// UnmarshalJSON reads an audience from its JSON representation. +func (s *Audience) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + switch v := v.(type) { + case string: + *s = []string{v} + case []interface{}: + a := make([]string, len(v)) + for i, e := range v { + s, ok := e.(string) + if !ok { + return ErrUnmarshalAudience + } + a[i] = s + } + *s = a + default: + return ErrUnmarshalAudience + } + + return nil +} + +func (s Audience) Contains(v string) bool { + for _, a := range s { + if a == v { + return true + } + } + return false +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go new file mode 100644 index 00000000..4cf97b54 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go @@ -0,0 +1,22 @@ +/*- + * Copyright 2017 Square Inc. + * + * 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 jwt provides an implementation of the JSON Web Token standard. + +*/ +package jwt diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go new file mode 100644 index 00000000..09f76ae4 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go @@ -0,0 +1,53 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import "errors" + +// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled. +var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience") + +// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled. +var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate") + +// ErrInvalidClaims indicates that given claims have invalid type. +var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object") + +// ErrInvalidIssuer indicates invalid iss claim. +var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)") + +// ErrInvalidSubject indicates invalid sub claim. +var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)") + +// ErrInvalidAudience indicated invalid aud claim. +var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)") + +// ErrInvalidID indicates invalid jti claim. +var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)") + +// ErrNotValidYet indicates that token is used before time indicated in nbf claim. +var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)") + +// ErrExpired indicates that token is used after expiry time indicated in exp claim. +var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)") + +// ErrIssuedInTheFuture indicates that the iat field is in the future. +var ErrIssuedInTheFuture = errors.New("square/go-jose/jwt: validation field, token issued in the future (iat)") + +// ErrInvalidContentType indicates that token requires JWT cty header. +var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)") diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go new file mode 100644 index 00000000..47498840 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go @@ -0,0 +1,169 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "fmt" + "strings" + + jose "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/json" +) + +// JSONWebToken represents a JSON Web Token (as specified in RFC7519). +type JSONWebToken struct { + payload func(k interface{}) ([]byte, error) + unverifiedPayload func() []byte + Headers []jose.Header +} + +type NestedJSONWebToken struct { + enc *jose.JSONWebEncryption + Headers []jose.Header +} + +// Claims deserializes a JSONWebToken into dest using the provided key. +func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { + payloadKey := tryJWKS(t.Headers, key) + + b, err := t.payload(payloadKey) + if err != nil { + return err + } + + for _, d := range dest { + if err := json.Unmarshal(b, d); err != nil { + return err + } + } + + return nil +} + +// UnsafeClaimsWithoutVerification deserializes the claims of a +// JSONWebToken into the dests. For signed JWTs, the claims are not +// verified. This function won't work for encrypted JWTs. +func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error { + if t.unverifiedPayload == nil { + return fmt.Errorf("square/go-jose: Cannot get unverified claims") + } + claims := t.unverifiedPayload() + for _, d := range dest { + if err := json.Unmarshal(claims, d); err != nil { + return err + } + } + return nil +} + +func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) { + key := tryJWKS(t.Headers, decryptionKey) + + b, err := t.enc.Decrypt(key) + if err != nil { + return nil, err + } + + sig, err := ParseSigned(string(b)) + if err != nil { + return nil, err + } + + return sig, nil +} + +// ParseSigned parses token from JWS form. +func ParseSigned(s string) (*JSONWebToken, error) { + sig, err := jose.ParseSigned(s) + if err != nil { + return nil, err + } + headers := make([]jose.Header, len(sig.Signatures)) + for i, signature := range sig.Signatures { + headers[i] = signature.Header + } + + return &JSONWebToken{ + payload: sig.Verify, + unverifiedPayload: sig.UnsafePayloadWithoutVerification, + Headers: headers, + }, nil +} + +// ParseEncrypted parses token from JWE form. +func ParseEncrypted(s string) (*JSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + return &JSONWebToken{ + payload: enc.Decrypt, + Headers: []jose.Header{enc.Header}, + }, nil +} + +// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form. +func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string) + if strings.ToUpper(contentType) != "JWT" { + return nil, ErrInvalidContentType + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} + +func tryJWKS(headers []jose.Header, key interface{}) interface{} { + var jwks jose.JSONWebKeySet + + switch jwksType := key.(type) { + case *jose.JSONWebKeySet: + jwks = *jwksType + case jose.JSONWebKeySet: + jwks = jwksType + default: + return key + } + + var kid string + for _, header := range headers { + if header.KeyID != "" { + kid = header.KeyID + break + } + } + + if kid == "" { + return key + } + + keys := jwks.Key(kid) + if len(keys) == 0 { + return key + } + + return keys[0].Key +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go new file mode 100644 index 00000000..6f3ff4e8 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go @@ -0,0 +1,114 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import "time" + +const ( + // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims. + DefaultLeeway = 1.0 * time.Minute +) + +// Expected defines values used for protected claims validation. +// If field has zero value then validation is skipped. +type Expected struct { + // Issuer matches the "iss" claim exactly. + Issuer string + // Subject matches the "sub" claim exactly. + Subject string + // Audience matches the values in "aud" claim, regardless of their order. + Audience Audience + // ID matches the "jti" claim exactly. + ID string + // Time matches the "exp", "nbf" and "iat" claims with leeway. + Time time.Time +} + +// WithTime copies expectations with new time. +func (e Expected) WithTime(t time.Time) Expected { + e.Time = t + return e +} + +// Validate checks claims in a token against expected values. +// A default leeway value of one minute is used to compare time values. +// +// The default leeway will cause the token to be deemed valid until one +// minute after the expiration time. If you're a server application that +// wants to give an extra minute to client tokens, use this +// function. If you're a client application wondering if the server +// will accept your token, use ValidateWithLeeway with a leeway <=0, +// otherwise this function might make you think a token is valid when +// it is not. +func (c Claims) Validate(e Expected) error { + return c.ValidateWithLeeway(e, DefaultLeeway) +} + +// ValidateWithLeeway checks claims in a token against expected values. A +// custom leeway may be specified for comparing time values. You may pass a +// zero value to check time values with no leeway, but you should not that +// numeric date values are rounded to the nearest second and sub-second +// precision is not supported. +// +// The leeway gives some extra time to the token from the server's +// point of view. That is, if the token is expired, ValidateWithLeeway +// will still accept the token for 'leeway' amount of time. This fails +// if you're using this function to check if a server will accept your +// token, because it will think the token is valid even after it +// expires. So if you're a client validating if the token is valid to +// be submitted to a server, use leeway <=0, if you're a server +// validation a token, use leeway >=0. +func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { + if e.Issuer != "" && e.Issuer != c.Issuer { + return ErrInvalidIssuer + } + + if e.Subject != "" && e.Subject != c.Subject { + return ErrInvalidSubject + } + + if e.ID != "" && e.ID != c.ID { + return ErrInvalidID + } + + if len(e.Audience) != 0 { + for _, v := range e.Audience { + if !c.Audience.Contains(v) { + return ErrInvalidAudience + } + } + } + + if !e.Time.IsZero() { + if c.NotBefore != nil && e.Time.Add(leeway).Before(c.NotBefore.Time()) { + return ErrNotValidYet + } + + if c.Expiry != nil && e.Time.Add(-leeway).After(c.Expiry.Time()) { + return ErrExpired + } + + // IssuedAt is optional but cannot be in the future. This is not required by the RFC, but + // something is misconfigured if this happens and we should not trust it. + if c.IssuedAt != nil && e.Time.Add(leeway).Before(c.IssuedAt.Time()) { + return ErrIssuedInTheFuture + } + } + + return nil +} diff --git a/vendor/gopkg.in/square/go-jose.v2/opaque.go b/vendor/gopkg.in/square/go-jose.v2/opaque.go index df747f99..fc3e8d2e 100644 --- a/vendor/gopkg.in/square/go-jose.v2/opaque.go +++ b/vendor/gopkg.in/square/go-jose.v2/opaque.go @@ -17,7 +17,7 @@ package jose // OpaqueSigner is an interface that supports signing payloads with opaque -// private key(s). Private key operations preformed by implementors may, for +// private key(s). Private key operations performed by implementers may, for // example, occur in a hardware module. An OpaqueSigner may rotate signing keys // transparently to the user of this interface. type OpaqueSigner interface { diff --git a/vendor/gopkg.in/square/go-jose.v2/shared.go b/vendor/gopkg.in/square/go-jose.v2/shared.go index f8438641..f72e5a53 100644 --- a/vendor/gopkg.in/square/go-jose.v2/shared.go +++ b/vendor/gopkg.in/square/go-jose.v2/shared.go @@ -183,7 +183,7 @@ type Header struct { // Unverified certificate chain parsed from x5c header. certificates []*x509.Certificate - // Any headers not recognised above get unmarshaled + // Any headers not recognised above get unmarshalled // from JSON in a generic manner and placed in this map. ExtraHeaders map[HeaderKey]interface{} } @@ -295,12 +295,12 @@ func (parsed rawHeader) getAPV() (*byteBuffer, error) { return parsed.getByteBuffer(headerAPV) } -// getIV extracts parsed "iv" frpom the raw JSON. +// getIV extracts parsed "iv" from the raw JSON. func (parsed rawHeader) getIV() (*byteBuffer, error) { return parsed.getByteBuffer(headerIV) } -// getTag extracts parsed "tag" frpom the raw JSON. +// getTag extracts parsed "tag" from the raw JSON. func (parsed rawHeader) getTag() (*byteBuffer, error) { return parsed.getByteBuffer(headerTag) } diff --git a/vendor/gopkg.in/square/go-jose.v2/signing.go b/vendor/gopkg.in/square/go-jose.v2/signing.go index 664a51cc..bad820ce 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing.go @@ -370,7 +370,11 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter } } - input := obj.computeAuthData(payload, &signature) + input, err := obj.computeAuthData(payload, &signature) + if err != nil { + return ErrCryptoFailure + } + alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { @@ -421,7 +425,11 @@ outer: } } - input := obj.computeAuthData(payload, &signature) + input, err := obj.computeAuthData(payload, &signature) + if err != nil { + continue + } + alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 648e7327..3b296241 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,9 +62,6 @@ github.com/coredns/coredns/plugin/pkg/transport github.com/coredns/coredns/plugin/pkg/uniq github.com/coredns/coredns/plugin/test github.com/coredns/coredns/request -# github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 -## explicit -github.com/coreos/go-oidc/jose # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf ## explicit github.com/coreos/go-systemd/daemon @@ -530,17 +527,18 @@ google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb -# gopkg.in/coreos/go-oidc.v2 v2.1.0 +# gopkg.in/coreos/go-oidc.v2 v2.2.1 ## explicit gopkg.in/coreos/go-oidc.v2 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 ## explicit gopkg.in/natefinch/lumberjack.v2 -# gopkg.in/square/go-jose.v2 v2.4.0 +# gopkg.in/square/go-jose.v2 v2.6.0 ## explicit gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2/cipher gopkg.in/square/go-jose.v2/json +gopkg.in/square/go-jose.v2/jwt # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ## explicit gopkg.in/tomb.v1 From eee0d57ed001e99a596cb7cf1b018ff5b4515b0d Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 8 Mar 2022 11:03:46 +0000 Subject: [PATCH 011/238] TUN-5849: Remove configuration debug log --- connection/http2.go | 1 - 1 file changed, 1 deletion(-) diff --git a/connection/http2.go b/connection/http2.go index 55e73ad3..92104dad 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -123,7 +123,6 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { } case TypeConfiguration: - fmt.Println("TYPE CONFIGURATION?") if err := c.handleConfigurationUpdate(respWriter, r); err != nil { c.log.Error().Err(err) respWriter.WriteErrorResponse() From aeda35699e527df9fac2ca943687988f8bb666ce Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 8 Mar 2022 14:40:27 +0000 Subject: [PATCH 012/238] TUN-5850: Update CHANGES.md with latest releases --- CHANGES.md | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e81db9fe..ab14bb14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,39 @@ -**Experimental**: This is a new format for release notes. The format and availability is subject to change. +## 2022.3.1 +### Bug Fixes +- Various fixes to the reliability of `quic` protocol, including an edge case that could lead to cloudflared crashing. + +## 2022.3.0 +### New Features +- It is now possible to configure Ingress Rules to point to an origin served by unix socket with either HTTP or HTTPS. +If the origin starts with `unix:/` then we assume HTTP (existing behavior). Otherwise, the origin can start with +`unix+tls:/` for HTTPS. + +## 2022.2.1 +### New Features +- This project now has a new LICENSE that is more compliant with open source purposes. + +### Bug Fixes +- Various fixes to the reliability of `quic` protocol. + +## 2022.1.3 +### New Features +- New `cloudflared tunnel vnet` commands to allow for private routing to be virtualized. This means that the same CIDR +can now be used to point to two different Tunnels with `cloudflared tunnel route ip` command. More information will be +made available on blog.cloudflare.com and developers.cloudflare.com/cloudflare-one once the feature is globally available. + +### Bug Fixes +- Correctly handle proxying UDP datagrams with no payload. +- Bug fix for origins that use Server-Sent Events (SSE). + +## 2022.1.0 +### Improvements +- If a specific `protocol` property is defined (e.g. for `quic`), cloudflared no longer falls back to an older protocol +(such as `http2`) in face of connectivity errors. This is important because some features are only supported in a specific +protocol (e.g. UDP proxying only works for `quic`). Hence, if a user chooses a protocol, cloudflared now adheres to it +no matter what. + +### Bug Fixes +- Stopping cloudflared running with `quic` protocol now respects graceful shutdown. ## 2021.12.2 ### Bug Fixes From c54e8cd8e62e2b551bcedff0c25a80f4ec0f38fa Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 8 Mar 2022 14:59:53 +0000 Subject: [PATCH 013/238] TUN-5851: Update all references to point to Apache License 2.0 --- LICENSE | 347 +++++++++++++++++++--------------------- Makefile | 2 +- cmd/cloudflared/main.go | 2 +- 3 files changed, 171 insertions(+), 180 deletions(-) diff --git a/LICENSE b/LICENSE index 7642b8f5..7a4a3ea2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,211 +1,202 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -1. Definitions. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. + 1. Definitions. -"Licensor" shall mean the copyright owner or entity authorized by -the copyright owner that is granting the License. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -"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. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -"You" (or "Your") shall mean an individual or Legal Entity -exercising permissions granted by this 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. -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation -source, and configuration files. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -"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. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -"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). + "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. -"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. + "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). -"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." + "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. -"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. + "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." -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. + "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. -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. + 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. -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: + 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. -(a) You must give any other recipients of the Work or -Derivative Works a copy of this License; and + 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: -(b) You must cause any modified files to carry prominent notices -stating that You changed the files; and + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; 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 + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; 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. + (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 -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. + (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. -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. + 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. -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. + 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. -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. + 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. -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. + 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. -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. + 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. -END OF TERMS AND CONDITIONS + 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. -APPENDIX: How to apply the Apache License to your work. + END OF TERMS AND CONDITIONS -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. + APPENDIX: How to apply the Apache License to your work. -Copyright [yyyy] [name of copyright owner] + 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. -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 + Copyright [yyyy] [name of copyright owner] -http://www.apache.org/licenses/LICENSE-2.0 + 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 -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. + http://www.apache.org/licenses/LICENSE-2.0 - - -## Runtime Library Exception to the Apache 2.0 License: ## - - -As an exception, if you use this Software to compile your source code and -portions of this Software are embedded into the binary product as a result, -you may redistribute such product without providing attribution as would -otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. + 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. \ No newline at end of file diff --git a/Makefile b/Makefile index 11ecbc74..a3fbcf7e 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ define build_package fakeroot fpm -C $(PACKAGE_DIR) -s dir -t $(1) \ --description 'Cloudflare Tunnel daemon' \ --vendor 'Cloudflare' \ - --license 'Cloudflare Service Agreement' \ + --license 'Apache License Version 2.0' \ --url 'https://github.com/cloudflare/cloudflared' \ -m 'Cloudflare ' \ -a $(TARGET_ARCH) -v $(VERSION) -n $(DEB_PACKAGE_NAME) $(NIGHTLY_FLAGS) --after-install postinst.sh --after-remove postrm.sh \ diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 8732ad34..889e1f64 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -69,7 +69,7 @@ func main() { app.Copyright = fmt.Sprintf( `(c) %d Cloudflare Inc. Your installation of cloudflared software constitutes a symbol of your signature indicating that you accept - the terms of the Cloudflare License (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/license), + the terms of the Apache License Version 2.0 (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/license), Terms (https://www.cloudflare.com/terms/) and Privacy Policy (https://www.cloudflare.com/privacypolicy/).`, time.Now().Year(), ) From 9552bb7bc7ae15ea65adde84e1a36943cc228022 Mon Sep 17 00:00:00 2001 From: Piper McCorkle Date: Mon, 7 Mar 2022 18:05:48 -0600 Subject: [PATCH 014/238] TUN-5853 Add "install" make target and build package manager info into executable --- .gitignore | 1 + Makefile | 30 +++++++++++++++++++++--------- cmd/cloudflared/updater/update.go | 11 ++++++++--- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index d3b17958..c258e58c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ cscope.* /cloudflared.exe /cloudflared.msi /cloudflared-x86-64* +/cloudflared.1 /packaging .DS_Store *-session.log diff --git a/Makefile b/Makefile index a3fbcf7e..461384d4 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,9 @@ endif DATE := $(shell date -u '+%Y-%m-%d-%H%M UTC') VERSION_FLAGS := -X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)" +ifdef PACKAGE_MANAGER + VERSION_FLAGS := $(VERSION_FLAGS) -X "github.com/cloudflare/cloudflared/cmd/cloudflared/updater.BuiltForPackageManager=$(PACKAGE_MANAGER)" +endif LINK_FLAGS := ifeq ($(FIPS), true) @@ -37,10 +40,11 @@ ifneq ($(GO_BUILD_TAGS),) GO_BUILD_TAGS := -tags "$(GO_BUILD_TAGS)" endif -IMPORT_PATH := github.com/cloudflare/cloudflared -PACKAGE_DIR := $(CURDIR)/packaging -INSTALL_BINDIR := /usr/bin/ -MAN_DIR := /usr/share/man/man1/ +IMPORT_PATH := github.com/cloudflare/cloudflared +PACKAGE_DIR := $(CURDIR)/packaging +PREFIX := /usr +INSTALL_BINDIR := $(PREFIX)/bin/ +INSTALL_MANDIR := $(PREFIX)/share/man/man1/ LOCAL_ARCH ?= $(shell uname -m) ifneq ($(GOARCH),) @@ -141,12 +145,20 @@ publish-deb: cloudflared-deb publish-rpm: cloudflared-rpm $(call publish_package,rpm,yum) +cloudflared.1: cloudflared_man_template + cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > cloudflared.1 + +install: cloudflared cloudflared.1 + mkdir -p $(DESTDIR)$(INSTALL_BINDIR) $(DESTDIR)$(INSTALL_MANDIR) + install -m755 cloudflared $(DESTDIR)$(INSTALL_BINDIR)/cloudflared + install -m644 cloudflared.1 $(DESTDIR)$(INSTALL_MANDIR)/cloudflared.1 + # When we build packages, the package name will be FIPS-aware. # But we keep the binary installed by it to be named "cloudflared" regardless. define build_package mkdir -p $(PACKAGE_DIR) cp cloudflared $(PACKAGE_DIR)/cloudflared - cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > $(PACKAGE_DIR)/cloudflared.1 + cp cloudflared.1 $(PACKAGE_DIR)/cloudflared.1 fakeroot fpm -C $(PACKAGE_DIR) -s dir -t $(1) \ --description 'Cloudflare Tunnel daemon' \ --vendor 'Cloudflare' \ @@ -154,19 +166,19 @@ define build_package --url 'https://github.com/cloudflare/cloudflared' \ -m 'Cloudflare ' \ -a $(TARGET_ARCH) -v $(VERSION) -n $(DEB_PACKAGE_NAME) $(NIGHTLY_FLAGS) --after-install postinst.sh --after-remove postrm.sh \ - cloudflared=$(INSTALL_BINDIR) cloudflared.1=$(MAN_DIR) + cloudflared=$(INSTALL_BINDIR) cloudflared.1=$(INSTALL_MANDIR) endef .PHONY: cloudflared-deb -cloudflared-deb: cloudflared +cloudflared-deb: cloudflared cloudflared.1 $(call build_package,deb) .PHONY: cloudflared-rpm -cloudflared-rpm: cloudflared +cloudflared-rpm: cloudflared cloudflared.1 $(call build_package,rpm) .PHONY: cloudflared-pkg -cloudflared-pkg: cloudflared +cloudflared-pkg: cloudflared cloudflared.1 $(call build_package,osxpkg) .PHONY: cloudflared-msi diff --git a/cmd/cloudflared/updater/update.go b/cmd/cloudflared/updater/update.go index 23aa327a..5ad40dcb 100644 --- a/cmd/cloudflared/updater/update.go +++ b/cmd/cloudflared/updater/update.go @@ -30,7 +30,8 @@ const ( ) var ( - version string + version string + BuiltForPackageManager = "" ) // BinaryUpdated implements ExitCoder interface, the app will exit with status code 11 @@ -118,7 +119,11 @@ func Update(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if wasInstalledFromPackageManager() { - log.Error().Msg("cloudflared was installed by a package manager. Please update using the same method.") + packageManagerName := "a package manager" + if BuiltForPackageManager != "" { + packageManagerName = BuiltForPackageManager + } + log.Error().Msg(fmt.Sprintf("cloudflared was installed by %s. Please update using the same method.", packageManagerName)) return nil } @@ -281,7 +286,7 @@ func supportAutoUpdate(log *zerolog.Logger) bool { func wasInstalledFromPackageManager() bool { ok, _ := config.FileExists(filepath.Join(config.DefaultUnixConfigLocation, isManagedInstallFile)) - return ok + return len(BuiltForPackageManager) != 0 || ok } func isRunningFromTerminal() bool { From 5352b3cf0489e6568e148f40cd6e89660a7233cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Tue, 8 Mar 2022 16:10:24 +0000 Subject: [PATCH 015/238] TUN-5801: Add custom wrapper for OriginConfig for JSON serde --- config/configuration.go | 45 +++++++++++++++++++---- config/configuration_test.go | 58 ++++++++++++++++++++++++------ ingress/config.go | 16 ++++----- ingress/config_test.go | 24 ++++++------- orchestration/orchestrator_test.go | 10 +++--- 5 files changed, 111 insertions(+), 42 deletions(-) diff --git a/config/configuration.go b/config/configuration.go index 8b16d4fe..ffd3eb7c 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -1,12 +1,14 @@ package config import ( + "encoding/json" "fmt" "io" "net/url" "os" "path/filepath" "runtime" + "strconv" "time" homedir "github.com/mitchellh/go-homedir" @@ -49,7 +51,7 @@ func DefaultConfigDirectory() string { path := os.Getenv("CFDPATH") if path == "" { path = filepath.Join(os.Getenv("ProgramFiles(x86)"), "cloudflared") - if _, err := os.Stat(path); os.IsNotExist(err) { //doesn't exist, so return an empty failure string + if _, err := os.Stat(path); os.IsNotExist(err) { // doesn't exist, so return an empty failure string return "" } } @@ -138,7 +140,7 @@ func FindOrCreateConfigPath() string { defer file.Close() logDir := DefaultLogDirectory() - _ = os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs + _ = os.MkdirAll(logDir, os.ModePerm) // try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs c := Root{ LogDirectory: logDir, @@ -190,17 +192,17 @@ type UnvalidatedIngressRule struct { // - To specify a time.Duration in json, use int64 of the nanoseconds type OriginRequestConfig struct { // HTTP proxy timeout for establishing a new connection - ConnectTimeout *time.Duration `yaml:"connectTimeout" json:"connectTimeout"` + ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout"` // HTTP proxy timeout for completing a TLS handshake - TLSTimeout *time.Duration `yaml:"tlsTimeout" json:"tlsTimeout"` + TLSTimeout *CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout"` // HTTP proxy TCP keepalive duration - TCPKeepAlive *time.Duration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"` + TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"` // HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"` // HTTP proxy maximum keepalive connection pool size KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"` // HTTP proxy timeout for closing an idle connection - KeepAliveTimeout *time.Duration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"` + KeepAliveTimeout *CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"` // Sets the HTTP Host header for the local webserver. HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"` // Hostname on the origin server certificate. @@ -399,3 +401,34 @@ func ReadConfigFile(c *cli.Context, log *zerolog.Logger) (settings *configFileSe return &configuration, warnings, nil } + +// A CustomDuration is a Duration that has custom serialization for JSON. +// JSON in Javascript assumes that int fields are 32 bits and Duration fields are deserialized assuming that numbers +// are in nanoseconds, which in 32bit integers limits to just 2 seconds. +// This type assumes that when serializing/deserializing from JSON, that the number is in seconds, while it maintains +// the YAML serde assumptions. +type CustomDuration struct { + time.Duration +} + +func (s *CustomDuration) MarshalJSON() ([]byte, error) { + return json.Marshal(s.Duration.Seconds()) +} + +func (s *CustomDuration) UnmarshalJSON(data []byte) error { + seconds, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return err + } + + s.Duration = time.Duration(seconds * int64(time.Second)) + return nil +} + +func (s *CustomDuration) MarshalYAML() (interface{}, error) { + return s.Duration.String(), nil +} + +func (s *CustomDuration) UnmarshalYAML(unmarshal func(interface{}) error) error { + return unmarshal(&s.Duration) +} diff --git a/config/configuration_test.go b/config/configuration_test.go index 11db35db..d870913d 100644 --- a/config/configuration_test.go +++ b/config/configuration_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" yaml "gopkg.in/yaml.v2" ) @@ -110,14 +111,13 @@ counters: } -func TestUnmarshalOriginRequestConfig(t *testing.T) { - raw := []byte(` +var rawConfig = []byte(` { - "connectTimeout": 10000000000, - "tlsTimeout": 30000000000, - "tcpKeepAlive": 30000000000, + "connectTimeout": 10, + "tlsTimeout": 30, + "tcpKeepAlive": 30, "noHappyEyeballs": true, - "keepAliveTimeout": 60000000000, + "keepAliveTimeout": 60, "keepAliveConnections": 10, "httpHostHeader": "app.tunnel.com", "originServerName": "app.tunnel.com", @@ -142,13 +142,41 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) { ] } `) + +func TestMarshalUnmarshalOriginRequest(t *testing.T) { + testCases := []struct { + name string + marshalFunc func(in interface{}) (out []byte, err error) + unMarshalFunc func(in []byte, out interface{}) (err error) + baseUnit time.Duration + }{ + {"json", json.Marshal, json.Unmarshal, time.Second}, + {"yaml", yaml.Marshal, yaml.Unmarshal, time.Nanosecond}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assertConfig(t, tc.marshalFunc, tc.unMarshalFunc, tc.baseUnit) + }) + } +} + +func assertConfig( + t *testing.T, + marshalFunc func(in interface{}) (out []byte, err error), + unMarshalFunc func(in []byte, out interface{}) (err error), + baseUnit time.Duration, +) { var config OriginRequestConfig - assert.NoError(t, json.Unmarshal(raw, &config)) - assert.Equal(t, time.Second*10, *config.ConnectTimeout) - assert.Equal(t, time.Second*30, *config.TLSTimeout) - assert.Equal(t, time.Second*30, *config.TCPKeepAlive) + var config2 OriginRequestConfig + + assert.NoError(t, unMarshalFunc(rawConfig, &config)) + + assert.Equal(t, baseUnit*10, config.ConnectTimeout.Duration) + assert.Equal(t, baseUnit*30, config.TLSTimeout.Duration) + assert.Equal(t, baseUnit*30, config.TCPKeepAlive.Duration) assert.Equal(t, true, *config.NoHappyEyeballs) - assert.Equal(t, time.Second*60, *config.KeepAliveTimeout) + assert.Equal(t, baseUnit*60, config.KeepAliveTimeout.Duration) assert.Equal(t, 10, *config.KeepAliveConnections) assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader) assert.Equal(t, "app.tunnel.com", *config.OriginServerName) @@ -176,4 +204,12 @@ func TestUnmarshalOriginRequestConfig(t *testing.T) { }, } assert.Equal(t, ipRules, config.IPRules) + + // validate that serializing and deserializing again matches the deserialization from raw string + result, err := marshalFunc(config) + require.NoError(t, err) + err = unMarshalFunc(result, &config2) + require.NoError(t, err) + + require.Equal(t, config2, config) } diff --git a/ingress/config.go b/ingress/config.go index b389bc9b..f8284b43 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -158,13 +158,13 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { ProxyAddress: defaultProxyAddress, } if c.ConnectTimeout != nil { - out.ConnectTimeout = *c.ConnectTimeout + out.ConnectTimeout = c.ConnectTimeout.Duration } if c.TLSTimeout != nil { - out.TLSTimeout = *c.TLSTimeout + out.TLSTimeout = c.TLSTimeout.Duration } if c.TCPKeepAlive != nil { - out.TCPKeepAlive = *c.TCPKeepAlive + out.TCPKeepAlive = c.TCPKeepAlive.Duration } if c.NoHappyEyeballs != nil { out.NoHappyEyeballs = *c.NoHappyEyeballs @@ -173,7 +173,7 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { out.KeepAliveConnections = *c.KeepAliveConnections } if c.KeepAliveTimeout != nil { - out.KeepAliveTimeout = *c.KeepAliveTimeout + out.KeepAliveTimeout = c.KeepAliveTimeout.Duration } if c.HTTPHostHeader != nil { out.HTTPHostHeader = *c.HTTPHostHeader @@ -257,13 +257,13 @@ type OriginRequestConfig struct { func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) { if val := overrides.ConnectTimeout; val != nil { - defaults.ConnectTimeout = *val + defaults.ConnectTimeout = val.Duration } } func (defaults *OriginRequestConfig) setTLSTimeout(overrides config.OriginRequestConfig) { if val := overrides.TLSTimeout; val != nil { - defaults.TLSTimeout = *val + defaults.TLSTimeout = val.Duration } } @@ -281,13 +281,13 @@ func (defaults *OriginRequestConfig) setKeepAliveConnections(overrides config.Or func (defaults *OriginRequestConfig) setKeepAliveTimeout(overrides config.OriginRequestConfig) { if val := overrides.KeepAliveTimeout; val != nil { - defaults.KeepAliveTimeout = *val + defaults.KeepAliveTimeout = val.Duration } } func (defaults *OriginRequestConfig) setTCPKeepAlive(overrides config.OriginRequestConfig) { if val := overrides.TCPKeepAlive; val != nil { - defaults.TCPKeepAlive = *val + defaults.TCPKeepAlive = val.Duration } } diff --git a/ingress/config_test.go b/ingress/config_test.go index ff0a3c0a..e520c9bf 100644 --- a/ingress/config_test.go +++ b/ingress/config_test.go @@ -191,12 +191,12 @@ ingress: rawConfig := []byte(` { "originRequest": { - "connectTimeout": 60000000000, - "tlsTimeout": 1000000000, + "connectTimeout": 60, + "tlsTimeout": 1, "noHappyEyeballs": true, - "tcpKeepAlive": 1000000000, + "tcpKeepAlive": 1, "keepAliveConnections": 1, - "keepAliveTimeout": 1000000000, + "keepAliveTimeout": 1, "httpHostHeader": "abc", "originServerName": "a1", "caPool": "/tmp/path0", @@ -228,12 +228,12 @@ ingress: "hostname": "*", "service": "https://localhost:8001", "originRequest": { - "connectTimeout": 120000000000, - "tlsTimeout": 2000000000, + "connectTimeout": 120, + "tlsTimeout": 2, "noHappyEyeballs": false, - "tcpKeepAlive": 2000000000, + "tcpKeepAlive": 2, "keepAliveConnections": 2, - "keepAliveTimeout": 2000000000, + "keepAliveTimeout": 2, "httpHostHeader": "def", "originServerName": "b2", "caPool": "/tmp/path1", @@ -360,12 +360,12 @@ ingress: "hostname": "*", "service": "https://localhost:8001", "originRequest": { - "connectTimeout": 120000000000, - "tlsTimeout": 2000000000, + "connectTimeout": 120, + "tlsTimeout": 2, "noHappyEyeballs": false, - "tcpKeepAlive": 2000000000, + "tcpKeepAlive": 2, "keepAliveConnections": 2, - "keepAliveTimeout": 2000000000, + "keepAliveTimeout": 2, "httpHostHeader": "def", "originServerName": "b2", "caPool": "/tmp/path1", diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 4fe6c7b1..78de169a 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -58,7 +58,7 @@ func TestUpdateConfiguration(t *testing.T) { { "unknown_field": "not_deserialized", "originRequest": { - "connectTimeout": 90000000000, + "connectTimeout": 90, "noHappyEyeballs": true }, "ingress": [ @@ -68,7 +68,7 @@ func TestUpdateConfiguration(t *testing.T) { "service": "http://192.16.19.1:443", "originRequest": { "noTLSVerify": true, - "connectTimeout": 10000000000 + "connectTimeout": 10 } }, { @@ -76,7 +76,7 @@ func TestUpdateConfiguration(t *testing.T) { "service": "http://172.32.20.6:80", "originRequest": { "noTLSVerify": true, - "connectTimeout": 30000000000 + "connectTimeout": 30 } }, { @@ -192,7 +192,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) { configJSONV1 = []byte(fmt.Sprintf(` { "originRequest": { - "connectTimeout": 90000000000, + "connectTimeout": 90, "noHappyEyeballs": true }, "ingress": [ @@ -201,7 +201,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) { "service": "%s", "originRequest": { "httpHostHeader": "%s", - "connectTimeout": 10000000000 + "connectTimeout": 10 } }, { From a50c0ca9ad6d0aa492a88429ddb023bb57f0494c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 10 Mar 2022 10:48:03 +0000 Subject: [PATCH 016/238] TUN-5833: Create constant for allow-remote-config --- supervisor/tunnel.go | 1 + 1 file changed, 1 insertion(+) diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index bca5e081..36fcbb5c 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -32,6 +32,7 @@ const ( dialTimeout = 15 * time.Second FeatureSerializedHeaders = "serialized_headers" FeatureQuickReconnects = "quick_reconnects" + FeatureAllowRemoteConfig = "allow_remote_config" ) type TunnelConfig struct { From 8cbd222e1097a5c07d7060f6831d6af820e354e5 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 9 Mar 2022 15:16:54 -0800 Subject: [PATCH 017/238] TUN-5703: Add prometheus metric for current configuration version --- orchestration/metrics.go | 25 +++++++++++++++++++++++++ orchestration/orchestrator.go | 1 + 2 files changed, 26 insertions(+) create mode 100644 orchestration/metrics.go diff --git a/orchestration/metrics.go b/orchestration/metrics.go new file mode 100644 index 00000000..1fa5e8a5 --- /dev/null +++ b/orchestration/metrics.go @@ -0,0 +1,25 @@ +package orchestration + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +const ( + MetricsNamespace = "cloudflared" + MetricsSubsystem = "orchestration" +) + +var ( + configVersion = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: MetricsNamespace, + Subsystem: MetricsSubsystem, + Name: "config_version", + Help: "Configuration Version", + }, + ) +) + +func init() { + prometheus.MustRegister(configVersion) +} diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index d072e966..59d8db57 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -95,6 +95,7 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up Int32("version", version). Str("config", string(config)). Msg("Updated to new configuration") + configVersion.Set(float64(version)) return &tunnelpogs.UpdateConfigurationResponse{ LastAppliedVersion: o.currentVersion, } From ca43b0357f9af8a7200267b610ccb2336ec69717 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 15 Mar 2022 14:18:21 +0000 Subject: [PATCH 018/238] TUN-5867: Return error if service was already installed --- cmd/cloudflared/service_template.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index 2aa29277..13725648 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -43,6 +43,10 @@ func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error { if err != nil { return err } + if _, err = os.Stat(resolvedPath); err == nil { + return fmt.Errorf("cloudflared service is already installed at %s", resolvedPath) + } + var buffer bytes.Buffer err = tmpl.Execute(&buffer, args) if err != nil { From 057a0cc7580b407d376ca9c69703f0c20741e067 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 16 Mar 2022 11:27:35 +0000 Subject: [PATCH 019/238] TUN-5833: Send feature `allow_remote_config` if Tunnel is run with --token --- cmd/cloudflared/tunnel/configuration.go | 4 ++++ cmd/cloudflared/tunnel/subcommand_context.go | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 27ce90e0..d7d6da8d 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -191,6 +191,10 @@ func prepareTunnelConfig( } log.Info().Msgf("Generated Connector ID: %s", clientUUID) features := append(c.StringSlice("features"), supervisor.FeatureSerializedHeaders) + if c.IsSet(TunnelTokenFlag) { + features = append(features, supervisor.FeatureAllowRemoteConfig) + log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API") + } namedTunnel.Client = tunnelpogs.ClientInfo{ ClientID: clientUUID[:], Features: dedup(features), diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index 53609c3a..8490aa0c 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -220,7 +220,6 @@ func (sc *subcommandContext) create(name string, credentialsFilePath string, sec } fmt.Println(" Keep this file secret. To revoke these credentials, delete the tunnel.") fmt.Printf("\nCreated tunnel %s with id %s\n", tunnel.Name, tunnel.ID) - fmt.Printf("\nTunnel Token: %s\n", tunnel.Token) return &tunnel.Tunnel, nil } From e1a9e98cca27d3fba0a858d87f4c2d6d2f6013d5 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 16 Mar 2022 14:24:18 +0000 Subject: [PATCH 020/238] Release 2022.3.2 --- RELEASE_NOTES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3cd05d6f..661f1916 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,15 @@ +2022.3.2 +- 2022-03-10 TUN-5833: Create constant for allow-remote-config +- 2022-03-15 TUN-5867: Return error if service was already installed +- 2022-03-16 TUN-5833: Send feature `allow_remote_config` if Tunnel is run with --token +- 2022-03-08 TUN-5849: Remove configuration debug log +- 2022-03-08 TUN-5850: Update CHANGES.md with latest releases +- 2022-03-08 TUN-5851: Update all references to point to Apache License 2.0 +- 2022-03-07 TUN-5853 Add "install" make target and build package manager info into executable +- 2022-03-08 TUN-5801: Add custom wrapper for OriginConfig for JSON serde +- 2022-03-09 TUN-5703: Add prometheus metric for current configuration version +- 2022-02-05 CC-796: Remove dependency on unsupported version of go-oidc + 2022.3.1 - 2022-03-04 TUN-5837: Log panic recovery in http2 logic with debug level log - 2022-03-04 TUN-5696: HTTP/2 Configuration Update From 398cc8b1347bbcaaf13376a14570e56515d3dc1d Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Thu, 17 Mar 2022 17:51:53 -0500 Subject: [PATCH 021/238] TUN-5893: Start windows service on install, stop on uninstall. Previously user had to manually start the service after running 'cloudflared tunnel install' and stop the service before running uninstall command. --- cmd/cloudflared/windows_service.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 6006c0b0..a3d1441d 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -212,12 +212,14 @@ func installWindowsService(c *cli.Context) error { s.Delete() return errors.Wrap(err, "Cannot install event logger") } + err = configRecoveryOption(s.Handle) if err != nil { log.Err(err).Msg("Cannot set service recovery actions") log.Info().Msgf("See %s to manually configure service recovery actions", windowsServiceUrl) } - return nil + + return s.Start() } func uninstallWindowsService(c *cli.Context) error { @@ -236,6 +238,14 @@ func uninstallWindowsService(c *cli.Context) error { return fmt.Errorf("Service %s is not installed", windowsServiceName) } defer s.Close() + + if status, err := s.Query(); err == nil && status.State == svc.Running { + log.Info().Msg("Stopping Cloudflare Tunnel agent service") + if _, err := s.Control(svc.Stop); err != nil { + log.Info().Err(err).Msg("Failed to stop Cloudflare Tunnel agent service, you may need to stop it manually to complete uninstall.") + } + } + err = s.Delete() if err != nil { return errors.Wrap(err, "Cannot delete service") From 05b903a32e34da2ad121927ff9f19412e9e964c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 17 Mar 2022 22:34:35 +0000 Subject: [PATCH 022/238] Revert "CC-796: Remove dependency on unsupported version of go-oidc" This reverts commit 0899d6a13619cba03bc0c933ef0c7032c2393311. --- go.mod | 6 +- go.sum | 10 +- sshgen/sshgen.go | 18 +- sshgen/sshgen_test.go | 36 +- token/token.go | 17 +- vendor/github.com/coreos/go-oidc/LICENSE | 202 +++++++++++ vendor/github.com/coreos/go-oidc/NOTICE | 5 + .../github.com/coreos/go-oidc/jose/claims.go | 126 +++++++ vendor/github.com/coreos/go-oidc/jose/doc.go | 2 + vendor/github.com/coreos/go-oidc/jose/jose.go | 112 ++++++ vendor/github.com/coreos/go-oidc/jose/jwk.go | 135 +++++++ vendor/github.com/coreos/go-oidc/jose/jws.go | 51 +++ vendor/github.com/coreos/go-oidc/jose/jwt.go | 82 +++++ vendor/github.com/coreos/go-oidc/jose/sig.go | 24 ++ .../github.com/coreos/go-oidc/jose/sig_rsa.go | 67 ++++ vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml | 6 +- vendor/gopkg.in/coreos/go-oidc.v2/oidc.go | 34 +- vendor/gopkg.in/coreos/go-oidc.v2/verify.go | 11 +- vendor/gopkg.in/square/go-jose.v2/.gitignore | 1 - vendor/gopkg.in/square/go-jose.v2/.travis.yml | 9 +- .../square/go-jose.v2/cipher/cbc_hmac.go | 2 +- vendor/gopkg.in/square/go-jose.v2/crypter.go | 1 - vendor/gopkg.in/square/go-jose.v2/encoding.go | 14 +- .../gopkg.in/square/go-jose.v2/json/decode.go | 52 +-- .../gopkg.in/square/go-jose.v2/json/stream.go | 7 +- vendor/gopkg.in/square/go-jose.v2/jwk.go | 174 +-------- vendor/gopkg.in/square/go-jose.v2/jws.go | 6 +- .../gopkg.in/square/go-jose.v2/jwt/builder.go | 334 ------------------ .../gopkg.in/square/go-jose.v2/jwt/claims.go | 121 ------- vendor/gopkg.in/square/go-jose.v2/jwt/doc.go | 22 -- .../gopkg.in/square/go-jose.v2/jwt/errors.go | 53 --- vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go | 169 --------- .../square/go-jose.v2/jwt/validation.go | 114 ------ vendor/gopkg.in/square/go-jose.v2/opaque.go | 2 +- vendor/gopkg.in/square/go-jose.v2/shared.go | 6 +- vendor/gopkg.in/square/go-jose.v2/signing.go | 12 +- vendor/modules.txt | 8 +- 37 files changed, 905 insertions(+), 1146 deletions(-) create mode 100644 vendor/github.com/coreos/go-oidc/LICENSE create mode 100644 vendor/github.com/coreos/go-oidc/NOTICE create mode 100644 vendor/github.com/coreos/go-oidc/jose/claims.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/doc.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/jose.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/jwk.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/jws.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/jwt.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/sig.go create mode 100644 vendor/github.com/coreos/go-oidc/jose/sig_rsa.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/builder.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/claims.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/doc.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/errors.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go delete mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/validation.go diff --git a/go.mod b/go.mod index 83328b33..59b8ec7c 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 + github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -50,15 +51,14 @@ require ( golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.43.0 // indirect + gopkg.in/coreos/go-oidc.v2 v2.1.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/square/go-jose.v2 v2.6.0 + gopkg.in/square/go-jose.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect zombiezen.com/go/capnproto2 v2.18.0+incompatible ) -require gopkg.in/coreos/go-oidc.v2 v2.2.1 - require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect diff --git a/go.sum b/go.sum index 44c59257..ed534dcb 100644 --- a/go.sum +++ b/go.sum @@ -131,6 +131,8 @@ github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/coredns v1.8.7 h1:wVMjAnyFnY7Mc18AFO+9qbGD6ODPtdVUIlzoWrHr3hk= github.com/coredns/coredns v1.8.7/go.mod h1:bFmbgEfeRz5aizL2VsQ5LRlsvJuXWkgG/MWG9zxqjVM= +github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 h1:7CNPV0LWRCa1FNmqg700pbXhzvmoaXKyfxWRkjRym7Q= +github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1050,15 +1052,15 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/coreos/go-oidc.v2 v2.2.1 h1:MY5SZClJ7vhjKfr64a4nHAOV/c3WH2gB9BMrR64J1Mc= -gopkg.in/coreos/go-oidc.v2 v2.2.1/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= +gopkg.in/coreos/go-oidc.v2 v2.1.0 h1:E8PjVFdj/SLDKB0hvb70KTbMbYVHjqztiQdSkIg8E+I= +gopkg.in/coreos/go-oidc.v2 v2.1.0/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A= +gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/sshgen/sshgen.go b/sshgen/sshgen.go index 27e73715..9fba2053 100644 --- a/sshgen/sshgen.go +++ b/sshgen/sshgen.go @@ -15,10 +15,10 @@ import ( "net/url" "time" + "github.com/coreos/go-oidc/jose" homedir "github.com/mitchellh/go-homedir" "github.com/pkg/errors" gossh "golang.org/x/crypto/ssh" - "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -87,33 +87,37 @@ func SignCert(token, pubKey string) (string, error) { return "", errors.New("invalid token") } - parsedToken, err := jwt.ParseSigned(token) + jwt, err := jose.ParseJWT(token) if err != nil { return "", errors.Wrap(err, "failed to parse JWT") } - claims := jwt.Claims{} - err = parsedToken.UnsafeClaimsWithoutVerification(&claims) + claims, err := jwt.Claims() if err != nil { return "", errors.Wrap(err, "failed to retrieve JWT claims") } + issuer, _, err := claims.StringClaim("iss") + if err != nil { + return "", errors.Wrap(err, "failed to retrieve JWT iss") + } + buf, err := json.Marshal(&signPayload{ PublicKey: pubKey, JWT: token, - Issuer: claims.Issuer, + Issuer: issuer, }) if err != nil { return "", errors.Wrap(err, "failed to marshal signPayload") } var res *http.Response if mockRequest != nil { - res, err = mockRequest(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = mockRequest(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } else { client := http.Client{ Timeout: 10 * time.Second, } - res, err = client.Post(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = client.Post(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } if err != nil { diff --git a/sshgen/sshgen_test.go b/sshgen/sshgen_test.go index c2716f59..99ac8021 100644 --- a/sshgen/sshgen_test.go +++ b/sshgen/sshgen_test.go @@ -4,6 +4,8 @@ package sshgen import ( + "crypto/rand" + "crypto/rsa" "encoding/json" "fmt" "io" @@ -16,9 +18,8 @@ import ( "testing" "time" + "github.com/coreos/go-oidc/jose" "github.com/stretchr/testify/assert" - "gopkg.in/square/go-jose.v2" - "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -96,25 +97,22 @@ func TestCertGenSuccess(t *testing.T) { } func tokenGenerator() string { - iat := time.Now() - exp := time.Now().Add(time.Minute * 5) + iat := time.Now().Unix() + exp := time.Now().Add(time.Minute * 5).Unix() + claims := jose.Claims{} + claims.Add("aud", audTest) + claims.Add("iat", iat) + claims.Add("nonce", nonceTest) + claims.Add("exp", exp) - claims := jwt.Claims{ - Audience: jwt.Audience{audTest}, - IssuedAt: jwt.NewNumericDate(iat), - Expiry: jwt.NewNumericDate(exp), - } - - key := []byte("secret") - signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT")) + k, err := rsa.GenerateKey(rand.Reader, 512) if err != nil { - panic(err) + return "" } - - signedToken, err := jwt.Signed(signer).Claims(claims).CompactSerialize() - if err != nil { - panic(err) + signer := jose.NewSignerRSA("asdf", *k) + token, terr := jose.NewSignedJWT(claims, signer) + if terr != nil { + return "" } - - return signedToken + return token.Encode() } diff --git a/token/token.go b/token/token.go index abccf257..d4f237a9 100644 --- a/token/token.go +++ b/token/token.go @@ -13,9 +13,9 @@ import ( "syscall" "time" + "github.com/coreos/go-oidc/jose" "github.com/pkg/errors" "github.com/rs/zerolog" - "gopkg.in/square/go-jose.v2" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/retry" @@ -342,7 +342,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) + err = json.Unmarshal(token.Payload, &payload) if err != nil { return "", err } @@ -351,7 +351,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { err := os.Remove(path) return "", err } - return token.CompactSerialize() + return token.Encode(), nil } func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { @@ -364,7 +364,7 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) + err = json.Unmarshal(token.Payload, &payload) if err != nil { return "", err } @@ -373,21 +373,22 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { err := os.Remove(path) return "", err } - return token.CompactSerialize() + return token.Encode(), nil } // GetTokenIfExists will return the token from local storage if it exists and not expired -func getTokenIfExists(path string) (*jose.JSONWebSignature, error) { +func getTokenIfExists(path string) (*jose.JWT, error) { content, err := ioutil.ReadFile(path) if err != nil { return nil, err } - token, err := jose.ParseSigned(string(content)) + token, err := jose.ParseJWT(string(content)) if err != nil { return nil, err } - return token, nil + + return &token, nil } // RemoveTokenIfExists removes the a token from local storage if it exists diff --git a/vendor/github.com/coreos/go-oidc/LICENSE b/vendor/github.com/coreos/go-oidc/LICENSE new file mode 100644 index 00000000..e06d2081 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/LICENSE @@ -0,0 +1,202 @@ +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/coreos/go-oidc/NOTICE b/vendor/github.com/coreos/go-oidc/NOTICE new file mode 100644 index 00000000..b39ddfa5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2014 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-oidc/jose/claims.go b/vendor/github.com/coreos/go-oidc/jose/claims.go new file mode 100644 index 00000000..8b48bfd2 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/claims.go @@ -0,0 +1,126 @@ +package jose + +import ( + "encoding/json" + "fmt" + "math" + "time" +) + +type Claims map[string]interface{} + +func (c Claims) Add(name string, value interface{}) { + c[name] = value +} + +func (c Claims) StringClaim(name string) (string, bool, error) { + cl, ok := c[name] + if !ok { + return "", false, nil + } + + v, ok := cl.(string) + if !ok { + return "", false, fmt.Errorf("unable to parse claim as string: %v", name) + } + + return v, true, nil +} + +func (c Claims) StringsClaim(name string) ([]string, bool, error) { + cl, ok := c[name] + if !ok { + return nil, false, nil + } + + if v, ok := cl.([]string); ok { + return v, true, nil + } + + // When unmarshaled, []string will become []interface{}. + if v, ok := cl.([]interface{}); ok { + var ret []string + for _, vv := range v { + str, ok := vv.(string) + if !ok { + return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) + } + ret = append(ret, str) + } + return ret, true, nil + } + + return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) +} + +func (c Claims) Int64Claim(name string) (int64, bool, error) { + cl, ok := c[name] + if !ok { + return 0, false, nil + } + + v, ok := cl.(int64) + if !ok { + vf, ok := cl.(float64) + if !ok { + return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name) + } + v = int64(vf) + } + + return v, true, nil +} + +func (c Claims) Float64Claim(name string) (float64, bool, error) { + cl, ok := c[name] + if !ok { + return 0, false, nil + } + + v, ok := cl.(float64) + if !ok { + vi, ok := cl.(int64) + if !ok { + return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name) + } + v = float64(vi) + } + + return v, true, nil +} + +func (c Claims) TimeClaim(name string) (time.Time, bool, error) { + v, ok, err := c.Float64Claim(name) + if !ok || err != nil { + return time.Time{}, ok, err + } + + s := math.Trunc(v) + ns := (v - s) * math.Pow(10, 9) + return time.Unix(int64(s), int64(ns)).UTC(), true, nil +} + +func decodeClaims(payload []byte) (Claims, error) { + var c Claims + if err := json.Unmarshal(payload, &c); err != nil { + return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err) + } + return c, nil +} + +func marshalClaims(c Claims) ([]byte, error) { + b, err := json.Marshal(c) + if err != nil { + return nil, err + } + return b, nil +} + +func encodeClaims(c Claims) (string, error) { + b, err := marshalClaims(c) + if err != nil { + return "", err + } + + return encodeSegment(b), nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/doc.go b/vendor/github.com/coreos/go-oidc/jose/doc.go new file mode 100644 index 00000000..b5e13217 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/doc.go @@ -0,0 +1,2 @@ +// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead. +package jose diff --git a/vendor/github.com/coreos/go-oidc/jose/jose.go b/vendor/github.com/coreos/go-oidc/jose/jose.go new file mode 100644 index 00000000..62099265 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jose.go @@ -0,0 +1,112 @@ +package jose + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "strings" +) + +const ( + HeaderMediaType = "typ" + HeaderKeyAlgorithm = "alg" + HeaderKeyID = "kid" +) + +const ( + // Encryption Algorithm Header Parameter Values for JWS + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6 + AlgHS256 = "HS256" + AlgHS384 = "HS384" + AlgHS512 = "HS512" + AlgRS256 = "RS256" + AlgRS384 = "RS384" + AlgRS512 = "RS512" + AlgES256 = "ES256" + AlgES384 = "ES384" + AlgES512 = "ES512" + AlgPS256 = "PS256" + AlgPS384 = "PS384" + AlgPS512 = "PS512" + AlgNone = "none" +) + +const ( + // Algorithm Header Parameter Values for JWE + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1 + AlgRSA15 = "RSA1_5" + AlgRSAOAEP = "RSA-OAEP" + AlgRSAOAEP256 = "RSA-OAEP-256" + AlgA128KW = "A128KW" + AlgA192KW = "A192KW" + AlgA256KW = "A256KW" + AlgDir = "dir" + AlgECDHES = "ECDH-ES" + AlgECDHESA128KW = "ECDH-ES+A128KW" + AlgECDHESA192KW = "ECDH-ES+A192KW" + AlgECDHESA256KW = "ECDH-ES+A256KW" + AlgA128GCMKW = "A128GCMKW" + AlgA192GCMKW = "A192GCMKW" + AlgA256GCMKW = "A256GCMKW" + AlgPBES2HS256A128KW = "PBES2-HS256+A128KW" + AlgPBES2HS384A192KW = "PBES2-HS384+A192KW" + AlgPBES2HS512A256KW = "PBES2-HS512+A256KW" +) + +const ( + // Encryption Algorithm Header Parameter Values for JWE + // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22 + EncA128CBCHS256 = "A128CBC-HS256" + EncA128CBCHS384 = "A128CBC-HS384" + EncA256CBCHS512 = "A256CBC-HS512" + EncA128GCM = "A128GCM" + EncA192GCM = "A192GCM" + EncA256GCM = "A256GCM" +) + +type JOSEHeader map[string]string + +func (j JOSEHeader) Validate() error { + if _, exists := j[HeaderKeyAlgorithm]; !exists { + return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm) + } + + return nil +} + +func decodeHeader(seg string) (JOSEHeader, error) { + b, err := decodeSegment(seg) + if err != nil { + return nil, err + } + + var h JOSEHeader + err = json.Unmarshal(b, &h) + if err != nil { + return nil, err + } + + return h, nil +} + +func encodeHeader(h JOSEHeader) (string, error) { + b, err := json.Marshal(h) + if err != nil { + return "", err + } + + return encodeSegment(b), nil +} + +// Decode JWT specific base64url encoding with padding stripped +func decodeSegment(seg string) ([]byte, error) { + if l := len(seg) % 4; l != 0 { + seg += strings.Repeat("=", 4-l) + } + return base64.URLEncoding.DecodeString(seg) +} + +// Encode JWT specific base64url encoding with padding stripped +func encodeSegment(seg []byte) string { + return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go new file mode 100644 index 00000000..119f073f --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwk.go @@ -0,0 +1,135 @@ +package jose + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "encoding/json" + "math/big" + "strings" +) + +// JSON Web Key +// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5 +type JWK struct { + ID string + Type string + Alg string + Use string + Exponent int + Modulus *big.Int + Secret []byte +} + +type jwkJSON struct { + ID string `json:"kid"` + Type string `json:"kty"` + Alg string `json:"alg"` + Use string `json:"use"` + Exponent string `json:"e"` + Modulus string `json:"n"` +} + +func (j *JWK) MarshalJSON() ([]byte, error) { + t := jwkJSON{ + ID: j.ID, + Type: j.Type, + Alg: j.Alg, + Use: j.Use, + Exponent: encodeExponent(j.Exponent), + Modulus: encodeModulus(j.Modulus), + } + + return json.Marshal(&t) +} + +func (j *JWK) UnmarshalJSON(data []byte) error { + var t jwkJSON + err := json.Unmarshal(data, &t) + if err != nil { + return err + } + + e, err := decodeExponent(t.Exponent) + if err != nil { + return err + } + + n, err := decodeModulus(t.Modulus) + if err != nil { + return err + } + + j.ID = t.ID + j.Type = t.Type + j.Alg = t.Alg + j.Use = t.Use + j.Exponent = e + j.Modulus = n + + return nil +} + +type JWKSet struct { + Keys []JWK `json:"keys"` +} + +func decodeExponent(e string) (int, error) { + decE, err := decodeBase64URLPaddingOptional(e) + if err != nil { + return 0, err + } + var eBytes []byte + if len(decE) < 8 { + eBytes = make([]byte, 8-len(decE), 8) + eBytes = append(eBytes, decE...) + } else { + eBytes = decE + } + eReader := bytes.NewReader(eBytes) + var E uint64 + err = binary.Read(eReader, binary.BigEndian, &E) + if err != nil { + return 0, err + } + return int(E), nil +} + +func encodeExponent(e int) string { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, uint64(e)) + var idx int + for ; idx < 8; idx++ { + if b[idx] != 0x0 { + break + } + } + return base64.RawURLEncoding.EncodeToString(b[idx:]) +} + +// Turns a URL encoded modulus of a key into a big int. +func decodeModulus(n string) (*big.Int, error) { + decN, err := decodeBase64URLPaddingOptional(n) + if err != nil { + return nil, err + } + N := big.NewInt(0) + N.SetBytes(decN) + return N, nil +} + +func encodeModulus(n *big.Int) string { + return base64.RawURLEncoding.EncodeToString(n.Bytes()) +} + +// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not. +// The stdlib version currently doesn't handle this. +// We can get rid of this is if this bug: +// https://github.com/golang/go/issues/4237 +// ever closes. +func decodeBase64URLPaddingOptional(e string) ([]byte, error) { + if m := len(e) % 4; m != 0 { + e += strings.Repeat("=", 4-m) + } + return base64.URLEncoding.DecodeString(e) +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jws.go b/vendor/github.com/coreos/go-oidc/jose/jws.go new file mode 100644 index 00000000..1049ece8 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jws.go @@ -0,0 +1,51 @@ +package jose + +import ( + "fmt" + "strings" +) + +type JWS struct { + RawHeader string + Header JOSEHeader + RawPayload string + Payload []byte + Signature []byte +} + +// Given a raw encoded JWS token parses it and verifies the structure. +func ParseJWS(raw string) (JWS, error) { + parts := strings.Split(raw, ".") + if len(parts) != 3 { + return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts)) + } + + rawSig := parts[2] + jws := JWS{ + RawHeader: parts[0], + RawPayload: parts[1], + } + + header, err := decodeHeader(jws.RawHeader) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err) + } + if err = header.Validate(); err != nil { + return JWS{}, fmt.Errorf("malformed JWS, %s", err) + } + jws.Header = header + + payload, err := decodeSegment(jws.RawPayload) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err) + } + jws.Payload = payload + + sig, err := decodeSegment(rawSig) + if err != nil { + return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err) + } + jws.Signature = sig + + return jws, nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt.go b/vendor/github.com/coreos/go-oidc/jose/jwt.go new file mode 100644 index 00000000..3b3e9634 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/jwt.go @@ -0,0 +1,82 @@ +package jose + +import "strings" + +type JWT JWS + +func ParseJWT(token string) (jwt JWT, err error) { + jws, err := ParseJWS(token) + if err != nil { + return + } + + return JWT(jws), nil +} + +func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) { + jwt = JWT{} + + jwt.Header = header + jwt.Header[HeaderMediaType] = "JWT" + + claimBytes, err := marshalClaims(claims) + if err != nil { + return + } + jwt.Payload = claimBytes + + eh, err := encodeHeader(header) + if err != nil { + return + } + jwt.RawHeader = eh + + ec, err := encodeClaims(claims) + if err != nil { + return + } + jwt.RawPayload = ec + + return +} + +func (j *JWT) KeyID() (string, bool) { + kID, ok := j.Header[HeaderKeyID] + return kID, ok +} + +func (j *JWT) Claims() (Claims, error) { + return decodeClaims(j.Payload) +} + +// Encoded data part of the token which may be signed. +func (j *JWT) Data() string { + return strings.Join([]string{j.RawHeader, j.RawPayload}, ".") +} + +// Full encoded JWT token string in format: header.claims.signature +func (j *JWT) Encode() string { + d := j.Data() + s := encodeSegment(j.Signature) + return strings.Join([]string{d, s}, ".") +} + +func NewSignedJWT(claims Claims, s Signer) (*JWT, error) { + header := JOSEHeader{ + HeaderKeyAlgorithm: s.Alg(), + HeaderKeyID: s.ID(), + } + + jwt, err := NewJWT(header, claims) + if err != nil { + return nil, err + } + + sig, err := s.Sign([]byte(jwt.Data())) + if err != nil { + return nil, err + } + jwt.Signature = sig + + return &jwt, nil +} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig.go b/vendor/github.com/coreos/go-oidc/jose/sig.go new file mode 100644 index 00000000..7b2b253c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/sig.go @@ -0,0 +1,24 @@ +package jose + +import ( + "fmt" +) + +type Verifier interface { + ID() string + Alg() string + Verify(sig []byte, data []byte) error +} + +type Signer interface { + Verifier + Sign(data []byte) (sig []byte, err error) +} + +func NewVerifier(jwk JWK) (Verifier, error) { + if jwk.Type != "RSA" { + return nil, fmt.Errorf("unsupported key type %q", jwk.Type) + } + + return NewVerifierRSA(jwk) +} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go new file mode 100644 index 00000000..004e45dd --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go @@ -0,0 +1,67 @@ +package jose + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "fmt" +) + +type VerifierRSA struct { + KeyID string + Hash crypto.Hash + PublicKey rsa.PublicKey +} + +type SignerRSA struct { + PrivateKey rsa.PrivateKey + VerifierRSA +} + +func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) { + if jwk.Alg != "" && jwk.Alg != "RS256" { + return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg) + } + + v := VerifierRSA{ + KeyID: jwk.ID, + PublicKey: rsa.PublicKey{ + N: jwk.Modulus, + E: jwk.Exponent, + }, + Hash: crypto.SHA256, + } + + return &v, nil +} + +func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA { + return &SignerRSA{ + PrivateKey: key, + VerifierRSA: VerifierRSA{ + KeyID: kid, + PublicKey: key.PublicKey, + Hash: crypto.SHA256, + }, + } +} + +func (v *VerifierRSA) ID() string { + return v.KeyID +} + +func (v *VerifierRSA) Alg() string { + return "RS256" +} + +func (v *VerifierRSA) Verify(sig []byte, data []byte) error { + h := v.Hash.New() + h.Write(data) + return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig) +} + +func (s *SignerRSA) Sign(data []byte) ([]byte, error) { + h := s.Hash.New() + h.Write(data) + return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil)) +} diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml index 3fddaaac..6ff9dd96 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml +++ b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml @@ -1,13 +1,13 @@ language: go go: - - "1.12" - - "1.13" + - "1.9" + - "1.10" install: - go get -v -t github.com/coreos/go-oidc/... - go get golang.org/x/tools/cmd/cover - - go get golang.org/x/lint/golint + - go get github.com/golang/lint/golint script: - ./test diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go index b39cb515..508b39d3 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go @@ -69,7 +69,6 @@ type Provider struct { authURL string tokenURL string userInfoURL string - algorithms []string // Raw claims returned by the server. rawClaims []byte @@ -83,27 +82,11 @@ type cachedKeys struct { } type providerJSON struct { - Issuer string `json:"issuer"` - AuthURL string `json:"authorization_endpoint"` - TokenURL string `json:"token_endpoint"` - JWKSURL string `json:"jwks_uri"` - UserInfoURL string `json:"userinfo_endpoint"` - Algorithms []string `json:"id_token_signing_alg_values_supported"` -} - -// supportedAlgorithms is a list of algorithms explicitly supported by this -// package. If a provider supports other algorithms, such as HS256 or none, -// those values won't be passed to the IDTokenVerifier. -var supportedAlgorithms = map[string]bool{ - RS256: true, - RS384: true, - RS512: true, - ES256: true, - ES384: true, - ES512: true, - PS256: true, - PS384: true, - PS512: true, + Issuer string `json:"issuer"` + AuthURL string `json:"authorization_endpoint"` + TokenURL string `json:"token_endpoint"` + JWKSURL string `json:"jwks_uri"` + UserInfoURL string `json:"userinfo_endpoint"` } // NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. @@ -140,18 +123,11 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { if p.Issuer != issuer { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } - var algs []string - for _, a := range p.Algorithms { - if supportedAlgorithms[a] { - algs = append(algs, a) - } - } return &Provider{ issuer: p.Issuer, authURL: p.AuthURL, tokenURL: p.TokenURL, userInfoURL: p.UserInfoURL, - algorithms: algs, rawClaims: body, remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), }, nil diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go index d43f0662..ff7555db 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go @@ -79,9 +79,7 @@ type Config struct { ClientID string // If specified, only this set of algorithms may be used to sign the JWT. // - // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this - // defaults to the set of algorithms the provider supports. Otherwise this values - // defaults to RS256. + // Since many providers only support RS256, SupportedSigningAlgs defaults to this value. SupportedSigningAlgs []string // If true, no ClientID check performed. Must be true if ClientID field is empty. @@ -107,13 +105,6 @@ type Config struct { // The returned IDTokenVerifier is tied to the Provider's context and its behavior is // undefined once the Provider's context is canceled. func (p *Provider) Verifier(config *Config) *IDTokenVerifier { - if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { - // Make a copy so we don't modify the config values. - cp := &Config{} - *cp = *config - cp.SupportedSigningAlgs = p.algorithms - config = cp - } return NewVerifier(p.issuer, p.remoteKeySet, config) } diff --git a/vendor/gopkg.in/square/go-jose.v2/.gitignore b/vendor/gopkg.in/square/go-jose.v2/.gitignore index 95a85158..5b4d73b6 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.gitignore +++ b/vendor/gopkg.in/square/go-jose.v2/.gitignore @@ -5,4 +5,3 @@ *.pem *.cov jose-util/jose-util -jose-util.t.err \ No newline at end of file diff --git a/vendor/gopkg.in/square/go-jose.v2/.travis.yml b/vendor/gopkg.in/square/go-jose.v2/.travis.yml index 391b99a4..9ab2abf6 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.travis.yml +++ b/vendor/gopkg.in/square/go-jose.v2/.travis.yml @@ -8,8 +8,8 @@ matrix: - go: tip go: -- '1.14.x' -- '1.15.x' +- '1.11.x' +- '1.12.x' - tip go_import_path: gopkg.in/square/go-jose.v2 @@ -26,8 +26,6 @@ before_install: - go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls - go get github.com/stretchr/testify/assert -- go get github.com/stretchr/testify/require -- go get github.com/google/go-cmp/cmp - go get golang.org/x/tools/cmd/cover || true - go get code.google.com/p/go.tools/cmd/cover || true - pip install cram --user @@ -37,9 +35,10 @@ script: - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov - go test ./json -v # no coverage for forked encoding/json package -- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util +- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t - cd .. after_success: - gocovmerge *.cov */*.cov > merged.coverprofile - $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci + diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go index f6465c04..126b85ce 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go @@ -150,7 +150,7 @@ func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { return hmac.Sum(nil)[:ctx.authtagBytes] } -// resize ensures that the given slice has a capacity of at least n bytes. +// resize ensures the the given slice has a capacity of at least n bytes. // If the capacity of the slice is less than n, a new slice is allocated // and the existing data will be copied. func resize(in []byte, n uint64) (head, tail []byte) { diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go index be7433e2..d24cabf6 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go @@ -216,7 +216,6 @@ func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *Encrypter if opts != nil { encrypter.compressionAlg = opts.Compression - encrypter.extraHeaders = opts.ExtraHeaders } for _, recipient := range rcpts { diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go index 70f7385c..b9687c64 100644 --- a/vendor/gopkg.in/square/go-jose.v2/encoding.go +++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go @@ -23,12 +23,13 @@ import ( "encoding/binary" "io" "math/big" - "strings" - "unicode" + "regexp" "gopkg.in/square/go-jose.v2/json" ) +var stripWhitespaceRegex = regexp.MustCompile("\\s") + // Helper function to serialize known-good objects. // Precondition: value is not a nil pointer. func mustSerializeJSON(value interface{}) []byte { @@ -55,14 +56,7 @@ func mustSerializeJSON(value interface{}) []byte { // Strip all newlines and whitespace func stripWhitespace(data string) string { - buf := strings.Builder{} - buf.Grow(len(data)) - for _, r := range data { - if !unicode.IsSpace(r) { - buf.WriteRune(r) - } - } - return buf.String() + return stripWhitespaceRegex.ReplaceAllString(data, "") } // Perform compression based on algorithm diff --git a/vendor/gopkg.in/square/go-jose.v2/json/decode.go b/vendor/gopkg.in/square/go-jose.v2/json/decode.go index 4dbc4146..37457e5a 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/decode.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/decode.go @@ -13,7 +13,6 @@ import ( "encoding/base64" "errors" "fmt" - "math" "reflect" "runtime" "strconv" @@ -246,18 +245,6 @@ func isValidNumber(s string) bool { return s == "" } -type NumberUnmarshalType int - -const ( - // unmarshal a JSON number into an interface{} as a float64 - UnmarshalFloat NumberUnmarshalType = iota - // unmarshal a JSON number into an interface{} as a `json.Number` - UnmarshalJSONNumber - // unmarshal a JSON number into an interface{} as a int64 - // if value is an integer otherwise float64 - UnmarshalIntOrFloat -) - // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -265,7 +252,7 @@ type decodeState struct { scan scanner nextscan scanner // for calls to nextValue savedError error - numberType NumberUnmarshalType + useNumber bool } // errPhase is used for errors that should not happen unless @@ -736,38 +723,17 @@ func (d *decodeState) literal(v reflect.Value) { d.literalStore(d.data[start:d.off], v, false) } -// convertNumber converts the number literal s to a float64, int64 or a Number -// depending on d.numberDecodeType. +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. func (d *decodeState) convertNumber(s string) (interface{}, error) { - switch d.numberType { - - case UnmarshalJSONNumber: + if d.useNumber { return Number(s), nil - case UnmarshalIntOrFloat: - v, err := strconv.ParseInt(s, 10, 64) - if err == nil { - return v, nil - } - - // tries to parse integer number in scientific notation - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - - // if it has no decimal value use int64 - if fi, fd := math.Modf(f); fd == 0.0 { - return int64(fi), nil - } - return f, nil - default: - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - return f, nil } - + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil } var numberType = reflect.TypeOf(Number("")) diff --git a/vendor/gopkg.in/square/go-jose.v2/json/stream.go b/vendor/gopkg.in/square/go-jose.v2/json/stream.go index 9b2b926b..8ddcf4d2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/stream.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/stream.go @@ -31,14 +31,9 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } -// Deprecated: Use `SetNumberType` instead // UseNumber causes the Decoder to unmarshal a number into an interface{} as a // Number instead of as a float64. -func (dec *Decoder) UseNumber() { dec.d.numberType = UnmarshalJSONNumber } - -// SetNumberType causes the Decoder to unmarshal a number into an interface{} as a -// Number, float64 or int64 depending on `t` enum value. -func (dec *Decoder) SetNumberType(t NumberUnmarshalType) { dec.d.numberType = t } +func (dec *Decoder) UseNumber() { dec.d.useNumber = true } // Decode reads the next JSON-encoded value from its // input and stores it in the value pointed to by v. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk.go b/vendor/gopkg.in/square/go-jose.v2/jwk.go index 222e260c..936a0202 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk.go @@ -17,20 +17,15 @@ package jose import ( - "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" - "crypto/sha1" - "crypto/sha256" "crypto/x509" "encoding/base64" - "encoding/hex" "errors" "fmt" "math/big" - "net/url" "reflect" "strings" @@ -62,31 +57,16 @@ type rawJSONWebKey struct { Dq *byteBuffer `json:"dq,omitempty"` Qi *byteBuffer `json:"qi,omitempty"` // Certificates - X5c []string `json:"x5c,omitempty"` - X5u *url.URL `json:"x5u,omitempty"` - X5tSHA1 string `json:"x5t,omitempty"` - X5tSHA256 string `json:"x5t#S256,omitempty"` + X5c []string `json:"x5c,omitempty"` } // JSONWebKey represents a public or private key in JWK format. type JSONWebKey struct { - // Cryptographic key, can be a symmetric or asymmetric key. - Key interface{} - // Key identifier, parsed from `kid` header. - KeyID string - // Key algorithm, parsed from `alg` header. - Algorithm string - // Key use, parsed from `use` header. - Use string - - // X.509 certificate chain, parsed from `x5c` header. + Key interface{} Certificates []*x509.Certificate - // X.509 certificate URL, parsed from `x5u` header. - CertificatesURL *url.URL - // X.509 certificate thumbprint (SHA-1), parsed from `x5t` header. - CertificateThumbprintSHA1 []byte - // X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header. - CertificateThumbprintSHA256 []byte + KeyID string + Algorithm string + Use string } // MarshalJSON serializes the given key to its JSON representation. @@ -125,39 +105,6 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) } - x5tSHA1Len := len(k.CertificateThumbprintSHA1) - x5tSHA256Len := len(k.CertificateThumbprintSHA256) - if x5tSHA1Len > 0 { - if x5tSHA1Len != sha1.Size { - return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) - } - raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) - } - if x5tSHA256Len > 0 { - if x5tSHA256Len != sha256.Size { - return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) - } - raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) - } - - // If cert chain is attached (as opposed to being behind a URL), check the - // keys thumbprints to make sure they match what is expected. This is to - // ensure we don't accidentally produce a JWK with semantically inconsistent - // data in the headers. - if len(k.Certificates) > 0 { - expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) - expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) - - if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { - return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") - } - if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { - return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") - } - } - - raw.X5u = k.CertificatesURL - return json.Marshal(raw) } @@ -169,61 +116,28 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { return err } - certs, err := parseCertificateChain(raw.X5c) - if err != nil { - return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) - } - var key interface{} - var certPub interface{} - var keyPub interface{} - - if len(certs) > 0 { - // We need to check that leaf public key matches the key embedded in this - // JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise - // the JWK parsed could be semantically invalid. Technically, should also - // check key usage fields and other extensions on the cert here, but the - // standard doesn't exactly explain how they're supposed to map from the - // JWK representation to the X.509 extensions. - certPub = certs[0].PublicKey - } - switch raw.Kty { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() - if err == nil { - keyPub = key.(*ecdsa.PrivateKey).Public() - } } else { key, err = raw.ecPublicKey() - keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() - if err == nil { - keyPub = key.(*rsa.PrivateKey).Public() - } } else { key, err = raw.rsaPublicKey() - keyPub = key } case "oct": - if certPub != nil { - return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") - } key, err = raw.symmetricKey() case "OKP": if raw.Crv == "Ed25519" && raw.X != nil { if raw.D != nil { key, err = raw.edPrivateKey() - if err == nil { - keyPub = key.(ed25519.PrivateKey).Public() - } } else { key, err = raw.edPublicKey() - keyPub = key } } else { err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) @@ -232,78 +146,12 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) } - if err != nil { - return - } + if err == nil { + *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} - if certPub != nil && keyPub != nil { - if !reflect.DeepEqual(certPub, keyPub) { - return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match") - } - } - - *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} - - k.CertificatesURL = raw.X5u - - // x5t parameters are base64url-encoded SHA thumbprints - // See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8 - x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) - if err != nil { - return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") - } - - // RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex, - // for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded - // checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll - // try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum. - if len(x5tSHA1bytes) == 2*sha1.Size { - hx, err := hex.DecodeString(string(x5tSHA1bytes)) + k.Certificates, err = parseCertificateChain(raw.X5c) if err != nil { - return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) - - } - x5tSHA1bytes = hx - } - - k.CertificateThumbprintSHA1 = x5tSHA1bytes - - x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) - if err != nil { - return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") - } - - if len(x5tSHA256bytes) == 2*sha256.Size { - hx256, err := hex.DecodeString(string(x5tSHA256bytes)) - if err != nil { - return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) - } - x5tSHA256bytes = hx256 - } - - k.CertificateThumbprintSHA256 = x5tSHA256bytes - - x5tSHA1Len := len(k.CertificateThumbprintSHA1) - x5tSHA256Len := len(k.CertificateThumbprintSHA256) - if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { - return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") - } - if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { - return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") - } - - // If certificate chain *and* thumbprints are set, verify correctness. - if len(k.Certificates) > 0 { - leaf := k.Certificates[0] - sha1sum := sha1.Sum(leaf.Raw) - sha256sum := sha256.Sum256(leaf.Raw) - - if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { - return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") - } - - if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { - return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") + return fmt.Errorf("failed to unmarshal x5c field: %s", err) } } @@ -332,7 +180,7 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` -const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}` +const edThumbprintTemplate = `{"crv":"%s","kty":"OKP",x":"%s"}` func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { coordLength := curveSize(curve) @@ -406,7 +254,7 @@ func (k *JSONWebKey) IsPublic() bool { } } -// Public creates JSONWebKey with corresponding public key if JWK represents asymmetric private key. +// Public creates JSONWebKey with corresponding publik key if JWK represents asymmetric private key. func (k *JSONWebKey) Public() JSONWebKey { if k.IsPublic() { return *k diff --git a/vendor/gopkg.in/square/go-jose.v2/jws.go b/vendor/gopkg.in/square/go-jose.v2/jws.go index 7e261f93..e52a4766 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws.go @@ -102,14 +102,14 @@ func (sig Signature) mergedHeaders() rawHeader { } // Compute data to be signed -func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) { +func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte { var authData bytes.Buffer protectedHeader := new(rawHeader) if signature.original != nil && signature.original.Protected != nil { if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil { - return nil, err + panic(err) } authData.WriteString(signature.original.Protected.base64()) } else if signature.protected != nil { @@ -134,7 +134,7 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature authData.Write(payload) } - return authData.Bytes(), nil + return authData.Bytes() } // parseSignedFull parses a message in full format. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go deleted file mode 100644 index 3afa9030..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go +++ /dev/null @@ -1,334 +0,0 @@ -/*- - * Copyright 2016 Zbigniew Mandziejewicz - * Copyright 2016 Square, Inc. - * - * 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 jwt - -import ( - "bytes" - "reflect" - - "gopkg.in/square/go-jose.v2/json" - - "gopkg.in/square/go-jose.v2" -) - -// Builder is a utility for making JSON Web Tokens. Calls can be chained, and -// errors are accumulated until the final call to CompactSerialize/FullSerialize. -type Builder interface { - // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims - // into single JSON object. If you are passing private claims, make sure to set - // struct field tags to specify the name for the JSON key to be used when - // serializing. - Claims(i interface{}) Builder - // Token builds a JSONWebToken from provided data. - Token() (*JSONWebToken, error) - // FullSerialize serializes a token using the full serialization format. - FullSerialize() (string, error) - // CompactSerialize serializes a token using the compact serialization format. - CompactSerialize() (string, error) -} - -// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens. -// Calls can be chained, and errors are accumulated until final call to -// CompactSerialize/FullSerialize. -type NestedBuilder interface { - // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims - // into single JSON object. If you are passing private claims, make sure to set - // struct field tags to specify the name for the JSON key to be used when - // serializing. - Claims(i interface{}) NestedBuilder - // Token builds a NestedJSONWebToken from provided data. - Token() (*NestedJSONWebToken, error) - // FullSerialize serializes a token using the full serialization format. - FullSerialize() (string, error) - // CompactSerialize serializes a token using the compact serialization format. - CompactSerialize() (string, error) -} - -type builder struct { - payload map[string]interface{} - err error -} - -type signedBuilder struct { - builder - sig jose.Signer -} - -type encryptedBuilder struct { - builder - enc jose.Encrypter -} - -type nestedBuilder struct { - builder - sig jose.Signer - enc jose.Encrypter -} - -// Signed creates builder for signed tokens. -func Signed(sig jose.Signer) Builder { - return &signedBuilder{ - sig: sig, - } -} - -// Encrypted creates builder for encrypted tokens. -func Encrypted(enc jose.Encrypter) Builder { - return &encryptedBuilder{ - enc: enc, - } -} - -// SignedAndEncrypted creates builder for signed-then-encrypted tokens. -// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type. -func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder { - if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" { - return &nestedBuilder{ - builder: builder{ - err: ErrInvalidContentType, - }, - } - } - return &nestedBuilder{ - sig: sig, - enc: enc, - } -} - -func (b builder) claims(i interface{}) builder { - if b.err != nil { - return b - } - - m, ok := i.(map[string]interface{}) - switch { - case ok: - return b.merge(m) - case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: - m, err := normalize(i) - if err != nil { - return builder{ - err: err, - } - } - return b.merge(m) - default: - return builder{ - err: ErrInvalidClaims, - } - } -} - -func normalize(i interface{}) (map[string]interface{}, error) { - m := make(map[string]interface{}) - - raw, err := json.Marshal(i) - if err != nil { - return nil, err - } - - d := json.NewDecoder(bytes.NewReader(raw)) - d.SetNumberType(json.UnmarshalJSONNumber) - - if err := d.Decode(&m); err != nil { - return nil, err - } - - return m, nil -} - -func (b *builder) merge(m map[string]interface{}) builder { - p := make(map[string]interface{}) - for k, v := range b.payload { - p[k] = v - } - for k, v := range m { - p[k] = v - } - - return builder{ - payload: p, - } -} - -func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { - return &JSONWebToken{ - payload: p, - Headers: h, - }, nil -} - -func (b *signedBuilder) Claims(i interface{}) Builder { - return &signedBuilder{ - builder: b.builder.claims(i), - sig: b.sig, - } -} - -func (b *signedBuilder) Token() (*JSONWebToken, error) { - sig, err := b.sign() - if err != nil { - return nil, err - } - - h := make([]jose.Header, len(sig.Signatures)) - for i, v := range sig.Signatures { - h[i] = v.Header - } - - return b.builder.token(sig.Verify, h) -} - -func (b *signedBuilder) CompactSerialize() (string, error) { - sig, err := b.sign() - if err != nil { - return "", err - } - - return sig.CompactSerialize() -} - -func (b *signedBuilder) FullSerialize() (string, error) { - sig, err := b.sign() - if err != nil { - return "", err - } - - return sig.FullSerialize(), nil -} - -func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) { - if b.err != nil { - return nil, b.err - } - - p, err := json.Marshal(b.payload) - if err != nil { - return nil, err - } - - return b.sig.Sign(p) -} - -func (b *encryptedBuilder) Claims(i interface{}) Builder { - return &encryptedBuilder{ - builder: b.builder.claims(i), - enc: b.enc, - } -} - -func (b *encryptedBuilder) CompactSerialize() (string, error) { - enc, err := b.encrypt() - if err != nil { - return "", err - } - - return enc.CompactSerialize() -} - -func (b *encryptedBuilder) FullSerialize() (string, error) { - enc, err := b.encrypt() - if err != nil { - return "", err - } - - return enc.FullSerialize(), nil -} - -func (b *encryptedBuilder) Token() (*JSONWebToken, error) { - enc, err := b.encrypt() - if err != nil { - return nil, err - } - - return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) -} - -func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { - if b.err != nil { - return nil, b.err - } - - p, err := json.Marshal(b.payload) - if err != nil { - return nil, err - } - - return b.enc.Encrypt(p) -} - -func (b *nestedBuilder) Claims(i interface{}) NestedBuilder { - return &nestedBuilder{ - builder: b.builder.claims(i), - sig: b.sig, - enc: b.enc, - } -} - -func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) { - enc, err := b.signAndEncrypt() - if err != nil { - return nil, err - } - - return &NestedJSONWebToken{ - enc: enc, - Headers: []jose.Header{enc.Header}, - }, nil -} - -func (b *nestedBuilder) CompactSerialize() (string, error) { - enc, err := b.signAndEncrypt() - if err != nil { - return "", err - } - - return enc.CompactSerialize() -} - -func (b *nestedBuilder) FullSerialize() (string, error) { - enc, err := b.signAndEncrypt() - if err != nil { - return "", err - } - - return enc.FullSerialize(), nil -} - -func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) { - if b.err != nil { - return nil, b.err - } - - p, err := json.Marshal(b.payload) - if err != nil { - return nil, err - } - - sig, err := b.sig.Sign(p) - if err != nil { - return nil, err - } - - p2, err := sig.CompactSerialize() - if err != nil { - return nil, err - } - - return b.enc.Encrypt([]byte(p2)) -} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go deleted file mode 100644 index 5f40ef3b..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go +++ /dev/null @@ -1,121 +0,0 @@ -/*- - * Copyright 2016 Zbigniew Mandziejewicz - * Copyright 2016 Square, Inc. - * - * 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 jwt - -import ( - "strconv" - "time" - - "gopkg.in/square/go-jose.v2/json" -) - -// Claims represents public claim values (as specified in RFC 7519). -type Claims struct { - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - Audience Audience `json:"aud,omitempty"` - Expiry *NumericDate `json:"exp,omitempty"` - NotBefore *NumericDate `json:"nbf,omitempty"` - IssuedAt *NumericDate `json:"iat,omitempty"` - ID string `json:"jti,omitempty"` -} - -// NumericDate represents date and time as the number of seconds since the -// epoch, ignoring leap seconds. Non-integer values can be represented -// in the serialized format, but we round to the nearest second. -// See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2 -type NumericDate int64 - -// NewNumericDate constructs NumericDate from time.Time value. -func NewNumericDate(t time.Time) *NumericDate { - if t.IsZero() { - return nil - } - - // While RFC 7519 technically states that NumericDate values may be - // non-integer values, we don't bother serializing timestamps in - // claims with sub-second accurancy and just round to the nearest - // second instead. Not convined sub-second accuracy is useful here. - out := NumericDate(t.Unix()) - return &out -} - -// MarshalJSON serializes the given NumericDate into its JSON representation. -func (n NumericDate) MarshalJSON() ([]byte, error) { - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// UnmarshalJSON reads a date from its JSON representation. -func (n *NumericDate) UnmarshalJSON(b []byte) error { - s := string(b) - - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return ErrUnmarshalNumericDate - } - - *n = NumericDate(f) - return nil -} - -// Time returns time.Time representation of NumericDate. -func (n *NumericDate) Time() time.Time { - if n == nil { - return time.Time{} - } - return time.Unix(int64(*n), 0) -} - -// Audience represents the recipients that the token is intended for. -type Audience []string - -// UnmarshalJSON reads an audience from its JSON representation. -func (s *Audience) UnmarshalJSON(b []byte) error { - var v interface{} - if err := json.Unmarshal(b, &v); err != nil { - return err - } - - switch v := v.(type) { - case string: - *s = []string{v} - case []interface{}: - a := make([]string, len(v)) - for i, e := range v { - s, ok := e.(string) - if !ok { - return ErrUnmarshalAudience - } - a[i] = s - } - *s = a - default: - return ErrUnmarshalAudience - } - - return nil -} - -func (s Audience) Contains(v string) bool { - for _, a := range s { - if a == v { - return true - } - } - return false -} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go deleted file mode 100644 index 4cf97b54..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/*- - * Copyright 2017 Square Inc. - * - * 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 jwt provides an implementation of the JSON Web Token standard. - -*/ -package jwt diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go deleted file mode 100644 index 09f76ae4..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go +++ /dev/null @@ -1,53 +0,0 @@ -/*- - * Copyright 2016 Zbigniew Mandziejewicz - * Copyright 2016 Square, Inc. - * - * 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 jwt - -import "errors" - -// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled. -var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience") - -// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled. -var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate") - -// ErrInvalidClaims indicates that given claims have invalid type. -var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object") - -// ErrInvalidIssuer indicates invalid iss claim. -var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)") - -// ErrInvalidSubject indicates invalid sub claim. -var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)") - -// ErrInvalidAudience indicated invalid aud claim. -var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)") - -// ErrInvalidID indicates invalid jti claim. -var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)") - -// ErrNotValidYet indicates that token is used before time indicated in nbf claim. -var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)") - -// ErrExpired indicates that token is used after expiry time indicated in exp claim. -var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)") - -// ErrIssuedInTheFuture indicates that the iat field is in the future. -var ErrIssuedInTheFuture = errors.New("square/go-jose/jwt: validation field, token issued in the future (iat)") - -// ErrInvalidContentType indicates that token requires JWT cty header. -var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)") diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go deleted file mode 100644 index 47498840..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go +++ /dev/null @@ -1,169 +0,0 @@ -/*- - * Copyright 2016 Zbigniew Mandziejewicz - * Copyright 2016 Square, Inc. - * - * 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 jwt - -import ( - "fmt" - "strings" - - jose "gopkg.in/square/go-jose.v2" - "gopkg.in/square/go-jose.v2/json" -) - -// JSONWebToken represents a JSON Web Token (as specified in RFC7519). -type JSONWebToken struct { - payload func(k interface{}) ([]byte, error) - unverifiedPayload func() []byte - Headers []jose.Header -} - -type NestedJSONWebToken struct { - enc *jose.JSONWebEncryption - Headers []jose.Header -} - -// Claims deserializes a JSONWebToken into dest using the provided key. -func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { - payloadKey := tryJWKS(t.Headers, key) - - b, err := t.payload(payloadKey) - if err != nil { - return err - } - - for _, d := range dest { - if err := json.Unmarshal(b, d); err != nil { - return err - } - } - - return nil -} - -// UnsafeClaimsWithoutVerification deserializes the claims of a -// JSONWebToken into the dests. For signed JWTs, the claims are not -// verified. This function won't work for encrypted JWTs. -func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error { - if t.unverifiedPayload == nil { - return fmt.Errorf("square/go-jose: Cannot get unverified claims") - } - claims := t.unverifiedPayload() - for _, d := range dest { - if err := json.Unmarshal(claims, d); err != nil { - return err - } - } - return nil -} - -func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) { - key := tryJWKS(t.Headers, decryptionKey) - - b, err := t.enc.Decrypt(key) - if err != nil { - return nil, err - } - - sig, err := ParseSigned(string(b)) - if err != nil { - return nil, err - } - - return sig, nil -} - -// ParseSigned parses token from JWS form. -func ParseSigned(s string) (*JSONWebToken, error) { - sig, err := jose.ParseSigned(s) - if err != nil { - return nil, err - } - headers := make([]jose.Header, len(sig.Signatures)) - for i, signature := range sig.Signatures { - headers[i] = signature.Header - } - - return &JSONWebToken{ - payload: sig.Verify, - unverifiedPayload: sig.UnsafePayloadWithoutVerification, - Headers: headers, - }, nil -} - -// ParseEncrypted parses token from JWE form. -func ParseEncrypted(s string) (*JSONWebToken, error) { - enc, err := jose.ParseEncrypted(s) - if err != nil { - return nil, err - } - - return &JSONWebToken{ - payload: enc.Decrypt, - Headers: []jose.Header{enc.Header}, - }, nil -} - -// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form. -func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) { - enc, err := jose.ParseEncrypted(s) - if err != nil { - return nil, err - } - - contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string) - if strings.ToUpper(contentType) != "JWT" { - return nil, ErrInvalidContentType - } - - return &NestedJSONWebToken{ - enc: enc, - Headers: []jose.Header{enc.Header}, - }, nil -} - -func tryJWKS(headers []jose.Header, key interface{}) interface{} { - var jwks jose.JSONWebKeySet - - switch jwksType := key.(type) { - case *jose.JSONWebKeySet: - jwks = *jwksType - case jose.JSONWebKeySet: - jwks = jwksType - default: - return key - } - - var kid string - for _, header := range headers { - if header.KeyID != "" { - kid = header.KeyID - break - } - } - - if kid == "" { - return key - } - - keys := jwks.Key(kid) - if len(keys) == 0 { - return key - } - - return keys[0].Key -} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go deleted file mode 100644 index 6f3ff4e8..00000000 --- a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * Copyright 2016 Zbigniew Mandziejewicz - * Copyright 2016 Square, Inc. - * - * 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 jwt - -import "time" - -const ( - // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims. - DefaultLeeway = 1.0 * time.Minute -) - -// Expected defines values used for protected claims validation. -// If field has zero value then validation is skipped. -type Expected struct { - // Issuer matches the "iss" claim exactly. - Issuer string - // Subject matches the "sub" claim exactly. - Subject string - // Audience matches the values in "aud" claim, regardless of their order. - Audience Audience - // ID matches the "jti" claim exactly. - ID string - // Time matches the "exp", "nbf" and "iat" claims with leeway. - Time time.Time -} - -// WithTime copies expectations with new time. -func (e Expected) WithTime(t time.Time) Expected { - e.Time = t - return e -} - -// Validate checks claims in a token against expected values. -// A default leeway value of one minute is used to compare time values. -// -// The default leeway will cause the token to be deemed valid until one -// minute after the expiration time. If you're a server application that -// wants to give an extra minute to client tokens, use this -// function. If you're a client application wondering if the server -// will accept your token, use ValidateWithLeeway with a leeway <=0, -// otherwise this function might make you think a token is valid when -// it is not. -func (c Claims) Validate(e Expected) error { - return c.ValidateWithLeeway(e, DefaultLeeway) -} - -// ValidateWithLeeway checks claims in a token against expected values. A -// custom leeway may be specified for comparing time values. You may pass a -// zero value to check time values with no leeway, but you should not that -// numeric date values are rounded to the nearest second and sub-second -// precision is not supported. -// -// The leeway gives some extra time to the token from the server's -// point of view. That is, if the token is expired, ValidateWithLeeway -// will still accept the token for 'leeway' amount of time. This fails -// if you're using this function to check if a server will accept your -// token, because it will think the token is valid even after it -// expires. So if you're a client validating if the token is valid to -// be submitted to a server, use leeway <=0, if you're a server -// validation a token, use leeway >=0. -func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { - if e.Issuer != "" && e.Issuer != c.Issuer { - return ErrInvalidIssuer - } - - if e.Subject != "" && e.Subject != c.Subject { - return ErrInvalidSubject - } - - if e.ID != "" && e.ID != c.ID { - return ErrInvalidID - } - - if len(e.Audience) != 0 { - for _, v := range e.Audience { - if !c.Audience.Contains(v) { - return ErrInvalidAudience - } - } - } - - if !e.Time.IsZero() { - if c.NotBefore != nil && e.Time.Add(leeway).Before(c.NotBefore.Time()) { - return ErrNotValidYet - } - - if c.Expiry != nil && e.Time.Add(-leeway).After(c.Expiry.Time()) { - return ErrExpired - } - - // IssuedAt is optional but cannot be in the future. This is not required by the RFC, but - // something is misconfigured if this happens and we should not trust it. - if c.IssuedAt != nil && e.Time.Add(leeway).Before(c.IssuedAt.Time()) { - return ErrIssuedInTheFuture - } - } - - return nil -} diff --git a/vendor/gopkg.in/square/go-jose.v2/opaque.go b/vendor/gopkg.in/square/go-jose.v2/opaque.go index fc3e8d2e..df747f99 100644 --- a/vendor/gopkg.in/square/go-jose.v2/opaque.go +++ b/vendor/gopkg.in/square/go-jose.v2/opaque.go @@ -17,7 +17,7 @@ package jose // OpaqueSigner is an interface that supports signing payloads with opaque -// private key(s). Private key operations performed by implementers may, for +// private key(s). Private key operations preformed by implementors may, for // example, occur in a hardware module. An OpaqueSigner may rotate signing keys // transparently to the user of this interface. type OpaqueSigner interface { diff --git a/vendor/gopkg.in/square/go-jose.v2/shared.go b/vendor/gopkg.in/square/go-jose.v2/shared.go index f72e5a53..f8438641 100644 --- a/vendor/gopkg.in/square/go-jose.v2/shared.go +++ b/vendor/gopkg.in/square/go-jose.v2/shared.go @@ -183,7 +183,7 @@ type Header struct { // Unverified certificate chain parsed from x5c header. certificates []*x509.Certificate - // Any headers not recognised above get unmarshalled + // Any headers not recognised above get unmarshaled // from JSON in a generic manner and placed in this map. ExtraHeaders map[HeaderKey]interface{} } @@ -295,12 +295,12 @@ func (parsed rawHeader) getAPV() (*byteBuffer, error) { return parsed.getByteBuffer(headerAPV) } -// getIV extracts parsed "iv" from the raw JSON. +// getIV extracts parsed "iv" frpom the raw JSON. func (parsed rawHeader) getIV() (*byteBuffer, error) { return parsed.getByteBuffer(headerIV) } -// getTag extracts parsed "tag" from the raw JSON. +// getTag extracts parsed "tag" frpom the raw JSON. func (parsed rawHeader) getTag() (*byteBuffer, error) { return parsed.getByteBuffer(headerTag) } diff --git a/vendor/gopkg.in/square/go-jose.v2/signing.go b/vendor/gopkg.in/square/go-jose.v2/signing.go index bad820ce..664a51cc 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing.go @@ -370,11 +370,7 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter } } - input, err := obj.computeAuthData(payload, &signature) - if err != nil { - return ErrCryptoFailure - } - + input := obj.computeAuthData(payload, &signature) alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { @@ -425,11 +421,7 @@ outer: } } - input, err := obj.computeAuthData(payload, &signature) - if err != nil { - continue - } - + input := obj.computeAuthData(payload, &signature) alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 3b296241..648e7327 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,6 +62,9 @@ github.com/coredns/coredns/plugin/pkg/transport github.com/coredns/coredns/plugin/pkg/uniq github.com/coredns/coredns/plugin/test github.com/coredns/coredns/request +# github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 +## explicit +github.com/coreos/go-oidc/jose # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf ## explicit github.com/coreos/go-systemd/daemon @@ -527,18 +530,17 @@ google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb -# gopkg.in/coreos/go-oidc.v2 v2.2.1 +# gopkg.in/coreos/go-oidc.v2 v2.1.0 ## explicit gopkg.in/coreos/go-oidc.v2 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 ## explicit gopkg.in/natefinch/lumberjack.v2 -# gopkg.in/square/go-jose.v2 v2.6.0 +# gopkg.in/square/go-jose.v2 v2.4.0 ## explicit gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2/cipher gopkg.in/square/go-jose.v2/json -gopkg.in/square/go-jose.v2/jwt # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ## explicit gopkg.in/tomb.v1 From 1b511b2d259db8e2c220313d34b74d443189b383 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 18 Mar 2022 09:42:45 +0000 Subject: [PATCH 023/238] TUN-5881: Clarify success (or lack thereof) of (un)installing cloudflared service --- cmd/cloudflared/linux_service.go | 41 ++++++++++++++++++------------ cmd/cloudflared/macos_service.go | 35 +++++++++++++++---------- cmd/cloudflared/windows_service.go | 32 +++++++++++++---------- 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 2d3060e7..4f0b53eb 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -19,16 +19,16 @@ import ( func runApp(app *cli.App, graceShutdownC chan struct{}) { app.Commands = append(app.Commands, &cli.Command{ Name: "service", - Usage: "Manages the Cloudflare Tunnel system service", + Usage: "Manages the cloudflared system service", Subcommands: []*cli.Command{ { Name: "install", - Usage: "Install Cloudflare Tunnel as a system service", + Usage: "Install cloudflared as a system service", Action: cliutil.ConfiguredAction(installLinuxService), }, { Name: "uninstall", - Usage: "Uninstall the Cloudflare Tunnel service", + Usage: "Uninstall the cloudflared service", Action: cliutil.ConfiguredAction(uninstallLinuxService), }, }, @@ -49,7 +49,7 @@ var systemdTemplates = []ServiceTemplate{ { Path: "/etc/systemd/system/cloudflared.service", Content: `[Unit] -Description=Cloudflare Tunnel +Description=cloudflared After=network.target [Service] @@ -66,7 +66,7 @@ WantedBy=multi-user.target { Path: "/etc/systemd/system/cloudflared-update.service", Content: `[Unit] -Description=Update Cloudflare Tunnel +Description=Update cloudflared After=network.target [Service] @@ -76,7 +76,7 @@ ExecStart=/bin/bash -c '{{ .Path }} update; code=$?; if [ $code -eq 11 ]; then s { Path: "/etc/systemd/system/cloudflared-update.timer", Content: `[Unit] -Description=Update Cloudflare Tunnel +Description=Update cloudflared [Timer] OnCalendar=daily @@ -93,7 +93,7 @@ var sysvTemplate = ServiceTemplate{ Content: `#!/bin/sh # For RedHat and cousins: # chkconfig: 2345 99 01 -# description: Cloudflare Tunnel agent +# description: cloudflared # processname: {{.Path}} ### BEGIN INIT INFO # Provides: {{.Path}} @@ -101,8 +101,8 @@ var sysvTemplate = ServiceTemplate{ # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Cloudflare Tunnel -# Description: Cloudflare Tunnel agent +# Short-Description: cloudflared +# Description: cloudflared agent ### END INIT INFO name=$(basename $(readlink -f $0)) cmd="{{.Path}} --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}" @@ -212,11 +212,16 @@ func installLinuxService(c *cli.Context) error { switch { case isSystemd(): log.Info().Msgf("Using Systemd") - return installSystemd(&templateArgs, log) + err = installSystemd(&templateArgs, log) default: log.Info().Msgf("Using SysV") - return installSysv(&templateArgs, log) + err = installSysv(&templateArgs, log) } + + if err == nil { + log.Info().Msg("Linux service for cloudflared installed successfully") + } + return err } func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) { @@ -271,7 +276,7 @@ func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) erro log.Err(err).Msg("systemctl start cloudflared-update.timer error") return err } - log.Info().Msg("systemctl daemon-reload") + log.Info().Msg("running systemctl daemon-reload") return runCommand("systemctl", "daemon-reload") } @@ -301,14 +306,20 @@ func installSysv(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { func uninstallLinuxService(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) + var err error switch { case isSystemd(): log.Info().Msg("Using Systemd") - return uninstallSystemd(log) + err = uninstallSystemd(log) default: log.Info().Msg("Using SysV") - return uninstallSysv(log) + err = uninstallSysv(log) } + + if err == nil { + log.Info().Msg("Linux service for cloudflared uninstalled successfully") + } + return err } func uninstallSystemd(log *zerolog.Logger) error { @@ -326,7 +337,6 @@ func uninstallSystemd(log *zerolog.Logger) error { return err } } - log.Info().Msgf("Successfully uninstalled cloudflared service from systemd") return nil } @@ -345,6 +355,5 @@ func uninstallSysv(log *zerolog.Logger) error { continue } } - log.Info().Msgf("Successfully uninstalled cloudflared service from sysv") return nil } diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go index 542b8849..507412f7 100644 --- a/cmd/cloudflared/macos_service.go +++ b/cmd/cloudflared/macos_service.go @@ -21,16 +21,16 @@ const ( func runApp(app *cli.App, graceShutdownC chan struct{}) { app.Commands = append(app.Commands, &cli.Command{ Name: "service", - Usage: "Manages the Cloudflare Tunnel launch agent", + Usage: "Manages the cloudflared launch agent", Subcommands: []*cli.Command{ { Name: "install", - Usage: "Install Cloudflare Tunnel as an user launch agent", + Usage: "Install cloudflared as an user launch agent", Action: cliutil.ConfiguredAction(installLaunchd), }, { Name: "uninstall", - Usage: "Uninstall the Cloudflare Tunnel launch agent", + Usage: "Uninstall the cloudflared launch agent", Action: cliutil.ConfiguredAction(uninstallLaunchd), }, }, @@ -114,12 +114,12 @@ func installLaunchd(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if isRootUser() { - log.Info().Msg("Installing Cloudflare Tunnel client as a system launch daemon. " + - "Cloudflare Tunnel client will run at boot") + log.Info().Msg("Installing cloudflared client as a system launch daemon. " + + "cloudflared client will run at boot") } else { - log.Info().Msg("Installing Cloudflare Tunnel client as an user launch agent. " + - "Note that Cloudflare Tunnel client will only run when the user is logged in. " + - "If you want to run Cloudflare Tunnel client at boot, install with root permission. " + + log.Info().Msg("Installing cloudflared client as an user launch agent. " + + "Note that cloudflared client will only run when the user is logged in. " + + "If you want to run cloudflared client at boot, install with root permission. " + "For more information, visit https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/run-as-service") } etPath, err := os.Executable() @@ -163,16 +163,20 @@ func installLaunchd(c *cli.Context) error { } log.Info().Msgf("Outputs are logged to %s and %s", stderrPath, stdoutPath) - return runCommand("launchctl", "load", plistPath) + err = runCommand("launchctl", "load", plistPath) + if err == nil { + log.Info().Msg("MacOS service for cloudflared installed successfully") + } + return err } func uninstallLaunchd(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) if isRootUser() { - log.Info().Msg("Uninstalling Cloudflare Tunnel as a system launch daemon") + log.Info().Msg("Uninstalling cloudflared as a system launch daemon") } else { - log.Info().Msg("Uninstalling Cloudflare Tunnel as an user launch agent") + log.Info().Msg("Uninstalling cloudflared as a user launch agent") } installPath, err := installPath() if err != nil { @@ -194,10 +198,13 @@ func uninstallLaunchd(c *cli.Context) error { } err = runCommand("launchctl", "unload", plistPath) if err != nil { - log.Err(err).Msg("error unloading") + log.Err(err).Msg("error unloading launchd") return err } - log.Info().Msgf("Outputs are logged to %s and %s", stderrPath, stdoutPath) - return launchdTemplate.Remove() + err = launchdTemplate.Remove() + if err == nil { + log.Info().Msg("Launchd for cloudflared was uninstalled successfully") + } + return err } diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index a3d1441d..6d12191a 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -26,8 +26,8 @@ import ( const ( windowsServiceName = "Cloudflared" - windowsServiceDescription = "Cloudflare Tunnel agent" - windowsServiceUrl = "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/run-as-service#windows" + windowsServiceDescription = "Cloudflared agent" + windowsServiceUrl = "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/as-a-service/windows/" recoverActionDelay = time.Second * 20 failureCountResetPeriod = time.Hour * 24 @@ -46,16 +46,16 @@ const ( func runApp(app *cli.App, graceShutdownC chan struct{}) { app.Commands = append(app.Commands, &cli.Command{ Name: "service", - Usage: "Manages the Cloudflare Tunnel Windows service", + Usage: "Manages the cloudflared Windows service", Subcommands: []*cli.Command{ { Name: "install", - Usage: "Install Cloudflare Tunnel as a Windows service", + Usage: "Install cloudflared as a Windows service", Action: cliutil.ConfiguredAction(installWindowsService), }, { Name: "uninstall", - Usage: "Uninstall the Cloudflare Tunnel service", + Usage: "Uninstall the cloudflared service", Action: cliutil.ConfiguredAction(uninstallWindowsService), }, }, @@ -177,7 +177,7 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques func installWindowsService(c *cli.Context) error { zeroLogger := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) - zeroLogger.Info().Msg("Installing Cloudflare Tunnel Windows service") + zeroLogger.Info().Msg("Installing cloudflared Windows service") exepath, err := os.Executable() if err != nil { return errors.Wrap(err, "Cannot find path name that start the process") @@ -206,7 +206,7 @@ func installWindowsService(c *cli.Context) error { return errors.Wrap(err, "Cannot install service") } defer s.Close() - log.Info().Msg("Cloudflare Tunnel agent service is installed") + log.Info().Msg("cloudflared agent service is installed") err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info) if err != nil { s.Delete() @@ -219,7 +219,11 @@ func installWindowsService(c *cli.Context) error { log.Info().Msgf("See %s to manually configure service recovery actions", windowsServiceUrl) } - return s.Start() + err = s.Start() + if err == nil { + log.Info().Msg("Agent service for cloudflared installed successfully") + } + return err } func uninstallWindowsService(c *cli.Context) error { @@ -227,7 +231,7 @@ func uninstallWindowsService(c *cli.Context) error { With(). Str(LogFieldWindowsServiceName, windowsServiceName).Logger() - log.Info().Msg("Uninstalling Cloudflare Tunnel Windows Service") + log.Info().Msg("Uninstalling cloudflared agent service") m, err := mgr.Connect() if err != nil { return errors.Wrap(err, "Cannot establish a connection to the service control manager") @@ -235,22 +239,22 @@ func uninstallWindowsService(c *cli.Context) error { defer m.Disconnect() s, err := m.OpenService(windowsServiceName) if err != nil { - return fmt.Errorf("Service %s is not installed", windowsServiceName) + return fmt.Errorf("Agent service %s is not installed, so it could not be uninstalled", windowsServiceName) } defer s.Close() if status, err := s.Query(); err == nil && status.State == svc.Running { - log.Info().Msg("Stopping Cloudflare Tunnel agent service") + log.Info().Msg("Stopping cloudflared agent service") if _, err := s.Control(svc.Stop); err != nil { - log.Info().Err(err).Msg("Failed to stop Cloudflare Tunnel agent service, you may need to stop it manually to complete uninstall.") + log.Info().Err(err).Msg("Failed to stop cloudflared agent service, you may need to stop it manually to complete uninstall.") } } err = s.Delete() if err != nil { - return errors.Wrap(err, "Cannot delete service") + return errors.Wrap(err, "Cannot delete agent service") } - log.Info().Msg("Cloudflare Tunnel agent service is uninstalled") + log.Info().Msg("Agent service for cloudflared was uninstalled successfully") err = eventlog.Remove(windowsServiceName) if err != nil { return errors.Wrap(err, "Cannot remove event logger") From 9422ea8ed8f92a6be37555e87c1b59c2a9fa6045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Fri, 18 Mar 2022 17:11:44 +0000 Subject: [PATCH 024/238] CC-796: Remove dependency on unsupported version of go-oidc --- go.mod | 6 +- go.sum | 10 +- sshgen/sshgen.go | 18 +- sshgen/sshgen_test.go | 36 +- token/token.go | 17 +- vendor/github.com/coreos/go-oidc/LICENSE | 202 ----------- vendor/github.com/coreos/go-oidc/NOTICE | 5 - .../github.com/coreos/go-oidc/jose/claims.go | 126 ------- vendor/github.com/coreos/go-oidc/jose/doc.go | 2 - vendor/github.com/coreos/go-oidc/jose/jose.go | 112 ------ vendor/github.com/coreos/go-oidc/jose/jwk.go | 135 ------- vendor/github.com/coreos/go-oidc/jose/jws.go | 51 --- vendor/github.com/coreos/go-oidc/jose/jwt.go | 82 ----- vendor/github.com/coreos/go-oidc/jose/sig.go | 24 -- .../github.com/coreos/go-oidc/jose/sig_rsa.go | 67 ---- vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml | 6 +- vendor/gopkg.in/coreos/go-oidc.v2/oidc.go | 34 +- vendor/gopkg.in/coreos/go-oidc.v2/verify.go | 11 +- vendor/gopkg.in/square/go-jose.v2/.gitignore | 1 + vendor/gopkg.in/square/go-jose.v2/.travis.yml | 9 +- .../square/go-jose.v2/cipher/cbc_hmac.go | 2 +- vendor/gopkg.in/square/go-jose.v2/crypter.go | 1 + vendor/gopkg.in/square/go-jose.v2/encoding.go | 14 +- .../gopkg.in/square/go-jose.v2/json/decode.go | 52 ++- .../gopkg.in/square/go-jose.v2/json/stream.go | 7 +- vendor/gopkg.in/square/go-jose.v2/jwk.go | 174 ++++++++- vendor/gopkg.in/square/go-jose.v2/jws.go | 6 +- .../gopkg.in/square/go-jose.v2/jwt/builder.go | 334 ++++++++++++++++++ .../gopkg.in/square/go-jose.v2/jwt/claims.go | 121 +++++++ vendor/gopkg.in/square/go-jose.v2/jwt/doc.go | 22 ++ .../gopkg.in/square/go-jose.v2/jwt/errors.go | 53 +++ vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go | 169 +++++++++ .../square/go-jose.v2/jwt/validation.go | 114 ++++++ vendor/gopkg.in/square/go-jose.v2/opaque.go | 2 +- vendor/gopkg.in/square/go-jose.v2/shared.go | 6 +- vendor/gopkg.in/square/go-jose.v2/signing.go | 12 +- vendor/modules.txt | 8 +- 37 files changed, 1146 insertions(+), 905 deletions(-) delete mode 100644 vendor/github.com/coreos/go-oidc/LICENSE delete mode 100644 vendor/github.com/coreos/go-oidc/NOTICE delete mode 100644 vendor/github.com/coreos/go-oidc/jose/claims.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/doc.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jose.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jwk.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jws.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/jwt.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/sig.go delete mode 100644 vendor/github.com/coreos/go-oidc/jose/sig_rsa.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/builder.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/claims.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/doc.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/errors.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go create mode 100644 vendor/gopkg.in/square/go-jose.v2/jwt/validation.go diff --git a/go.mod b/go.mod index 59b8ec7c..83328b33 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 - github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -51,14 +50,15 @@ require ( golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.43.0 // indirect - gopkg.in/coreos/go-oidc.v2 v2.1.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/square/go-jose.v2 v2.4.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect zombiezen.com/go/capnproto2 v2.18.0+incompatible ) +require gopkg.in/coreos/go-oidc.v2 v2.2.1 + require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect diff --git a/go.sum b/go.sum index ed534dcb..44c59257 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,6 @@ github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/coredns v1.8.7 h1:wVMjAnyFnY7Mc18AFO+9qbGD6ODPtdVUIlzoWrHr3hk= github.com/coredns/coredns v1.8.7/go.mod h1:bFmbgEfeRz5aizL2VsQ5LRlsvJuXWkgG/MWG9zxqjVM= -github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 h1:7CNPV0LWRCa1FNmqg700pbXhzvmoaXKyfxWRkjRym7Q= -github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1052,15 +1050,15 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/coreos/go-oidc.v2 v2.1.0 h1:E8PjVFdj/SLDKB0hvb70KTbMbYVHjqztiQdSkIg8E+I= -gopkg.in/coreos/go-oidc.v2 v2.1.0/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= +gopkg.in/coreos/go-oidc.v2 v2.2.1 h1:MY5SZClJ7vhjKfr64a4nHAOV/c3WH2gB9BMrR64J1Mc= +gopkg.in/coreos/go-oidc.v2 v2.2.1/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A= -gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/sshgen/sshgen.go b/sshgen/sshgen.go index 9fba2053..27e73715 100644 --- a/sshgen/sshgen.go +++ b/sshgen/sshgen.go @@ -15,10 +15,10 @@ import ( "net/url" "time" - "github.com/coreos/go-oidc/jose" homedir "github.com/mitchellh/go-homedir" "github.com/pkg/errors" gossh "golang.org/x/crypto/ssh" + "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -87,37 +87,33 @@ func SignCert(token, pubKey string) (string, error) { return "", errors.New("invalid token") } - jwt, err := jose.ParseJWT(token) + parsedToken, err := jwt.ParseSigned(token) if err != nil { return "", errors.Wrap(err, "failed to parse JWT") } - claims, err := jwt.Claims() + claims := jwt.Claims{} + err = parsedToken.UnsafeClaimsWithoutVerification(&claims) if err != nil { return "", errors.Wrap(err, "failed to retrieve JWT claims") } - issuer, _, err := claims.StringClaim("iss") - if err != nil { - return "", errors.Wrap(err, "failed to retrieve JWT iss") - } - buf, err := json.Marshal(&signPayload{ PublicKey: pubKey, JWT: token, - Issuer: issuer, + Issuer: claims.Issuer, }) if err != nil { return "", errors.Wrap(err, "failed to marshal signPayload") } var res *http.Response if mockRequest != nil { - res, err = mockRequest(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = mockRequest(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } else { client := http.Client{ Timeout: 10 * time.Second, } - res, err = client.Post(issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) + res, err = client.Post(claims.Issuer+signEndpoint, "application/json", bytes.NewBuffer(buf)) } if err != nil { diff --git a/sshgen/sshgen_test.go b/sshgen/sshgen_test.go index 99ac8021..c2716f59 100644 --- a/sshgen/sshgen_test.go +++ b/sshgen/sshgen_test.go @@ -4,8 +4,6 @@ package sshgen import ( - "crypto/rand" - "crypto/rsa" "encoding/json" "fmt" "io" @@ -18,8 +16,9 @@ import ( "testing" "time" - "github.com/coreos/go-oidc/jose" "github.com/stretchr/testify/assert" + "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/jwt" "github.com/cloudflare/cloudflared/config" cfpath "github.com/cloudflare/cloudflared/token" @@ -97,22 +96,25 @@ func TestCertGenSuccess(t *testing.T) { } func tokenGenerator() string { - iat := time.Now().Unix() - exp := time.Now().Add(time.Minute * 5).Unix() - claims := jose.Claims{} - claims.Add("aud", audTest) - claims.Add("iat", iat) - claims.Add("nonce", nonceTest) - claims.Add("exp", exp) + iat := time.Now() + exp := time.Now().Add(time.Minute * 5) - k, err := rsa.GenerateKey(rand.Reader, 512) + claims := jwt.Claims{ + Audience: jwt.Audience{audTest}, + IssuedAt: jwt.NewNumericDate(iat), + Expiry: jwt.NewNumericDate(exp), + } + + key := []byte("secret") + signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT")) if err != nil { - return "" + panic(err) } - signer := jose.NewSignerRSA("asdf", *k) - token, terr := jose.NewSignedJWT(claims, signer) - if terr != nil { - return "" + + signedToken, err := jwt.Signed(signer).Claims(claims).CompactSerialize() + if err != nil { + panic(err) } - return token.Encode() + + return signedToken } diff --git a/token/token.go b/token/token.go index d4f237a9..abccf257 100644 --- a/token/token.go +++ b/token/token.go @@ -13,9 +13,9 @@ import ( "syscall" "time" - "github.com/coreos/go-oidc/jose" "github.com/pkg/errors" "github.com/rs/zerolog" + "gopkg.in/square/go-jose.v2" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/retry" @@ -342,7 +342,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.Payload, &payload) + err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) if err != nil { return "", err } @@ -351,7 +351,7 @@ func GetOrgTokenIfExists(authDomain string) (string, error) { err := os.Remove(path) return "", err } - return token.Encode(), nil + return token.CompactSerialize() } func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { @@ -364,7 +364,7 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { return "", err } var payload jwtPayload - err = json.Unmarshal(token.Payload, &payload) + err = json.Unmarshal(token.UnsafePayloadWithoutVerification(), &payload) if err != nil { return "", err } @@ -373,22 +373,21 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) { err := os.Remove(path) return "", err } - return token.Encode(), nil + return token.CompactSerialize() } // GetTokenIfExists will return the token from local storage if it exists and not expired -func getTokenIfExists(path string) (*jose.JWT, error) { +func getTokenIfExists(path string) (*jose.JSONWebSignature, error) { content, err := ioutil.ReadFile(path) if err != nil { return nil, err } - token, err := jose.ParseJWT(string(content)) + token, err := jose.ParseSigned(string(content)) if err != nil { return nil, err } - - return &token, nil + return token, nil } // RemoveTokenIfExists removes the a token from local storage if it exists diff --git a/vendor/github.com/coreos/go-oidc/LICENSE b/vendor/github.com/coreos/go-oidc/LICENSE deleted file mode 100644 index e06d2081..00000000 --- a/vendor/github.com/coreos/go-oidc/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -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/coreos/go-oidc/NOTICE b/vendor/github.com/coreos/go-oidc/NOTICE deleted file mode 100644 index b39ddfa5..00000000 --- a/vendor/github.com/coreos/go-oidc/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2014 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-oidc/jose/claims.go b/vendor/github.com/coreos/go-oidc/jose/claims.go deleted file mode 100644 index 8b48bfd2..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/claims.go +++ /dev/null @@ -1,126 +0,0 @@ -package jose - -import ( - "encoding/json" - "fmt" - "math" - "time" -) - -type Claims map[string]interface{} - -func (c Claims) Add(name string, value interface{}) { - c[name] = value -} - -func (c Claims) StringClaim(name string) (string, bool, error) { - cl, ok := c[name] - if !ok { - return "", false, nil - } - - v, ok := cl.(string) - if !ok { - return "", false, fmt.Errorf("unable to parse claim as string: %v", name) - } - - return v, true, nil -} - -func (c Claims) StringsClaim(name string) ([]string, bool, error) { - cl, ok := c[name] - if !ok { - return nil, false, nil - } - - if v, ok := cl.([]string); ok { - return v, true, nil - } - - // When unmarshaled, []string will become []interface{}. - if v, ok := cl.([]interface{}); ok { - var ret []string - for _, vv := range v { - str, ok := vv.(string) - if !ok { - return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) - } - ret = append(ret, str) - } - return ret, true, nil - } - - return nil, false, fmt.Errorf("unable to parse claim as string array: %v", name) -} - -func (c Claims) Int64Claim(name string) (int64, bool, error) { - cl, ok := c[name] - if !ok { - return 0, false, nil - } - - v, ok := cl.(int64) - if !ok { - vf, ok := cl.(float64) - if !ok { - return 0, false, fmt.Errorf("unable to parse claim as int64: %v", name) - } - v = int64(vf) - } - - return v, true, nil -} - -func (c Claims) Float64Claim(name string) (float64, bool, error) { - cl, ok := c[name] - if !ok { - return 0, false, nil - } - - v, ok := cl.(float64) - if !ok { - vi, ok := cl.(int64) - if !ok { - return 0, false, fmt.Errorf("unable to parse claim as float64: %v", name) - } - v = float64(vi) - } - - return v, true, nil -} - -func (c Claims) TimeClaim(name string) (time.Time, bool, error) { - v, ok, err := c.Float64Claim(name) - if !ok || err != nil { - return time.Time{}, ok, err - } - - s := math.Trunc(v) - ns := (v - s) * math.Pow(10, 9) - return time.Unix(int64(s), int64(ns)).UTC(), true, nil -} - -func decodeClaims(payload []byte) (Claims, error) { - var c Claims - if err := json.Unmarshal(payload, &c); err != nil { - return nil, fmt.Errorf("malformed JWT claims, unable to decode: %v", err) - } - return c, nil -} - -func marshalClaims(c Claims) ([]byte, error) { - b, err := json.Marshal(c) - if err != nil { - return nil, err - } - return b, nil -} - -func encodeClaims(c Claims) (string, error) { - b, err := marshalClaims(c) - if err != nil { - return "", err - } - - return encodeSegment(b), nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/doc.go b/vendor/github.com/coreos/go-oidc/jose/doc.go deleted file mode 100644 index b5e13217..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package jose is DEPRECATED. Use gopkg.in/square/go-jose.v2 instead. -package jose diff --git a/vendor/github.com/coreos/go-oidc/jose/jose.go b/vendor/github.com/coreos/go-oidc/jose/jose.go deleted file mode 100644 index 62099265..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jose.go +++ /dev/null @@ -1,112 +0,0 @@ -package jose - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "strings" -) - -const ( - HeaderMediaType = "typ" - HeaderKeyAlgorithm = "alg" - HeaderKeyID = "kid" -) - -const ( - // Encryption Algorithm Header Parameter Values for JWS - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-6 - AlgHS256 = "HS256" - AlgHS384 = "HS384" - AlgHS512 = "HS512" - AlgRS256 = "RS256" - AlgRS384 = "RS384" - AlgRS512 = "RS512" - AlgES256 = "ES256" - AlgES384 = "ES384" - AlgES512 = "ES512" - AlgPS256 = "PS256" - AlgPS384 = "PS384" - AlgPS512 = "PS512" - AlgNone = "none" -) - -const ( - // Algorithm Header Parameter Values for JWE - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-4.1 - AlgRSA15 = "RSA1_5" - AlgRSAOAEP = "RSA-OAEP" - AlgRSAOAEP256 = "RSA-OAEP-256" - AlgA128KW = "A128KW" - AlgA192KW = "A192KW" - AlgA256KW = "A256KW" - AlgDir = "dir" - AlgECDHES = "ECDH-ES" - AlgECDHESA128KW = "ECDH-ES+A128KW" - AlgECDHESA192KW = "ECDH-ES+A192KW" - AlgECDHESA256KW = "ECDH-ES+A256KW" - AlgA128GCMKW = "A128GCMKW" - AlgA192GCMKW = "A192GCMKW" - AlgA256GCMKW = "A256GCMKW" - AlgPBES2HS256A128KW = "PBES2-HS256+A128KW" - AlgPBES2HS384A192KW = "PBES2-HS384+A192KW" - AlgPBES2HS512A256KW = "PBES2-HS512+A256KW" -) - -const ( - // Encryption Algorithm Header Parameter Values for JWE - // See: https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#page-22 - EncA128CBCHS256 = "A128CBC-HS256" - EncA128CBCHS384 = "A128CBC-HS384" - EncA256CBCHS512 = "A256CBC-HS512" - EncA128GCM = "A128GCM" - EncA192GCM = "A192GCM" - EncA256GCM = "A256GCM" -) - -type JOSEHeader map[string]string - -func (j JOSEHeader) Validate() error { - if _, exists := j[HeaderKeyAlgorithm]; !exists { - return fmt.Errorf("header missing %q parameter", HeaderKeyAlgorithm) - } - - return nil -} - -func decodeHeader(seg string) (JOSEHeader, error) { - b, err := decodeSegment(seg) - if err != nil { - return nil, err - } - - var h JOSEHeader - err = json.Unmarshal(b, &h) - if err != nil { - return nil, err - } - - return h, nil -} - -func encodeHeader(h JOSEHeader) (string, error) { - b, err := json.Marshal(h) - if err != nil { - return "", err - } - - return encodeSegment(b), nil -} - -// Decode JWT specific base64url encoding with padding stripped -func decodeSegment(seg string) ([]byte, error) { - if l := len(seg) % 4; l != 0 { - seg += strings.Repeat("=", 4-l) - } - return base64.URLEncoding.DecodeString(seg) -} - -// Encode JWT specific base64url encoding with padding stripped -func encodeSegment(seg []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwk.go b/vendor/github.com/coreos/go-oidc/jose/jwk.go deleted file mode 100644 index 119f073f..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jwk.go +++ /dev/null @@ -1,135 +0,0 @@ -package jose - -import ( - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/json" - "math/big" - "strings" -) - -// JSON Web Key -// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-36#page-5 -type JWK struct { - ID string - Type string - Alg string - Use string - Exponent int - Modulus *big.Int - Secret []byte -} - -type jwkJSON struct { - ID string `json:"kid"` - Type string `json:"kty"` - Alg string `json:"alg"` - Use string `json:"use"` - Exponent string `json:"e"` - Modulus string `json:"n"` -} - -func (j *JWK) MarshalJSON() ([]byte, error) { - t := jwkJSON{ - ID: j.ID, - Type: j.Type, - Alg: j.Alg, - Use: j.Use, - Exponent: encodeExponent(j.Exponent), - Modulus: encodeModulus(j.Modulus), - } - - return json.Marshal(&t) -} - -func (j *JWK) UnmarshalJSON(data []byte) error { - var t jwkJSON - err := json.Unmarshal(data, &t) - if err != nil { - return err - } - - e, err := decodeExponent(t.Exponent) - if err != nil { - return err - } - - n, err := decodeModulus(t.Modulus) - if err != nil { - return err - } - - j.ID = t.ID - j.Type = t.Type - j.Alg = t.Alg - j.Use = t.Use - j.Exponent = e - j.Modulus = n - - return nil -} - -type JWKSet struct { - Keys []JWK `json:"keys"` -} - -func decodeExponent(e string) (int, error) { - decE, err := decodeBase64URLPaddingOptional(e) - if err != nil { - return 0, err - } - var eBytes []byte - if len(decE) < 8 { - eBytes = make([]byte, 8-len(decE), 8) - eBytes = append(eBytes, decE...) - } else { - eBytes = decE - } - eReader := bytes.NewReader(eBytes) - var E uint64 - err = binary.Read(eReader, binary.BigEndian, &E) - if err != nil { - return 0, err - } - return int(E), nil -} - -func encodeExponent(e int) string { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, uint64(e)) - var idx int - for ; idx < 8; idx++ { - if b[idx] != 0x0 { - break - } - } - return base64.RawURLEncoding.EncodeToString(b[idx:]) -} - -// Turns a URL encoded modulus of a key into a big int. -func decodeModulus(n string) (*big.Int, error) { - decN, err := decodeBase64URLPaddingOptional(n) - if err != nil { - return nil, err - } - N := big.NewInt(0) - N.SetBytes(decN) - return N, nil -} - -func encodeModulus(n *big.Int) string { - return base64.RawURLEncoding.EncodeToString(n.Bytes()) -} - -// decodeBase64URLPaddingOptional decodes Base64 whether there is padding or not. -// The stdlib version currently doesn't handle this. -// We can get rid of this is if this bug: -// https://github.com/golang/go/issues/4237 -// ever closes. -func decodeBase64URLPaddingOptional(e string) ([]byte, error) { - if m := len(e) % 4; m != 0 { - e += strings.Repeat("=", 4-m) - } - return base64.URLEncoding.DecodeString(e) -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jws.go b/vendor/github.com/coreos/go-oidc/jose/jws.go deleted file mode 100644 index 1049ece8..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jws.go +++ /dev/null @@ -1,51 +0,0 @@ -package jose - -import ( - "fmt" - "strings" -) - -type JWS struct { - RawHeader string - Header JOSEHeader - RawPayload string - Payload []byte - Signature []byte -} - -// Given a raw encoded JWS token parses it and verifies the structure. -func ParseJWS(raw string) (JWS, error) { - parts := strings.Split(raw, ".") - if len(parts) != 3 { - return JWS{}, fmt.Errorf("malformed JWS, only %d segments", len(parts)) - } - - rawSig := parts[2] - jws := JWS{ - RawHeader: parts[0], - RawPayload: parts[1], - } - - header, err := decodeHeader(jws.RawHeader) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode header, %s", err) - } - if err = header.Validate(); err != nil { - return JWS{}, fmt.Errorf("malformed JWS, %s", err) - } - jws.Header = header - - payload, err := decodeSegment(jws.RawPayload) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode payload: %s", err) - } - jws.Payload = payload - - sig, err := decodeSegment(rawSig) - if err != nil { - return JWS{}, fmt.Errorf("malformed JWS, unable to decode signature: %s", err) - } - jws.Signature = sig - - return jws, nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/jwt.go b/vendor/github.com/coreos/go-oidc/jose/jwt.go deleted file mode 100644 index 3b3e9634..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/jwt.go +++ /dev/null @@ -1,82 +0,0 @@ -package jose - -import "strings" - -type JWT JWS - -func ParseJWT(token string) (jwt JWT, err error) { - jws, err := ParseJWS(token) - if err != nil { - return - } - - return JWT(jws), nil -} - -func NewJWT(header JOSEHeader, claims Claims) (jwt JWT, err error) { - jwt = JWT{} - - jwt.Header = header - jwt.Header[HeaderMediaType] = "JWT" - - claimBytes, err := marshalClaims(claims) - if err != nil { - return - } - jwt.Payload = claimBytes - - eh, err := encodeHeader(header) - if err != nil { - return - } - jwt.RawHeader = eh - - ec, err := encodeClaims(claims) - if err != nil { - return - } - jwt.RawPayload = ec - - return -} - -func (j *JWT) KeyID() (string, bool) { - kID, ok := j.Header[HeaderKeyID] - return kID, ok -} - -func (j *JWT) Claims() (Claims, error) { - return decodeClaims(j.Payload) -} - -// Encoded data part of the token which may be signed. -func (j *JWT) Data() string { - return strings.Join([]string{j.RawHeader, j.RawPayload}, ".") -} - -// Full encoded JWT token string in format: header.claims.signature -func (j *JWT) Encode() string { - d := j.Data() - s := encodeSegment(j.Signature) - return strings.Join([]string{d, s}, ".") -} - -func NewSignedJWT(claims Claims, s Signer) (*JWT, error) { - header := JOSEHeader{ - HeaderKeyAlgorithm: s.Alg(), - HeaderKeyID: s.ID(), - } - - jwt, err := NewJWT(header, claims) - if err != nil { - return nil, err - } - - sig, err := s.Sign([]byte(jwt.Data())) - if err != nil { - return nil, err - } - jwt.Signature = sig - - return &jwt, nil -} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig.go b/vendor/github.com/coreos/go-oidc/jose/sig.go deleted file mode 100644 index 7b2b253c..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/sig.go +++ /dev/null @@ -1,24 +0,0 @@ -package jose - -import ( - "fmt" -) - -type Verifier interface { - ID() string - Alg() string - Verify(sig []byte, data []byte) error -} - -type Signer interface { - Verifier - Sign(data []byte) (sig []byte, err error) -} - -func NewVerifier(jwk JWK) (Verifier, error) { - if jwk.Type != "RSA" { - return nil, fmt.Errorf("unsupported key type %q", jwk.Type) - } - - return NewVerifierRSA(jwk) -} diff --git a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go b/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go deleted file mode 100644 index 004e45dd..00000000 --- a/vendor/github.com/coreos/go-oidc/jose/sig_rsa.go +++ /dev/null @@ -1,67 +0,0 @@ -package jose - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "fmt" -) - -type VerifierRSA struct { - KeyID string - Hash crypto.Hash - PublicKey rsa.PublicKey -} - -type SignerRSA struct { - PrivateKey rsa.PrivateKey - VerifierRSA -} - -func NewVerifierRSA(jwk JWK) (*VerifierRSA, error) { - if jwk.Alg != "" && jwk.Alg != "RS256" { - return nil, fmt.Errorf("unsupported key algorithm %q", jwk.Alg) - } - - v := VerifierRSA{ - KeyID: jwk.ID, - PublicKey: rsa.PublicKey{ - N: jwk.Modulus, - E: jwk.Exponent, - }, - Hash: crypto.SHA256, - } - - return &v, nil -} - -func NewSignerRSA(kid string, key rsa.PrivateKey) *SignerRSA { - return &SignerRSA{ - PrivateKey: key, - VerifierRSA: VerifierRSA{ - KeyID: kid, - PublicKey: key.PublicKey, - Hash: crypto.SHA256, - }, - } -} - -func (v *VerifierRSA) ID() string { - return v.KeyID -} - -func (v *VerifierRSA) Alg() string { - return "RS256" -} - -func (v *VerifierRSA) Verify(sig []byte, data []byte) error { - h := v.Hash.New() - h.Write(data) - return rsa.VerifyPKCS1v15(&v.PublicKey, v.Hash, h.Sum(nil), sig) -} - -func (s *SignerRSA) Sign(data []byte) ([]byte, error) { - h := s.Hash.New() - h.Write(data) - return rsa.SignPKCS1v15(rand.Reader, &s.PrivateKey, s.Hash, h.Sum(nil)) -} diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml index 6ff9dd96..3fddaaac 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml +++ b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml @@ -1,13 +1,13 @@ language: go go: - - "1.9" - - "1.10" + - "1.12" + - "1.13" install: - go get -v -t github.com/coreos/go-oidc/... - go get golang.org/x/tools/cmd/cover - - go get github.com/golang/lint/golint + - go get golang.org/x/lint/golint script: - ./test diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go index 508b39d3..b39cb515 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go @@ -69,6 +69,7 @@ type Provider struct { authURL string tokenURL string userInfoURL string + algorithms []string // Raw claims returned by the server. rawClaims []byte @@ -82,11 +83,27 @@ type cachedKeys struct { } type providerJSON struct { - Issuer string `json:"issuer"` - AuthURL string `json:"authorization_endpoint"` - TokenURL string `json:"token_endpoint"` - JWKSURL string `json:"jwks_uri"` - UserInfoURL string `json:"userinfo_endpoint"` + Issuer string `json:"issuer"` + AuthURL string `json:"authorization_endpoint"` + TokenURL string `json:"token_endpoint"` + JWKSURL string `json:"jwks_uri"` + UserInfoURL string `json:"userinfo_endpoint"` + Algorithms []string `json:"id_token_signing_alg_values_supported"` +} + +// supportedAlgorithms is a list of algorithms explicitly supported by this +// package. If a provider supports other algorithms, such as HS256 or none, +// those values won't be passed to the IDTokenVerifier. +var supportedAlgorithms = map[string]bool{ + RS256: true, + RS384: true, + RS512: true, + ES256: true, + ES384: true, + ES512: true, + PS256: true, + PS384: true, + PS512: true, } // NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. @@ -123,11 +140,18 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { if p.Issuer != issuer { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } + var algs []string + for _, a := range p.Algorithms { + if supportedAlgorithms[a] { + algs = append(algs, a) + } + } return &Provider{ issuer: p.Issuer, authURL: p.AuthURL, tokenURL: p.TokenURL, userInfoURL: p.UserInfoURL, + algorithms: algs, rawClaims: body, remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), }, nil diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go index ff7555db..d43f0662 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go +++ b/vendor/gopkg.in/coreos/go-oidc.v2/verify.go @@ -79,7 +79,9 @@ type Config struct { ClientID string // If specified, only this set of algorithms may be used to sign the JWT. // - // Since many providers only support RS256, SupportedSigningAlgs defaults to this value. + // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this + // defaults to the set of algorithms the provider supports. Otherwise this values + // defaults to RS256. SupportedSigningAlgs []string // If true, no ClientID check performed. Must be true if ClientID field is empty. @@ -105,6 +107,13 @@ type Config struct { // The returned IDTokenVerifier is tied to the Provider's context and its behavior is // undefined once the Provider's context is canceled. func (p *Provider) Verifier(config *Config) *IDTokenVerifier { + if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { + // Make a copy so we don't modify the config values. + cp := &Config{} + *cp = *config + cp.SupportedSigningAlgs = p.algorithms + config = cp + } return NewVerifier(p.issuer, p.remoteKeySet, config) } diff --git a/vendor/gopkg.in/square/go-jose.v2/.gitignore b/vendor/gopkg.in/square/go-jose.v2/.gitignore index 5b4d73b6..95a85158 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.gitignore +++ b/vendor/gopkg.in/square/go-jose.v2/.gitignore @@ -5,3 +5,4 @@ *.pem *.cov jose-util/jose-util +jose-util.t.err \ No newline at end of file diff --git a/vendor/gopkg.in/square/go-jose.v2/.travis.yml b/vendor/gopkg.in/square/go-jose.v2/.travis.yml index 9ab2abf6..391b99a4 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.travis.yml +++ b/vendor/gopkg.in/square/go-jose.v2/.travis.yml @@ -8,8 +8,8 @@ matrix: - go: tip go: -- '1.11.x' -- '1.12.x' +- '1.14.x' +- '1.15.x' - tip go_import_path: gopkg.in/square/go-jose.v2 @@ -26,6 +26,8 @@ before_install: - go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls - go get github.com/stretchr/testify/assert +- go get github.com/stretchr/testify/require +- go get github.com/google/go-cmp/cmp - go get golang.org/x/tools/cmd/cover || true - go get code.google.com/p/go.tools/cmd/cover || true - pip install cram --user @@ -35,10 +37,9 @@ script: - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov - go test ./json -v # no coverage for forked encoding/json package -- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t +- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util - cd .. after_success: - gocovmerge *.cov */*.cov > merged.coverprofile - $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci - diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go index 126b85ce..f6465c04 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/cbc_hmac.go @@ -150,7 +150,7 @@ func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { return hmac.Sum(nil)[:ctx.authtagBytes] } -// resize ensures the the given slice has a capacity of at least n bytes. +// resize ensures that the given slice has a capacity of at least n bytes. // If the capacity of the slice is less than n, a new slice is allocated // and the existing data will be copied. func resize(in []byte, n uint64) (head, tail []byte) { diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go index d24cabf6..be7433e2 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go @@ -216,6 +216,7 @@ func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *Encrypter if opts != nil { encrypter.compressionAlg = opts.Compression + encrypter.extraHeaders = opts.ExtraHeaders } for _, recipient := range rcpts { diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go index b9687c64..70f7385c 100644 --- a/vendor/gopkg.in/square/go-jose.v2/encoding.go +++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go @@ -23,13 +23,12 @@ import ( "encoding/binary" "io" "math/big" - "regexp" + "strings" + "unicode" "gopkg.in/square/go-jose.v2/json" ) -var stripWhitespaceRegex = regexp.MustCompile("\\s") - // Helper function to serialize known-good objects. // Precondition: value is not a nil pointer. func mustSerializeJSON(value interface{}) []byte { @@ -56,7 +55,14 @@ func mustSerializeJSON(value interface{}) []byte { // Strip all newlines and whitespace func stripWhitespace(data string) string { - return stripWhitespaceRegex.ReplaceAllString(data, "") + buf := strings.Builder{} + buf.Grow(len(data)) + for _, r := range data { + if !unicode.IsSpace(r) { + buf.WriteRune(r) + } + } + return buf.String() } // Perform compression based on algorithm diff --git a/vendor/gopkg.in/square/go-jose.v2/json/decode.go b/vendor/gopkg.in/square/go-jose.v2/json/decode.go index 37457e5a..4dbc4146 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/decode.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/decode.go @@ -13,6 +13,7 @@ import ( "encoding/base64" "errors" "fmt" + "math" "reflect" "runtime" "strconv" @@ -245,6 +246,18 @@ func isValidNumber(s string) bool { return s == "" } +type NumberUnmarshalType int + +const ( + // unmarshal a JSON number into an interface{} as a float64 + UnmarshalFloat NumberUnmarshalType = iota + // unmarshal a JSON number into an interface{} as a `json.Number` + UnmarshalJSONNumber + // unmarshal a JSON number into an interface{} as a int64 + // if value is an integer otherwise float64 + UnmarshalIntOrFloat +) + // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -252,7 +265,7 @@ type decodeState struct { scan scanner nextscan scanner // for calls to nextValue savedError error - useNumber bool + numberType NumberUnmarshalType } // errPhase is used for errors that should not happen unless @@ -723,17 +736,38 @@ func (d *decodeState) literal(v reflect.Value) { d.literalStore(d.data[start:d.off], v, false) } -// convertNumber converts the number literal s to a float64 or a Number -// depending on the setting of d.useNumber. +// convertNumber converts the number literal s to a float64, int64 or a Number +// depending on d.numberDecodeType. func (d *decodeState) convertNumber(s string) (interface{}, error) { - if d.useNumber { + switch d.numberType { + + case UnmarshalJSONNumber: return Number(s), nil + case UnmarshalIntOrFloat: + v, err := strconv.ParseInt(s, 10, 64) + if err == nil { + return v, nil + } + + // tries to parse integer number in scientific notation + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + + // if it has no decimal value use int64 + if fi, fd := math.Modf(f); fd == 0.0 { + return int64(fi), nil + } + return f, nil + default: + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil } - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} - } - return f, nil + } var numberType = reflect.TypeOf(Number("")) diff --git a/vendor/gopkg.in/square/go-jose.v2/json/stream.go b/vendor/gopkg.in/square/go-jose.v2/json/stream.go index 8ddcf4d2..9b2b926b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/json/stream.go +++ b/vendor/gopkg.in/square/go-jose.v2/json/stream.go @@ -31,9 +31,14 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } +// Deprecated: Use `SetNumberType` instead // UseNumber causes the Decoder to unmarshal a number into an interface{} as a // Number instead of as a float64. -func (dec *Decoder) UseNumber() { dec.d.useNumber = true } +func (dec *Decoder) UseNumber() { dec.d.numberType = UnmarshalJSONNumber } + +// SetNumberType causes the Decoder to unmarshal a number into an interface{} as a +// Number, float64 or int64 depending on `t` enum value. +func (dec *Decoder) SetNumberType(t NumberUnmarshalType) { dec.d.numberType = t } // Decode reads the next JSON-encoded value from its // input and stores it in the value pointed to by v. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk.go b/vendor/gopkg.in/square/go-jose.v2/jwk.go index 936a0202..222e260c 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk.go @@ -17,15 +17,20 @@ package jose import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/sha1" + "crypto/sha256" "crypto/x509" "encoding/base64" + "encoding/hex" "errors" "fmt" "math/big" + "net/url" "reflect" "strings" @@ -57,16 +62,31 @@ type rawJSONWebKey struct { Dq *byteBuffer `json:"dq,omitempty"` Qi *byteBuffer `json:"qi,omitempty"` // Certificates - X5c []string `json:"x5c,omitempty"` + X5c []string `json:"x5c,omitempty"` + X5u *url.URL `json:"x5u,omitempty"` + X5tSHA1 string `json:"x5t,omitempty"` + X5tSHA256 string `json:"x5t#S256,omitempty"` } // JSONWebKey represents a public or private key in JWK format. type JSONWebKey struct { - Key interface{} + // Cryptographic key, can be a symmetric or asymmetric key. + Key interface{} + // Key identifier, parsed from `kid` header. + KeyID string + // Key algorithm, parsed from `alg` header. + Algorithm string + // Key use, parsed from `use` header. + Use string + + // X.509 certificate chain, parsed from `x5c` header. Certificates []*x509.Certificate - KeyID string - Algorithm string - Use string + // X.509 certificate URL, parsed from `x5u` header. + CertificatesURL *url.URL + // X.509 certificate thumbprint (SHA-1), parsed from `x5t` header. + CertificateThumbprintSHA1 []byte + // X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header. + CertificateThumbprintSHA256 []byte } // MarshalJSON serializes the given key to its JSON representation. @@ -105,6 +125,39 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) } + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 { + if x5tSHA1Len != sha1.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) + } + raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) + } + if x5tSHA256Len > 0 { + if x5tSHA256Len != sha256.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) + } + raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) + } + + // If cert chain is attached (as opposed to being behind a URL), check the + // keys thumbprints to make sure they match what is expected. This is to + // ensure we don't accidentally produce a JWK with semantically inconsistent + // data in the headers. + if len(k.Certificates) > 0 { + expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) + expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { + return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") + } + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { + return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") + } + } + + raw.X5u = k.CertificatesURL + return json.Marshal(raw) } @@ -116,28 +169,61 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { return err } + certs, err := parseCertificateChain(raw.X5c) + if err != nil { + return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) + } + var key interface{} + var certPub interface{} + var keyPub interface{} + + if len(certs) > 0 { + // We need to check that leaf public key matches the key embedded in this + // JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise + // the JWK parsed could be semantically invalid. Technically, should also + // check key usage fields and other extensions on the cert here, but the + // standard doesn't exactly explain how they're supposed to map from the + // JWK representation to the X.509 extensions. + certPub = certs[0].PublicKey + } + switch raw.Kty { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() + if err == nil { + keyPub = key.(*ecdsa.PrivateKey).Public() + } } else { key, err = raw.ecPublicKey() + keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() + if err == nil { + keyPub = key.(*rsa.PrivateKey).Public() + } } else { key, err = raw.rsaPublicKey() + keyPub = key } case "oct": + if certPub != nil { + return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") + } key, err = raw.symmetricKey() case "OKP": if raw.Crv == "Ed25519" && raw.X != nil { if raw.D != nil { key, err = raw.edPrivateKey() + if err == nil { + keyPub = key.(ed25519.PrivateKey).Public() + } } else { key, err = raw.edPublicKey() + keyPub = key } } else { err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) @@ -146,12 +232,78 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) } - if err == nil { - *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} + if err != nil { + return + } - k.Certificates, err = parseCertificateChain(raw.X5c) + if certPub != nil && keyPub != nil { + if !reflect.DeepEqual(certPub, keyPub) { + return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match") + } + } + + *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} + + k.CertificatesURL = raw.X5u + + // x5t parameters are base64url-encoded SHA thumbprints + // See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8 + x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") + } + + // RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex, + // for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded + // checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll + // try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum. + if len(x5tSHA1bytes) == 2*sha1.Size { + hx, err := hex.DecodeString(string(x5tSHA1bytes)) if err != nil { - return fmt.Errorf("failed to unmarshal x5c field: %s", err) + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) + + } + x5tSHA1bytes = hx + } + + k.CertificateThumbprintSHA1 = x5tSHA1bytes + + x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") + } + + if len(x5tSHA256bytes) == 2*sha256.Size { + hx256, err := hex.DecodeString(string(x5tSHA256bytes)) + if err != nil { + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) + } + x5tSHA256bytes = hx256 + } + + k.CertificateThumbprintSHA256 = x5tSHA256bytes + + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { + return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") + } + if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") + } + + // If certificate chain *and* thumbprints are set, verify correctness. + if len(k.Certificates) > 0 { + leaf := k.Certificates[0] + sha1sum := sha1.Sum(leaf.Raw) + sha256sum := sha256.Sum256(leaf.Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") + } + + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") } } @@ -180,7 +332,7 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` -const edThumbprintTemplate = `{"crv":"%s","kty":"OKP",x":"%s"}` +const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}` func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { coordLength := curveSize(curve) @@ -254,7 +406,7 @@ func (k *JSONWebKey) IsPublic() bool { } } -// Public creates JSONWebKey with corresponding publik key if JWK represents asymmetric private key. +// Public creates JSONWebKey with corresponding public key if JWK represents asymmetric private key. func (k *JSONWebKey) Public() JSONWebKey { if k.IsPublic() { return *k diff --git a/vendor/gopkg.in/square/go-jose.v2/jws.go b/vendor/gopkg.in/square/go-jose.v2/jws.go index e52a4766..7e261f93 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws.go @@ -102,14 +102,14 @@ func (sig Signature) mergedHeaders() rawHeader { } // Compute data to be signed -func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte { +func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) { var authData bytes.Buffer protectedHeader := new(rawHeader) if signature.original != nil && signature.original.Protected != nil { if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil { - panic(err) + return nil, err } authData.WriteString(signature.original.Protected.base64()) } else if signature.protected != nil { @@ -134,7 +134,7 @@ func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature authData.Write(payload) } - return authData.Bytes() + return authData.Bytes(), nil } // parseSignedFull parses a message in full format. diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go new file mode 100644 index 00000000..3afa9030 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go @@ -0,0 +1,334 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "bytes" + "reflect" + + "gopkg.in/square/go-jose.v2/json" + + "gopkg.in/square/go-jose.v2" +) + +// Builder is a utility for making JSON Web Tokens. Calls can be chained, and +// errors are accumulated until the final call to CompactSerialize/FullSerialize. +type Builder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) Builder + // Token builds a JSONWebToken from provided data. + Token() (*JSONWebToken, error) + // FullSerialize serializes a token using the full serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens. +// Calls can be chained, and errors are accumulated until final call to +// CompactSerialize/FullSerialize. +type NestedBuilder interface { + // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims + // into single JSON object. If you are passing private claims, make sure to set + // struct field tags to specify the name for the JSON key to be used when + // serializing. + Claims(i interface{}) NestedBuilder + // Token builds a NestedJSONWebToken from provided data. + Token() (*NestedJSONWebToken, error) + // FullSerialize serializes a token using the full serialization format. + FullSerialize() (string, error) + // CompactSerialize serializes a token using the compact serialization format. + CompactSerialize() (string, error) +} + +type builder struct { + payload map[string]interface{} + err error +} + +type signedBuilder struct { + builder + sig jose.Signer +} + +type encryptedBuilder struct { + builder + enc jose.Encrypter +} + +type nestedBuilder struct { + builder + sig jose.Signer + enc jose.Encrypter +} + +// Signed creates builder for signed tokens. +func Signed(sig jose.Signer) Builder { + return &signedBuilder{ + sig: sig, + } +} + +// Encrypted creates builder for encrypted tokens. +func Encrypted(enc jose.Encrypter) Builder { + return &encryptedBuilder{ + enc: enc, + } +} + +// SignedAndEncrypted creates builder for signed-then-encrypted tokens. +// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type. +func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder { + if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" { + return &nestedBuilder{ + builder: builder{ + err: ErrInvalidContentType, + }, + } + } + return &nestedBuilder{ + sig: sig, + enc: enc, + } +} + +func (b builder) claims(i interface{}) builder { + if b.err != nil { + return b + } + + m, ok := i.(map[string]interface{}) + switch { + case ok: + return b.merge(m) + case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: + m, err := normalize(i) + if err != nil { + return builder{ + err: err, + } + } + return b.merge(m) + default: + return builder{ + err: ErrInvalidClaims, + } + } +} + +func normalize(i interface{}) (map[string]interface{}, error) { + m := make(map[string]interface{}) + + raw, err := json.Marshal(i) + if err != nil { + return nil, err + } + + d := json.NewDecoder(bytes.NewReader(raw)) + d.SetNumberType(json.UnmarshalJSONNumber) + + if err := d.Decode(&m); err != nil { + return nil, err + } + + return m, nil +} + +func (b *builder) merge(m map[string]interface{}) builder { + p := make(map[string]interface{}) + for k, v := range b.payload { + p[k] = v + } + for k, v := range m { + p[k] = v + } + + return builder{ + payload: p, + } +} + +func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { + return &JSONWebToken{ + payload: p, + Headers: h, + }, nil +} + +func (b *signedBuilder) Claims(i interface{}) Builder { + return &signedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + } +} + +func (b *signedBuilder) Token() (*JSONWebToken, error) { + sig, err := b.sign() + if err != nil { + return nil, err + } + + h := make([]jose.Header, len(sig.Signatures)) + for i, v := range sig.Signatures { + h[i] = v.Header + } + + return b.builder.token(sig.Verify, h) +} + +func (b *signedBuilder) CompactSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.CompactSerialize() +} + +func (b *signedBuilder) FullSerialize() (string, error) { + sig, err := b.sign() + if err != nil { + return "", err + } + + return sig.FullSerialize(), nil +} + +func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.sig.Sign(p) +} + +func (b *encryptedBuilder) Claims(i interface{}) Builder { + return &encryptedBuilder{ + builder: b.builder.claims(i), + enc: b.enc, + } +} + +func (b *encryptedBuilder) CompactSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *encryptedBuilder) FullSerialize() (string, error) { + enc, err := b.encrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *encryptedBuilder) Token() (*JSONWebToken, error) { + enc, err := b.encrypt() + if err != nil { + return nil, err + } + + return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) +} + +func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + return b.enc.Encrypt(p) +} + +func (b *nestedBuilder) Claims(i interface{}) NestedBuilder { + return &nestedBuilder{ + builder: b.builder.claims(i), + sig: b.sig, + enc: b.enc, + } +} + +func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return nil, err + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} + +func (b *nestedBuilder) CompactSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.CompactSerialize() +} + +func (b *nestedBuilder) FullSerialize() (string, error) { + enc, err := b.signAndEncrypt() + if err != nil { + return "", err + } + + return enc.FullSerialize(), nil +} + +func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) { + if b.err != nil { + return nil, b.err + } + + p, err := json.Marshal(b.payload) + if err != nil { + return nil, err + } + + sig, err := b.sig.Sign(p) + if err != nil { + return nil, err + } + + p2, err := sig.CompactSerialize() + if err != nil { + return nil, err + } + + return b.enc.Encrypt([]byte(p2)) +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go new file mode 100644 index 00000000..5f40ef3b --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go @@ -0,0 +1,121 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "strconv" + "time" + + "gopkg.in/square/go-jose.v2/json" +) + +// Claims represents public claim values (as specified in RFC 7519). +type Claims struct { + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audience Audience `json:"aud,omitempty"` + Expiry *NumericDate `json:"exp,omitempty"` + NotBefore *NumericDate `json:"nbf,omitempty"` + IssuedAt *NumericDate `json:"iat,omitempty"` + ID string `json:"jti,omitempty"` +} + +// NumericDate represents date and time as the number of seconds since the +// epoch, ignoring leap seconds. Non-integer values can be represented +// in the serialized format, but we round to the nearest second. +// See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2 +type NumericDate int64 + +// NewNumericDate constructs NumericDate from time.Time value. +func NewNumericDate(t time.Time) *NumericDate { + if t.IsZero() { + return nil + } + + // While RFC 7519 technically states that NumericDate values may be + // non-integer values, we don't bother serializing timestamps in + // claims with sub-second accurancy and just round to the nearest + // second instead. Not convined sub-second accuracy is useful here. + out := NumericDate(t.Unix()) + return &out +} + +// MarshalJSON serializes the given NumericDate into its JSON representation. +func (n NumericDate) MarshalJSON() ([]byte, error) { + return []byte(strconv.FormatInt(int64(n), 10)), nil +} + +// UnmarshalJSON reads a date from its JSON representation. +func (n *NumericDate) UnmarshalJSON(b []byte) error { + s := string(b) + + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return ErrUnmarshalNumericDate + } + + *n = NumericDate(f) + return nil +} + +// Time returns time.Time representation of NumericDate. +func (n *NumericDate) Time() time.Time { + if n == nil { + return time.Time{} + } + return time.Unix(int64(*n), 0) +} + +// Audience represents the recipients that the token is intended for. +type Audience []string + +// UnmarshalJSON reads an audience from its JSON representation. +func (s *Audience) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + switch v := v.(type) { + case string: + *s = []string{v} + case []interface{}: + a := make([]string, len(v)) + for i, e := range v { + s, ok := e.(string) + if !ok { + return ErrUnmarshalAudience + } + a[i] = s + } + *s = a + default: + return ErrUnmarshalAudience + } + + return nil +} + +func (s Audience) Contains(v string) bool { + for _, a := range s { + if a == v { + return true + } + } + return false +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go new file mode 100644 index 00000000..4cf97b54 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go @@ -0,0 +1,22 @@ +/*- + * Copyright 2017 Square Inc. + * + * 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 jwt provides an implementation of the JSON Web Token standard. + +*/ +package jwt diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go new file mode 100644 index 00000000..09f76ae4 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go @@ -0,0 +1,53 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import "errors" + +// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled. +var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience") + +// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled. +var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate") + +// ErrInvalidClaims indicates that given claims have invalid type. +var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object") + +// ErrInvalidIssuer indicates invalid iss claim. +var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)") + +// ErrInvalidSubject indicates invalid sub claim. +var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)") + +// ErrInvalidAudience indicated invalid aud claim. +var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)") + +// ErrInvalidID indicates invalid jti claim. +var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)") + +// ErrNotValidYet indicates that token is used before time indicated in nbf claim. +var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)") + +// ErrExpired indicates that token is used after expiry time indicated in exp claim. +var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)") + +// ErrIssuedInTheFuture indicates that the iat field is in the future. +var ErrIssuedInTheFuture = errors.New("square/go-jose/jwt: validation field, token issued in the future (iat)") + +// ErrInvalidContentType indicates that token requires JWT cty header. +var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)") diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go new file mode 100644 index 00000000..47498840 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go @@ -0,0 +1,169 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import ( + "fmt" + "strings" + + jose "gopkg.in/square/go-jose.v2" + "gopkg.in/square/go-jose.v2/json" +) + +// JSONWebToken represents a JSON Web Token (as specified in RFC7519). +type JSONWebToken struct { + payload func(k interface{}) ([]byte, error) + unverifiedPayload func() []byte + Headers []jose.Header +} + +type NestedJSONWebToken struct { + enc *jose.JSONWebEncryption + Headers []jose.Header +} + +// Claims deserializes a JSONWebToken into dest using the provided key. +func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { + payloadKey := tryJWKS(t.Headers, key) + + b, err := t.payload(payloadKey) + if err != nil { + return err + } + + for _, d := range dest { + if err := json.Unmarshal(b, d); err != nil { + return err + } + } + + return nil +} + +// UnsafeClaimsWithoutVerification deserializes the claims of a +// JSONWebToken into the dests. For signed JWTs, the claims are not +// verified. This function won't work for encrypted JWTs. +func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error { + if t.unverifiedPayload == nil { + return fmt.Errorf("square/go-jose: Cannot get unverified claims") + } + claims := t.unverifiedPayload() + for _, d := range dest { + if err := json.Unmarshal(claims, d); err != nil { + return err + } + } + return nil +} + +func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) { + key := tryJWKS(t.Headers, decryptionKey) + + b, err := t.enc.Decrypt(key) + if err != nil { + return nil, err + } + + sig, err := ParseSigned(string(b)) + if err != nil { + return nil, err + } + + return sig, nil +} + +// ParseSigned parses token from JWS form. +func ParseSigned(s string) (*JSONWebToken, error) { + sig, err := jose.ParseSigned(s) + if err != nil { + return nil, err + } + headers := make([]jose.Header, len(sig.Signatures)) + for i, signature := range sig.Signatures { + headers[i] = signature.Header + } + + return &JSONWebToken{ + payload: sig.Verify, + unverifiedPayload: sig.UnsafePayloadWithoutVerification, + Headers: headers, + }, nil +} + +// ParseEncrypted parses token from JWE form. +func ParseEncrypted(s string) (*JSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + return &JSONWebToken{ + payload: enc.Decrypt, + Headers: []jose.Header{enc.Header}, + }, nil +} + +// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form. +func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) { + enc, err := jose.ParseEncrypted(s) + if err != nil { + return nil, err + } + + contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string) + if strings.ToUpper(contentType) != "JWT" { + return nil, ErrInvalidContentType + } + + return &NestedJSONWebToken{ + enc: enc, + Headers: []jose.Header{enc.Header}, + }, nil +} + +func tryJWKS(headers []jose.Header, key interface{}) interface{} { + var jwks jose.JSONWebKeySet + + switch jwksType := key.(type) { + case *jose.JSONWebKeySet: + jwks = *jwksType + case jose.JSONWebKeySet: + jwks = jwksType + default: + return key + } + + var kid string + for _, header := range headers { + if header.KeyID != "" { + kid = header.KeyID + break + } + } + + if kid == "" { + return key + } + + keys := jwks.Key(kid) + if len(keys) == 0 { + return key + } + + return keys[0].Key +} diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go new file mode 100644 index 00000000..6f3ff4e8 --- /dev/null +++ b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go @@ -0,0 +1,114 @@ +/*- + * Copyright 2016 Zbigniew Mandziejewicz + * Copyright 2016 Square, Inc. + * + * 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 jwt + +import "time" + +const ( + // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims. + DefaultLeeway = 1.0 * time.Minute +) + +// Expected defines values used for protected claims validation. +// If field has zero value then validation is skipped. +type Expected struct { + // Issuer matches the "iss" claim exactly. + Issuer string + // Subject matches the "sub" claim exactly. + Subject string + // Audience matches the values in "aud" claim, regardless of their order. + Audience Audience + // ID matches the "jti" claim exactly. + ID string + // Time matches the "exp", "nbf" and "iat" claims with leeway. + Time time.Time +} + +// WithTime copies expectations with new time. +func (e Expected) WithTime(t time.Time) Expected { + e.Time = t + return e +} + +// Validate checks claims in a token against expected values. +// A default leeway value of one minute is used to compare time values. +// +// The default leeway will cause the token to be deemed valid until one +// minute after the expiration time. If you're a server application that +// wants to give an extra minute to client tokens, use this +// function. If you're a client application wondering if the server +// will accept your token, use ValidateWithLeeway with a leeway <=0, +// otherwise this function might make you think a token is valid when +// it is not. +func (c Claims) Validate(e Expected) error { + return c.ValidateWithLeeway(e, DefaultLeeway) +} + +// ValidateWithLeeway checks claims in a token against expected values. A +// custom leeway may be specified for comparing time values. You may pass a +// zero value to check time values with no leeway, but you should not that +// numeric date values are rounded to the nearest second and sub-second +// precision is not supported. +// +// The leeway gives some extra time to the token from the server's +// point of view. That is, if the token is expired, ValidateWithLeeway +// will still accept the token for 'leeway' amount of time. This fails +// if you're using this function to check if a server will accept your +// token, because it will think the token is valid even after it +// expires. So if you're a client validating if the token is valid to +// be submitted to a server, use leeway <=0, if you're a server +// validation a token, use leeway >=0. +func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { + if e.Issuer != "" && e.Issuer != c.Issuer { + return ErrInvalidIssuer + } + + if e.Subject != "" && e.Subject != c.Subject { + return ErrInvalidSubject + } + + if e.ID != "" && e.ID != c.ID { + return ErrInvalidID + } + + if len(e.Audience) != 0 { + for _, v := range e.Audience { + if !c.Audience.Contains(v) { + return ErrInvalidAudience + } + } + } + + if !e.Time.IsZero() { + if c.NotBefore != nil && e.Time.Add(leeway).Before(c.NotBefore.Time()) { + return ErrNotValidYet + } + + if c.Expiry != nil && e.Time.Add(-leeway).After(c.Expiry.Time()) { + return ErrExpired + } + + // IssuedAt is optional but cannot be in the future. This is not required by the RFC, but + // something is misconfigured if this happens and we should not trust it. + if c.IssuedAt != nil && e.Time.Add(leeway).Before(c.IssuedAt.Time()) { + return ErrIssuedInTheFuture + } + } + + return nil +} diff --git a/vendor/gopkg.in/square/go-jose.v2/opaque.go b/vendor/gopkg.in/square/go-jose.v2/opaque.go index df747f99..fc3e8d2e 100644 --- a/vendor/gopkg.in/square/go-jose.v2/opaque.go +++ b/vendor/gopkg.in/square/go-jose.v2/opaque.go @@ -17,7 +17,7 @@ package jose // OpaqueSigner is an interface that supports signing payloads with opaque -// private key(s). Private key operations preformed by implementors may, for +// private key(s). Private key operations performed by implementers may, for // example, occur in a hardware module. An OpaqueSigner may rotate signing keys // transparently to the user of this interface. type OpaqueSigner interface { diff --git a/vendor/gopkg.in/square/go-jose.v2/shared.go b/vendor/gopkg.in/square/go-jose.v2/shared.go index f8438641..f72e5a53 100644 --- a/vendor/gopkg.in/square/go-jose.v2/shared.go +++ b/vendor/gopkg.in/square/go-jose.v2/shared.go @@ -183,7 +183,7 @@ type Header struct { // Unverified certificate chain parsed from x5c header. certificates []*x509.Certificate - // Any headers not recognised above get unmarshaled + // Any headers not recognised above get unmarshalled // from JSON in a generic manner and placed in this map. ExtraHeaders map[HeaderKey]interface{} } @@ -295,12 +295,12 @@ func (parsed rawHeader) getAPV() (*byteBuffer, error) { return parsed.getByteBuffer(headerAPV) } -// getIV extracts parsed "iv" frpom the raw JSON. +// getIV extracts parsed "iv" from the raw JSON. func (parsed rawHeader) getIV() (*byteBuffer, error) { return parsed.getByteBuffer(headerIV) } -// getTag extracts parsed "tag" frpom the raw JSON. +// getTag extracts parsed "tag" from the raw JSON. func (parsed rawHeader) getTag() (*byteBuffer, error) { return parsed.getByteBuffer(headerTag) } diff --git a/vendor/gopkg.in/square/go-jose.v2/signing.go b/vendor/gopkg.in/square/go-jose.v2/signing.go index 664a51cc..bad820ce 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing.go @@ -370,7 +370,11 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter } } - input := obj.computeAuthData(payload, &signature) + input, err := obj.computeAuthData(payload, &signature) + if err != nil { + return ErrCryptoFailure + } + alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { @@ -421,7 +425,11 @@ outer: } } - input := obj.computeAuthData(payload, &signature) + input, err := obj.computeAuthData(payload, &signature) + if err != nil { + continue + } + alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 648e7327..3b296241 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,9 +62,6 @@ github.com/coredns/coredns/plugin/pkg/transport github.com/coredns/coredns/plugin/pkg/uniq github.com/coredns/coredns/plugin/test github.com/coredns/coredns/request -# github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 -## explicit -github.com/coreos/go-oidc/jose # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf ## explicit github.com/coreos/go-systemd/daemon @@ -530,17 +527,18 @@ google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb -# gopkg.in/coreos/go-oidc.v2 v2.1.0 +# gopkg.in/coreos/go-oidc.v2 v2.2.1 ## explicit gopkg.in/coreos/go-oidc.v2 # gopkg.in/natefinch/lumberjack.v2 v2.0.0 ## explicit gopkg.in/natefinch/lumberjack.v2 -# gopkg.in/square/go-jose.v2 v2.4.0 +# gopkg.in/square/go-jose.v2 v2.6.0 ## explicit gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2/cipher gopkg.in/square/go-jose.v2/json +gopkg.in/square/go-jose.v2/jwt # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ## explicit gopkg.in/tomb.v1 From 5e2e757403c0c05142ce5dbbd4eac6effb77aa79 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 18 Mar 2022 18:14:07 +0000 Subject: [PATCH 025/238] TUN-5907: Change notes for 2022.3.3 --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ab14bb14..4477dd0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## 2022.3.3 +### Bug Fixes +- `cloudflared service install` now starts the underlying agent service on Windows operating system (similarly to the +behaviour in Linux and MacOS). + ## 2022.3.1 ### Bug Fixes - Various fixes to the reliability of `quic` protocol, including an edge case that could lead to cloudflared crashing. From 6eeaf4be4b03398a676f1488e61da13acaedb5df Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 18 Mar 2022 18:30:38 +0000 Subject: [PATCH 026/238] Release 2022.3.3 --- RELEASE_NOTES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 661f1916..01898cee 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,10 @@ +2022.3.3 +- 2022-03-17 TUN-5893: Start windows service on install, stop on uninstall. Previously user had to manually start the service after running 'cloudflared tunnel install' and stop the service before running uninstall command. +- 2022-03-17 Revert "CC-796: Remove dependency on unsupported version of go-oidc" +- 2022-03-18 TUN-5881: Clarify success (or lack thereof) of (un)installing cloudflared service +- 2022-03-18 CC-796: Remove dependency on unsupported version of go-oidc +- 2022-03-18 TUN-5907: Change notes for 2022.3.3 + 2022.3.2 - 2022-03-10 TUN-5833: Create constant for allow-remote-config - 2022-03-15 TUN-5867: Return error if service was already installed From e2a8302bbca9804507e64d77feeecb04298260b5 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 14 Mar 2022 10:51:10 -0700 Subject: [PATCH 027/238] TUN-5869: Add configuration endpoint in metrics server --- cmd/cloudflared/proxydns/cmd.go | 2 +- cmd/cloudflared/tunnel/cmd.go | 12 ++--- config/configuration.go | 2 +- ingress/config.go | 80 +++++++++++++++--------------- ingress/config_test.go | 26 +++++----- ingress/ingress.go | 17 ++++--- ingress/ingress_test.go | 14 +++--- ingress/origin_service.go | 53 +++++++++++++++++--- ingress/rule.go | 27 +++++++--- ingress/rule_test.go | 78 ++++++++++++++++++++++++++++- metrics/metrics.go | 26 +++++++++- orchestration/orchestrator.go | 27 ++++++++++ orchestration/orchestrator_test.go | 7 +-- 13 files changed, 275 insertions(+), 96 deletions(-) diff --git a/cmd/cloudflared/proxydns/cmd.go b/cmd/cloudflared/proxydns/cmd.go index bd6c0e25..ae03d8c6 100644 --- a/cmd/cloudflared/proxydns/cmd.go +++ b/cmd/cloudflared/proxydns/cmd.go @@ -73,7 +73,7 @@ func Run(c *cli.Context) error { log.Fatal().Err(err).Msg("Failed to open the metrics listener") } - go metrics.ServeMetrics(metricsListener, nil, nil, "", log) + go metrics.ServeMetrics(metricsListener, nil, nil, "", nil, log) listener, err := tunneldns.CreateListener( c.String("address"), diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 43eee046..b6895967 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -340,6 +340,11 @@ func StartServer( return err } + orchestrator, err := orchestration.NewOrchestrator(ctx, dynamicConfig, tunnelConfig.Tags, tunnelConfig.Log) + if err != nil { + return err + } + metricsListener, err := listeners.Listen("tcp", c.String("metrics")) if err != nil { log.Err(err).Msg("Error opening metrics server listener") @@ -351,14 +356,9 @@ func StartServer( defer wg.Done() readinessServer := metrics.NewReadyServer(log) observer.RegisterSink(readinessServer) - errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, log) + errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, orchestrator, log) }() - orchestrator, err := orchestration.NewOrchestrator(ctx, dynamicConfig, tunnelConfig.Tags, tunnelConfig.Log) - if err != nil { - return err - } - reconnectCh := make(chan supervisor.ReconnectSignal, 1) if c.IsSet("stdin-control") { log.Info().Msg("Enabling control through stdin") diff --git a/config/configuration.go b/config/configuration.go index ffd3eb7c..c73a3a46 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -411,7 +411,7 @@ type CustomDuration struct { time.Duration } -func (s *CustomDuration) MarshalJSON() ([]byte, error) { +func (s CustomDuration) MarshalJSON() ([]byte, error) { return json.Marshal(s.Duration.Seconds()) } diff --git a/ingress/config.go b/ingress/config.go index f8284b43..0b59ecff 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -11,14 +11,16 @@ import ( "github.com/cloudflare/cloudflared/tlsconfig" ) -const ( - defaultConnectTimeout = 30 * time.Second - defaultTLSTimeout = 10 * time.Second - defaultTCPKeepAlive = 30 * time.Second - defaultKeepAliveConnections = 100 - defaultKeepAliveTimeout = 90 * time.Second - defaultProxyAddress = "127.0.0.1" +var ( + defaultConnectTimeout = config.CustomDuration{Duration: 30 * time.Second} + defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second} + defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second} + defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second} +) +const ( + defaultProxyAddress = "127.0.0.1" + defaultKeepAliveConnections = 100 SSHServerFlag = "ssh-server" Socks5Flag = "socks5" ProxyConnectTimeoutFlag = "proxy-connect-timeout" @@ -68,12 +70,12 @@ func (rc *RemoteConfig) UnmarshalJSON(b []byte) error { } func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { - var connectTimeout time.Duration = defaultConnectTimeout - var tlsTimeout time.Duration = defaultTLSTimeout - var tcpKeepAlive time.Duration = defaultTCPKeepAlive + var connectTimeout config.CustomDuration = defaultConnectTimeout + var tlsTimeout config.CustomDuration = defaultTLSTimeout + var tcpKeepAlive config.CustomDuration = defaultTCPKeepAlive var noHappyEyeballs bool var keepAliveConnections int = defaultKeepAliveConnections - var keepAliveTimeout time.Duration = defaultKeepAliveTimeout + var keepAliveTimeout config.CustomDuration = defaultKeepAliveTimeout var httpHostHeader string var originServerName string var caPool string @@ -84,13 +86,13 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { var proxyPort uint var proxyType string if flag := ProxyConnectTimeoutFlag; c.IsSet(flag) { - connectTimeout = c.Duration(flag) + connectTimeout = config.CustomDuration{Duration: c.Duration(flag)} } if flag := ProxyTLSTimeoutFlag; c.IsSet(flag) { - tlsTimeout = c.Duration(flag) + tlsTimeout = config.CustomDuration{Duration: c.Duration(flag)} } if flag := ProxyTCPKeepAliveFlag; c.IsSet(flag) { - tcpKeepAlive = c.Duration(flag) + tcpKeepAlive = config.CustomDuration{Duration: c.Duration(flag)} } if flag := ProxyNoHappyEyeballsFlag; c.IsSet(flag) { noHappyEyeballs = c.Bool(flag) @@ -99,7 +101,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { keepAliveConnections = c.Int(flag) } if flag := ProxyKeepAliveTimeoutFlag; c.IsSet(flag) { - keepAliveTimeout = c.Duration(flag) + keepAliveTimeout = config.CustomDuration{Duration: c.Duration(flag)} } if flag := HTTPHostHeaderFlag; c.IsSet(flag) { httpHostHeader = c.String(flag) @@ -158,13 +160,13 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { ProxyAddress: defaultProxyAddress, } if c.ConnectTimeout != nil { - out.ConnectTimeout = c.ConnectTimeout.Duration + out.ConnectTimeout = *c.ConnectTimeout } if c.TLSTimeout != nil { - out.TLSTimeout = c.TLSTimeout.Duration + out.TLSTimeout = *c.TLSTimeout } if c.TCPKeepAlive != nil { - out.TCPKeepAlive = c.TCPKeepAlive.Duration + out.TCPKeepAlive = *c.TCPKeepAlive } if c.NoHappyEyeballs != nil { out.NoHappyEyeballs = *c.NoHappyEyeballs @@ -173,7 +175,7 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { out.KeepAliveConnections = *c.KeepAliveConnections } if c.KeepAliveTimeout != nil { - out.KeepAliveTimeout = c.KeepAliveTimeout.Duration + out.KeepAliveTimeout = *c.KeepAliveTimeout } if c.HTTPHostHeader != nil { out.HTTPHostHeader = *c.HTTPHostHeader @@ -218,52 +220,52 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { // Note: To specify a time.Duration in go-yaml, use e.g. "3s" or "24h". type OriginRequestConfig struct { // HTTP proxy timeout for establishing a new connection - ConnectTimeout time.Duration `yaml:"connectTimeout"` + ConnectTimeout config.CustomDuration `yaml:"connectTimeout" json:"connectTimeout"` // HTTP proxy timeout for completing a TLS handshake - TLSTimeout time.Duration `yaml:"tlsTimeout"` + TLSTimeout config.CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout"` // HTTP proxy TCP keepalive duration - TCPKeepAlive time.Duration `yaml:"tcpKeepAlive"` + TCPKeepAlive config.CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"` // HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback - NoHappyEyeballs bool `yaml:"noHappyEyeballs"` + NoHappyEyeballs bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"` // HTTP proxy timeout for closing an idle connection - KeepAliveTimeout time.Duration `yaml:"keepAliveTimeout"` + KeepAliveTimeout config.CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"` // HTTP proxy maximum keepalive connection pool size - KeepAliveConnections int `yaml:"keepAliveConnections"` + KeepAliveConnections int `yaml:"keepAliveConnections" json:"keepAliveConnections"` // Sets the HTTP Host header for the local webserver. - HTTPHostHeader string `yaml:"httpHostHeader"` + HTTPHostHeader string `yaml:"httpHostHeader" json:"httpHostHeader"` // Hostname on the origin server certificate. - OriginServerName string `yaml:"originServerName"` + OriginServerName string `yaml:"originServerName" json:"originServerName"` // Path to the CA for the certificate of your origin. // This option should be used only if your certificate is not signed by Cloudflare. - CAPool string `yaml:"caPool"` + CAPool string `yaml:"caPool" json:"caPool"` // Disables TLS verification of the certificate presented by your origin. // Will allow any certificate from the origin to be accepted. // Note: The connection from your machine to Cloudflare's Edge is still encrypted. - NoTLSVerify bool `yaml:"noTLSVerify"` + NoTLSVerify bool `yaml:"noTLSVerify" json:"noTLSVerify"` // Disables chunked transfer encoding. // Useful if you are running a WSGI server. - DisableChunkedEncoding bool `yaml:"disableChunkedEncoding"` + DisableChunkedEncoding bool `yaml:"disableChunkedEncoding" json:"disableChunkedEncoding"` // Runs as jump host - BastionMode bool `yaml:"bastionMode"` + BastionMode bool `yaml:"bastionMode" json:"bastionMode"` // Listen address for the proxy. - ProxyAddress string `yaml:"proxyAddress"` + ProxyAddress string `yaml:"proxyAddress" json:"proxyAddress"` // Listen port for the proxy. - ProxyPort uint `yaml:"proxyPort"` + ProxyPort uint `yaml:"proxyPort" json:"proxyPort"` // What sort of proxy should be started - ProxyType string `yaml:"proxyType"` + ProxyType string `yaml:"proxyType" json:"proxyType"` // IP rules for the proxy service - IPRules []ipaccess.Rule `yaml:"ipRules"` + IPRules []ipaccess.Rule `yaml:"ipRules" json:"ipRules"` } func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) { if val := overrides.ConnectTimeout; val != nil { - defaults.ConnectTimeout = val.Duration + defaults.ConnectTimeout = *val } } func (defaults *OriginRequestConfig) setTLSTimeout(overrides config.OriginRequestConfig) { if val := overrides.TLSTimeout; val != nil { - defaults.TLSTimeout = val.Duration + defaults.TLSTimeout = *val } } @@ -281,13 +283,13 @@ func (defaults *OriginRequestConfig) setKeepAliveConnections(overrides config.Or func (defaults *OriginRequestConfig) setKeepAliveTimeout(overrides config.OriginRequestConfig) { if val := overrides.KeepAliveTimeout; val != nil { - defaults.KeepAliveTimeout = val.Duration + defaults.KeepAliveTimeout = *val } } func (defaults *OriginRequestConfig) setTCPKeepAlive(overrides config.OriginRequestConfig) { if val := overrides.TCPKeepAlive; val != nil { - defaults.TCPKeepAlive = val.Duration + defaults.TCPKeepAlive = *val } } diff --git a/ingress/config_test.go b/ingress/config_test.go index e520c9bf..55229137 100644 --- a/ingress/config_test.go +++ b/ingress/config_test.go @@ -65,7 +65,7 @@ func TestUnmarshalRemoteConfigOverridesGlobal(t *testing.T) { err := json.Unmarshal(rawConfig, &remoteConfig) require.NoError(t, err) require.True(t, remoteConfig.Ingress.Rules[0].Config.NoTLSVerify) - require.True(t, remoteConfig.Ingress.defaults.NoHappyEyeballs) + require.True(t, remoteConfig.Ingress.Defaults.NoHappyEyeballs) } func TestOriginRequestConfigOverrides(t *testing.T) { @@ -74,11 +74,11 @@ func TestOriginRequestConfigOverrides(t *testing.T) { // root-level configuration. actual0 := ing.Rules[0].Config expected0 := OriginRequestConfig{ - ConnectTimeout: 1 * time.Minute, - TLSTimeout: 1 * time.Second, - TCPKeepAlive: 1 * time.Second, + ConnectTimeout: config.CustomDuration{Duration: 1 * time.Minute}, + TLSTimeout: config.CustomDuration{Duration: 1 * time.Second}, + TCPKeepAlive: config.CustomDuration{Duration: 1 * time.Second}, NoHappyEyeballs: true, - KeepAliveTimeout: 1 * time.Second, + KeepAliveTimeout: config.CustomDuration{Duration: 1 * time.Second}, KeepAliveConnections: 1, HTTPHostHeader: "abc", OriginServerName: "a1", @@ -99,11 +99,11 @@ func TestOriginRequestConfigOverrides(t *testing.T) { // Rule 1 overrode all the root-level config. actual1 := ing.Rules[1].Config expected1 := OriginRequestConfig{ - ConnectTimeout: 2 * time.Minute, - TLSTimeout: 2 * time.Second, - TCPKeepAlive: 2 * time.Second, + ConnectTimeout: config.CustomDuration{Duration: 2 * time.Minute}, + TLSTimeout: config.CustomDuration{Duration: 2 * time.Second}, + TCPKeepAlive: config.CustomDuration{Duration: 2 * time.Second}, NoHappyEyeballs: false, - KeepAliveTimeout: 2 * time.Second, + KeepAliveTimeout: config.CustomDuration{Duration: 2 * time.Second}, KeepAliveConnections: 2, HTTPHostHeader: "def", OriginServerName: "b2", @@ -286,11 +286,11 @@ func TestOriginRequestConfigDefaults(t *testing.T) { // Rule 1 overrode all defaults. actual1 := ing.Rules[1].Config expected1 := OriginRequestConfig{ - ConnectTimeout: 2 * time.Minute, - TLSTimeout: 2 * time.Second, - TCPKeepAlive: 2 * time.Second, + ConnectTimeout: config.CustomDuration{Duration: 2 * time.Minute}, + TLSTimeout: config.CustomDuration{Duration: 2 * time.Second}, + TCPKeepAlive: config.CustomDuration{Duration: 2 * time.Second}, NoHappyEyeballs: false, - KeepAliveTimeout: 2 * time.Second, + KeepAliveTimeout: config.CustomDuration{Duration: 2 * time.Second}, KeepAliveConnections: 2, HTTPHostHeader: "def", OriginServerName: "b2", diff --git a/ingress/ingress.go b/ingress/ingress.go index 801bc551..8d2ec5ba 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -64,8 +64,8 @@ func matchHost(ruleHost, reqHost string) bool { // Ingress maps eyeball requests to origins. type Ingress struct { - Rules []Rule - defaults OriginRequestConfig + Rules []Rule `json:"ingress"` + Defaults OriginRequestConfig `json:"originRequest"` } // NewSingleOrigin constructs an Ingress set with only one rule, constructed from @@ -86,7 +86,7 @@ func NewSingleOrigin(c *cli.Context, allowURLFromArgs bool) (Ingress, error) { Config: setConfig(defaults, config.OriginRequestConfig{}), }, }, - defaults: defaults, + Defaults: defaults, } return ing, err } @@ -180,7 +180,7 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq } srv := newStatusCode(status) service = &srv - } else if r.Service == "hello_world" || r.Service == "hello-world" || r.Service == "helloworld" { + } else if r.Service == HelloWorldService || r.Service == "hello-world" || r.Service == "helloworld" { service = new(helloWorld) } else if r.Service == ServiceSocksProxy { rules := make([]ipaccess.Rule, len(r.OriginRequest.IPRules)) @@ -230,23 +230,24 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq return Ingress{}, err } - var pathRegex *regexp.Regexp + var pathRegexp *Regexp if r.Path != "" { var err error - pathRegex, err = regexp.Compile(r.Path) + regex, err := regexp.Compile(r.Path) if err != nil { return Ingress{}, errors.Wrapf(err, "Rule #%d has an invalid regex", i+1) } + pathRegexp = &Regexp{Regexp: regex} } rules[i] = Rule{ Hostname: r.Hostname, Service: service, - Path: pathRegex, + Path: pathRegexp, Config: cfg, } } - return Ingress{Rules: rules, defaults: defaults}, nil + return Ingress{Rules: rules, Defaults: defaults}, nil } func validateHostname(r config.UnvalidatedIngressRule, ruleIndex, totalRules int) error { diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 1e999a4e..7ca8965a 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -482,12 +482,12 @@ func TestSingleOriginSetsConfig(t *testing.T) { ingress, err := NewSingleOrigin(cliCtx, allowURLFromArgs) require.NoError(t, err) - assert.Equal(t, time.Second, ingress.Rules[0].Config.ConnectTimeout) - assert.Equal(t, time.Second, ingress.Rules[0].Config.TLSTimeout) - assert.Equal(t, time.Second, ingress.Rules[0].Config.TCPKeepAlive) + assert.Equal(t, config.CustomDuration{Duration: time.Second}, ingress.Rules[0].Config.ConnectTimeout) + assert.Equal(t, config.CustomDuration{Duration: time.Second}, ingress.Rules[0].Config.TLSTimeout) + assert.Equal(t, config.CustomDuration{Duration: time.Second}, ingress.Rules[0].Config.TCPKeepAlive) assert.True(t, ingress.Rules[0].Config.NoHappyEyeballs) assert.Equal(t, 10, ingress.Rules[0].Config.KeepAliveConnections) - assert.Equal(t, time.Second, ingress.Rules[0].Config.KeepAliveTimeout) + assert.Equal(t, config.CustomDuration{Duration: time.Second}, ingress.Rules[0].Config.KeepAliveTimeout) assert.Equal(t, "example.com:8080", ingress.Rules[0].Config.HTTPHostHeader) assert.Equal(t, "example.com", ingress.Rules[0].Config.OriginServerName) assert.Equal(t, "/etc/certs/ca.pem", ingress.Rules[0].Config.CAPool) @@ -508,7 +508,7 @@ func TestFindMatchingRule(t *testing.T) { }, { Hostname: "tunnel-b.example.com", - Path: mustParsePath(t, "/health"), + Path: MustParsePath(t, "/health"), }, { Hostname: "*", @@ -591,10 +591,10 @@ func TestIsHTTPService(t *testing.T) { } } -func mustParsePath(t *testing.T, path string) *regexp.Regexp { +func MustParsePath(t *testing.T, path string) *Regexp { regexp, err := regexp.Compile(path) assert.NoError(t, err) - return regexp + return &Regexp{Regexp: regexp} } func MustParseURL(t *testing.T, rawURL string) *url.URL { diff --git a/ingress/origin_service.go b/ingress/origin_service.go index c76c98a4..239b6e93 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -3,6 +3,7 @@ package ingress import ( "context" "crypto/tls" + "encoding/json" "fmt" "io" "net" @@ -20,7 +21,8 @@ import ( ) const ( - HelloWorldService = "Hello World test origin" + HelloWorldService = "hello_world" + HttpStatusService = "http_status" ) // OriginService is something a tunnel can proxy traffic to. @@ -31,6 +33,7 @@ type OriginService interface { // starting the origin service. // Implementor of services managed by cloudflared should terminate the service if shutdownC is closed start(log *zerolog.Logger, shutdownC <-chan struct{}, cfg OriginRequestConfig) error + MarshalJSON() ([]byte, error) } // unixSocketPath is an OriginService representing a unix socket (which accepts HTTP or HTTPS) @@ -41,7 +44,11 @@ type unixSocketPath struct { } func (o *unixSocketPath) String() string { - return "unix socket: " + o.path + scheme := "" + if o.scheme == "https" { + scheme = "+tls" + } + return fmt.Sprintf("unix%s:%s", scheme, o.path) } func (o *unixSocketPath) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { @@ -53,6 +60,10 @@ func (o *unixSocketPath) start(log *zerolog.Logger, _ <-chan struct{}, cfg Origi return nil } +func (o unixSocketPath) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + type httpService struct { url *url.URL hostHeader string @@ -73,6 +84,10 @@ func (o *httpService) String() string { return o.url.String() } +func (o httpService) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + // rawTCPService dials TCP to the destination specified by the client // It's used by warp routing type rawTCPService struct { @@ -87,6 +102,10 @@ func (o *rawTCPService) start(log *zerolog.Logger, _ <-chan struct{}, cfg Origin return nil } +func (o rawTCPService) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + // tcpOverWSService models TCP origins serving eyeballs connecting over websocket, such as // cloudflared access commands. type tcpOverWSService struct { @@ -153,6 +172,10 @@ func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg Ori return nil } +func (o tcpOverWSService) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + func (o *socksProxyOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { return nil } @@ -161,6 +184,10 @@ func (o *socksProxyOverWSService) String() string { return ServiceSocksProxy } +func (o socksProxyOverWSService) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + // HelloWorld is an OriginService for the built-in Hello World server. // Users only use this for testing and experimenting with cloudflared. type helloWorld struct { @@ -197,6 +224,10 @@ func (o *helloWorld) start( return nil } +func (o helloWorld) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + // statusCode is an OriginService that just responds with a given HTTP status. // Typical use-case is "user wants the catch-all rule to just respond 404". type statusCode struct { @@ -213,7 +244,7 @@ func newStatusCode(status int) statusCode { } func (o *statusCode) String() string { - return fmt.Sprintf("HTTP %d", o.resp.StatusCode) + return fmt.Sprintf("http_status:%d", o.resp.StatusCode) } func (o *statusCode) start( @@ -224,6 +255,10 @@ func (o *statusCode) start( return nil } +func (o statusCode) MarshalJSON() ([]byte, error) { + return json.Marshal(o.String()) +} + type NopReadCloser struct{} // Read always returns EOF to signal end of input @@ -245,8 +280,8 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol Proxy: http.ProxyFromEnvironment, MaxIdleConns: cfg.KeepAliveConnections, MaxIdleConnsPerHost: cfg.KeepAliveConnections, - IdleConnTimeout: cfg.KeepAliveTimeout, - TLSHandshakeTimeout: cfg.TLSTimeout, + IdleConnTimeout: cfg.KeepAliveTimeout.Duration, + TLSHandshakeTimeout: cfg.TLSTimeout.Duration, ExpectContinueTimeout: 1 * time.Second, TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify}, } @@ -255,8 +290,8 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol } dialer := &net.Dialer{ - Timeout: cfg.ConnectTimeout, - KeepAlive: cfg.TCPKeepAlive, + Timeout: cfg.ConnectTimeout.Duration, + KeepAlive: cfg.TCPKeepAlive.Duration, } if cfg.NoHappyEyeballs { dialer.FallbackDelay = -1 // As of Golang 1.12, a negative delay disables "happy eyeballs" @@ -296,3 +331,7 @@ func (mos MockOriginHTTPService) String() string { func (mos MockOriginHTTPService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { return nil } + +func (mos MockOriginHTTPService) MarshalJSON() ([]byte, error) { + return json.Marshal(mos.String()) +} diff --git a/ingress/rule.go b/ingress/rule.go index e91b4139..08499919 100644 --- a/ingress/rule.go +++ b/ingress/rule.go @@ -1,6 +1,7 @@ package ingress import ( + "encoding/json" "regexp" "strings" ) @@ -9,18 +10,18 @@ import ( // service running on the given URL. type Rule struct { // Requests for this hostname will be proxied to this rule's service. - Hostname string + Hostname string `json:"hostname"` // Path is an optional regex that can specify path-driven ingress rules. - Path *regexp.Regexp + Path *Regexp `json:"path"` // A (probably local) address. Requests for a hostname which matches this // rule's hostname pattern will be proxied to the service running on this // address. - Service OriginService + Service OriginService `json:"service"` // Configure the request cloudflared sends to this specific origin. - Config OriginRequestConfig + Config OriginRequestConfig `json:"originRequest"` } // MultiLineString is for outputting rules in a human-friendly way when Cloudflared @@ -32,9 +33,9 @@ func (r Rule) MultiLineString() string { out.WriteString(r.Hostname) out.WriteRune('\n') } - if r.Path != nil { + if r.Path != nil && r.Path.Regexp != nil { out.WriteString("\tpath: ") - out.WriteString(r.Path.String()) + out.WriteString(r.Path.Regexp.String()) out.WriteRune('\n') } out.WriteString("\tservice: ") @@ -45,6 +46,18 @@ func (r Rule) MultiLineString() string { // Matches checks if the rule matches a given hostname/path combination. func (r *Rule) Matches(hostname, path string) bool { hostMatch := r.Hostname == "" || r.Hostname == "*" || matchHost(r.Hostname, hostname) - pathMatch := r.Path == nil || r.Path.MatchString(path) + pathMatch := r.Path == nil || r.Path.Regexp == nil || r.Path.Regexp.MatchString(path) return hostMatch && pathMatch } + +// Regexp adds unmarshalling from json for regexp.Regexp +type Regexp struct { + *regexp.Regexp +} + +func (r *Regexp) MarshalJSON() ([]byte, error) { + if r.Regexp == nil { + return json.Marshal(nil) + } + return json.Marshal(r.Regexp.String()) +} diff --git a/ingress/rule_test.go b/ingress/rule_test.go index f5bfcd92..7561709a 100644 --- a/ingress/rule_test.go +++ b/ingress/rule_test.go @@ -1,6 +1,7 @@ package ingress import ( + "encoding/json" "io" "net/http/httptest" "net/url" @@ -8,12 +9,14 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cloudflare/cloudflared/config" ) func Test_rule_matches(t *testing.T) { type fields struct { Hostname string - Path *regexp.Regexp + Path *Regexp Service OriginService } type args struct { @@ -99,13 +102,35 @@ func Test_rule_matches(t *testing.T) { name: "Hostname and path", fields: fields{ Hostname: "*.example.com", - Path: regexp.MustCompile("/static/.*\\.html"), + Path: &Regexp{Regexp: regexp.MustCompile("/static/.*\\.html")}, }, args: args{ requestURL: MustParseURL(t, "https://www.example.com/static/index.html"), }, want: true, }, + { + name: "Hostname and empty Regex", + fields: fields{ + Hostname: "example.com", + Path: &Regexp{}, + }, + args: args{ + requestURL: MustParseURL(t, "https://example.com/"), + }, + want: true, + }, + { + name: "Hostname and nil path", + fields: fields{ + Hostname: "example.com", + Path: nil, + }, + args: args{ + requestURL: MustParseURL(t, "https://example.com/"), + }, + want: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -144,3 +169,52 @@ func TestStaticHTTPStatus(t *testing.T) { sendReq() sendReq() } + +func TestMarshalJSON(t *testing.T) { + localhost8000 := MustParseURL(t, "https://localhost:8000") + defaultConfig := setConfig(originRequestFromConfig(config.OriginRequestConfig{}), config.OriginRequestConfig{}) + tests := []struct { + name string + path *Regexp + expected string + want bool + }{ + { + name: "Nil", + path: nil, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + want: true, + }, + { + name: "Nil regex", + path: &Regexp{Regexp: nil}, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + want: true, + }, + { + name: "Empty", + path: &Regexp{Regexp: regexp.MustCompile("")}, + expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + want: true, + }, + { + name: "Basic", + path: &Regexp{Regexp: regexp.MustCompile("/echo")}, + expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Rule{ + Hostname: "example.com", + Service: &httpService{url: localhost8000}, + Path: tt.path, + Config: defaultConfig, + } + bytes, err := json.Marshal(r) + require.NoError(t, err) + require.Equal(t, tt.expected, string(bytes)) + }) + } +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 3210e840..0129c49a 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -22,7 +22,16 @@ const ( startupTime = time.Millisecond * 500 ) -func newMetricsHandler(readyServer *ReadyServer, quickTunnelHostname string) *mux.Router { +type orchestrator interface { + GetConfigJSON() ([]byte, error) +} + +func newMetricsHandler( + readyServer *ReadyServer, + quickTunnelHostname string, + orchestrator orchestrator, + log *zerolog.Logger, +) *mux.Router { router := mux.NewRouter() router.PathPrefix("/debug/").Handler(http.DefaultServeMux) @@ -36,6 +45,18 @@ func newMetricsHandler(readyServer *ReadyServer, quickTunnelHostname string) *mu router.HandleFunc("/quicktunnel", func(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, `{"hostname":"%s"}`, quickTunnelHostname) }) + if orchestrator != nil { + router.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { + json, err := orchestrator.GetConfigJSON() + if err != nil { + w.WriteHeader(500) + _, _ = fmt.Fprintf(w, "ERR: %v", err) + log.Err(err).Msg("Failed to serve config") + return + } + _, _ = w.Write(json) + }) + } return router } @@ -45,6 +66,7 @@ func ServeMetrics( shutdownC <-chan struct{}, readyServer *ReadyServer, quickTunnelHostname string, + orchestrator orchestrator, log *zerolog.Logger, ) (err error) { var wg sync.WaitGroup @@ -52,7 +74,7 @@ func ServeMetrics( trace.AuthRequest = func(*http.Request) (bool, bool) { return true, true } // TODO: parameterize ReadTimeout and WriteTimeout. The maximum time we can // profile CPU usage depends on WriteTimeout - h := newMetricsHandler(readyServer, quickTunnelHostname) + h := newMetricsHandler(readyServer, quickTunnelHostname, orchestrator, log) server := &http.Server{ ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index 59d8db57..9206cc21 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/proxy" @@ -130,6 +131,32 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEn return nil } +// GetConfigJSON returns the current version and configuration as JSON +func (o *Orchestrator) GetConfigJSON() ([]byte, error) { + o.lock.RLock() + defer o.lock.RUnlock() + var currentConfiguration = struct { + Version int32 `json:"version"` + Config struct { + Ingress []ingress.Rule `json:"ingress"` + WarpRouting config.WarpRoutingConfig `json:"warp-routing"` + OriginRequest ingress.OriginRequestConfig `json:"originRequest"` + } `json:"config"` + }{ + Version: o.currentVersion, + Config: struct { + Ingress []ingress.Rule `json:"ingress"` + WarpRouting config.WarpRoutingConfig `json:"warp-routing"` + OriginRequest ingress.OriginRequestConfig `json:"originRequest"` + }{ + Ingress: o.config.Ingress.Rules, + WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled}, + OriginRequest: o.config.Ingress.Defaults, + }, + } + return json.Marshal(currentConfiguration) +} + // GetOriginProxy returns an interface to proxy to origin. It satisfies connection.ConfigManager interface func (o *Orchestrator) GetOriginProxy() (connection.OriginProxy, error) { val := o.proxy.Load() diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 78de169a..f3638ea7 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -17,6 +17,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/proxy" @@ -99,7 +100,7 @@ func TestUpdateConfiguration(t *testing.T) { require.Equal(t, "http://192.16.19.1:443", configV2.Ingress.Rules[0].Service.String()) require.Len(t, configV2.Ingress.Rules, 3) // originRequest of this ingress rule overrides global default - require.Equal(t, time.Second*10, configV2.Ingress.Rules[0].Config.ConnectTimeout) + require.Equal(t, config.CustomDuration{Duration: time.Second * 10}, configV2.Ingress.Rules[0].Config.ConnectTimeout) require.Equal(t, true, configV2.Ingress.Rules[0].Config.NoTLSVerify) // Inherited from global default require.Equal(t, true, configV2.Ingress.Rules[0].Config.NoHappyEyeballs) @@ -108,14 +109,14 @@ func TestUpdateConfiguration(t *testing.T) { require.True(t, configV2.Ingress.Rules[1].Matches("jira.tunnel.org", "/users")) require.Equal(t, "http://172.32.20.6:80", configV2.Ingress.Rules[1].Service.String()) // originRequest of this ingress rule overrides global default - require.Equal(t, time.Second*30, configV2.Ingress.Rules[1].Config.ConnectTimeout) + require.Equal(t, config.CustomDuration{Duration: time.Second * 30}, configV2.Ingress.Rules[1].Config.ConnectTimeout) require.Equal(t, true, configV2.Ingress.Rules[1].Config.NoTLSVerify) // Inherited from global default require.Equal(t, true, configV2.Ingress.Rules[1].Config.NoHappyEyeballs) // Validate ingress rule 2, it's the catch-all rule require.True(t, configV2.Ingress.Rules[2].Matches("blogs.tunnel.io", "/2022/02/10")) // Inherited from global default - require.Equal(t, time.Second*90, configV2.Ingress.Rules[2].Config.ConnectTimeout) + require.Equal(t, config.CustomDuration{Duration: time.Second * 90}, configV2.Ingress.Rules[2].Config.ConnectTimeout) require.Equal(t, false, configV2.Ingress.Rules[2].Config.NoTLSVerify) require.Equal(t, true, configV2.Ingress.Rules[2].Config.NoHappyEyeballs) require.True(t, configV2.WarpRoutingEnabled) From 470e6c35c570f13081f56dc1110c241b085d546b Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 22 Mar 2022 11:22:18 +0000 Subject: [PATCH 028/238] TUN-5918: Clean up text in cloudflared tunnel --help --- cmd/cloudflared/tunnel/cmd.go | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index b6895967..345c8f80 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -127,26 +127,34 @@ func buildTunnelCommand(subcommands []*cli.Command) *cli.Command { Name: "tunnel", Action: cliutil.ConfiguredAction(TunnelCommand), Category: "Tunnel", - Usage: "Make a locally-running web service accessible over the internet using Cloudflare Tunnel.", + Usage: "Use Cloudflare Tunnel to expose private services to the Internet or to Cloudflare connected private users.", ArgsUsage: " ", - Description: `Cloudflare Tunnel asks you to specify a hostname on a Cloudflare-powered - domain you control and a local address. Traffic from that hostname is routed - (optionally via a Cloudflare Load Balancer) to this machine and appears on the - specified port where it can be served. + Description: ` Cloudflare Tunnel allows to expose private services without opening any ingress port on this machine. It can expose: + A) Locally reachable HTTP-based private services to the Internet on DNS with Cloudflare as authority (which you can +then protect with Cloudflare Access). + B) Locally reachable TCP/UDP-based private services to Cloudflare connected private users in the same account, e.g., +those enrolled to a Zero Trust WARP Client. - This feature requires your Cloudflare account be subscribed to the Cloudflare Smart Routing feature. +You can manage your Tunnels via dash.teams.cloudflare.com. This approach will only require you to run a single command +later in each machine where you wish to run a Tunnel. - To use, begin by calling login to download a certificate: +Alternatively, you can manage your Tunnels via the command line. Begin by obtaining a certificate to be able to do so: - $ cloudflared tunnel login + $ cloudflared tunnel login - With your certificate installed you can then launch your first tunnel, - replacing my.site.com with a subdomain of your site: +With your certificate installed you can then get started with Tunnels: - $ cloudflared tunnel --hostname my.site.com --url http://localhost:8080 + $ cloudflared tunnel create my-first-tunnel + $ cloudflared tunnel route dns my-first-tunnel my-first-tunnel.mydomain.com + $ cloudflared tunnel run --hello-world my-first-tunnel - If you have a web server running on port 8080 (in this example), it will be available on - the internet!`, +You can now access my-first-tunnel.mydomain.com and be served an example page by your local cloudflared process. + +For exposing local TCP/UDP services by IP to your privately connected users, check out: + + $ cloudflared tunnel route ip --help + +See https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/ for more info.`, Subcommands: subcommands, Flags: tunnelFlags(false), } From 4836216a9b779fde432ec4138a1bfef610bc9042 Mon Sep 17 00:00:00 2001 From: Piper McCorkle Date: Tue, 22 Mar 2022 13:55:32 -0500 Subject: [PATCH 029/238] TUN-5895 run brew bump-formula-pr on release --- .teamcity/update-homebrew-core.sh | 23 +++++++++++++++++++++++ cfsetup.yaml | 2 ++ 2 files changed, 25 insertions(+) create mode 100755 .teamcity/update-homebrew-core.sh diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh new file mode 100755 index 00000000..4a40a47d --- /dev/null +++ b/.teamcity/update-homebrew-core.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -euo pipefail + +if ! VERSION="$(git describe --tags --exact-match 2>/dev/null)" ; then + echo "Skipping public release for an untagged commit." + echo "##teamcity[buildStatus status='SUCCESS' text='Skipped due to lack of tag']" + exit 0 +fi + +if [[ "${HOMEBREW_GITHUB_API_TOKEN:-}" == "" ]] ; then + echo "Missing GITHUB_API_TOKEN" + exit 1 +fi + +# "install" Homebrew +git clone https://github.com/Homebrew/brew tmp/homebrew +eval "$(tmp/homebrew/bin/brew shellenv)" +brew update --force --quiet +chmod -R go-w "$(brew --prefix)/share/zsh" + +# bump formula pr +brew bump-formula-pr cloudflared diff --git a/cfsetup.yaml b/cfsetup.yaml index 39aa09a5..5180efa4 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -192,8 +192,10 @@ stretch: &stretch builddeps: - openssh-client - s3cmd + - jq post-cache: - .teamcity/update-homebrew.sh + - .teamcity/update-homebrew-core.sh github-message-release: build_dir: *build_dir builddeps: From 98736a03e136a27383d0902c88fdc664f4778a3d Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 22 Mar 2022 12:46:07 +0000 Subject: [PATCH 030/238] TUN-5915: New cloudflared command to allow to retrieve the token credentials for a Tunnel --- CHANGES.md | 6 +++ cfapi/client.go | 1 + cfapi/tunnel.go | 17 ++++++ cmd/cloudflared/tunnel/cmd.go | 1 + cmd/cloudflared/tunnel/subcommand_context.go | 15 ++++++ .../tunnel/subcommand_context_test.go | 4 ++ cmd/cloudflared/tunnel/subcommands.go | 53 +++++++++++++++++++ component-tests/config.py | 17 +++--- component-tests/test_token.py | 35 ++++++++++++ connection/connection.go | 11 ++++ 10 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 component-tests/test_token.py diff --git a/CHANGES.md b/CHANGES.md index 4477dd0c..91da8fea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +## 2022.3.4 +### New Features +- It is now possible to retrieve the credentials that allow to run a Tunnel in case you forgot/lost them. This is +achievable with: `cloudflared tunnel token --cred-file /path/to/file.json TUNNEL`. This new feature only works for +Tunnels created with cloudflared version 2022.3.0 or more recent. + ## 2022.3.3 ### Bug Fixes - `cloudflared service install` now starts the underlying agent service on Windows operating system (similarly to the diff --git a/cfapi/client.go b/cfapi/client.go index d1e794f7..fa5e7cb2 100644 --- a/cfapi/client.go +++ b/cfapi/client.go @@ -7,6 +7,7 @@ import ( type TunnelClient interface { CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error) GetTunnel(tunnelID uuid.UUID) (*Tunnel, error) + GetTunnelToken(tunnelID uuid.UUID) (string, error) DeleteTunnel(tunnelID uuid.UUID) error ListTunnels(filter *TunnelFilter) ([]*Tunnel, error) ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error) diff --git a/cfapi/tunnel.go b/cfapi/tunnel.go index cede09c9..87caf230 100644 --- a/cfapi/tunnel.go +++ b/cfapi/tunnel.go @@ -116,6 +116,23 @@ func (r *RESTClient) GetTunnel(tunnelID uuid.UUID) (*Tunnel, error) { return nil, r.statusCodeToError("get tunnel", resp) } +func (r *RESTClient) GetTunnelToken(tunnelID uuid.UUID) (token string, err error) { + endpoint := r.baseEndpoints.accountLevel + endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v/token", tunnelID)) + resp, err := r.sendRequest("GET", endpoint, nil) + if err != nil { + return "", errors.Wrap(err, "REST request failed") + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + err = parseResponse(resp.Body, &token) + return token, err + } + + return "", r.statusCodeToError("get tunnel token", resp) +} + func (r *RESTClient) DeleteTunnel(tunnelID uuid.UUID) error { endpoint := r.baseEndpoints.accountLevel endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v", tunnelID)) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 345c8f80..3eed3436 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -109,6 +109,7 @@ func Commands() []*cli.Command { buildIngressSubcommand(), buildDeleteCommand(), buildCleanupCommand(), + buildTokenCommand(), // for compatibility, allow following as tunnel subcommands proxydns.Command(true), cliutil.RemovedCommand("db-connect"), diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index 8490aa0c..df1d95f0 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -341,6 +341,21 @@ func (sc *subcommandContext) cleanupConnections(tunnelIDs []uuid.UUID) error { return nil } +func (sc *subcommandContext) getTunnelTokenCredentials(tunnelID uuid.UUID) (*connection.TunnelToken, error) { + client, err := sc.client() + if err != nil { + return nil, err + } + + token, err := client.GetTunnelToken(tunnelID) + if err != nil { + sc.log.Err(err).Msgf("Could not get the Token for the given Tunnel %v", tunnelID) + return nil, err + } + + return ParseToken(token) +} + func (sc *subcommandContext) route(tunnelID uuid.UUID, r cfapi.HostnameRoute) (cfapi.HostnameRouteResult, error) { client, err := sc.client() if err != nil { diff --git a/cmd/cloudflared/tunnel/subcommand_context_test.go b/cmd/cloudflared/tunnel/subcommand_context_test.go index 31d04b05..82b866d9 100644 --- a/cmd/cloudflared/tunnel/subcommand_context_test.go +++ b/cmd/cloudflared/tunnel/subcommand_context_test.go @@ -216,6 +216,10 @@ func (d *deleteMockTunnelStore) GetTunnel(tunnelID uuid.UUID) (*cfapi.Tunnel, er return &tunnel.tunnel, nil } +func (d *deleteMockTunnelStore) GetTunnelToken(tunnelID uuid.UUID) (string, error) { + return "token", nil +} + func (d *deleteMockTunnelStore) DeleteTunnel(tunnelID uuid.UUID) error { tunnel, ok := d.mockTunnels[tunnelID] if !ok { diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 905c8ad6..70c16545 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -714,6 +714,59 @@ func cleanupCommand(c *cli.Context) error { return sc.cleanupConnections(tunnelIDs) } +func buildTokenCommand() *cli.Command { + return &cli.Command{ + Name: "token", + Action: cliutil.ConfiguredAction(tokenCommand), + Usage: "Fetch the credentials token for an existing tunnel (by name or UUID) that allows to run it", + UsageText: "cloudflared tunnel [tunnel command options] token [subcommand options] TUNNEL", + Description: "cloudflared tunnel token will fetch the credentials token for a given tunnel (by its name or UUID), which is then used to run the tunnel. This command fails if the tunnel does not exist or has been deleted. Use the flag `cloudflared tunnel token --cred-file /my/path/file.json TUNNEL` to output the token to the credentials JSON file. Note: this command only works for Tunnels created since cloudflared version 2022.3.0", + Flags: []cli.Flag{credentialsFileFlagCLIOnly}, + CustomHelpTemplate: commandHelpTemplate(), + } +} + +func tokenCommand(c *cli.Context) error { + sc, err := newSubcommandContext(c) + if err != nil { + return errors.Wrap(err, "error setting up logger") + } + + warningChecker := updater.StartWarningCheck(c) + defer warningChecker.LogWarningIfAny(sc.log) + + if c.NArg() != 1 { + return cliutil.UsageError(`"cloudflared tunnel token" requires exactly 1 argument, the name or UUID of tunnel to fetch the credentials token for.`) + } + tunnelID, err := sc.findID(c.Args().First()) + if err != nil { + return errors.Wrap(err, "error parsing tunnel ID") + } + + token, err := sc.getTunnelTokenCredentials(tunnelID) + if err != nil { + return err + } + + if path := c.String(CredFileFlag); path != "" { + credentials := token.Credentials() + err := writeTunnelCredentials(path, &credentials) + if err != nil { + return errors.Wrapf(err, "error writing token credentials to JSON file in path %s", path) + } + + return nil + } + + encodedToken, err := token.Encode() + if err != nil { + return err + } + + fmt.Printf("%s", encodedToken) + return nil +} + func buildRouteCommand() *cli.Command { return &cli.Command{ Name: "route", diff --git a/component-tests/config.py b/component-tests/config.py index f53732f2..dd549081 100644 --- a/component-tests/config.py +++ b/component-tests/config.py @@ -72,13 +72,18 @@ class NamedTunnelConfig(NamedTunnelBaseConfig): return config - def get_token(self): - with open(self.credentials_file) as json_file: - creds = json.load(json_file) - token_dict = {"a": creds["AccountTag"], "t": creds["TunnelID"], "s": creds["TunnelSecret"]} - token_json_str = json.dumps(token_dict) + def get_tunnel_id(self): + return self.full_config["tunnel"] - return base64.b64encode(token_json_str.encode('utf-8')) + def get_token(self): + creds = self.get_credentials_json() + token_dict = {"a": creds["AccountTag"], "t": creds["TunnelID"], "s": creds["TunnelSecret"]} + token_json_str = json.dumps(token_dict) + return base64.b64encode(token_json_str.encode('utf-8')) + + def get_credentials_json(self): + with open(self.credentials_file) as json_file: + return json.load(json_file) @dataclass(frozen=True) diff --git a/component-tests/test_token.py b/component-tests/test_token.py new file mode 100644 index 00000000..ed99cb05 --- /dev/null +++ b/component-tests/test_token.py @@ -0,0 +1,35 @@ +import base64 +import json + +from setup import get_config_from_file, persist_origin_cert +from util import start_cloudflared + + +class TestToken: + def test_get_token(self, tmp_path, component_tests_config): + config = component_tests_config() + tunnel_id = config.get_tunnel_id() + + token_args = ["--origincert", cert_path(), "token", tunnel_id] + output = start_cloudflared(tmp_path, config, token_args) + + assert parse_token(config.get_token()) == parse_token(output.stdout) + + def test_get_credentials_file(self, tmp_path, component_tests_config): + config = component_tests_config() + tunnel_id = config.get_tunnel_id() + + cred_file = tmp_path / "test_get_credentials_file.json" + token_args = ["--origincert", cert_path(), "token", "--cred-file", cred_file, tunnel_id] + start_cloudflared(tmp_path, config, token_args) + + with open(cred_file) as json_file: + assert config.get_credentials_json() == json.load(json_file) + + +def cert_path(): + return get_config_from_file()["origincert"] + + +def parse_token(token): + return json.loads(base64.b64decode(token)) diff --git a/connection/connection.go b/connection/connection.go index ad14a85c..154350a6 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -2,6 +2,7 @@ package connection import ( "context" + "encoding/base64" "fmt" "io" "math" @@ -11,6 +12,7 @@ import ( "time" "github.com/google/uuid" + "github.com/pkg/errors" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" @@ -65,6 +67,15 @@ func (t TunnelToken) Credentials() Credentials { } } +func (t TunnelToken) Encode() (string, error) { + val, err := json.Marshal(t) + if err != nil { + return "", errors.Wrap(err, "could not JSON encode token") + } + + return base64.StdEncoding.EncodeToString(val), nil +} + type ClassicTunnelProperties struct { Hostname string OriginCert []byte From 62e1330e45ecc984061fa6eabd8dc7cb62c2c98c Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 24 Mar 2022 17:19:23 +0000 Subject: [PATCH 031/238] TUN-5933: Better messaging to help user when installing service if it is already installed --- cmd/cloudflared/service_template.go | 11 ++++++++++- cmd/cloudflared/windows_service.go | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index 13725648..ad7901d1 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -44,7 +44,7 @@ func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error { return err } if _, err = os.Stat(resolvedPath); err == nil { - return fmt.Errorf("cloudflared service is already installed at %s", resolvedPath) + return fmt.Errorf(serviceAlreadyExistsWarn(resolvedPath)) } var buffer bytes.Buffer @@ -75,6 +75,15 @@ func (st *ServiceTemplate) Remove() error { return nil } +func serviceAlreadyExistsWarn(service string) string { + return fmt.Sprintf("cloudflared service is already installed at %s; if you are running a cloudflared tunnel, you "+ + "can point it to multiple origins, avoiding the need to run more than one cloudflared service in the "+ + "same machine; otherwise if you are really sure, you can do `cloudflared service uninstall` to clean "+ + "up the existing service and then try again this command", + service, + ) +} + func runCommand(command string, args ...string) error { cmd := exec.Command(command, args...) stderr, err := cmd.StderrPipe() diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 6d12191a..79b7f431 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -191,7 +191,7 @@ func installWindowsService(c *cli.Context) error { log := zeroLogger.With().Str(LogFieldWindowsServiceName, windowsServiceName).Logger() if err == nil { s.Close() - return fmt.Errorf("Service %s already exists", windowsServiceName) + return fmt.Errorf(serviceAlreadyExistsWarn(windowsServiceName)) } extraArgs, err := getServiceExtraArgsFromCliArgs(c, &log) if err != nil { From 092e76eb55b996b4ee11e6574e64be6d6d9e22e4 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 25 Mar 2022 10:51:15 +0000 Subject: [PATCH 032/238] TUN-5954: Start cloudflared service in Linux too similarly to other OSs --- CHANGES.md | 6 +++++- cmd/cloudflared/linux_service.go | 32 +++++++++++++++++++++-------- cmd/cloudflared/service_template.go | 4 ++-- component-tests/test_service.py | 2 -- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 91da8fea..0a09668b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,10 +4,14 @@ achievable with: `cloudflared tunnel token --cred-file /path/to/file.json TUNNEL`. This new feature only works for Tunnels created with cloudflared version 2022.3.0 or more recent. +### Bug Fixes +- `cloudflared service install` now starts the underlying agent service on Linux operating system (similarly to the +behaviour in Windows and MacOS). + ## 2022.3.3 ### Bug Fixes - `cloudflared service install` now starts the underlying agent service on Windows operating system (similarly to the -behaviour in Linux and MacOS). +behaviour in MacOS). ## 2022.3.1 ### Bug Fixes diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 4f0b53eb..cc379cdb 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -43,11 +43,12 @@ const ( serviceConfigFile = "config.yml" serviceCredentialFile = "cert.pem" serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile + cloudflaredService = "cloudflared.service" ) var systemdTemplates = []ServiceTemplate{ { - Path: "/etc/systemd/system/cloudflared.service", + Path: fmt.Sprintf("/etc/systemd/system/%s", cloudflaredService), Content: `[Unit] Description=cloudflared After=network.target @@ -268,16 +269,19 @@ func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) erro return err } } - if err := runCommand("systemctl", "enable", "cloudflared.service"); err != nil { - log.Err(err).Msg("systemctl enable cloudflared.service error") + if err := runCommand("systemctl", "enable", cloudflaredService); err != nil { + log.Err(err).Msgf("systemctl enable %s error", cloudflaredService) return err } if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil { log.Err(err).Msg("systemctl start cloudflared-update.timer error") return err } - log.Info().Msg("running systemctl daemon-reload") - return runCommand("systemctl", "daemon-reload") + if err := runCommand("systemctl", "daemon-reload"); err != nil { + log.Err(err).Msg("systemctl daemon-reload error") + return err + } + return runCommand("systemctl", "start", cloudflaredService) } func installSysv(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { @@ -300,7 +304,7 @@ func installSysv(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { continue } } - return nil + return runCommand("service", "cloudflared", "start") } func uninstallLinuxService(c *cli.Context) error { @@ -323,8 +327,12 @@ func uninstallLinuxService(c *cli.Context) error { } func uninstallSystemd(log *zerolog.Logger) error { - if err := runCommand("systemctl", "disable", "cloudflared.service"); err != nil { - log.Err(err).Msg("systemctl disable cloudflared.service error") + if err := runCommand("systemctl", "disable", cloudflaredService); err != nil { + log.Err(err).Msgf("systemctl disable %s error", cloudflaredService) + return err + } + if err := runCommand("systemctl", "stop", cloudflaredService); err != nil { + log.Err(err).Msgf("systemctl stop %s error", cloudflaredService) return err } if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil { @@ -337,10 +345,18 @@ func uninstallSystemd(log *zerolog.Logger) error { return err } } + if err := runCommand("systemctl", "daemon-reload"); err != nil { + log.Err(err).Msg("systemctl daemon-reload error") + return err + } return nil } func uninstallSysv(log *zerolog.Logger) error { + if err := runCommand("service", "cloudflared", "stop"); err != nil { + log.Err(err).Msg("service cloudflared stop error") + return err + } if err := sysvTemplate.Remove(); err != nil { log.Err(err).Msg("error removing service template") return err diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index ad7901d1..3040766f 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -95,10 +95,10 @@ func runCommand(command string, args ...string) error { return fmt.Errorf("error starting %s: %v", command, err) } - _, _ = ioutil.ReadAll(stderr) + output, _ := ioutil.ReadAll(stderr) err = cmd.Wait() if err != nil { - return fmt.Errorf("%s returned with error: %v", command, err) + return fmt.Errorf("%s %v returned with error code %v due to: %v", command, args, err, string(output)) } return nil } diff --git a/component-tests/test_service.py b/component-tests/test_service.py index 2e02db6b..f8ace641 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -141,12 +141,10 @@ class TestServiceMode: def sysv_service_scenario(self, config, tmp_path, extra_assertions=None, use_token=False): with self.run_service(tmp_path, config, root=True, use_token=use_token): - self.sysv_cmd("start") self.sysv_cmd("status") wait_tunnel_ready(tunnel_url=config.get_url()) if extra_assertions is not None: extra_assertions() - self.sysv_cmd("stop") # Service install copies config file to /etc/cloudflared/config.yml subprocess.run(["sudo", "rm", "/etc/cloudflared/config.yml"]) From eb6697ae9870fd7a48ae99154e432e988f06d459 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 25 Mar 2022 16:32:15 +0000 Subject: [PATCH 033/238] Release 2022.3.4 --- RELEASE_NOTES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 01898cee..c8fa7800 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,11 @@ +2022.3.4 +- 2022-03-22 TUN-5918: Clean up text in cloudflared tunnel --help +- 2022-03-22 TUN-5895 run brew bump-formula-pr on release +- 2022-03-22 TUN-5915: New cloudflared command to allow to retrieve the token credentials for a Tunnel +- 2022-03-24 TUN-5933: Better messaging to help user when installing service if it is already installed +- 2022-03-25 TUN-5954: Start cloudflared service in Linux too similarly to other OSs +- 2022-03-14 TUN-5869: Add configuration endpoint in metrics server + 2022.3.3 - 2022-03-17 TUN-5893: Start windows service on install, stop on uninstall. Previously user had to manually start the service after running 'cloudflared tunnel install' and stop the service before running uninstall command. - 2022-03-17 Revert "CC-796: Remove dependency on unsupported version of go-oidc" From 7e6fc49979d6ee83c9926499fd083233321b8246 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 25 Mar 2022 12:18:49 -0700 Subject: [PATCH 034/238] TUN-5959: tidy go.mod --- go.mod | 37 ++++++++++++++++++------------------- go.sum | 2 -- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 83328b33..bc8abee8 100644 --- a/go.mod +++ b/go.mod @@ -3,76 +3,67 @@ module github.com/cloudflare/cloudflared go 1.17 require ( - github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect - github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 - github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect - github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/gdamore/tcell v1.3.0 github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 - github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect - github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.0.4 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/json-iterator/go v1.1.12 - github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucas-clemente/quic-go v0.24.0 github.com/mattn/go-colorable v0.1.8 github.com/miekg/dns v1.1.45 github.com/mitchellh/go-homedir v1.1.0 - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 - github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 - github.com/prometheus/common v0.32.1 // indirect github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 github.com/rs/zerolog v1.20.0 - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b - google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect - google.golang.org/grpc v1.43.0 // indirect + gopkg.in/coreos/go-oidc.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect zombiezen.com/go/capnproto2 v2.18.0+incompatible ) -require gopkg.in/coreos/go-oidc.v2 v2.2.1 - require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/coredns/caddy v1.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect + github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect + github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect + github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect + github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.0.3 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect @@ -84,16 +75,24 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.6.0 // indirect github.com/rivo/uniseg v0.1.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect golang.org/x/mod v0.4.2 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect + google.golang.org/grpc v1.43.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d diff --git a/go.sum b/go.sum index 44c59257..3c7888fa 100644 --- a/go.sum +++ b/go.sum @@ -103,7 +103,6 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -509,7 +508,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= From 8fd6074d6766c56953394b3fdd30b41dc5f6915c Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 25 Mar 2022 18:44:17 +0000 Subject: [PATCH 035/238] TUN-5958: Fix release to homebrew core --- cfsetup.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cfsetup.yaml b/cfsetup.yaml index 5180efa4..4dc51ad4 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -193,6 +193,7 @@ stretch: &stretch - openssh-client - s3cmd - jq + - build-essential post-cache: - .teamcity/update-homebrew.sh - .teamcity/update-homebrew-core.sh From c5d16622448977f0b5f16d38c55c8a552a272c91 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 28 Mar 2022 10:53:22 +0100 Subject: [PATCH 036/238] TUN-5960: Do not log the tunnel token or json credentials --- CHANGES.md | 5 ++++ cmd/cloudflared/tunnel/configuration.go | 37 +++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0a09668b..21d581cf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## 2022.4.0 +### Bug Fixes +- `cloudflared tunnel run` no longer logs the Tunnel token or JSON credentials in clear text as those are the secret +that allows to run the Tunnel. + ## 2022.3.4 ### New Features - It is now possible to retrieve the credentials that allow to run a Tunnel in case you forgot/lost them. This is diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index d7d6da8d..1ff02c93 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" "golang.org/x/crypto/ssh/terminal" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" @@ -31,14 +32,16 @@ import ( ) const LogFieldOriginCertPath = "originCertPath" +const secretValue = "*****" var ( developerPortal = "https://developers.cloudflare.com/argo-tunnel" - quickStartUrl = developerPortal + "/quickstart/quickstart/" serviceUrl = developerPortal + "/reference/service/" argumentsUrl = developerPortal + "/reference/arguments/" LogFieldHostname = "hostname" + + secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories @@ -65,7 +68,11 @@ func generateRandomClientID(log *zerolog.Logger) (string, error) { func logClientOptions(c *cli.Context, log *zerolog.Logger) { flags := make(map[string]interface{}) for _, flag := range c.FlagNames() { - flags[flag] = c.Generic(flag) + if isSecretFlag(flag) { + flags[flag] = secretValue + } else { + flags[flag] = c.Generic(flag) + } } if len(flags) > 0 { @@ -79,7 +86,11 @@ func logClientOptions(c *cli.Context, log *zerolog.Logger) { if strings.Contains(env, "TUNNEL_") { vars := strings.Split(env, "=") if len(vars) == 2 { - envs[vars[0]] = vars[1] + if isSecretEnvVar(vars[0]) { + envs[vars[0]] = secretValue + } else { + envs[vars[0]] = vars[1] + } } } } @@ -88,6 +99,26 @@ func logClientOptions(c *cli.Context, log *zerolog.Logger) { } } +func isSecretFlag(key string) bool { + for _, flag := range secretFlags { + if flag.Name == key { + return true + } + } + return false +} + +func isSecretEnvVar(key string) bool { + for _, flag := range secretFlags { + for _, secretEnvVar := range flag.EnvVars { + if secretEnvVar == key { + return true + } + } + } + return false +} + func dnsProxyStandAlone(c *cli.Context, namedTunnel *connection.NamedTunnelProperties) bool { return c.IsSet("proxy-dns") && (!c.IsSet("hostname") && !c.IsSet("tag") && !c.IsSet("hello-world") && namedTunnel == nil) } From c0f85ab85b7def0d61ccff4fa5c2a126345abecd Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 28 Mar 2022 10:06:28 +0100 Subject: [PATCH 037/238] TUN-5956: Add timeout to session manager APIs --- datagramsession/manager.go | 13 ++++++++++++- datagramsession/manager_test.go | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/datagramsession/manager.go b/datagramsession/manager.go index caf9d4fd..c4144279 100644 --- a/datagramsession/manager.go +++ b/datagramsession/manager.go @@ -3,6 +3,7 @@ package datagramsession import ( "context" "io" + "time" "github.com/google/uuid" "github.com/lucas-clemente/quic-go" @@ -12,6 +13,7 @@ import ( const ( requestChanCapacity = 16 + defaultReqTimeout = time.Second * 5 ) // Manager defines the APIs to manage sessions from the same transport. @@ -31,9 +33,11 @@ type manager struct { transport transport sessions map[uuid.UUID]*Session log *zerolog.Logger + // timeout waiting for an API to finish. This can be overriden in test + timeout time.Duration } -func NewManager(transport transport, log *zerolog.Logger) Manager { +func NewManager(transport transport, log *zerolog.Logger) *manager { return &manager{ registrationChan: make(chan *registerSessionEvent), unregistrationChan: make(chan *unregisterSessionEvent), @@ -42,6 +46,7 @@ func NewManager(transport transport, log *zerolog.Logger) Manager { transport: transport, sessions: make(map[uuid.UUID]*Session), log: log, + timeout: defaultReqTimeout, } } @@ -89,9 +94,12 @@ func (m *manager) Serve(ctx context.Context) error { } func (m *manager) RegisterSession(ctx context.Context, sessionID uuid.UUID, originProxy io.ReadWriteCloser) (*Session, error) { + ctx, cancel := context.WithTimeout(ctx, m.timeout) + defer cancel() event := newRegisterSessionEvent(sessionID, originProxy) select { case <-ctx.Done(): + m.log.Error().Msg("Datagram session registration timeout") return nil, ctx.Err() case m.registrationChan <- event: session := <-event.resultChan @@ -106,6 +114,8 @@ func (m *manager) registerSession(ctx context.Context, registration *registerSes } func (m *manager) UnregisterSession(ctx context.Context, sessionID uuid.UUID, message string, byRemote bool) error { + ctx, cancel := context.WithTimeout(ctx, m.timeout) + defer cancel() event := &unregisterSessionEvent{ sessionID: sessionID, err: &errClosedSession{ @@ -115,6 +125,7 @@ func (m *manager) UnregisterSession(ctx context.Context, sessionID uuid.UUID, me } select { case <-ctx.Done(): + m.log.Error().Msg("Datagram session unregistration timeout") return ctx.Err() case m.unregistrationChan <- event: return nil diff --git a/datagramsession/manager_test.go b/datagramsession/manager_test.go index e81147df..823c55bb 100644 --- a/datagramsession/manager_test.go +++ b/datagramsession/manager_test.go @@ -120,6 +120,28 @@ func TestManagerServe(t *testing.T) { <-serveDone } +func TestTimeout(t *testing.T) { + const ( + testTimeout = time.Millisecond * 50 + ) + log := zerolog.Nop() + transport := &mockQUICTransport{ + reqChan: newDatagramChannel(1), + respChan: newDatagramChannel(1), + } + mg := NewManager(transport, &log) + mg.timeout = testTimeout + ctx := context.Background() + sessionID := uuid.New() + // session manager is not running, so event loop is not running and therefore calling the APIs should timeout + session, err := mg.RegisterSession(ctx, sessionID, nil) + require.ErrorIs(t, err, context.DeadlineExceeded) + require.Nil(t, session) + + err = mg.UnregisterSession(ctx, sessionID, "session gone", true) + require.ErrorIs(t, err, context.DeadlineExceeded) +} + type mockOrigin struct { expectMsgCount int expectedMsg []byte From 98deb95eaec1a2e2e43320e526e6a192a4eba249 Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 30 Mar 2022 11:06:21 +0100 Subject: [PATCH 038/238] TUN-5842: Fix flaky TestConcurrentUpdateAndRead by making sure resources are released --- orchestration/orchestrator_test.go | 63 +++++++++++++++++++----------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index f3638ea7..5afcb8d9 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -249,7 +249,10 @@ func TestConcurrentUpdateAndRead(t *testing.T) { } ) - orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, &testLogger) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + orchestrator, err := NewOrchestrator(ctx, initConfig, testTags, &testLogger) require.NoError(t, err) updateWithValidation(t, orchestrator, 1, configJSONV1) @@ -265,8 +268,9 @@ func TestConcurrentUpdateAndRead(t *testing.T) { wg.Add(1) go func(i int, originProxy connection.OriginProxy) { defer wg.Done() - resp, err := proxyHTTP(t, originProxy, hostname) - require.NoError(t, err) + resp, err := proxyHTTP(originProxy, hostname) + require.NoError(t, err, "proxyHTTP %d failed %v", i, err) + defer resp.Body.Close() var warpRoutingDisabled bool // The response can be from initOrigin, http_status:204 or http_status:418 @@ -290,7 +294,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) { // Once we have originProxy, it won't be changed by configuration updates. // We can infer the version by the ProxyHTTP response code pr, pw := io.Pipe() - // concurrentRespWriter makes sure ResponseRecorder is not read/write concurrently, and read waits for the first write + w := newRespReadWriteFlusher() // Write TCP message and make sure it's echo back. This has to be done in a go routune since ProxyTCP doesn't @@ -303,7 +307,14 @@ func TestConcurrentUpdateAndRead(t *testing.T) { tcpEyeball(t, pw, tcpBody, w) }() } - proxyTCP(t, originProxy, tcpOrigin.Addr().String(), w, pr, warpRoutingDisabled) + + err = proxyTCP(ctx, originProxy, tcpOrigin.Addr().String(), w, pr) + if warpRoutingDisabled { + require.Error(t, err, "expect proxyTCP %d to return error", i) + } else { + require.NoError(t, err, "proxyTCP %d failed %v", i, err) + } + }(i, originProxy) if i == concurrentRequests/4 { @@ -319,6 +330,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) { wg.Add(1) go func() { defer wg.Done() + // Makes sure v2 is applied before v3 <-appliedV2 updateWithValidation(t, orchestrator, 3, configJSONV3) }() @@ -328,14 +340,18 @@ func TestConcurrentUpdateAndRead(t *testing.T) { wg.Wait() } -func proxyHTTP(t *testing.T, originProxy connection.OriginProxy, hostname string) (*http.Response, error) { +func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Response, error) { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", hostname), nil) - require.NoError(t, err) + if err != nil { + return nil, err + } w := httptest.NewRecorder() log := zerolog.Nop() respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeHTTP, &log) - require.NoError(t, err) + if err != nil { + return nil, err + } err = originProxy.ProxyHTTP(respWriter, req, false) if err != nil { @@ -356,13 +372,17 @@ func tcpEyeball(t *testing.T, reqWriter io.WriteCloser, body string, respReadWri require.Equal(t, writeN, n) } -func proxyTCP(t *testing.T, originProxy connection.OriginProxy, originAddr string, w http.ResponseWriter, reqBody io.ReadCloser, expectErr bool) { +func proxyTCP(ctx context.Context, originProxy connection.OriginProxy, originAddr string, w http.ResponseWriter, reqBody io.ReadCloser) error { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", originAddr), reqBody) - require.NoError(t, err) + if err != nil { + return err + } log := zerolog.Nop() respWriter, err := connection.NewHTTP2RespWriter(req, w, connection.TypeTCP, &log) - require.NoError(t, err) + if err != nil { + return err + } tcpReq := &connection.TCPRequest{ Dest: originAddr, @@ -370,12 +390,8 @@ func proxyTCP(t *testing.T, originProxy connection.OriginProxy, originAddr strin LBProbe: false, } rws := connection.NewHTTPResponseReadWriterAcker(respWriter, req) - if expectErr { - require.Error(t, originProxy.ProxyTCP(context.Background(), rws, tcpReq)) - return - } - require.NoError(t, originProxy.ProxyTCP(context.Background(), rws, tcpReq)) + return originProxy.ProxyTCP(ctx, rws, tcpReq) } func serveTCPOrigin(t *testing.T, tcpOrigin net.Listener, wg *sync.WaitGroup) { @@ -471,7 +487,7 @@ func TestClosePreviousProxies(t *testing.T) { originProxyV1, err := orchestrator.GetOriginProxy() require.NoError(t, err) - resp, err := proxyHTTP(t, originProxyV1, hostname) + resp, err := proxyHTTP(originProxyV1, hostname) require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode) @@ -479,12 +495,12 @@ func TestClosePreviousProxies(t *testing.T) { originProxyV2, err := orchestrator.GetOriginProxy() require.NoError(t, err) - resp, err = proxyHTTP(t, originProxyV2, hostname) + resp, err = proxyHTTP(originProxyV2, hostname) require.NoError(t, err) require.Equal(t, http.StatusTeapot, resp.StatusCode) // The hello-world server in config v1 should have been stopped - resp, err = proxyHTTP(t, originProxyV1, hostname) + resp, err = proxyHTTP(originProxyV1, hostname) require.Error(t, err) require.Nil(t, resp) @@ -495,7 +511,7 @@ func TestClosePreviousProxies(t *testing.T) { require.NoError(t, err) require.NotEqual(t, originProxyV1, originProxyV3) - resp, err = proxyHTTP(t, originProxyV3, hostname) + resp, err = proxyHTTP(originProxyV3, hostname) require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode) @@ -504,7 +520,7 @@ func TestClosePreviousProxies(t *testing.T) { // Wait for proxies to shutdown time.Sleep(time.Millisecond * 10) - resp, err = proxyHTTP(t, originProxyV3, hostname) + resp, err = proxyHTTP(originProxyV3, hostname) require.Error(t, err) require.Nil(t, resp) } @@ -553,6 +569,9 @@ func TestPersistentConnection(t *testing.T) { tcpReqReader, tcpReqWriter := io.Pipe() tcpRespReadWriter := newRespReadWriteFlusher() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + var wg sync.WaitGroup wg.Add(3) // Start TCP origin @@ -570,7 +589,7 @@ func TestPersistentConnection(t *testing.T) { // Simulate cloudflared recieving a TCP connection go func() { defer wg.Done() - proxyTCP(t, originProxy, tcpOrigin.Addr().String(), tcpRespReadWriter, tcpReqReader, false) + require.NoError(t, proxyTCP(ctx, originProxy, tcpOrigin.Addr().String(), tcpRespReadWriter, tcpReqReader)) }() // Simulate cloudflared recieving a WS connection go func() { From 636ec75010aa98aed964f2d315531880a7200e7a Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Wed, 30 Mar 2022 07:09:09 -0700 Subject: [PATCH 039/238] carrier: fix dropped errors --- carrier/websocket.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/carrier/websocket.go b/carrier/websocket.go index 0bf7cea9..a26e0737 100644 --- a/carrier/websocket.go +++ b/carrier/websocket.go @@ -55,6 +55,9 @@ func createWebsocketStream(options *StartOptions, log *zerolog.Logger) (*cfwebso } dump, err := httputil.DumpRequest(req, false) + if err != nil { + return nil, err + } log.Debug().Msgf("Websocket request: %s", string(dump)) dialer := &websocket.Dialer{ @@ -182,6 +185,9 @@ func createAccessWebSocketStream(options *StartOptions, log *zerolog.Logger) (*w } dump, err := httputil.DumpRequest(req, false) + if err != nil { + return nil, nil, err + } log.Debug().Msgf("Access Websocket request: %s", string(dump)) conn, resp, err := clientConnect(req, nil) From 12302ba1bf741af0f607b6031923a31cd44d2ac5 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 1 Apr 2022 15:58:51 +0100 Subject: [PATCH 040/238] TUN-5973: Add backoff for non-recoverable errors as well Errors that are non-recoverable can lead to one of two things happening: 1. That connection lying dead and cloudflared not retrying to make that connection. 2. cloudflared resolving to a different edge addr to retry connection. We should subject these errors to a backoff as well. This will result in us introducing a backoff for 1. When we are going to let the connection become stale anyway and 2. When we are about to try a different edge addr. --- edgediscovery/edgediscovery.go | 3 ++- supervisor/tunnel.go | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index 9f76b6fa..df9c42cc 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -114,7 +114,8 @@ func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { // note: if oldAddr were not nil, it will become available on the next iteration return nil, errNoAddressesLeft } - log.Debug().Msg("edgediscovery - GetDifferentAddr: Giving connection its new address") + log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address: %v from the address list: %v", + addr, ed.regions.AvailableAddrs()) return addr, nil } diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 36fcbb5c..d6a588f4 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -171,17 +171,15 @@ func ServeTunnelLoop( protocolFallback.protocol, gracefulShutdownC, ) - if !recoverable { - return err - } - config.Observer.SendReconnect(connIndex) - - duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) - if !ok { - return err + if recoverable { + duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) + if !ok { + return err + } + config.Observer.SendReconnect(connIndex) + connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) } - connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) select { case <-ctx.Done(): @@ -189,6 +187,10 @@ func ServeTunnelLoop( case <-gracefulShutdownC: return nil case <-protocolFallback.BackoffTimer(): + if !recoverable { + return err + } + if !selectNextProtocol( connLog.Logger(), protocolFallback, From a0f6eb9d5eb17b7ffc3ef33ef1553ef1b0b7af59 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 5 Apr 2022 23:07:10 +0100 Subject: [PATCH 041/238] TUN-5992: Use QUIC protocol for remotely managed tunnels when protocol is unspecified --- cmd/cloudflared/tunnel/configuration.go | 10 ++++++++-- cmd/cloudflared/tunnel/subcommands.go | 2 +- connection/protocol.go | 6 +++--- connection/protocol_test.go | 24 ++++++++++++------------ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 1ff02c93..0226a87e 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -214,6 +214,9 @@ func prepareTunnelConfig( ingressRules ingress.Ingress classicTunnel *connection.ClassicTunnelProperties ) + + transportProtocol := c.String("protocol") + cfg := config.GetConfiguration() if isNamedTunnel { clientUUID, err := uuid.NewRandom() @@ -223,8 +226,11 @@ func prepareTunnelConfig( log.Info().Msgf("Generated Connector ID: %s", clientUUID) features := append(c.StringSlice("features"), supervisor.FeatureSerializedHeaders) if c.IsSet(TunnelTokenFlag) { + if transportProtocol == connection.AutoSelectFlag { + transportProtocol = connection.QUIC.String() + } features = append(features, supervisor.FeatureAllowRemoteConfig) - log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API") + log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API. Defaulting to protocol: quic") } namedTunnel.Client = tunnelpogs.ClientInfo{ ClientID: clientUUID[:], @@ -268,7 +274,7 @@ func prepareTunnelConfig( } warpRoutingEnabled := isWarpRoutingEnabled(cfg.WarpRouting, isNamedTunnel) - protocolSelector, err := connection.NewProtocolSelector(c.String("protocol"), warpRoutingEnabled, namedTunnel, edgediscovery.ProtocolPercentage, supervisor.ResolveTTL, log) + protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, edgediscovery.ProtocolPercentage, supervisor.ResolveTTL, log) if err != nil { return nil, nil, err } diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 70c16545..e9825654 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -134,7 +134,7 @@ var ( } selectProtocolFlag = altsrc.NewStringFlag(&cli.StringFlag{ Name: "protocol", - Value: "auto", + Value: connection.AutoSelectFlag, Aliases: []string{"p"}, Usage: fmt.Sprintf("Protocol implementation to connect with Cloudflare's edge network. %s", connection.AvailableProtocolFlagMessage), EnvVars: []string{"TUNNEL_TRANSPORT_PROTOCOL"}, diff --git a/connection/protocol.go b/connection/protocol.go index b94bb80e..ce97bffc 100644 --- a/connection/protocol.go +++ b/connection/protocol.go @@ -19,7 +19,7 @@ const ( edgeH2TLSServerName = "h2.cftunnel.com" // edgeQUICServerName is the server name to establish quic connection with edge. edgeQUICServerName = "quic.cftunnel.com" - autoSelectFlag = "auto" + AutoSelectFlag = "auto" ) var ( @@ -247,7 +247,7 @@ func selectNamedTunnelProtocols( // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and // fallback on failures. - if protocolFlag == autoSelectFlag { + if protocolFlag == AutoSelectFlag { return newAutoProtocolSelector(protocol, []Protocol{QUIC, HTTP2, H2mux}, threshold, fetchFunc, ttl, log), nil } @@ -272,7 +272,7 @@ func selectWarpRoutingProtocols( // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and // fallback on failures. - if protocolFlag == autoSelectFlag { + if protocolFlag == AutoSelectFlag { return newAutoProtocolSelector(protocol, []Protocol{QUICWarp, HTTP2Warp}, threshold, fetchFunc, ttl, log), nil } diff --git a/connection/protocol_test.go b/connection/protocol_test.go index 9ab5aae3..9eef7b6c 100644 --- a/connection/protocol_test.go +++ b/connection/protocol_test.go @@ -91,14 +91,14 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "named tunnel quic and http2 disabled", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: H2mux, fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}, edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: -1}), namedTunnelConfig: testNamedTunnelProperties, }, { name: "named tunnel quic disabled", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: HTTP2, // Hasfallback true is because if http2 fails, then we further fallback to h2mux. hasFallback: true, @@ -108,21 +108,21 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "named tunnel auto all http2 disabled", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: H2mux, fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}), namedTunnelConfig: testNamedTunnelProperties, }, { name: "named tunnel auto to h2mux", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: H2mux, fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}), namedTunnelConfig: testNamedTunnelProperties, }, { name: "named tunnel auto to http2", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: HTTP2, hasFallback: true, expectedFallback: H2mux, @@ -131,7 +131,7 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "named tunnel auto to quic", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: QUIC, hasFallback: true, expectedFallback: HTTP2, @@ -167,7 +167,7 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "warp routing quic", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: QUICWarp, hasFallback: true, expectedFallback: HTTP2Warp, @@ -177,7 +177,7 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "warp routing auto", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: HTTP2Warp, hasFallback: false, fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}), @@ -186,7 +186,7 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "warp routing auto- quic", - protocol: "auto", + protocol: AutoSelectFlag, expectedProtocol: QUICWarp, hasFallback: true, expectedFallback: HTTP2Warp, @@ -209,7 +209,7 @@ func TestNewProtocolSelector(t *testing.T) { }, { name: "named tunnel fetch error", - protocol: "auto", + protocol: AutoSelectFlag, fetchFunc: mockFetcher(true), namedTunnelConfig: testNamedTunnelProperties, expectedProtocol: HTTP2, @@ -237,7 +237,7 @@ func TestNewProtocolSelector(t *testing.T) { func TestAutoProtocolSelectorRefresh(t *testing.T) { fetcher := dynamicMockFetcher{} - selector, err := NewProtocolSelector("auto", noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log) + selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log) assert.NoError(t, err) assert.Equal(t, H2mux, selector.Current()) @@ -297,7 +297,7 @@ func TestHTTP2ProtocolSelectorRefresh(t *testing.T) { func TestProtocolSelectorRefreshTTL(t *testing.T) { fetcher := dynamicMockFetcher{} fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}} - selector, err := NewProtocolSelector("auto", noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), time.Hour, &log) + selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), time.Hour, &log) assert.NoError(t, err) assert.Equal(t, QUIC, selector.Current()) From 377a9a8d27171e9639ac2d8a67c10bbebcb8f34b Mon Sep 17 00:00:00 2001 From: Misaka No <96560028+misakano7545@users.noreply.github.com> Date: Wed, 6 Apr 2022 17:43:58 +0800 Subject: [PATCH 042/238] Update Makefile --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 461384d4..4fda3d6a 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,8 @@ else ifeq ($(LOCAL_ARCH),arm64) TARGET_ARCH ?= arm64 else ifeq ($(shell echo $(LOCAL_ARCH) | head -c 4),armv) TARGET_ARCH ?= arm +else ifeq ($(LOCAL_ARCH),s390x) + TARGET_ARCH ?= s390x else $(error This system's architecture $(LOCAL_ARCH) isn't supported) endif @@ -291,4 +293,4 @@ vet: .PHONY: goimports goimports: - for d in $$(go list -mod=readonly -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc) ; do goimports -format-only -local github.com/cloudflare/cloudflared -w $$d ; done \ No newline at end of file + for d in $$(go list -mod=readonly -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc) ; do goimports -format-only -local github.com/cloudflare/cloudflared -w $$d ; done From b12272529fb5349de826ac6d283beff514d255fe Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 6 Apr 2022 09:32:05 +0100 Subject: [PATCH 043/238] TUN-5995: Update prometheus to 1.12.1 to avoid vulnerabilities --- go.mod | 8 +- go.sum | 14 +- .../github.com/cespare/xxhash/v2/.travis.yml | 8 - vendor/github.com/cespare/xxhash/v2/README.md | 6 +- vendor/github.com/cespare/xxhash/v2/xxhash.go | 1 - .../cespare/xxhash/v2/xxhash_amd64.s | 62 +- .../cespare/xxhash/v2/xxhash_unsafe.go | 55 +- .../client_golang/prometheus/README.md | 2 +- .../prometheus/build_info_collector.go | 38 + .../client_golang/prometheus/collector.go | 8 + .../client_golang/prometheus/counter.go | 8 +- .../client_golang/prometheus/go_collector.go | 494 +++---- .../prometheus/go_collector_go116.go | 107 ++ .../prometheus/go_collector_go117.go | 408 ++++++ .../client_golang/prometheus/histogram.go | 28 + .../prometheus/internal/go_runtime_metrics.go | 142 ++ .../prometheus/process_collector_other.go | 1 + .../prometheus/promhttp/instrument_client.go | 28 +- .../prometheus/promhttp/instrument_server.go | 111 +- .../prometheus/promhttp/option.go | 31 + .../client_golang/prometheus/value.go | 6 +- vendor/github.com/prometheus/procfs/Makefile | 2 + .../prometheus/procfs/Makefile.common | 15 +- vendor/github.com/prometheus/procfs/README.md | 4 +- .../github.com/prometheus/procfs/cmdline.go | 30 + vendor/github.com/prometheus/procfs/doc.go | 2 +- .../prometheus/procfs/fixtures.ttar | 1178 ++++++++++++++++- vendor/github.com/prometheus/procfs/mdstat.go | 105 +- .../prometheus/procfs/net_ip_socket.go | 10 +- .../github.com/prometheus/procfs/netstat.go | 68 + .../prometheus/procfs/proc_cgroup.go | 2 +- .../github.com/prometheus/procfs/proc_stat.go | 32 +- .../github.com/prometheus/procfs/zoneinfo.go | 1 - vendor/golang.org/x/sys/unix/zerrors_linux.go | 23 +- .../x/sys/unix/zerrors_linux_386.go | 3 + .../x/sys/unix/zerrors_linux_amd64.go | 3 + .../x/sys/unix/zerrors_linux_arm.go | 3 + .../x/sys/unix/zerrors_linux_arm64.go | 3 + .../x/sys/unix/zerrors_linux_mips.go | 3 + .../x/sys/unix/zerrors_linux_mips64.go | 3 + .../x/sys/unix/zerrors_linux_mips64le.go | 3 + .../x/sys/unix/zerrors_linux_mipsle.go | 3 + .../x/sys/unix/zerrors_linux_ppc.go | 3 + .../x/sys/unix/zerrors_linux_ppc64.go | 3 + .../x/sys/unix/zerrors_linux_ppc64le.go | 3 + .../x/sys/unix/zerrors_linux_riscv64.go | 3 + .../x/sys/unix/zerrors_linux_s390x.go | 3 + .../x/sys/unix/zerrors_linux_sparc64.go | 3 + .../x/sys/unix/zsysnum_linux_386.go | 1 + .../x/sys/unix/zsysnum_linux_amd64.go | 1 + .../x/sys/unix/zsysnum_linux_arm.go | 1 + .../x/sys/unix/zsysnum_linux_arm64.go | 1 + .../x/sys/unix/zsysnum_linux_mips.go | 1 + .../x/sys/unix/zsysnum_linux_mips64.go | 1 + .../x/sys/unix/zsysnum_linux_mips64le.go | 1 + .../x/sys/unix/zsysnum_linux_mipsle.go | 1 + .../x/sys/unix/zsysnum_linux_ppc.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 1 + .../x/sys/unix/zsysnum_linux_riscv64.go | 1 + .../x/sys/unix/zsysnum_linux_s390x.go | 1 + .../x/sys/unix/zsysnum_linux_sparc64.go | 1 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 32 +- .../x/sys/windows/syscall_windows.go | 2 + .../golang.org/x/sys/windows/types_windows.go | 2 + .../x/sys/windows/zsyscall_windows.go | 14 + vendor/modules.txt | 8 +- 67 files changed, 2675 insertions(+), 477 deletions(-) delete mode 100644 vendor/github.com/cespare/xxhash/v2/.travis.yml create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/build_info_collector.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/go_collector_go117.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go create mode 100644 vendor/github.com/prometheus/procfs/cmdline.go create mode 100644 vendor/github.com/prometheus/procfs/netstat.go diff --git a/go.mod b/go.mod index bc8abee8..96ce0408 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/miekg/dns v1.1.45 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_model v0.2.0 github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 github.com/rs/zerolog v1.20.0 @@ -32,7 +32,7 @@ require ( golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b gopkg.in/coreos/go-oidc.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -46,7 +46,7 @@ require ( github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/coredns/caddy v1.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect @@ -79,7 +79,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.6.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect github.com/rivo/uniseg v0.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect golang.org/x/mod v0.4.2 // indirect diff --git a/go.sum b/go.sum index 3c7888fa..acfd57fa 100644 --- a/go.sum +++ b/go.sum @@ -103,9 +103,11 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 h1:PLTB4iA6sOgAItzQY642tYdcGKfG/7i2gu93JQGgUcM= @@ -477,8 +479,9 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -494,8 +497,9 @@ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -508,6 +512,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -796,8 +801,9 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/vendor/github.com/cespare/xxhash/v2/.travis.yml b/vendor/github.com/cespare/xxhash/v2/.travis.yml deleted file mode 100644 index c516ea88..00000000 --- a/vendor/github.com/cespare/xxhash/v2/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go -go: - - "1.x" - - master -env: - - TAGS="" - - TAGS="-tags purego" -script: go test $TAGS -v ./... diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md index 2fd8693c..792b4a60 100644 --- a/vendor/github.com/cespare/xxhash/v2/README.md +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -1,7 +1,7 @@ # xxhash -[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash) -[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash) +[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) +[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) xxhash is a Go implementation of the 64-bit [xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a @@ -64,4 +64,6 @@ $ go test -benchtime 10s -bench '/xxhash,direct,bytes' - [InfluxDB](https://github.com/influxdata/influxdb) - [Prometheus](https://github.com/prometheus/prometheus) +- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) - [FreeCache](https://github.com/coocood/freecache) +- [FastCache](https://github.com/VictoriaMetrics/fastcache) diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go index db0b35fb..15c835d5 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -193,7 +193,6 @@ func (d *Digest) UnmarshalBinary(b []byte) error { b, d.v4 = consumeUint64(b) b, d.total = consumeUint64(b) copy(d.mem[:], b) - b = b[len(d.mem):] d.n = int(d.total % uint64(len(d.mem))) return nil } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s index d580e32a..be8db5bf 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s @@ -6,7 +6,7 @@ // Register allocation: // AX h -// CX pointer to advance through b +// SI pointer to advance through b // DX n // BX loop end // R8 v1, k1 @@ -16,39 +16,39 @@ // R12 tmp // R13 prime1v // R14 prime2v -// R15 prime4v +// DI prime4v -// round reads from and advances the buffer pointer in CX. +// round reads from and advances the buffer pointer in SI. // It assumes that R13 has prime1v and R14 has prime2v. #define round(r) \ - MOVQ (CX), R12 \ - ADDQ $8, CX \ + MOVQ (SI), R12 \ + ADDQ $8, SI \ IMULQ R14, R12 \ ADDQ R12, r \ ROLQ $31, r \ IMULQ R13, r // mergeRound applies a merge round on the two registers acc and val. -// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v. +// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v. #define mergeRound(acc, val) \ IMULQ R14, val \ ROLQ $31, val \ IMULQ R13, val \ XORQ val, acc \ IMULQ R13, acc \ - ADDQ R15, acc + ADDQ DI, acc // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT, $0-32 // Load fixed primes. MOVQ ·prime1v(SB), R13 MOVQ ·prime2v(SB), R14 - MOVQ ·prime4v(SB), R15 + MOVQ ·prime4v(SB), DI // Load slice. - MOVQ b_base+0(FP), CX + MOVQ b_base+0(FP), SI MOVQ b_len+8(FP), DX - LEAQ (CX)(DX*1), BX + LEAQ (SI)(DX*1), BX // The first loop limit will be len(b)-32. SUBQ $32, BX @@ -65,14 +65,14 @@ TEXT ·Sum64(SB), NOSPLIT, $0-32 XORQ R11, R11 SUBQ R13, R11 - // Loop until CX > BX. + // Loop until SI > BX. blockLoop: round(R8) round(R9) round(R10) round(R11) - CMPQ CX, BX + CMPQ SI, BX JLE blockLoop MOVQ R8, AX @@ -100,16 +100,16 @@ noBlocks: afterBlocks: ADDQ DX, AX - // Right now BX has len(b)-32, and we want to loop until CX > len(b)-8. + // Right now BX has len(b)-32, and we want to loop until SI > len(b)-8. ADDQ $24, BX - CMPQ CX, BX + CMPQ SI, BX JG fourByte wordLoop: // Calculate k1. - MOVQ (CX), R8 - ADDQ $8, CX + MOVQ (SI), R8 + ADDQ $8, SI IMULQ R14, R8 ROLQ $31, R8 IMULQ R13, R8 @@ -117,18 +117,18 @@ wordLoop: XORQ R8, AX ROLQ $27, AX IMULQ R13, AX - ADDQ R15, AX + ADDQ DI, AX - CMPQ CX, BX + CMPQ SI, BX JLE wordLoop fourByte: ADDQ $4, BX - CMPQ CX, BX + CMPQ SI, BX JG singles - MOVL (CX), R8 - ADDQ $4, CX + MOVL (SI), R8 + ADDQ $4, SI IMULQ R13, R8 XORQ R8, AX @@ -138,19 +138,19 @@ fourByte: singles: ADDQ $4, BX - CMPQ CX, BX + CMPQ SI, BX JGE finalize singlesLoop: - MOVBQZX (CX), R12 - ADDQ $1, CX + MOVBQZX (SI), R12 + ADDQ $1, SI IMULQ ·prime5v(SB), R12 XORQ R12, AX ROLQ $11, AX IMULQ R13, AX - CMPQ CX, BX + CMPQ SI, BX JL singlesLoop finalize: @@ -179,9 +179,9 @@ TEXT ·writeBlocks(SB), NOSPLIT, $0-40 MOVQ ·prime2v(SB), R14 // Load slice. - MOVQ b_base+8(FP), CX + MOVQ b_base+8(FP), SI MOVQ b_len+16(FP), DX - LEAQ (CX)(DX*1), BX + LEAQ (SI)(DX*1), BX SUBQ $32, BX // Load vN from d. @@ -199,7 +199,7 @@ blockLoop: round(R10) round(R11) - CMPQ CX, BX + CMPQ SI, BX JLE blockLoop // Copy vN back to d. @@ -208,8 +208,8 @@ blockLoop: MOVQ R10, 16(AX) MOVQ R11, 24(AX) - // The number of bytes written is CX minus the old base pointer. - SUBQ b_base+8(FP), CX - MOVQ CX, ret+32(FP) + // The number of bytes written is SI minus the old base pointer. + SUBQ b_base+8(FP), SI + MOVQ SI, ret+32(FP) RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go index 53bf76ef..376e0ca2 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -6,41 +6,52 @@ package xxhash import ( - "reflect" "unsafe" ) -// Notes: -// -// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ -// for some discussion about these unsafe conversions. -// // In the future it's possible that compiler optimizations will make these -// unsafe operations unnecessary: https://golang.org/issue/2205. +// XxxString functions unnecessary by realizing that calls such as +// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205. +// If that happens, even if we keep these functions they can be replaced with +// the trivial safe code. + +// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is: // -// Both of these wrapper functions still incur function call overhead since they -// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write -// for strings to squeeze out a bit more speed. Mid-stack inlining should -// eventually fix this. +// var b []byte +// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) +// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data +// bh.Len = len(s) +// bh.Cap = len(s) +// +// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough +// weight to this sequence of expressions that any function that uses it will +// not be inlined. Instead, the functions below use a different unsafe +// conversion designed to minimize the inliner weight and allow both to be +// inlined. There is also a test (TestInlining) which verifies that these are +// inlined. +// +// See https://github.com/golang/go/issues/42739 for discussion. // Sum64String computes the 64-bit xxHash digest of s. // It may be faster than Sum64([]byte(s)) by avoiding a copy. func Sum64String(s string) uint64 { - var b []byte - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data - bh.Len = len(s) - bh.Cap = len(s) + b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) return Sum64(b) } // WriteString adds more data to d. It always returns len(s), nil. // It may be faster than Write([]byte(s)) by avoiding a copy. func (d *Digest) WriteString(s string) (n int, err error) { - var b []byte - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data - bh.Len = len(s) - bh.Cap = len(s) - return d.Write(b) + d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))) + // d.Write always returns len(s), nil. + // Ignoring the return output and returning these fixed values buys a + // savings of 6 in the inliner's cost model. + return len(s), nil +} + +// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout +// of the first two words is the same as the layout of a string. +type sliceHeader struct { + s string + cap int } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/README.md b/vendor/github.com/prometheus/client_golang/prometheus/README.md index 44986bff..c67ff1b7 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/README.md +++ b/vendor/github.com/prometheus/client_golang/prometheus/README.md @@ -1 +1 @@ -See [![go-doc](https://godoc.org/github.com/prometheus/client_golang/prometheus?status.svg)](https://godoc.org/github.com/prometheus/client_golang/prometheus). +See [![Go Reference](https://pkg.go.dev/badge/github.com/prometheus/client_golang/prometheus.svg)](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus). diff --git a/vendor/github.com/prometheus/client_golang/prometheus/build_info_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/build_info_collector.go new file mode 100644 index 00000000..450189f3 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/build_info_collector.go @@ -0,0 +1,38 @@ +// Copyright 2021 The Prometheus Authors +// 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 prometheus + +import "runtime/debug" + +// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector. +// See there for documentation. +// +// Deprecated: Use collectors.NewBuildInfoCollector instead. +func NewBuildInfoCollector() Collector { + path, version, sum := "unknown", "unknown", "unknown" + if bi, ok := debug.ReadBuildInfo(); ok { + path = bi.Main.Path + version = bi.Main.Version + sum = bi.Main.Sum + } + c := &selfCollector{MustNewConstMetric( + NewDesc( + "go_build_info", + "Build information about the main Go module.", + nil, Labels{"path": path, "version": version, "checksum": sum}, + ), + GaugeValue, 1)} + c.init(c.self) + return c +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collector.go b/vendor/github.com/prometheus/client_golang/prometheus/collector.go index 1e839650..ac1ca3cf 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/collector.go @@ -118,3 +118,11 @@ func (c *selfCollector) Describe(ch chan<- *Desc) { func (c *selfCollector) Collect(ch chan<- Metric) { ch <- c.self } + +// collectorMetric is a metric that is also a collector. +// Because of selfCollector, most (if not all) Metrics in +// this package are also collectors. +type collectorMetric interface { + Metric + Collector +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index 3f8fd790..00d70f09 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -133,10 +133,14 @@ func (c *counter) Inc() { atomic.AddUint64(&c.valInt, 1) } -func (c *counter) Write(out *dto.Metric) error { +func (c *counter) get() float64 { fval := math.Float64frombits(atomic.LoadUint64(&c.valBits)) ival := atomic.LoadUint64(&c.valInt) - val := fval + float64(ival) + return fval + float64(ival) +} + +func (c *counter) Write(out *dto.Metric) error { + val := c.get() var exemplar *dto.Exemplar if e := c.exemplar.Load(); e != nil { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go index a96ed1ce..08195b41 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go @@ -16,32 +16,209 @@ package prometheus import ( "runtime" "runtime/debug" - "sync" "time" ) -type goCollector struct { +func goRuntimeMemStats() memStatsMetrics { + return memStatsMetrics{ + { + desc: NewDesc( + memstatNamespace("alloc_bytes"), + "Number of bytes allocated and still in use.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("alloc_bytes_total"), + "Total number of bytes allocated, even if freed.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) }, + valType: CounterValue, + }, { + desc: NewDesc( + memstatNamespace("sys_bytes"), + "Number of bytes obtained from system.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("lookups_total"), + "Total number of pointer lookups.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) }, + valType: CounterValue, + }, { + desc: NewDesc( + memstatNamespace("mallocs_total"), + "Total number of mallocs.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) }, + valType: CounterValue, + }, { + desc: NewDesc( + memstatNamespace("frees_total"), + "Total number of frees.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) }, + valType: CounterValue, + }, { + desc: NewDesc( + memstatNamespace("heap_alloc_bytes"), + "Number of heap bytes allocated and still in use.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("heap_sys_bytes"), + "Number of heap bytes obtained from system.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("heap_idle_bytes"), + "Number of heap bytes waiting to be used.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("heap_inuse_bytes"), + "Number of heap bytes that are in use.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("heap_released_bytes"), + "Number of heap bytes released to OS.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("heap_objects"), + "Number of allocated objects.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("stack_inuse_bytes"), + "Number of bytes in use by the stack allocator.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("stack_sys_bytes"), + "Number of bytes obtained from system for stack allocator.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("mspan_inuse_bytes"), + "Number of bytes in use by mspan structures.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("mspan_sys_bytes"), + "Number of bytes used for mspan structures obtained from system.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("mcache_inuse_bytes"), + "Number of bytes in use by mcache structures.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("mcache_sys_bytes"), + "Number of bytes used for mcache structures obtained from system.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("buck_hash_sys_bytes"), + "Number of bytes used by the profiling bucket hash table.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("gc_sys_bytes"), + "Number of bytes used for garbage collection system metadata.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("other_sys_bytes"), + "Number of bytes used for other system allocations.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("next_gc_bytes"), + "Number of heap bytes when next garbage collection will take place.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) }, + valType: GaugeValue, + }, { + desc: NewDesc( + memstatNamespace("gc_cpu_fraction"), + "The fraction of this program's available CPU time used by the GC since the program started.", + nil, nil, + ), + eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction }, + valType: GaugeValue, + }, + } +} + +type baseGoCollector struct { goroutinesDesc *Desc threadsDesc *Desc gcDesc *Desc + gcLastTimeDesc *Desc goInfoDesc *Desc - - // ms... are memstats related. - msLast *runtime.MemStats // Previously collected memstats. - msLastTimestamp time.Time - msMtx sync.Mutex // Protects msLast and msLastTimestamp. - msMetrics memStatsMetrics - msRead func(*runtime.MemStats) // For mocking in tests. - msMaxWait time.Duration // Wait time for fresh memstats. - msMaxAge time.Duration // Maximum allowed age of old memstats. } -// NewGoCollector is the obsolete version of collectors.NewGoCollector. -// See there for documentation. -// -// Deprecated: Use collectors.NewGoCollector instead. -func NewGoCollector() Collector { - return &goCollector{ +func newBaseGoCollector() baseGoCollector { + return baseGoCollector{ goroutinesDesc: NewDesc( "go_goroutines", "Number of goroutines that currently exist.", @@ -54,243 +231,28 @@ func NewGoCollector() Collector { "go_gc_duration_seconds", "A summary of the pause duration of garbage collection cycles.", nil, nil), + gcLastTimeDesc: NewDesc( + memstatNamespace("last_gc_time_seconds"), + "Number of seconds since 1970 of last garbage collection.", + nil, nil), goInfoDesc: NewDesc( "go_info", "Information about the Go environment.", nil, Labels{"version": runtime.Version()}), - msLast: &runtime.MemStats{}, - msRead: runtime.ReadMemStats, - msMaxWait: time.Second, - msMaxAge: 5 * time.Minute, - msMetrics: memStatsMetrics{ - { - desc: NewDesc( - memstatNamespace("alloc_bytes"), - "Number of bytes allocated and still in use.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("alloc_bytes_total"), - "Total number of bytes allocated, even if freed.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) }, - valType: CounterValue, - }, { - desc: NewDesc( - memstatNamespace("sys_bytes"), - "Number of bytes obtained from system.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("lookups_total"), - "Total number of pointer lookups.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) }, - valType: CounterValue, - }, { - desc: NewDesc( - memstatNamespace("mallocs_total"), - "Total number of mallocs.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) }, - valType: CounterValue, - }, { - desc: NewDesc( - memstatNamespace("frees_total"), - "Total number of frees.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) }, - valType: CounterValue, - }, { - desc: NewDesc( - memstatNamespace("heap_alloc_bytes"), - "Number of heap bytes allocated and still in use.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("heap_sys_bytes"), - "Number of heap bytes obtained from system.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("heap_idle_bytes"), - "Number of heap bytes waiting to be used.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("heap_inuse_bytes"), - "Number of heap bytes that are in use.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("heap_released_bytes"), - "Number of heap bytes released to OS.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("heap_objects"), - "Number of allocated objects.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("stack_inuse_bytes"), - "Number of bytes in use by the stack allocator.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("stack_sys_bytes"), - "Number of bytes obtained from system for stack allocator.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("mspan_inuse_bytes"), - "Number of bytes in use by mspan structures.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("mspan_sys_bytes"), - "Number of bytes used for mspan structures obtained from system.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("mcache_inuse_bytes"), - "Number of bytes in use by mcache structures.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("mcache_sys_bytes"), - "Number of bytes used for mcache structures obtained from system.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("buck_hash_sys_bytes"), - "Number of bytes used by the profiling bucket hash table.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("gc_sys_bytes"), - "Number of bytes used for garbage collection system metadata.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("other_sys_bytes"), - "Number of bytes used for other system allocations.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("next_gc_bytes"), - "Number of heap bytes when next garbage collection will take place.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("last_gc_time_seconds"), - "Number of seconds since 1970 of last garbage collection.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 }, - valType: GaugeValue, - }, { - desc: NewDesc( - memstatNamespace("gc_cpu_fraction"), - "The fraction of this program's available CPU time used by the GC since the program started.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction }, - valType: GaugeValue, - }, - }, } } -func memstatNamespace(s string) string { - return "go_memstats_" + s -} - // Describe returns all descriptions of the collector. -func (c *goCollector) Describe(ch chan<- *Desc) { +func (c *baseGoCollector) Describe(ch chan<- *Desc) { ch <- c.goroutinesDesc ch <- c.threadsDesc ch <- c.gcDesc + ch <- c.gcLastTimeDesc ch <- c.goInfoDesc - for _, i := range c.msMetrics { - ch <- i.desc - } } // Collect returns the current state of all metrics of the collector. -func (c *goCollector) Collect(ch chan<- Metric) { - var ( - ms = &runtime.MemStats{} - done = make(chan struct{}) - ) - // Start reading memstats first as it might take a while. - go func() { - c.msRead(ms) - c.msMtx.Lock() - c.msLast = ms - c.msLastTimestamp = time.Now() - c.msMtx.Unlock() - close(done) - }() - +func (c *baseGoCollector) Collect(ch chan<- Metric) { ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine())) n, _ := runtime.ThreadCreateProfile(nil) ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n)) @@ -305,63 +267,19 @@ func (c *goCollector) Collect(ch chan<- Metric) { } quantiles[0.0] = stats.PauseQuantiles[0].Seconds() ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles) + ch <- MustNewConstMetric(c.gcLastTimeDesc, GaugeValue, float64(stats.LastGC.UnixNano())/1e9) ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1) - - timer := time.NewTimer(c.msMaxWait) - select { - case <-done: // Our own ReadMemStats succeeded in time. Use it. - timer.Stop() // Important for high collection frequencies to not pile up timers. - c.msCollect(ch, ms) - return - case <-timer.C: // Time out, use last memstats if possible. Continue below. - } - c.msMtx.Lock() - if time.Since(c.msLastTimestamp) < c.msMaxAge { - // Last memstats are recent enough. Collect from them under the lock. - c.msCollect(ch, c.msLast) - c.msMtx.Unlock() - return - } - // If we are here, the last memstats are too old or don't exist. We have - // to wait until our own ReadMemStats finally completes. For that to - // happen, we have to release the lock. - c.msMtx.Unlock() - <-done - c.msCollect(ch, ms) } -func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) { - for _, i := range c.msMetrics { - ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms)) - } +func memstatNamespace(s string) string { + return "go_memstats_" + s } -// memStatsMetrics provide description, value, and value type for memstat metrics. +// memStatsMetrics provide description, evaluator, runtime/metrics name, and +// value type for memstat metrics. type memStatsMetrics []struct { desc *Desc eval func(*runtime.MemStats) float64 valType ValueType } - -// NewBuildInfoCollector is the obsolete version of collectors.NewBuildInfoCollector. -// See there for documentation. -// -// Deprecated: Use collectors.NewBuildInfoCollector instead. -func NewBuildInfoCollector() Collector { - path, version, sum := "unknown", "unknown", "unknown" - if bi, ok := debug.ReadBuildInfo(); ok { - path = bi.Main.Path - version = bi.Main.Version - sum = bi.Main.Sum - } - c := &selfCollector{MustNewConstMetric( - NewDesc( - "go_build_info", - "Build information about the main Go module.", - nil, Labels{"path": path, "version": version, "checksum": sum}, - ), - GaugeValue, 1)} - c.init(c.self) - return c -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go new file mode 100644 index 00000000..24526131 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go @@ -0,0 +1,107 @@ +// Copyright 2021 The Prometheus Authors +// 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. + +//go:build !go1.17 +// +build !go1.17 + +package prometheus + +import ( + "runtime" + "sync" + "time" +) + +type goCollector struct { + base baseGoCollector + + // ms... are memstats related. + msLast *runtime.MemStats // Previously collected memstats. + msLastTimestamp time.Time + msMtx sync.Mutex // Protects msLast and msLastTimestamp. + msMetrics memStatsMetrics + msRead func(*runtime.MemStats) // For mocking in tests. + msMaxWait time.Duration // Wait time for fresh memstats. + msMaxAge time.Duration // Maximum allowed age of old memstats. +} + +// NewGoCollector is the obsolete version of collectors.NewGoCollector. +// See there for documentation. +// +// Deprecated: Use collectors.NewGoCollector instead. +func NewGoCollector() Collector { + return &goCollector{ + base: newBaseGoCollector(), + msLast: &runtime.MemStats{}, + msRead: runtime.ReadMemStats, + msMaxWait: time.Second, + msMaxAge: 5 * time.Minute, + msMetrics: goRuntimeMemStats(), + } +} + +// Describe returns all descriptions of the collector. +func (c *goCollector) Describe(ch chan<- *Desc) { + c.base.Describe(ch) + for _, i := range c.msMetrics { + ch <- i.desc + } +} + +// Collect returns the current state of all metrics of the collector. +func (c *goCollector) Collect(ch chan<- Metric) { + var ( + ms = &runtime.MemStats{} + done = make(chan struct{}) + ) + // Start reading memstats first as it might take a while. + go func() { + c.msRead(ms) + c.msMtx.Lock() + c.msLast = ms + c.msLastTimestamp = time.Now() + c.msMtx.Unlock() + close(done) + }() + + // Collect base non-memory metrics. + c.base.Collect(ch) + + timer := time.NewTimer(c.msMaxWait) + select { + case <-done: // Our own ReadMemStats succeeded in time. Use it. + timer.Stop() // Important for high collection frequencies to not pile up timers. + c.msCollect(ch, ms) + return + case <-timer.C: // Time out, use last memstats if possible. Continue below. + } + c.msMtx.Lock() + if time.Since(c.msLastTimestamp) < c.msMaxAge { + // Last memstats are recent enough. Collect from them under the lock. + c.msCollect(ch, c.msLast) + c.msMtx.Unlock() + return + } + // If we are here, the last memstats are too old or don't exist. We have + // to wait until our own ReadMemStats finally completes. For that to + // happen, we have to release the lock. + c.msMtx.Unlock() + <-done + c.msCollect(ch, ms) +} + +func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) { + for _, i := range c.msMetrics { + ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms)) + } +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go117.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go117.go new file mode 100644 index 00000000..d43bdcdd --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go117.go @@ -0,0 +1,408 @@ +// Copyright 2021 The Prometheus Authors +// 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. + +//go:build go1.17 +// +build go1.17 + +package prometheus + +import ( + "math" + "runtime" + "runtime/metrics" + "strings" + "sync" + + //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. + "github.com/golang/protobuf/proto" + "github.com/prometheus/client_golang/prometheus/internal" + dto "github.com/prometheus/client_model/go" +) + +type goCollector struct { + base baseGoCollector + + // mu protects updates to all fields ensuring a consistent + // snapshot is always produced by Collect. + mu sync.Mutex + + // rm... fields all pertain to the runtime/metrics package. + rmSampleBuf []metrics.Sample + rmSampleMap map[string]*metrics.Sample + rmMetrics []collectorMetric + + // With Go 1.17, the runtime/metrics package was introduced. + // From that point on, metric names produced by the runtime/metrics + // package could be generated from runtime/metrics names. However, + // these differ from the old names for the same values. + // + // This field exist to export the same values under the old names + // as well. + msMetrics memStatsMetrics +} + +// NewGoCollector is the obsolete version of collectors.NewGoCollector. +// See there for documentation. +// +// Deprecated: Use collectors.NewGoCollector instead. +func NewGoCollector() Collector { + descriptions := metrics.All() + + // Collect all histogram samples so that we can get their buckets. + // The API guarantees that the buckets are always fixed for the lifetime + // of the process. + var histograms []metrics.Sample + for _, d := range descriptions { + if d.Kind == metrics.KindFloat64Histogram { + histograms = append(histograms, metrics.Sample{Name: d.Name}) + } + } + metrics.Read(histograms) + bucketsMap := make(map[string][]float64) + for i := range histograms { + bucketsMap[histograms[i].Name] = histograms[i].Value.Float64Histogram().Buckets + } + + // Generate a Desc and ValueType for each runtime/metrics metric. + metricSet := make([]collectorMetric, 0, len(descriptions)) + sampleBuf := make([]metrics.Sample, 0, len(descriptions)) + sampleMap := make(map[string]*metrics.Sample, len(descriptions)) + for i := range descriptions { + d := &descriptions[i] + namespace, subsystem, name, ok := internal.RuntimeMetricsToProm(d) + if !ok { + // Just ignore this metric; we can't do anything with it here. + // If a user decides to use the latest version of Go, we don't want + // to fail here. This condition is tested elsewhere. + continue + } + + // Set up sample buffer for reading, and a map + // for quick lookup of sample values. + sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name}) + sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1] + + var m collectorMetric + if d.Kind == metrics.KindFloat64Histogram { + _, hasSum := rmExactSumMap[d.Name] + unit := d.Name[strings.IndexRune(d.Name, ':')+1:] + m = newBatchHistogram( + NewDesc( + BuildFQName(namespace, subsystem, name), + d.Description, + nil, + nil, + ), + internal.RuntimeMetricsBucketsForUnit(bucketsMap[d.Name], unit), + hasSum, + ) + } else if d.Cumulative { + m = NewCounter(CounterOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: name, + Help: d.Description, + }) + } else { + m = NewGauge(GaugeOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: name, + Help: d.Description, + }) + } + metricSet = append(metricSet, m) + } + return &goCollector{ + base: newBaseGoCollector(), + rmSampleBuf: sampleBuf, + rmSampleMap: sampleMap, + rmMetrics: metricSet, + msMetrics: goRuntimeMemStats(), + } +} + +// Describe returns all descriptions of the collector. +func (c *goCollector) Describe(ch chan<- *Desc) { + c.base.Describe(ch) + for _, i := range c.msMetrics { + ch <- i.desc + } + for _, m := range c.rmMetrics { + ch <- m.Desc() + } +} + +// Collect returns the current state of all metrics of the collector. +func (c *goCollector) Collect(ch chan<- Metric) { + // Collect base non-memory metrics. + c.base.Collect(ch) + + // Collect must be thread-safe, so prevent concurrent use of + // rmSampleBuf. Just read into rmSampleBuf but write all the data + // we get into our Metrics or MemStats. + // + // This lock also ensures that the Metrics we send out are all from + // the same updates, ensuring their mutual consistency insofar as + // is guaranteed by the runtime/metrics package. + // + // N.B. This locking is heavy-handed, but Collect is expected to be called + // relatively infrequently. Also the core operation here, metrics.Read, + // is fast (O(tens of microseconds)) so contention should certainly be + // low, though channel operations and any allocations may add to that. + c.mu.Lock() + defer c.mu.Unlock() + + // Populate runtime/metrics sample buffer. + metrics.Read(c.rmSampleBuf) + + // Update all our metrics from rmSampleBuf. + for i, sample := range c.rmSampleBuf { + // N.B. switch on concrete type because it's significantly more efficient + // than checking for the Counter and Gauge interface implementations. In + // this case, we control all the types here. + switch m := c.rmMetrics[i].(type) { + case *counter: + // Guard against decreases. This should never happen, but a failure + // to do so will result in a panic, which is a harsh consequence for + // a metrics collection bug. + v0, v1 := m.get(), unwrapScalarRMValue(sample.Value) + if v1 > v0 { + m.Add(unwrapScalarRMValue(sample.Value) - m.get()) + } + m.Collect(ch) + case *gauge: + m.Set(unwrapScalarRMValue(sample.Value)) + m.Collect(ch) + case *batchHistogram: + m.update(sample.Value.Float64Histogram(), c.exactSumFor(sample.Name)) + m.Collect(ch) + default: + panic("unexpected metric type") + } + } + // ms is a dummy MemStats that we populate ourselves so that we can + // populate the old metrics from it. + var ms runtime.MemStats + memStatsFromRM(&ms, c.rmSampleMap) + for _, i := range c.msMetrics { + ch <- MustNewConstMetric(i.desc, i.valType, i.eval(&ms)) + } +} + +// unwrapScalarRMValue unwraps a runtime/metrics value that is assumed +// to be scalar and returns the equivalent float64 value. Panics if the +// value is not scalar. +func unwrapScalarRMValue(v metrics.Value) float64 { + switch v.Kind() { + case metrics.KindUint64: + return float64(v.Uint64()) + case metrics.KindFloat64: + return v.Float64() + case metrics.KindBad: + // Unsupported metric. + // + // This should never happen because we always populate our metric + // set from the runtime/metrics package. + panic("unexpected unsupported metric") + default: + // Unsupported metric kind. + // + // This should never happen because we check for this during initialization + // and flag and filter metrics whose kinds we don't understand. + panic("unexpected unsupported metric kind") + } +} + +var rmExactSumMap = map[string]string{ + "/gc/heap/allocs-by-size:bytes": "/gc/heap/allocs:bytes", + "/gc/heap/frees-by-size:bytes": "/gc/heap/frees:bytes", +} + +// exactSumFor takes a runtime/metrics metric name (that is assumed to +// be of kind KindFloat64Histogram) and returns its exact sum and whether +// its exact sum exists. +// +// The runtime/metrics API for histograms doesn't currently expose exact +// sums, but some of the other metrics are in fact exact sums of histograms. +func (c *goCollector) exactSumFor(rmName string) float64 { + sumName, ok := rmExactSumMap[rmName] + if !ok { + return 0 + } + s, ok := c.rmSampleMap[sumName] + if !ok { + return 0 + } + return unwrapScalarRMValue(s.Value) +} + +func memStatsFromRM(ms *runtime.MemStats, rm map[string]*metrics.Sample) { + lookupOrZero := func(name string) uint64 { + if s, ok := rm[name]; ok { + return s.Value.Uint64() + } + return 0 + } + + // Currently, MemStats adds tiny alloc count to both Mallocs AND Frees. + // The reason for this is because MemStats couldn't be extended at the time + // but there was a desire to have Mallocs at least be a little more representative, + // while having Mallocs - Frees still represent a live object count. + // Unfortunately, MemStats doesn't actually export a large allocation count, + // so it's impossible to pull this number out directly. + tinyAllocs := lookupOrZero("/gc/heap/tiny/allocs:objects") + ms.Mallocs = lookupOrZero("/gc/heap/allocs:objects") + tinyAllocs + ms.Frees = lookupOrZero("/gc/heap/frees:objects") + tinyAllocs + + ms.TotalAlloc = lookupOrZero("/gc/heap/allocs:bytes") + ms.Sys = lookupOrZero("/memory/classes/total:bytes") + ms.Lookups = 0 // Already always zero. + ms.HeapAlloc = lookupOrZero("/memory/classes/heap/objects:bytes") + ms.Alloc = ms.HeapAlloc + ms.HeapInuse = ms.HeapAlloc + lookupOrZero("/memory/classes/heap/unused:bytes") + ms.HeapReleased = lookupOrZero("/memory/classes/heap/released:bytes") + ms.HeapIdle = ms.HeapReleased + lookupOrZero("/memory/classes/heap/free:bytes") + ms.HeapSys = ms.HeapInuse + ms.HeapIdle + ms.HeapObjects = lookupOrZero("/gc/heap/objects:objects") + ms.StackInuse = lookupOrZero("/memory/classes/heap/stacks:bytes") + ms.StackSys = ms.StackInuse + lookupOrZero("/memory/classes/os-stacks:bytes") + ms.MSpanInuse = lookupOrZero("/memory/classes/metadata/mspan/inuse:bytes") + ms.MSpanSys = ms.MSpanInuse + lookupOrZero("/memory/classes/metadata/mspan/free:bytes") + ms.MCacheInuse = lookupOrZero("/memory/classes/metadata/mcache/inuse:bytes") + ms.MCacheSys = ms.MCacheInuse + lookupOrZero("/memory/classes/metadata/mcache/free:bytes") + ms.BuckHashSys = lookupOrZero("/memory/classes/profiling/buckets:bytes") + ms.GCSys = lookupOrZero("/memory/classes/metadata/other:bytes") + ms.OtherSys = lookupOrZero("/memory/classes/other:bytes") + ms.NextGC = lookupOrZero("/gc/heap/goal:bytes") + + // N.B. LastGC is omitted because runtime.GCStats already has this. + // See https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 + // for more details. + ms.LastGC = 0 + + // N.B. GCCPUFraction is intentionally omitted. This metric is not useful, + // and often misleading due to the fact that it's an average over the lifetime + // of the process. + // See https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 + // for more details. + ms.GCCPUFraction = 0 +} + +// batchHistogram is a mutable histogram that is updated +// in batches. +type batchHistogram struct { + selfCollector + + // Static fields updated only once. + desc *Desc + hasSum bool + + // Because this histogram operates in batches, it just uses a + // single mutex for everything. updates are always serialized + // but Write calls may operate concurrently with updates. + // Contention between these two sources should be rare. + mu sync.Mutex + buckets []float64 // Inclusive lower bounds, like runtime/metrics. + counts []uint64 + sum float64 // Used if hasSum is true. +} + +// newBatchHistogram creates a new batch histogram value with the given +// Desc, buckets, and whether or not it has an exact sum available. +// +// buckets must always be from the runtime/metrics package, following +// the same conventions. +func newBatchHistogram(desc *Desc, buckets []float64, hasSum bool) *batchHistogram { + h := &batchHistogram{ + desc: desc, + buckets: buckets, + // Because buckets follows runtime/metrics conventions, there's + // 1 more value in the buckets list than there are buckets represented, + // because in runtime/metrics, the bucket values represent *boundaries*, + // and non-Inf boundaries are inclusive lower bounds for that bucket. + counts: make([]uint64, len(buckets)-1), + hasSum: hasSum, + } + h.init(h) + return h +} + +// update updates the batchHistogram from a runtime/metrics histogram. +// +// sum must be provided if the batchHistogram was created to have an exact sum. +// h.buckets must be a strict subset of his.Buckets. +func (h *batchHistogram) update(his *metrics.Float64Histogram, sum float64) { + counts, buckets := his.Counts, his.Buckets + + h.mu.Lock() + defer h.mu.Unlock() + + // Clear buckets. + for i := range h.counts { + h.counts[i] = 0 + } + // Copy and reduce buckets. + var j int + for i, count := range counts { + h.counts[j] += count + if buckets[i+1] == h.buckets[j+1] { + j++ + } + } + if h.hasSum { + h.sum = sum + } +} + +func (h *batchHistogram) Desc() *Desc { + return h.desc +} + +func (h *batchHistogram) Write(out *dto.Metric) error { + h.mu.Lock() + defer h.mu.Unlock() + + sum := float64(0) + if h.hasSum { + sum = h.sum + } + dtoBuckets := make([]*dto.Bucket, 0, len(h.counts)) + totalCount := uint64(0) + for i, count := range h.counts { + totalCount += count + if !h.hasSum { + // N.B. This computed sum is an underestimate. + sum += h.buckets[i] * float64(count) + } + + // Skip the +Inf bucket, but only for the bucket list. + // It must still count for sum and totalCount. + if math.IsInf(h.buckets[i+1], 1) { + break + } + // Float64Histogram's upper bound is exclusive, so make it inclusive + // by obtaining the next float64 value down, in order. + upperBound := math.Nextafter(h.buckets[i+1], h.buckets[i]) + dtoBuckets = append(dtoBuckets, &dto.Bucket{ + CumulativeCount: proto.Uint64(totalCount), + UpperBound: proto.Float64(upperBound), + }) + } + out.Histogram = &dto.Histogram{ + Bucket: dtoBuckets, + SampleCount: proto.Uint64(totalCount), + SampleSum: proto.Float64(sum), + } + return nil +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index 8425640b..893802fd 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -116,6 +116,34 @@ func ExponentialBuckets(start, factor float64, count int) []float64 { return buckets } +// ExponentialBucketsRange creates 'count' buckets, where the lowest bucket is +// 'min' and the highest bucket is 'max'. The final +Inf bucket is not counted +// and not included in the returned slice. The returned slice is meant to be +// used for the Buckets field of HistogramOpts. +// +// The function panics if 'count' is 0 or negative, if 'min' is 0 or negative. +func ExponentialBucketsRange(min, max float64, count int) []float64 { + if count < 1 { + panic("ExponentialBucketsRange count needs a positive count") + } + if min <= 0 { + panic("ExponentialBucketsRange min needs to be greater than 0") + } + + // Formula for exponential buckets. + // max = min*growthFactor^(bucketCount-1) + + // We know max/min and highest bucket. Solve for growthFactor. + growthFactor := math.Pow(max/min, 1.0/float64(count-1)) + + // Now that we know growthFactor, solve for each bucket. + buckets := make([]float64, count) + for i := 1; i <= count; i++ { + buckets[i-1] = min * math.Pow(growthFactor, float64(i-1)) + } + return buckets +} + // HistogramOpts bundles the options for creating a Histogram metric. It is // mandatory to set Name to a non-empty string. All other fields are optional // and can safely be left at their zero value, although it is strongly diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go new file mode 100644 index 00000000..fe0a5218 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go @@ -0,0 +1,142 @@ +// Copyright 2021 The Prometheus Authors +// 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. + +//go:build go1.17 +// +build go1.17 + +package internal + +import ( + "math" + "path" + "runtime/metrics" + "strings" + + "github.com/prometheus/common/model" +) + +// RuntimeMetricsToProm produces a Prometheus metric name from a runtime/metrics +// metric description and validates whether the metric is suitable for integration +// with Prometheus. +// +// Returns false if a name could not be produced, or if Prometheus does not understand +// the runtime/metrics Kind. +// +// Note that the main reason a name couldn't be produced is if the runtime/metrics +// package exports a name with characters outside the valid Prometheus metric name +// character set. This is theoretically possible, but should never happen in practice. +// Still, don't rely on it. +func RuntimeMetricsToProm(d *metrics.Description) (string, string, string, bool) { + namespace := "go" + + comp := strings.SplitN(d.Name, ":", 2) + key := comp[0] + unit := comp[1] + + // The last path element in the key is the name, + // the rest is the subsystem. + subsystem := path.Dir(key[1:] /* remove leading / */) + name := path.Base(key) + + // subsystem is translated by replacing all / and - with _. + subsystem = strings.ReplaceAll(subsystem, "/", "_") + subsystem = strings.ReplaceAll(subsystem, "-", "_") + + // unit is translated assuming that the unit contains no + // non-ASCII characters. + unit = strings.ReplaceAll(unit, "-", "_") + unit = strings.ReplaceAll(unit, "*", "_") + unit = strings.ReplaceAll(unit, "/", "_per_") + + // name has - replaced with _ and is concatenated with the unit and + // other data. + name = strings.ReplaceAll(name, "-", "_") + name = name + "_" + unit + if d.Cumulative { + name = name + "_total" + } + + valid := model.IsValidMetricName(model.LabelValue(namespace + "_" + subsystem + "_" + name)) + switch d.Kind { + case metrics.KindUint64: + case metrics.KindFloat64: + case metrics.KindFloat64Histogram: + default: + valid = false + } + return namespace, subsystem, name, valid +} + +// RuntimeMetricsBucketsForUnit takes a set of buckets obtained for a runtime/metrics histogram +// type (so, lower-bound inclusive) and a unit from a runtime/metrics name, and produces +// a reduced set of buckets. This function always removes any -Inf bucket as it's represented +// as the bottom-most upper-bound inclusive bucket in Prometheus. +func RuntimeMetricsBucketsForUnit(buckets []float64, unit string) []float64 { + switch unit { + case "bytes": + // Rebucket as powers of 2. + return rebucketExp(buckets, 2) + case "seconds": + // Rebucket as powers of 10 and then merge all buckets greater + // than 1 second into the +Inf bucket. + b := rebucketExp(buckets, 10) + for i := range b { + if b[i] <= 1 { + continue + } + b[i] = math.Inf(1) + b = b[:i+1] + break + } + return b + } + return buckets +} + +// rebucketExp takes a list of bucket boundaries (lower bound inclusive) and +// downsamples the buckets to those a multiple of base apart. The end result +// is a roughly exponential (in many cases, perfectly exponential) bucketing +// scheme. +func rebucketExp(buckets []float64, base float64) []float64 { + bucket := buckets[0] + var newBuckets []float64 + // We may see a -Inf here, in which case, add it and skip it + // since we risk producing NaNs otherwise. + // + // We need to preserve -Inf values to maintain runtime/metrics + // conventions. We'll strip it out later. + if bucket == math.Inf(-1) { + newBuckets = append(newBuckets, bucket) + buckets = buckets[1:] + bucket = buckets[0] + } + // From now on, bucket should always have a non-Inf value because + // Infs are only ever at the ends of the bucket lists, so + // arithmetic operations on it are non-NaN. + for i := 1; i < len(buckets); i++ { + if bucket >= 0 && buckets[i] < bucket*base { + // The next bucket we want to include is at least bucket*base. + continue + } else if bucket < 0 && buckets[i] < bucket/base { + // In this case the bucket we're targeting is negative, and since + // we're ascending through buckets here, we need to divide to get + // closer to zero exponentially. + continue + } + // The +Inf bucket will always be the last one, and we'll always + // end up including it here because bucket + newBuckets = append(newBuckets, bucket) + bucket = buckets[i] + } + return append(newBuckets, bucket) +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go index 3117461c..2dc3660d 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !windows // +build !windows package prometheus diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go index 83c49b66..861b4d21 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go @@ -49,7 +49,10 @@ func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripp // http.RoundTripper to observe the request result with the provided CounterVec. // The CounterVec must have zero, one, or two non-const non-curried labels. For // those, the only allowed label names are "code" and "method". The function -// panics otherwise. Partitioning of the CounterVec happens by HTTP status code +// panics otherwise. For the "method" label a predefined default label value set +// is used to filter given values. Values besides predefined values will count +// as `unknown` method.`WithExtraMethods` can be used to add more +// methods to the set. Partitioning of the CounterVec happens by HTTP status code // and/or HTTP method if the respective instance label names are present in the // CounterVec. For unpartitioned counting, use a CounterVec with zero labels. // @@ -57,13 +60,18 @@ func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripp // is not incremented. // // See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc { +func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { + rtOpts := &option{} + for _, o := range opts { + o(rtOpts) + } + code, method := checkLabels(counter) return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { resp, err := next.RoundTrip(r) if err == nil { - counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc() + counter.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Inc() } return resp, err }) @@ -73,7 +81,10 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou // http.RoundTripper to observe the request duration with the provided // ObserverVec. The ObserverVec must have zero, one, or two non-const // non-curried labels. For those, the only allowed label names are "code" and -// "method". The function panics otherwise. The Observe method of the Observer +// "method". The function panics otherwise. For the "method" label a predefined +// default label value set is used to filter given values. Values besides +// predefined values will count as `unknown` method. `WithExtraMethods` +// can be used to add more methods to the set. The Observe method of the Observer // in the ObserverVec is called with the request duration in // seconds. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For @@ -85,14 +96,19 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou // // Note that this method is only guaranteed to never observe negative durations // if used with Go1.9+. -func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc { +func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { + rtOpts := &option{} + for _, o := range opts { + o(rtOpts) + } + code, method := checkLabels(obs) return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { start := time.Now() resp, err := next.RoundTrip(r) if err == nil { - obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds()) + obs.With(labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...)).Observe(time.Since(start).Seconds()) } return resp, err }) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go index ab037db8..a23f0edc 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go @@ -45,7 +45,10 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl // http.Handler to observe the request duration with the provided ObserverVec. // The ObserverVec must have valid metric and label names and must have zero, // one, or two non-const non-curried labels. For those, the only allowed label -// names are "code" and "method". The function panics otherwise. The Observe +// names are "code" and "method". The function panics otherwise. For the "method" +// label a predefined default label value set is used to filter given values. +// Values besides predefined values will count as `unknown` method. +//`WithExtraMethods` can be used to add more methods to the set. The Observe // method of the Observer in the ObserverVec is called with the request duration // in seconds. Partitioning happens by HTTP status code and/or HTTP method if // the respective instance label names are present in the ObserverVec. For @@ -58,7 +61,12 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl // // Note that this method is only guaranteed to never observe negative durations // if used with Go1.9+. -func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { +func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { + mwOpts := &option{} + for _, o := range opts { + o(mwOpts) + } + code, method := checkLabels(obs) if code { @@ -67,14 +75,14 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht d := newDelegator(w, nil) next.ServeHTTP(d, r) - obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds()) + obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) }) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { now := time.Now() next.ServeHTTP(w, r) - obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds()) + obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) }) } @@ -82,7 +90,10 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht // to observe the request result with the provided CounterVec. The CounterVec // must have valid metric and label names and must have zero, one, or two // non-const non-curried labels. For those, the only allowed label names are -// "code" and "method". The function panics otherwise. Partitioning of the +// "code" and "method". The function panics otherwise. For the "method" +// label a predefined default label value set is used to filter given values. +// Values besides predefined values will count as `unknown` method. +// `WithExtraMethods` can be used to add more methods to the set. Partitioning of the // CounterVec happens by HTTP status code and/or HTTP method if the respective // instance label names are present in the CounterVec. For unpartitioned // counting, use a CounterVec with zero labels. @@ -92,20 +103,25 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht // If the wrapped Handler panics, the Counter is not incremented. // // See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc { +func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, opts ...Option) http.HandlerFunc { + mwOpts := &option{} + for _, o := range opts { + o(mwOpts) + } + code, method := checkLabels(counter) if code { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { d := newDelegator(w, nil) next.ServeHTTP(d, r) - counter.With(labels(code, method, r.Method, d.Status())).Inc() + counter.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Inc() }) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) - counter.With(labels(code, method, r.Method, 0)).Inc() + counter.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Inc() }) } @@ -114,7 +130,10 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) // until the response headers are written. The ObserverVec must have valid // metric and label names and must have zero, one, or two non-const non-curried // labels. For those, the only allowed label names are "code" and "method". The -// function panics otherwise. The Observe method of the Observer in the +// function panics otherwise. For the "method" label a predefined default label +// value set is used to filter given values. Values besides predefined values +// will count as `unknown` method.`WithExtraMethods` can be used to add more +// methods to the set. The Observe method of the Observer in the // ObserverVec is called with the request duration in seconds. Partitioning // happens by HTTP status code and/or HTTP method if the respective instance // label names are present in the ObserverVec. For unpartitioned observations, @@ -128,13 +147,18 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) // if used with Go1.9+. // // See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { +func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { + mwOpts := &option{} + for _, o := range opts { + o(mwOpts) + } + code, method := checkLabels(obs) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { now := time.Now() d := newDelegator(w, func(status int) { - obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds()) + obs.With(labels(code, method, r.Method, status, mwOpts.extraMethods...)).Observe(time.Since(now).Seconds()) }) next.ServeHTTP(d, r) }) @@ -144,8 +168,11 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha // http.Handler to observe the request size with the provided ObserverVec. The // ObserverVec must have valid metric and label names and must have zero, one, // or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. The Observe method of -// the Observer in the ObserverVec is called with the request size in +// are "code" and "method". The function panics otherwise. For the "method" +// label a predefined default label value set is used to filter given values. +// Values besides predefined values will count as `unknown` method. +// `WithExtraMethods` can be used to add more methods to the set. The Observe +// method of the Observer in the ObserverVec is called with the request size in // bytes. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For // unpartitioned observations, use an ObserverVec with zero labels. Note that @@ -156,7 +183,12 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha // If the wrapped Handler panics, no values are reported. // // See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { +func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.HandlerFunc { + mwOpts := &option{} + for _, o := range opts { + o(mwOpts) + } + code, method := checkLabels(obs) if code { @@ -164,14 +196,14 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) d := newDelegator(w, nil) next.ServeHTTP(d, r) size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size)) + obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(size)) }) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) size := computeApproximateRequestSize(r) - obs.With(labels(code, method, r.Method, 0)).Observe(float64(size)) + obs.With(labels(code, method, r.Method, 0, mwOpts.extraMethods...)).Observe(float64(size)) }) } @@ -179,8 +211,11 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) // http.Handler to observe the response size with the provided ObserverVec. The // ObserverVec must have valid metric and label names and must have zero, one, // or two non-const non-curried labels. For those, the only allowed label names -// are "code" and "method". The function panics otherwise. The Observe method of -// the Observer in the ObserverVec is called with the response size in +// are "code" and "method". The function panics otherwise. For the "method" +// label a predefined default label value set is used to filter given values. +// Values besides predefined values will count as `unknown` method. +// `WithExtraMethods` can be used to add more methods to the set. The Observe +// method of the Observer in the ObserverVec is called with the response size in // bytes. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For // unpartitioned observations, use an ObserverVec with zero labels. Note that @@ -191,12 +226,18 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) // If the wrapped Handler panics, no values are reported. // // See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler { +func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler, opts ...Option) http.Handler { + mwOpts := &option{} + for _, o := range opts { + o(mwOpts) + } + code, method := checkLabels(obs) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { d := newDelegator(w, nil) next.ServeHTTP(d, r) - obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written())) + obs.With(labels(code, method, r.Method, d.Status(), mwOpts.extraMethods...)).Observe(float64(d.Written())) }) } @@ -290,7 +331,7 @@ func isLabelCurried(c prometheus.Collector, label string) bool { // unnecessary allocations on each request. var emptyLabels = prometheus.Labels{} -func labels(code, method bool, reqMethod string, status int) prometheus.Labels { +func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels { if !(code || method) { return emptyLabels } @@ -300,7 +341,7 @@ func labels(code, method bool, reqMethod string, status int) prometheus.Labels { labels["code"] = sanitizeCode(status) } if method { - labels["method"] = sanitizeMethod(reqMethod) + labels["method"] = sanitizeMethod(reqMethod, extraMethods...) } return labels @@ -330,7 +371,12 @@ func computeApproximateRequestSize(r *http.Request) int { return s } -func sanitizeMethod(m string) string { +// If the wrapped http.Handler has a known method, it will be sanitized and returned. +// Otherwise, "unknown" will be returned. The known method list can be extended +// as needed by using extraMethods parameter. +func sanitizeMethod(m string, extraMethods ...string) string { + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for + // the methods chosen as default. switch m { case "GET", "get": return "get" @@ -348,15 +394,25 @@ func sanitizeMethod(m string) string { return "options" case "NOTIFY", "notify": return "notify" + case "TRACE", "trace": + return "trace" + case "PATCH", "patch": + return "patch" default: - return strings.ToLower(m) + for _, method := range extraMethods { + if strings.EqualFold(m, method) { + return strings.ToLower(m) + } + } + return "unknown" } } // If the wrapped http.Handler has not set a status code, i.e. the value is -// currently 0, santizeCode will return 200, for consistency with behavior in +// currently 0, sanitizeCode will return 200, for consistency with behavior in // the stdlib. func sanitizeCode(s int) string { + // See for accepted codes https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml switch s { case 100: return "100" @@ -453,6 +509,9 @@ func sanitizeCode(s int) string { return "511" default: - return strconv.Itoa(s) + if s >= 100 && s <= 599 { + return strconv.Itoa(s) + } + return "unknown" } } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go new file mode 100644 index 00000000..35e41bd1 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Prometheus Authors +// 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 promhttp + +// Option are used to configure a middleware or round tripper.. +type Option func(*option) + +type option struct { + extraMethods []string +} + +// WithExtraMethods adds additional HTTP methods to the list of allowed methods. +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods for the default list. +// +// See the example for ExampleInstrumentHandlerWithExtraMethods for example usage. +func WithExtraMethods(methods ...string) Option { + return func(o *option) { + o.extraMethods = methods + } +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/value.go b/vendor/github.com/prometheus/client_golang/prometheus/value.go index c778711b..b4e0ae11 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/value.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/value.go @@ -21,7 +21,7 @@ import ( //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes" + "google.golang.org/protobuf/types/known/timestamppb" dto "github.com/prometheus/client_model/go" ) @@ -183,8 +183,8 @@ const ExemplarMaxRunes = 64 func newExemplar(value float64, ts time.Time, l Labels) (*dto.Exemplar, error) { e := &dto.Exemplar{} e.Value = proto.Float64(value) - tsProto, err := ptypes.TimestampProto(ts) - if err != nil { + tsProto := timestamppb.New(ts) + if err := tsProto.CheckValid(); err != nil { return nil, err } e.Timestamp = tsProto diff --git a/vendor/github.com/prometheus/procfs/Makefile b/vendor/github.com/prometheus/procfs/Makefile index 616a0d25..fa2bd5b5 100644 --- a/vendor/github.com/prometheus/procfs/Makefile +++ b/vendor/github.com/prometheus/procfs/Makefile @@ -18,6 +18,8 @@ include Makefile.common ./ttar -C $(dir $*) -x -f $*.ttar touch $@ +fixtures: fixtures/.unpacked + update_fixtures: rm -vf fixtures/.unpacked ./ttar -c -f fixtures.ttar fixtures/ diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index 3ac29c63..a1b1ca40 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -78,12 +78,12 @@ ifneq ($(shell which gotestsum),) endif endif -PROMU_VERSION ?= 0.7.0 +PROMU_VERSION ?= 0.12.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.18.0 +GOLANGCI_LINT_VERSION ?= v1.39.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) @@ -118,7 +118,7 @@ endif %: common-% ; .PHONY: common-all -common-all: precheck style check_license lint unused build test +common-all: precheck style check_license lint yamllint unused build test .PHONY: common-style common-style: @@ -198,6 +198,15 @@ else endif endif +.PHONY: common-yamllint +common-yamllint: + @echo ">> running yamllint on all YAML files in the repository" +ifeq (, $(shell which yamllint)) + @echo "yamllint not installed so skipping" +else + yamllint . +endif + # For backward-compatibility. .PHONY: common-staticcheck common-staticcheck: lint diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 55d1e326..43c37735 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -6,8 +6,8 @@ metrics from the pseudo-filesystems /proc and /sys. *WARNING*: This package is a work in progress. Its API may still break in backwards-incompatible ways without warnings. Use it at your own risk. -[![GoDoc](https://godoc.org/github.com/prometheus/procfs?status.png)](https://godoc.org/github.com/prometheus/procfs) -[![Build Status](https://travis-ci.org/prometheus/procfs.svg?branch=master)](https://travis-ci.org/prometheus/procfs) +[![Go Reference](https://pkg.go.dev/badge/github.com/prometheus/procfs.svg)](https://pkg.go.dev/github.com/prometheus/procfs) +[![CircleCI](https://circleci.com/gh/prometheus/procfs/tree/master.svg?style=svg)](https://circleci.com/gh/prometheus/procfs/tree/master) [![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/procfs)](https://goreportcard.com/report/github.com/prometheus/procfs) ## Usage diff --git a/vendor/github.com/prometheus/procfs/cmdline.go b/vendor/github.com/prometheus/procfs/cmdline.go new file mode 100644 index 00000000..bf4f3b48 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/cmdline.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Prometheus Authors +// 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 procfs + +import ( + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// CmdLine returns the command line of the kernel. +func (fs FS) CmdLine() ([]string, error) { + data, err := util.ReadFileNoStat(fs.proc.Path("cmdline")) + if err != nil { + return nil, err + } + + return strings.Fields(string(data)), nil +} diff --git a/vendor/github.com/prometheus/procfs/doc.go b/vendor/github.com/prometheus/procfs/doc.go index e2acd6d4..d31a8260 100644 --- a/vendor/github.com/prometheus/procfs/doc.go +++ b/vendor/github.com/prometheus/procfs/doc.go @@ -31,7 +31,7 @@ // log.Fatalf("could not get process: %s", err) // } // -// stat, err := p.NewStat() +// stat, err := p.Stat() // if err != nil { // log.Fatalf("could not get process stat: %s", err) // } diff --git a/vendor/github.com/prometheus/procfs/fixtures.ttar b/vendor/github.com/prometheus/procfs/fixtures.ttar index 1e76173d..5e7eeef4 100644 --- a/vendor/github.com/prometheus/procfs/fixtures.ttar +++ b/vendor/github.com/prometheus/procfs/fixtures.ttar @@ -644,6 +644,11 @@ Node 0, zone DMA32 759 572 791 475 194 45 12 0 Node 0, zone Normal 4381 1093 185 1530 567 102 4 0 0 0 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/cmdline +Lines: 1 +BOOT_IMAGE=/vmlinuz-5.11.0-22-generic root=UUID=456a0345-450d-4f7b-b7c9-43e3241d99ad ro quiet splash vt.handoff=7 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/proc/cpuinfo Lines: 216 processor : 0 @@ -2204,6 +2209,23 @@ Lines: 1 00015c73 00020e76 F0000769 00000000 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/proc/net/stat +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/net/stat/arp_cache +Lines: 3 +entries allocs destroys hash_grows lookups hits res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs unresolved_discards table_fulls +00000014 00000001 00000002 00000003 00000004 00000005 00000006 00000007 00000008 00000009 0000000a 0000000b 0000000c +00000014 0000000d 0000000e 0000000f 00000010 00000011 00000012 00000013 00000014 00000015 00000016 00000017 00000018 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/net/stat/ndisc_cache +Lines: 3 +entries allocs destroys hash_grows lookups hits res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs unresolved_discards table_fulls +00000024 000000f0 000000f1 000000f2 000000f3 000000f4 000000f5 000000f6 000000f7 000000f8 000000f9 000000fa 000000fb +00000024 000000fc 000000fd 000000fe 000000ff 00000100 00000101 00000102 00000103 00000104 00000105 00000106 00000107 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/proc/net/tcp Lines: 4 sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode @@ -3455,6 +3477,460 @@ Mode: 664 Directory: fixtures/sys/class Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/drm +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/drm/card0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/drm/card0/device +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/aer_dev_correctable +Lines: 9 +RxErr 0 +BadTLP 0 +BadDLLP 0 +Rollover 0 +Timeout 0 +NonFatalErr 0 +CorrIntErr 0 +HeaderOF 0 +TOTAL_ERR_COR 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/aer_dev_fatal +Lines: 19 +Undefined 0 +DLP 0 +SDES 0 +TLP 0 +FCP 0 +CmpltTO 0 +CmpltAbrt 0 +UnxCmplt 0 +RxOF 0 +MalfTLP 0 +ECRC 0 +UnsupReq 0 +ACSViol 0 +UncorrIntErr 0 +BlockedTLP 0 +AtomicOpBlocked 0 +TLPBlockedErr 0 +PoisonTLPBlocked 0 +TOTAL_ERR_FATAL 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/aer_dev_nonfatal +Lines: 19 +Undefined 0 +DLP 0 +SDES 0 +TLP 0 +FCP 0 +CmpltTO 0 +CmpltAbrt 0 +UnxCmplt 0 +RxOF 0 +MalfTLP 0 +ECRC 0 +UnsupReq 0 +ACSViol 0 +UncorrIntErr 0 +BlockedTLP 0 +AtomicOpBlocked 0 +TLPBlockedErr 0 +PoisonTLPBlocked 0 +TOTAL_ERR_NONFATAL 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/ari_enabled +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/boot_vga +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/broken_parity_status +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/class +Lines: 1 +0x030000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/consistent_dma_mask_bits +Lines: 1 +44 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/current_link_speed +Lines: 1 +8.0 GT/s PCIe +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/current_link_width +Lines: 1 +16 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/d3cold_allowed +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/device +Lines: 1 +0x687f +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/dma_mask_bits +Lines: 1 +44 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/driver_override +Lines: 1 +(null) +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/enable +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/gpu_busy_percent +Lines: 1 +4 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/irq +Lines: 1 +95 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/local_cpulist +Lines: 1 +0-15 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/local_cpus +Lines: 1 +0000ffff +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/max_link_speed +Lines: 1 +8.0 GT/s PCIe +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/max_link_width +Lines: 1 +16 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_gtt_total +Lines: 1 +8573157376 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_gtt_used +Lines: 1 +144560128 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_vis_vram_total +Lines: 1 +8573157376 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_vis_vram_used +Lines: 1 +1490378752 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_vram_total +Lines: 1 +8573157376 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_vram_used +Lines: 1 +1490378752 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/mem_info_vram_vendor +Lines: 1 +samsung +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/modalias +Lines: 1 +pci:v00001002d0000687Fsv00001043sd000004C4bc03sc00i00 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/msi_bus +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/numa_node +Lines: 1 +-1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pcie_bw +Lines: 1 +6641 815 256 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pcie_replay_count +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/power_dpm_force_performance_level +Lines: 1 +manual +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/power_dpm_state +Lines: 1 +performance +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/power_state +Lines: 1 +D0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_cur_state +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_dpm_dcefclk +Lines: 5 +0: 600Mhz * +1: 720Mhz +2: 800Mhz +3: 847Mhz +4: 900Mhz +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_dpm_mclk +Lines: 4 +0: 167Mhz * +1: 500Mhz +2: 800Mhz +3: 945Mhz +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_dpm_pcie +Lines: 2 +0: 8.0GT/s, x16 +1: 8.0GT/s, x16 * +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_dpm_sclk +Lines: 8 +0: 852Mhz * +1: 991Mhz +2: 1084Mhz +3: 1138Mhz +4: 1200Mhz +5: 1401Mhz +6: 1536Mhz +7: 1630Mhz +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_dpm_socclk +Lines: 8 +0: 600Mhz +1: 720Mhz * +2: 800Mhz +3: 847Mhz +4: 900Mhz +5: 960Mhz +6: 1028Mhz +7: 1107Mhz +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_features +Lines: 32 +Current ppfeatures: 0x0000000019a1ff4f +FEATURES BITMASK ENABLEMENT +DPM_PREFETCHER 0x0000000000000001 Y +GFXCLK_DPM 0x0000000000000002 Y +UCLK_DPM 0x0000000000000004 Y +SOCCLK_DPM 0x0000000000000008 Y +UVD_DPM 0x0000000000000010 N +VCE_DPM 0x0000000000000020 N +ULV 0x0000000000000040 Y +MP0CLK_DPM 0x0000000000000080 N +LINK_DPM 0x0000000000000100 Y +DCEFCLK_DPM 0x0000000000000200 Y +AVFS 0x0000000000000400 Y +GFXCLK_DS 0x0000000000000800 Y +SOCCLK_DS 0x0000000000001000 Y +LCLK_DS 0x0000000000002000 Y +PPT 0x0000000000004000 Y +TDC 0x0000000000008000 Y +THERMAL 0x0000000000010000 Y +GFX_PER_CU_CG 0x0000000000020000 N +RM 0x0000000000040000 N +DCEFCLK_DS 0x0000000000080000 N +ACDC 0x0000000000100000 N +VR0HOT 0x0000000000200000 Y +VR1HOT 0x0000000000400000 N +FW_CTF 0x0000000000800000 Y +LED_DISPLAY 0x0000000001000000 Y +FAN_CONTROL 0x0000000002000000 N +FAST_PPT 0x0000000004000000 N +DIDT 0x0000000008000000 Y +ACG 0x0000000010000000 Y +PCC_LIMIT 0x0000000020000000 N +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_force_state +Lines: 1 + +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_mclk_od +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_num_states +Lines: 3 +states: 2 +0 boot +1 performance +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_od_clk_voltage +Lines: 18 +OD_SCLK: +0: 852Mhz 800mV +1: 991Mhz 900mV +2: 1084Mhz 950mV +3: 1138Mhz 1000mV +4: 1200Mhz 1050mV +5: 1401Mhz 1100mV +6: 1536Mhz 1150mV +7: 1630Mhz 1200mV +OD_MCLK: +0: 167Mhz 800mV +1: 500Mhz 800mV +2: 800Mhz 950mV +3: 945Mhz 1100mV +OD_RANGE: +SCLK: 852MHz 2400MHz +MCLK: 167MHz 1500MHz +VDDC: 800mV 1200mV +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_power_profile_mode +Lines: 8 +NUM MODE_NAME BUSY_SET_POINT FPS USE_RLC_BUSY MIN_ACTIVE_LEVEL + 0 BOOTUP_DEFAULT : 70 60 0 0 + 1 3D_FULL_SCREEN*: 70 60 1 3 + 2 POWER_SAVING : 90 60 0 0 + 3 VIDEO : 70 60 0 0 + 4 VR : 70 90 0 0 + 5 COMPUTE : 30 60 0 6 + 6 CUSTOM : 0 0 0 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/pp_sclk_od +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/product_name +Lines: 1 + +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/product_number +Lines: 1 + +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/resource +Lines: 13 +0x0000007c00000000 0x0000007dffffffff 0x000000000014220c +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000007e00000000 0x0000007e0fffffff 0x000000000014220c +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x000000000000d000 0x000000000000d0ff 0x0000000000040101 +0x00000000fcd00000 0x00000000fcd7ffff 0x0000000000040200 +0x00000000fcd80000 0x00000000fcd9ffff 0x0000000000046200 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +0x0000000000000000 0x0000000000000000 0x0000000000000000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/revision +Lines: 1 +0xc1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/serial_number +Lines: 1 + +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/subsystem_device +Lines: 1 +0x04c4 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/subsystem_vendor +Lines: 1 +0x1043 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/thermal_throttling_logging +Lines: 1 +0000:09:00.0: thermal throttling logging enabled, with interval 60 seconds +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/uevent +Lines: 6 +DRIVER=amdgpu +PCI_CLASS=30000 +PCI_ID=1002:687F +PCI_SUBSYS_ID=1043:04C4 +PCI_SLOT_NAME=0000:09:00.0 +MODALIAS=pci:v00001002d0000687Fsv00001043sd000004C4bc03sc00i00 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/unique_id +Lines: 1 +0123456789abcdef +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/vbios_version +Lines: 1 +115-D050PIL-100 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/drm/card0/device/vendor +Lines: 1 +0x1002 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/fc_host Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3592,6 +4068,128 @@ Mode: 644 Directory: fixtures/sys/class/infiniband Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/infiniband/hfi1_0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/board_id +Lines: 1 +HPE 100Gb 1-port OP101 QSFP28 x16 PCIe Gen3 with Intel Omni-Path Adapter +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/fw_ver +Lines: 1 +1.27.0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/infiniband/hfi1_0/ports +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/infiniband/hfi1_0/ports/1 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/VL15_dropped +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/excessive_buffer_overrun_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/link_downed +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/link_error_recovery +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/local_link_integrity_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_constraint_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_data +Lines: 1 +345091702026 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_packets +Lines: 1 +638036947 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_remote_physical_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_rcv_switch_relay_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_xmit_constraint_errors +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_xmit_data +Lines: 1 +273558326543 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_xmit_discards +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_xmit_packets +Lines: 1 +568318856 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/port_xmit_wait +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/counters/symbol_error +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/phys_state +Lines: 1 +5: LinkUp +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/rate +Lines: 1 +100 Gb/sec (4X EDR) +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/infiniband/hfi1_0/ports/1/state +Lines: 1 +4: ACTIVE +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/infiniband/mlx4_0 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3960,6 +4558,32 @@ Lines: 1 1 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/nvme +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/nvme/nvme0 +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/nvme/nvme0/firmware_rev +Lines: 1 +1B2QEXP7 +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/nvme/nvme0/model +Lines: 1 +Samsung SSD 970 PRO 512GB +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/nvme/nvme0/serial +Lines: 1 +S680HF8N190894I +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/nvme/nvme0/state +Lines: 1 +live +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/power_supply Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4164,6 +4788,33 @@ Path: fixtures/sys/class/powercap/intel-rapl:a/uevent Lines: 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/scsi_tape +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/nst0 +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/nst0a +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/nst0l +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/nst0m +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/st0 +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/st0a +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/st0l +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/scsi_tape/st0m +SymlinkTo: ../../devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/thermal Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4575,6 +5226,475 @@ Mode: 444 Directory: fixtures/sys/devices/pci0000:00 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0a/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0l/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/nst0m/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0a/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0l/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/in_flight +Lines: 1 +1EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/io_ns +Lines: 1 +9247011087720EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/other_cnt +Lines: 1 +1409EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/read_byte_cnt +Lines: 1 +979383912EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/read_cnt +Lines: 1 +3741EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/read_ns +Lines: 1 +33788355744EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/resid_cnt +Lines: 1 +19EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/write_byte_cnt +Lines: 1 +1496246784000EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/write_cnt +Lines: 1 +53772916EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:00/0000:00:00.0/host0/port-0:0/end_device-0:0/target0:0:0/0:0:0:0/scsi_tape/st0m/stats/write_ns +Lines: 1 +5233597394395EOF +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4978,35 +6098,6 @@ Mode: 644 Directory: fixtures/sys/devices/system Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Directory: fixtures/sys/devices/system/node -Mode: 775 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Directory: fixtures/sys/devices/system/node/node1 -Mode: 755 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/devices/system/node/node1/vmstat -Lines: 6 -nr_free_pages 1 -nr_zone_inactive_anon 2 -nr_zone_active_anon 3 -nr_zone_inactive_file 4 -nr_zone_active_file 5 -nr_zone_unevictable 6 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Directory: fixtures/sys/devices/system/node/node2 -Mode: 755 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/devices/system/node/node2/vmstat -Lines: 6 -nr_free_pages 7 -nr_zone_inactive_anon 8 -nr_zone_active_anon 9 -nr_zone_inactive_file 10 -nr_zone_active_file 11 -nr_zone_unevictable 12 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/devices/system/clocksource Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5254,6 +6345,35 @@ Mode: 644 Directory: fixtures/sys/devices/system/cpu/cpufreq/policy1 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/system/node +Mode: 775 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/system/node/node1 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/system/node/node1/vmstat +Lines: 6 +nr_free_pages 1 +nr_zone_inactive_anon 2 +nr_zone_active_anon 3 +nr_zone_inactive_file 4 +nr_zone_active_file 5 +nr_zone_unevictable 6 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/devices/system/node/node2 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/system/node/node2/vmstat +Lines: 6 +nr_free_pages 7 +nr_zone_inactive_anon 8 +nr_zone_active_anon 9 +nr_zone_inactive_file 10 +nr_zone_active_file 11 +nr_zone_unevictable 12 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/github.com/prometheus/procfs/mdstat.go b/vendor/github.com/prometheus/procfs/mdstat.go index 4c4493bf..f0b9e5f7 100644 --- a/vendor/github.com/prometheus/procfs/mdstat.go +++ b/vendor/github.com/prometheus/procfs/mdstat.go @@ -22,9 +22,12 @@ import ( ) var ( - statusLineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`) - recoveryLineRE = regexp.MustCompile(`\((\d+)/\d+\)`) - componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`) + statusLineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[([U_]+)\]`) + recoveryLineBlocksRE = regexp.MustCompile(`\((\d+)/\d+\)`) + recoveryLinePctRE = regexp.MustCompile(`= (.+)%`) + recoveryLineFinishRE = regexp.MustCompile(`finish=(.+)min`) + recoveryLineSpeedRE = regexp.MustCompile(`speed=(.+)[A-Z]`) + componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`) ) // MDStat holds info parsed from /proc/mdstat. @@ -39,12 +42,20 @@ type MDStat struct { DisksTotal int64 // Number of failed disks. DisksFailed int64 + // Number of "down" disks. (the _ indicator in the status line) + DisksDown int64 // Spare disks in the device. DisksSpare int64 // Number of blocks the device holds. BlocksTotal int64 // Number of blocks on the device that are in sync. BlocksSynced int64 + // progress percentage of current sync + BlocksSyncedPct float64 + // estimated finishing time for current sync (in minutes) + BlocksSyncedFinishTime float64 + // current sync speed (in Kilobytes/sec) + BlocksSyncedSpeed float64 // Name of md component devices Devices []string } @@ -91,7 +102,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { // Failed disks have the suffix (F) & Spare disks have the suffix (S). fail := int64(strings.Count(line, "(F)")) spare := int64(strings.Count(line, "(S)")) - active, total, size, err := evalStatusLine(lines[i], lines[i+1]) + active, total, down, size, err := evalStatusLine(lines[i], lines[i+1]) if err != nil { return nil, fmt.Errorf("error parsing md device lines: %w", err) @@ -105,6 +116,9 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { // If device is syncing at the moment, get the number of currently // synced bytes, otherwise that number equals the size of the device. syncedBlocks := size + speed := float64(0) + finish := float64(0) + pct := float64(0) recovering := strings.Contains(lines[syncLineIdx], "recovery") resyncing := strings.Contains(lines[syncLineIdx], "resync") checking := strings.Contains(lines[syncLineIdx], "check") @@ -124,7 +138,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { strings.Contains(lines[syncLineIdx], "DELAYED") { syncedBlocks = 0 } else { - syncedBlocks, err = evalRecoveryLine(lines[syncLineIdx]) + syncedBlocks, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx]) if err != nil { return nil, fmt.Errorf("error parsing sync line in md device %q: %w", mdName, err) } @@ -132,69 +146,104 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { } mdStats = append(mdStats, MDStat{ - Name: mdName, - ActivityState: state, - DisksActive: active, - DisksFailed: fail, - DisksSpare: spare, - DisksTotal: total, - BlocksTotal: size, - BlocksSynced: syncedBlocks, - Devices: evalComponentDevices(deviceFields), + Name: mdName, + ActivityState: state, + DisksActive: active, + DisksFailed: fail, + DisksDown: down, + DisksSpare: spare, + DisksTotal: total, + BlocksTotal: size, + BlocksSynced: syncedBlocks, + BlocksSyncedPct: pct, + BlocksSyncedFinishTime: finish, + BlocksSyncedSpeed: speed, + Devices: evalComponentDevices(deviceFields), }) } return mdStats, nil } -func evalStatusLine(deviceLine, statusLine string) (active, total, size int64, err error) { +func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) { sizeStr := strings.Fields(statusLine)[0] size, err = strconv.ParseInt(sizeStr, 10, 64) if err != nil { - return 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) } if strings.Contains(deviceLine, "raid0") || strings.Contains(deviceLine, "linear") { // In the device deviceLine, only disks have a number associated with them in []. total = int64(strings.Count(deviceLine, "[")) - return total, total, size, nil + return total, total, 0, size, nil } if strings.Contains(deviceLine, "inactive") { - return 0, 0, size, nil + return 0, 0, 0, size, nil } matches := statusLineRE.FindStringSubmatch(statusLine) - if len(matches) != 4 { - return 0, 0, 0, fmt.Errorf("couldn't find all the substring matches: %s", statusLine) + if len(matches) != 5 { + return 0, 0, 0, 0, fmt.Errorf("couldn't find all the substring matches: %s", statusLine) } total, err = strconv.ParseInt(matches[2], 10, 64) if err != nil { - return 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) } active, err = strconv.ParseInt(matches[3], 10, 64) if err != nil { - return 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) + return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) } + down = int64(strings.Count(matches[4], "_")) - return active, total, size, nil + return active, total, down, size, nil } -func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, err error) { - matches := recoveryLineRE.FindStringSubmatch(recoveryLine) +func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, finish float64, speed float64, err error) { + matches := recoveryLineBlocksRE.FindStringSubmatch(recoveryLine) if len(matches) != 2 { - return 0, fmt.Errorf("unexpected recoveryLine: %s", recoveryLine) + return 0, 0, 0, 0, fmt.Errorf("unexpected recoveryLine: %s", recoveryLine) } syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64) if err != nil { - return 0, fmt.Errorf("error parsing int from recoveryLine %q: %w", recoveryLine, err) + return 0, 0, 0, 0, fmt.Errorf("error parsing int from recoveryLine %q: %w", recoveryLine, err) } - return syncedBlocks, nil + // Get percentage complete + matches = recoveryLinePctRE.FindStringSubmatch(recoveryLine) + if len(matches) != 2 { + return syncedBlocks, 0, 0, 0, fmt.Errorf("unexpected recoveryLine matching percentage: %s", recoveryLine) + } + pct, err = strconv.ParseFloat(strings.TrimSpace(matches[1]), 64) + if err != nil { + return syncedBlocks, 0, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + } + + // Get time expected left to complete + matches = recoveryLineFinishRE.FindStringSubmatch(recoveryLine) + if len(matches) != 2 { + return syncedBlocks, pct, 0, 0, fmt.Errorf("unexpected recoveryLine matching est. finish time: %s", recoveryLine) + } + finish, err = strconv.ParseFloat(matches[1], 64) + if err != nil { + return syncedBlocks, pct, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + } + + // Get recovery speed + matches = recoveryLineSpeedRE.FindStringSubmatch(recoveryLine) + if len(matches) != 2 { + return syncedBlocks, pct, finish, 0, fmt.Errorf("unexpected recoveryLine matching speed: %s", recoveryLine) + } + speed, err = strconv.ParseFloat(matches[1], 64) + if err != nil { + return syncedBlocks, pct, finish, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) + } + + return syncedBlocks, pct, finish, speed, nil } func evalComponentDevices(deviceFields []string) []string { diff --git a/vendor/github.com/prometheus/procfs/net_ip_socket.go b/vendor/github.com/prometheus/procfs/net_ip_socket.go index ac01dd84..8c9ee3de 100644 --- a/vendor/github.com/prometheus/procfs/net_ip_socket.go +++ b/vendor/github.com/prometheus/procfs/net_ip_socket.go @@ -65,6 +65,7 @@ type ( TxQueue uint64 RxQueue uint64 UID uint64 + Inode uint64 } ) @@ -150,9 +151,9 @@ func parseIP(hexIP string) (net.IP, error) { // parseNetIPSocketLine parses a single line, represented by a list of fields. func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { line := &netIPSocketLine{} - if len(fields) < 8 { + if len(fields) < 10 { return nil, fmt.Errorf( - "cannot parse net socket line as it has less then 8 columns %q", + "cannot parse net socket line as it has less then 10 columns %q", strings.Join(fields, " "), ) } @@ -216,5 +217,10 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) { return nil, fmt.Errorf("cannot parse uid value in socket line: %w", err) } + // inode + if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil { + return nil, fmt.Errorf("cannot parse inode value in socket line: %w", err) + } + return line, nil } diff --git a/vendor/github.com/prometheus/procfs/netstat.go b/vendor/github.com/prometheus/procfs/netstat.go new file mode 100644 index 00000000..94d892f1 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/netstat.go @@ -0,0 +1,68 @@ +// Copyright 2020 The Prometheus Authors +// 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 procfs + +import ( + "bufio" + "os" + "path/filepath" + "strconv" + "strings" +) + +// NetStat contains statistics for all the counters from one file +type NetStat struct { + Filename string + Stats map[string][]uint64 +} + +// NetStat retrieves stats from /proc/net/stat/ +func (fs FS) NetStat() ([]NetStat, error) { + statFiles, err := filepath.Glob(fs.proc.Path("net/stat/*")) + if err != nil { + return nil, err + } + + var netStatsTotal []NetStat + + for _, filePath := range statFiles { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + + netStatFile := NetStat{ + Filename: filepath.Base(filePath), + Stats: make(map[string][]uint64), + } + scanner := bufio.NewScanner(file) + scanner.Scan() + // First string is always a header for stats + var headers []string + headers = append(headers, strings.Fields(scanner.Text())...) + + // Other strings represent per-CPU counters + for scanner.Scan() { + for num, counter := range strings.Fields(scanner.Text()) { + value, err := strconv.ParseUint(counter, 16, 32) + if err != nil { + return nil, err + } + netStatFile.Stats[headers[num]] = append(netStatFile.Stats[headers[num]], value) + } + } + netStatsTotal = append(netStatsTotal, netStatFile) + } + return netStatsTotal, nil +} diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go index 0094a13c..be45b798 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroup.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go @@ -90,7 +90,7 @@ func parseCgroups(data []byte) ([]Cgroup, error) { // control hierarchy running on this system. On every system (v1 and v2), all hierarchies contain all processes, // so the len of the returned struct is equal to the number of active hierarchies on this system func (p Proc) Cgroups() ([]Cgroup, error) { - data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/cgroup", p.PID)) + data, err := util.ReadFileNoStat(p.path("cgroup")) if err != nil { return nil, err } diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go index 67ca0e9f..8c7b6e80 100644 --- a/vendor/github.com/prometheus/procfs/proc_stat.go +++ b/vendor/github.com/prometheus/procfs/proc_stat.go @@ -100,6 +100,15 @@ type ProcStat struct { VSize uint // Resident set size in pages. RSS int + // Soft limit in bytes on the rss of the process. + RSSLimit uint64 + // Real-time scheduling priority, a number in the range 1 to 99 for processes + // scheduled under a real-time policy, or 0, for non-real-time processes. + RTPriority uint + // Scheduling policy. + Policy uint + // Aggregated block I/O delays, measured in clock ticks (centiseconds). + DelayAcctBlkIOTicks uint64 proc fs.FS } @@ -119,7 +128,8 @@ func (p Proc) Stat() (ProcStat, error) { } var ( - ignore int + ignoreInt64 int64 + ignoreUint64 uint64 s = ProcStat{PID: p.PID, proc: p.fs} l = bytes.Index(data, []byte("(")) @@ -151,10 +161,28 @@ func (p Proc) Stat() (ProcStat, error) { &s.Priority, &s.Nice, &s.NumThreads, - &ignore, + &ignoreInt64, &s.Starttime, &s.VSize, &s.RSS, + &s.RSSLimit, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreUint64, + &ignoreInt64, + &ignoreInt64, + &s.RTPriority, + &s.Policy, + &s.DelayAcctBlkIOTicks, ) if err != nil { return ProcStat{}, err diff --git a/vendor/github.com/prometheus/procfs/zoneinfo.go b/vendor/github.com/prometheus/procfs/zoneinfo.go index 0b9bb679..209e2ac9 100644 --- a/vendor/github.com/prometheus/procfs/zoneinfo.go +++ b/vendor/github.com/prometheus/procfs/zoneinfo.go @@ -99,7 +99,6 @@ func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) { continue } if strings.HasPrefix(strings.TrimSpace(line), "per-node stats") { - zoneinfoElement.Zone = "" continue } parts := strings.Fields(strings.TrimSpace(line)) diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index bcc45d10..4e542058 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -741,6 +741,7 @@ const ( ETH_P_QINQ2 = 0x9200 ETH_P_QINQ3 = 0x9300 ETH_P_RARP = 0x8035 + ETH_P_REALTEK = 0x8899 ETH_P_SCA = 0x6007 ETH_P_SLOW = 0x8809 ETH_P_SNAP = 0x5 @@ -810,10 +811,12 @@ const ( FAN_EPIDFD = -0x2 FAN_EVENT_INFO_TYPE_DFID = 0x3 FAN_EVENT_INFO_TYPE_DFID_NAME = 0x2 + FAN_EVENT_INFO_TYPE_ERROR = 0x5 FAN_EVENT_INFO_TYPE_FID = 0x1 FAN_EVENT_INFO_TYPE_PIDFD = 0x4 FAN_EVENT_METADATA_LEN = 0x18 FAN_EVENT_ON_CHILD = 0x8000000 + FAN_FS_ERROR = 0x8000 FAN_MARK_ADD = 0x1 FAN_MARK_DONT_FOLLOW = 0x4 FAN_MARK_FILESYSTEM = 0x100 @@ -1827,6 +1830,8 @@ const ( PERF_MEM_BLK_DATA = 0x2 PERF_MEM_BLK_NA = 0x1 PERF_MEM_BLK_SHIFT = 0x28 + PERF_MEM_HOPS_0 = 0x1 + PERF_MEM_HOPS_SHIFT = 0x2b PERF_MEM_LOCK_LOCKED = 0x2 PERF_MEM_LOCK_NA = 0x1 PERF_MEM_LOCK_SHIFT = 0x18 @@ -1986,6 +1991,9 @@ const ( PR_SCHED_CORE_CREATE = 0x1 PR_SCHED_CORE_GET = 0x0 PR_SCHED_CORE_MAX = 0x4 + PR_SCHED_CORE_SCOPE_PROCESS_GROUP = 0x2 + PR_SCHED_CORE_SCOPE_THREAD = 0x0 + PR_SCHED_CORE_SCOPE_THREAD_GROUP = 0x1 PR_SCHED_CORE_SHARE_FROM = 0x3 PR_SCHED_CORE_SHARE_TO = 0x2 PR_SET_CHILD_SUBREAPER = 0x24 @@ -2167,12 +2175,23 @@ const ( RTCF_NAT = 0x800000 RTCF_VALVE = 0x200000 RTC_AF = 0x20 + RTC_BSM_DIRECT = 0x1 + RTC_BSM_DISABLED = 0x0 + RTC_BSM_LEVEL = 0x2 + RTC_BSM_STANDBY = 0x3 RTC_FEATURE_ALARM = 0x0 + RTC_FEATURE_ALARM_RES_2S = 0x3 RTC_FEATURE_ALARM_RES_MINUTE = 0x1 - RTC_FEATURE_CNT = 0x3 + RTC_FEATURE_BACKUP_SWITCH_MODE = 0x6 + RTC_FEATURE_CNT = 0x7 + RTC_FEATURE_CORRECTION = 0x5 RTC_FEATURE_NEED_WEEK_DAY = 0x2 + RTC_FEATURE_UPDATE_INTERRUPT = 0x4 RTC_IRQF = 0x80 RTC_MAX_FREQ = 0x2000 + RTC_PARAM_BACKUP_SWITCH_MODE = 0x2 + RTC_PARAM_CORRECTION = 0x1 + RTC_PARAM_FEATURES = 0x0 RTC_PF = 0x40 RTC_UF = 0x10 RTF_ADDRCLASSMASK = 0xf8000000 @@ -2532,6 +2551,8 @@ const ( SO_VM_SOCKETS_BUFFER_MIN_SIZE = 0x1 SO_VM_SOCKETS_BUFFER_SIZE = 0x0 SO_VM_SOCKETS_CONNECT_TIMEOUT = 0x6 + SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW = 0x8 + SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD = 0x6 SO_VM_SOCKETS_NONBLOCK_TXRX = 0x7 SO_VM_SOCKETS_PEER_HOST_VM_ID = 0x3 SO_VM_SOCKETS_TRUSTED = 0x5 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 3ca40ca7..234fd4a5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -250,6 +250,8 @@ const ( RTC_EPOCH_SET = 0x4004700e RTC_IRQP_READ = 0x8004700b RTC_IRQP_SET = 0x4004700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x801c7011 @@ -327,6 +329,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index ead33209..58619b75 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -251,6 +251,8 @@ const ( RTC_EPOCH_SET = 0x4008700e RTC_IRQP_READ = 0x8008700b RTC_IRQP_SET = 0x4008700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x80207011 @@ -328,6 +330,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 39bdc945..3a64ff59 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -257,6 +257,8 @@ const ( RTC_EPOCH_SET = 0x4004700e RTC_IRQP_READ = 0x8004700b RTC_IRQP_SET = 0x4004700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x801c7011 @@ -334,6 +336,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 9aec987d..abe0b925 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -247,6 +247,8 @@ const ( RTC_EPOCH_SET = 0x4008700e RTC_IRQP_READ = 0x8008700b RTC_IRQP_SET = 0x4008700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x80207011 @@ -324,6 +326,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index a8bba949..14d7a843 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -250,6 +250,8 @@ const ( RTC_EPOCH_SET = 0x8004700e RTC_IRQP_READ = 0x4004700b RTC_IRQP_SET = 0x8004700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x401c7011 @@ -327,6 +329,7 @@ const ( SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index ee9e7e20..99e7c4ac 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -250,6 +250,8 @@ const ( RTC_EPOCH_SET = 0x8008700e RTC_IRQP_READ = 0x4008700b RTC_IRQP_SET = 0x8008700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x40207011 @@ -327,6 +329,7 @@ const ( SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index ba4b288a..496364c3 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -250,6 +250,8 @@ const ( RTC_EPOCH_SET = 0x8008700e RTC_IRQP_READ = 0x4008700b RTC_IRQP_SET = 0x8008700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x40207011 @@ -327,6 +329,7 @@ const ( SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index bc93afc3..3e408308 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -250,6 +250,8 @@ const ( RTC_EPOCH_SET = 0x8004700e RTC_IRQP_READ = 0x4004700b RTC_IRQP_SET = 0x8004700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x401c7011 @@ -327,6 +329,7 @@ const ( SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 9295e694..1151a7df 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -305,6 +305,8 @@ const ( RTC_EPOCH_SET = 0x8004700e RTC_IRQP_READ = 0x4004700b RTC_IRQP_SET = 0x8004700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x401c7011 @@ -382,6 +384,7 @@ const ( SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index 1fa081c9..ed17f249 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -309,6 +309,8 @@ const ( RTC_EPOCH_SET = 0x8008700e RTC_IRQP_READ = 0x4008700b RTC_IRQP_SET = 0x8008700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x40207011 @@ -386,6 +388,7 @@ const ( SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 74b32114..d84a37c1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -309,6 +309,8 @@ const ( RTC_EPOCH_SET = 0x8008700e RTC_IRQP_READ = 0x4008700b RTC_IRQP_SET = 0x8008700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x40207011 @@ -386,6 +388,7 @@ const ( SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index c91c8ac5..5cafba83 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -238,6 +238,8 @@ const ( RTC_EPOCH_SET = 0x4008700e RTC_IRQP_READ = 0x8008700b RTC_IRQP_SET = 0x4008700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x80207011 @@ -315,6 +317,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index b66bf222..6d122da4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -313,6 +313,8 @@ const ( RTC_EPOCH_SET = 0x4008700e RTC_IRQP_READ = 0x8008700b RTC_IRQP_SET = 0x4008700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 RTC_PIE_OFF = 0x7006 RTC_PIE_ON = 0x7005 RTC_PLL_GET = 0x80207011 @@ -390,6 +392,7 @@ const ( SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 SO_REUSEADDR = 0x2 SO_REUSEPORT = 0xf SO_RXQ_OVFL = 0x28 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index f7fb149b..6bd19e51 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -304,6 +304,8 @@ const ( RTC_EPOCH_SET = 0x8008700e RTC_IRQP_READ = 0x4008700b RTC_IRQP_SET = 0x8008700c + RTC_PARAM_GET = 0x80187013 + RTC_PARAM_SET = 0x80187014 RTC_PIE_OFF = 0x20007006 RTC_PIE_ON = 0x20007005 RTC_PLL_GET = 0x40207011 @@ -381,6 +383,7 @@ const ( SO_RCVTIMEO = 0x2000 SO_RCVTIMEO_NEW = 0x44 SO_RCVTIMEO_OLD = 0x2000 + SO_RESERVE_MEM = 0x52 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_RXQ_OVFL = 0x24 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 31847d23..cac1f758 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -445,4 +445,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 3503cbbd..f327e4a0 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -367,4 +367,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index 5ecd24bf..fb06a08d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -409,4 +409,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 7e5c94cc..58285646 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -312,4 +312,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index e1e2a2bf..3b0418e6 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -429,4 +429,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 4445 SYS_LANDLOCK_RESTRICT_SELF = 4446 SYS_PROCESS_MRELEASE = 4448 + SYS_FUTEX_WAITV = 4449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 7651915a..314ebf16 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -359,4 +359,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 5445 SYS_LANDLOCK_RESTRICT_SELF = 5446 SYS_PROCESS_MRELEASE = 5448 + SYS_FUTEX_WAITV = 5449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index a26a2c05..b8fbb937 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -359,4 +359,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 5445 SYS_LANDLOCK_RESTRICT_SELF = 5446 SYS_PROCESS_MRELEASE = 5448 + SYS_FUTEX_WAITV = 5449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index fda9a6a9..ee309b2b 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -429,4 +429,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 4445 SYS_LANDLOCK_RESTRICT_SELF = 4446 SYS_PROCESS_MRELEASE = 4448 + SYS_FUTEX_WAITV = 4449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index e8496150..ac374810 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -436,4 +436,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 5ee0678a..5aa47211 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -408,4 +408,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index 29c0f9a3..0793ac1a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -408,4 +408,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 5c9a9a3b..a520962e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -310,4 +310,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 913f50f9..d1738586 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -373,4 +373,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 0de03a72..dfd5660f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -387,4 +387,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 ) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index f6f0d79c..66788f15 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -1144,7 +1144,8 @@ const ( PERF_RECORD_BPF_EVENT = 0x12 PERF_RECORD_CGROUP = 0x13 PERF_RECORD_TEXT_POKE = 0x14 - PERF_RECORD_MAX = 0x15 + PERF_RECORD_AUX_OUTPUT_HW_ID = 0x15 + PERF_RECORD_MAX = 0x16 PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0x0 PERF_RECORD_KSYMBOL_TYPE_BPF = 0x1 PERF_RECORD_KSYMBOL_TYPE_OOL = 0x2 @@ -1784,7 +1785,8 @@ const ( const ( NF_NETDEV_INGRESS = 0x0 - NF_NETDEV_NUMHOOKS = 0x1 + NF_NETDEV_EGRESS = 0x1 + NF_NETDEV_NUMHOOKS = 0x2 ) const ( @@ -3166,7 +3168,13 @@ const ( DEVLINK_ATTR_RELOAD_ACTION_INFO = 0xa2 DEVLINK_ATTR_RELOAD_ACTION_STATS = 0xa3 DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 0xa4 - DEVLINK_ATTR_MAX = 0xa9 + DEVLINK_ATTR_RATE_TYPE = 0xa5 + DEVLINK_ATTR_RATE_TX_SHARE = 0xa6 + DEVLINK_ATTR_RATE_TX_MAX = 0xa7 + DEVLINK_ATTR_RATE_NODE_NAME = 0xa8 + DEVLINK_ATTR_RATE_PARENT_NODE_NAME = 0xa9 + DEVLINK_ATTR_REGION_MAX_SNAPSHOTS = 0xaa + DEVLINK_ATTR_MAX = 0xaa DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0x0 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 0x1 DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0x0 @@ -3463,7 +3471,14 @@ const ( ETHTOOL_MSG_CABLE_TEST_ACT = 0x1a ETHTOOL_MSG_CABLE_TEST_TDR_ACT = 0x1b ETHTOOL_MSG_TUNNEL_INFO_GET = 0x1c - ETHTOOL_MSG_USER_MAX = 0x21 + ETHTOOL_MSG_FEC_GET = 0x1d + ETHTOOL_MSG_FEC_SET = 0x1e + ETHTOOL_MSG_MODULE_EEPROM_GET = 0x1f + ETHTOOL_MSG_STATS_GET = 0x20 + ETHTOOL_MSG_PHC_VCLOCKS_GET = 0x21 + ETHTOOL_MSG_MODULE_GET = 0x22 + ETHTOOL_MSG_MODULE_SET = 0x23 + ETHTOOL_MSG_USER_MAX = 0x23 ETHTOOL_MSG_KERNEL_NONE = 0x0 ETHTOOL_MSG_STRSET_GET_REPLY = 0x1 ETHTOOL_MSG_LINKINFO_GET_REPLY = 0x2 @@ -3494,7 +3509,14 @@ const ( ETHTOOL_MSG_CABLE_TEST_NTF = 0x1b ETHTOOL_MSG_CABLE_TEST_TDR_NTF = 0x1c ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY = 0x1d - ETHTOOL_MSG_KERNEL_MAX = 0x22 + ETHTOOL_MSG_FEC_GET_REPLY = 0x1e + ETHTOOL_MSG_FEC_NTF = 0x1f + ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY = 0x20 + ETHTOOL_MSG_STATS_GET_REPLY = 0x21 + ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY = 0x22 + ETHTOOL_MSG_MODULE_GET_REPLY = 0x23 + ETHTOOL_MSG_MODULE_NTF = 0x24 + ETHTOOL_MSG_KERNEL_MAX = 0x24 ETHTOOL_A_HEADER_UNSPEC = 0x0 ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 200b62a0..cf44e693 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -363,6 +363,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error) //sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) //sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) +//sys GetActiveProcessorCount(groupNumber uint16) (ret uint32) +//sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32) // Volume Management Functions //sys DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index bb31abda..e19471c6 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -3172,3 +3172,5 @@ type ModuleInfo struct { SizeOfImage uint32 EntryPoint uintptr } + +const ALL_PROCESSOR_GROUPS = 0xFFFF diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 1055d47e..9ea1a44f 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -226,6 +226,7 @@ var ( procFreeLibrary = modkernel32.NewProc("FreeLibrary") procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent") procGetACP = modkernel32.NewProc("GetACP") + procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts") procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") @@ -251,6 +252,7 @@ var ( procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives") procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW") + procGetMaximumProcessorCount = modkernel32.NewProc("GetMaximumProcessorCount") procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") procGetModuleHandleExW = modkernel32.NewProc("GetModuleHandleExW") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") @@ -1967,6 +1969,12 @@ func GetACP() (acp uint32) { return } +func GetActiveProcessorCount(groupNumber uint16) (ret uint32) { + r0, _, _ := syscall.Syscall(procGetActiveProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + ret = uint32(r0) + return +} + func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { @@ -2169,6 +2177,12 @@ func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err er return } +func GetMaximumProcessorCount(groupNumber uint16) (ret uint32) { + r0, _, _ := syscall.Syscall(procGetMaximumProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + ret = uint32(r0) + return +} + func GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) { r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size)) n = uint32(r0) diff --git a/vendor/modules.txt b/vendor/modules.txt index 3b296241..6abd2cd0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,7 +10,7 @@ github.com/beorn7/perks/quantile # github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 ## explicit; go 1.12 github.com/certifi/gocertifi -# github.com/cespare/xxhash/v2 v2.1.1 +# github.com/cespare/xxhash/v2 v2.1.2 ## explicit; go 1.11 github.com/cespare/xxhash/v2 # github.com/cheekybits/genny v1.0.0 @@ -291,7 +291,7 @@ github.com/pmezard/go-difflib/difflib ## explicit github.com/pquerna/cachecontrol github.com/pquerna/cachecontrol/cacheobject -# github.com/prometheus/client_golang v1.11.0 +# github.com/prometheus/client_golang v1.12.1 ## explicit; go 1.13 github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal @@ -305,7 +305,7 @@ github.com/prometheus/client_model/go github.com/prometheus/common/expfmt github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/model -# github.com/prometheus/procfs v0.6.0 +# github.com/prometheus/procfs v0.7.3 ## explicit; go 1.13 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs @@ -389,7 +389,7 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ## explicit golang.org/x/sync/errgroup -# golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e +# golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/execabs From 2f05f969e2e567ca30bcd10daaf42c4325494250 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 7 Apr 2022 10:51:52 +0100 Subject: [PATCH 044/238] TUN-5995: Force prometheus v1.12.1 usage --- go.mod | 3 +++ vendor/modules.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/go.mod b/go.mod index 96ce0408..1e571d95 100644 --- a/go.mod +++ b/go.mod @@ -98,3 +98,6 @@ require ( replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 + +// Avoid 'CVE-2022-21698' +replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 diff --git a/vendor/modules.txt b/vendor/modules.txt index 6abd2cd0..b6fe6b65 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -566,3 +566,4 @@ zombiezen.com/go/capnproto2/server zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d # github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 +# github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 From d433a0fa547862906460398a48dc3ecca51db9f6 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 7 Apr 2022 13:28:51 +0100 Subject: [PATCH 045/238] TUN-4130: cloudflared docker images now have a latest tag --- .docker-images | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.docker-images b/.docker-images index a93b0396..28bf15aa 100644 --- a/.docker-images +++ b/.docker-images @@ -2,6 +2,8 @@ images: - name: cloudflared dockerfile: Dockerfile context: . + versions: + - latest registries: - name: docker.io/cloudflare user: env:DOCKER_USER From b07b8b4d4b2e2606011b114317472745393fa7fe Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 7 Apr 2022 14:27:31 +0100 Subject: [PATCH 046/238] Release 2022.4.0 --- RELEASE_NOTES | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index c8fa7800..a6a3d58b 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,17 @@ +2022.4.0 +- 2022-04-01 TUN-5973: Add backoff for non-recoverable errors as well +- 2022-04-05 TUN-5992: Use QUIC protocol for remotely managed tunnels when protocol is unspecified +- 2022-04-06 Update Makefile +- 2022-04-06 TUN-5995: Update prometheus to 1.12.1 to avoid vulnerabilities +- 2022-04-07 TUN-5995: Force prometheus v1.12.1 usage +- 2022-04-07 TUN-4130: cloudflared docker images now have a latest tag +- 2022-03-30 TUN-5842: Fix flaky TestConcurrentUpdateAndRead by making sure resources are released +- 2022-03-30 carrier: fix dropped errors +- 2022-03-25 TUN-5959: tidy go.mod +- 2022-03-25 TUN-5958: Fix release to homebrew core +- 2022-03-28 TUN-5960: Do not log the tunnel token or json credentials +- 2022-03-28 TUN-5956: Add timeout to session manager APIs + 2022.3.4 - 2022-03-22 TUN-5918: Clean up text in cloudflared tunnel --help - 2022-03-22 TUN-5895 run brew bump-formula-pr on release From 0dc3428424424121c7fffd636ae054d2ca8b20f8 Mon Sep 17 00:00:00 2001 From: Piper McCorkle Date: Thu, 7 Apr 2022 16:52:21 -0500 Subject: [PATCH 047/238] TUN-6000 add version argument to bump-formula-pr --- .teamcity/update-homebrew-core.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh index 4a40a47d..d8e976ff 100755 --- a/.teamcity/update-homebrew-core.sh +++ b/.teamcity/update-homebrew-core.sh @@ -20,4 +20,4 @@ brew update --force --quiet chmod -R go-w "$(brew --prefix)/share/zsh" # bump formula pr -brew bump-formula-pr cloudflared +brew bump-formula-pr cloudflared --version="$VERSION" From d1a4710aa29ecb9bcc116fb84e8cade73bcb639b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 11 Apr 2022 11:44:42 +0100 Subject: [PATCH 048/238] TUN-6035: Reduce buffer size when proxying data --- cfio/copy.go | 27 +++++++++++++++++++++++++++ websocket/websocket.go | 4 +++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 cfio/copy.go diff --git a/cfio/copy.go b/cfio/copy.go new file mode 100644 index 00000000..6d837d5f --- /dev/null +++ b/cfio/copy.go @@ -0,0 +1,27 @@ +package cfio + +import ( + "io" + "sync" +) + +const defaultBufferSize = 16 * 1024 + +var bufferPool = sync.Pool{ + New: func() interface{} { + return make([]byte, defaultBufferSize) + }, +} + +func Copy(dst io.Writer, src io.Reader) (written int64, err error) { + _, okWriteTo := src.(io.WriterTo) + _, okReadFrom := dst.(io.ReaderFrom) + var buffer []byte = nil + + if !(okWriteTo || okReadFrom) { + buffer = bufferPool.Get().([]byte) + defer bufferPool.Put(buffer) + } + + return io.CopyBuffer(dst, src, buffer) +} diff --git a/websocket/websocket.go b/websocket/websocket.go index ee0bd225..0c879681 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -15,6 +15,8 @@ import ( "github.com/getsentry/raven-go" "github.com/gorilla/websocket" "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/cfio" ) // IsWebSocketUpgrade checks to see if the request is a WebSocket connection. @@ -146,7 +148,7 @@ func copyData(dst io.Writer, src io.Reader, dir string) (written int64, err erro } return copyBuffer(dst, src, dir) } else { - return io.Copy(dst, src) + return cfio.Copy(dst, src) } } From 9cde11f8e096ed92fb874ecbba69c1c79b48b707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 11 Apr 2022 17:58:18 +0100 Subject: [PATCH 049/238] TUN-6038: Reduce buffer size used for proxying data The buffer size was big to support a compression feature that we don't use anymore. As such, we can now reduce this and be more efficient with memory usage. --- proxy/pool.go | 29 ----------------------------- proxy/proxy.go | 9 ++------- 2 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 proxy/pool.go diff --git a/proxy/pool.go b/proxy/pool.go deleted file mode 100644 index fe2cf4a5..00000000 --- a/proxy/pool.go +++ /dev/null @@ -1,29 +0,0 @@ -package proxy - -import ( - "sync" -) - -type bufferPool struct { - // A bufferPool must not be copied after first use. - // https://golang.org/pkg/sync/#Pool - buffers sync.Pool -} - -func newBufferPool(bufferSize int) *bufferPool { - return &bufferPool{ - buffers: sync.Pool{ - New: func() interface{} { - return make([]byte, bufferSize) - }, - }, - } -} - -func (p *bufferPool) Get() []byte { - return p.buffers.Get().([]byte) -} - -func (p *bufferPool) Put(buf []byte) { - p.buffers.Put(buf) -} diff --git a/proxy/proxy.go b/proxy/proxy.go index 5ec8743b..3ab3c370 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -12,6 +12,7 @@ import ( "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/carrier" + "github.com/cloudflare/cloudflared/cfio" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/ingress" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" @@ -32,7 +33,6 @@ type Proxy struct { warpRouting *ingress.WarpRoutingService tags []tunnelpogs.Tag log *zerolog.Logger - bufferPool *bufferPool } // NewOriginProxy returns a new instance of the Proxy struct. @@ -46,7 +46,6 @@ func NewOriginProxy( ingressRules: ingressRules, tags: tags, log: log, - bufferPool: newBufferPool(512 * 1024), } if warpRoutingEnabled { proxy.warpRouting = ingress.NewWarpRoutingService() @@ -218,11 +217,7 @@ func (p *Proxy) proxyHTTPRequest( p.log.Debug().Msg("Detected Server-Side Events from Origin") p.writeEventStream(w, resp.Body) } else { - // Use CopyBuffer, because Copy only allocates a 32KiB buffer, and cross-stream - // compression generates dictionary on first write - buf := p.bufferPool.Get() - defer p.bufferPool.Put(buf) - _, _ = io.CopyBuffer(w, resp.Body, buf) + _, _ = cfio.Copy(w, resp.Body) } p.logOriginResponse(resp, fields) From def8f57dbc62172b3ba3974faee463f65c7ecd89 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 6 Apr 2022 16:20:29 -0700 Subject: [PATCH 050/238] TUN-5989: Add in-memory otlp exporter --- connection/connection.go | 3 +- connection/connection_test.go | 4 +- connection/h2mux.go | 3 +- connection/http2.go | 5 +- connection/quic.go | 12 +- connection/quic_test.go | 6 +- go.mod | 15 +- go.sum | 41 +- orchestration/orchestrator_test.go | 5 +- proxy/proxy.go | 9 +- proxy/proxy_test.go | 13 +- tracing/client.go | 90 + tracing/client_test.go | 161 ++ tracing/tracing.go | 116 + tracing/tracing_test.go | 50 + vendor/github.com/go-logr/logr/.golangci.yaml | 29 + vendor/github.com/go-logr/logr/CHANGELOG.md | 6 + .../github.com/go-logr/logr/CONTRIBUTING.md | 17 + vendor/github.com/go-logr/logr/LICENSE | 201 ++ vendor/github.com/go-logr/logr/README.md | 282 +++ vendor/github.com/go-logr/logr/discard.go | 54 + vendor/github.com/go-logr/logr/funcr/funcr.go | 787 +++++++ vendor/github.com/go-logr/logr/logr.go | 510 +++++ vendor/github.com/go-logr/stdr/LICENSE | 201 ++ vendor/github.com/go-logr/stdr/README.md | 6 + vendor/github.com/go-logr/stdr/stdr.go | 170 ++ .../grpc-gateway/v2/LICENSE.txt | 27 + .../v2/internal/httprule/BUILD.bazel | 35 + .../v2/internal/httprule/compile.go | 121 ++ .../grpc-gateway/v2/internal/httprule/fuzz.go | 11 + .../v2/internal/httprule/parse.go | 368 ++++ .../v2/internal/httprule/types.go | 60 + .../grpc-gateway/v2/runtime/BUILD.bazel | 91 + .../grpc-gateway/v2/runtime/context.go | 345 +++ .../grpc-gateway/v2/runtime/convert.go | 322 +++ .../grpc-gateway/v2/runtime/doc.go | 5 + .../grpc-gateway/v2/runtime/errors.go | 180 ++ .../grpc-gateway/v2/runtime/fieldmask.go | 165 ++ .../grpc-gateway/v2/runtime/handler.go | 223 ++ .../v2/runtime/marshal_httpbodyproto.go | 32 + .../grpc-gateway/v2/runtime/marshal_json.go | 45 + .../grpc-gateway/v2/runtime/marshal_jsonpb.go | 344 +++ .../grpc-gateway/v2/runtime/marshal_proto.go | 63 + .../grpc-gateway/v2/runtime/marshaler.go | 50 + .../v2/runtime/marshaler_registry.go | 109 + .../grpc-gateway/v2/runtime/mux.go | 356 ++++ .../grpc-gateway/v2/runtime/pattern.go | 383 ++++ .../grpc-gateway/v2/runtime/proto2_convert.go | 80 + .../grpc-gateway/v2/runtime/query.go | 329 +++ .../grpc-gateway/v2/utilities/BUILD.bazel | 27 + .../grpc-gateway/v2/utilities/doc.go | 2 + .../grpc-gateway/v2/utilities/pattern.go | 22 + .../v2/utilities/readerfactory.go | 20 + .../grpc-gateway/v2/utilities/trie.go | 174 ++ .../testify/assert/assertion_compare.go | 54 +- .../assert/assertion_compare_can_convert.go | 16 + .../assert/assertion_compare_legacy.go | 16 + .../testify/assert/assertion_format.go | 12 + .../testify/assert/assertion_forward.go | 24 + .../testify/assert/assertion_order.go | 8 +- .../stretchr/testify/assert/assertions.go | 112 +- .../stretchr/testify/require/require.go | 30 + .../testify/require/require_forward.go | 24 + .../contrib/propagators/Jaeger/context.go | 41 + .../contrib/propagators/Jaeger/doc.go | 17 + .../propagators/Jaeger/jaeger_propagator.go | 161 ++ .../contrib/propagators/LICENSE | 201 ++ .../go.opentelemetry.io/otel/.gitattributes | 3 + vendor/go.opentelemetry.io/otel/.gitignore | 20 + vendor/go.opentelemetry.io/otel/.gitmodules | 3 + vendor/go.opentelemetry.io/otel/.golangci.yml | 47 + .../otel/.markdown-link.json | 20 + .../otel/.markdownlint.yaml | 29 + vendor/go.opentelemetry.io/otel/CHANGELOG.md | 1860 +++++++++++++++++ vendor/go.opentelemetry.io/otel/CODEOWNERS | 17 + .../go.opentelemetry.io/otel/CONTRIBUTING.md | 522 +++++ vendor/go.opentelemetry.io/otel/LICENSE | 201 ++ vendor/go.opentelemetry.io/otel/Makefile | 209 ++ vendor/go.opentelemetry.io/otel/README.md | 108 + vendor/go.opentelemetry.io/otel/RELEASING.md | 132 ++ vendor/go.opentelemetry.io/otel/VERSIONING.md | 224 ++ .../go.opentelemetry.io/otel/attribute/doc.go | 16 + .../otel/attribute/encoder.go | 150 ++ .../otel/attribute/iterator.go | 143 ++ .../go.opentelemetry.io/otel/attribute/key.go | 134 ++ .../go.opentelemetry.io/otel/attribute/kv.go | 86 + .../go.opentelemetry.io/otel/attribute/set.go | 435 ++++ .../otel/attribute/type_string.go | 31 + .../otel/attribute/value.go | 271 +++ .../otel/baggage/baggage.go | 556 +++++ .../otel/baggage/context.go | 39 + .../go.opentelemetry.io/otel/baggage/doc.go | 20 + .../go.opentelemetry.io/otel/codes/codes.go | 106 + vendor/go.opentelemetry.io/otel/codes/doc.go | 21 + vendor/go.opentelemetry.io/otel/doc.go | 34 + .../go.opentelemetry.io/otel/error_handler.go | 38 + .../otel/exporters/otlp/otlptrace/LICENSE | 201 ++ .../otel/exporters/otlp/otlptrace/README.md | 51 + .../otel/exporters/otlp/otlptrace/clients.go | 54 + .../otel/exporters/otlp/otlptrace/exporter.go | 113 + .../internal/tracetransform/attribute.go | 158 ++ .../tracetransform/instrumentation.go | 30 + .../internal/tracetransform/resource.go | 28 + .../otlptrace/internal/tracetransform/span.go | 205 ++ .../go.opentelemetry.io/otel/get_main_pkgs.sh | 41 + vendor/go.opentelemetry.io/otel/handler.go | 98 + .../otel/internal/baggage/baggage.go | 43 + .../otel/internal/baggage/context.go | 95 + .../otel/internal/global/internal_logging.go | 63 + .../otel/internal/global/propagator.go | 82 + .../otel/internal/global/state.go | 127 ++ .../otel/internal/global/trace.go | 192 ++ .../otel/internal/rawhelpers.go | 55 + .../otel/internal_logging.go | 26 + .../go.opentelemetry.io/otel/propagation.go | 31 + .../otel/propagation/baggage.go | 58 + .../otel/propagation/doc.go | 24 + .../otel/propagation/propagation.go | 153 ++ .../otel/propagation/trace_context.go | 159 ++ vendor/go.opentelemetry.io/otel/sdk/LICENSE | 201 ++ .../otel/sdk/instrumentation/library.go | 33 + .../otel/sdk/internal/env/env.go | 186 ++ .../otel/sdk/internal/internal.go | 37 + .../otel/sdk/resource/auto.go | 72 + .../otel/sdk/resource/builtin.go | 108 + .../otel/sdk/resource/config.go | 199 ++ .../otel/sdk/resource/container.go | 100 + .../otel/sdk/resource/doc.go | 28 + .../otel/sdk/resource/env.go | 99 + .../otel/sdk/resource/os.go | 97 + .../otel/sdk/resource/os_release_darwin.go | 102 + .../otel/sdk/resource/os_release_unix.go | 154 ++ .../otel/sdk/resource/os_unix.go | 100 + .../otel/sdk/resource/os_unsupported.go | 34 + .../otel/sdk/resource/os_windows.go | 101 + .../otel/sdk/resource/process.go | 180 ++ .../otel/sdk/resource/resource.go | 280 +++ .../otel/sdk/trace/batch_span_processor.go | 396 ++++ .../go.opentelemetry.io/otel/sdk/trace/doc.go | 21 + .../otel/sdk/trace/event.go | 37 + .../otel/sdk/trace/evictedqueue.go | 44 + .../otel/sdk/trace/id_generator.go | 77 + .../otel/sdk/trace/link.go | 34 + .../otel/sdk/trace/provider.go | 450 ++++ .../otel/sdk/trace/sampler_env.go | 107 + .../otel/sdk/trace/sampling.go | 292 +++ .../otel/sdk/trace/simple_span_processor.go | 128 ++ .../otel/sdk/trace/snapshot.go | 138 ++ .../otel/sdk/trace/span.go | 797 +++++++ .../otel/sdk/trace/span_exporter.go | 47 + .../otel/sdk/trace/span_limits.go | 126 ++ .../otel/sdk/trace/span_processor.go | 67 + .../otel/sdk/trace/tracer.go | 156 ++ .../otel/semconv/v1.7.0/doc.go | 20 + .../otel/semconv/v1.7.0/exception.go | 20 + .../otel/semconv/v1.7.0/http.go | 314 +++ .../otel/semconv/v1.7.0/resource.go | 946 +++++++++ .../otel/semconv/v1.7.0/schema.go | 20 + .../otel/semconv/v1.7.0/trace.go | 1558 ++++++++++++++ vendor/go.opentelemetry.io/otel/trace.go | 44 + vendor/go.opentelemetry.io/otel/trace/LICENSE | 201 ++ .../go.opentelemetry.io/otel/trace/config.go | 316 +++ .../go.opentelemetry.io/otel/trace/context.go | 61 + vendor/go.opentelemetry.io/otel/trace/doc.go | 66 + .../otel/trace/nonrecording.go | 27 + vendor/go.opentelemetry.io/otel/trace/noop.go | 89 + .../go.opentelemetry.io/otel/trace/trace.go | 519 +++++ .../otel/trace/tracestate.go | 211 ++ .../otel/verify_examples.sh | 85 + vendor/go.opentelemetry.io/otel/version.go | 20 + vendor/go.opentelemetry.io/otel/versions.yaml | 58 + vendor/go.opentelemetry.io/proto/otlp/LICENSE | 201 ++ .../collector/trace/v1/trace_config.pb.go | 568 +++++ .../collector/trace/v1/trace_service.pb.go | 247 +++ .../collector/trace/v1/trace_service.pb.gw.go | 167 ++ .../trace/v1/trace_service_grpc.pb.go | 109 + .../proto/otlp/common/v1/common.pb.go | 681 ++++++ .../proto/otlp/resource/v1/resource.pb.go | 191 ++ .../proto/otlp/trace/v1/trace.pb.go | 1295 ++++++++++++ .../googleapis/api/httpbody/httpbody.pb.go | 236 +++ .../protobuf/field_mask/field_mask.go | 23 + vendor/google.golang.org/grpc/CONTRIBUTING.md | 7 +- .../grpc/attributes/attributes.go | 4 +- vendor/google.golang.org/grpc/clientconn.go | 2 +- .../grpc/credentials/insecure/insecure.go | 31 +- vendor/google.golang.org/grpc/dialoptions.go | 4 +- .../grpc/grpclog/loggerv2.go | 8 +- vendor/google.golang.org/grpc/interceptor.go | 9 +- .../grpc/internal/channelz/funcs.go | 69 +- .../grpc/internal/envconfig/xds.go | 19 +- .../grpc/internal/grpclog/grpclog.go | 8 +- .../grpc/internal/grpcutil/regex.go | 11 +- .../grpc/internal/internal.go | 7 +- .../grpc/internal/transport/transport.go | 6 + vendor/google.golang.org/grpc/regenerate.sh | 30 +- vendor/google.golang.org/grpc/server.go | 11 +- .../google.golang.org/grpc/service_config.go | 2 +- vendor/google.golang.org/grpc/stream.go | 10 +- vendor/google.golang.org/grpc/version.go | 2 +- vendor/google.golang.org/grpc/vet.sh | 2 +- .../protobuf/encoding/protojson/decode.go | 665 ++++++ .../protobuf/encoding/protojson/doc.go | 11 + .../protobuf/encoding/protojson/encode.go | 344 +++ .../encoding/protojson/well_known_types.go | 889 ++++++++ .../protobuf/encoding/protowire/wire.go | 19 +- .../protobuf/internal/encoding/json/decode.go | 340 +++ .../internal/encoding/json/decode_number.go | 254 +++ .../internal/encoding/json/decode_string.go | 91 + .../internal/encoding/json/decode_token.go | 192 ++ .../protobuf/internal/encoding/json/encode.go | 276 +++ .../protobuf/internal/encoding/text/decode.go | 2 +- .../protobuf/internal/errors/is_go112.go | 1 + .../protobuf/internal/errors/is_go113.go | 1 + .../internal/flags/proto_legacy_disable.go | 1 + .../internal/flags/proto_legacy_enable.go | 1 + .../protobuf/internal/impl/codec_map_go111.go | 1 + .../protobuf/internal/impl/codec_map_go112.go | 1 + .../protobuf/internal/impl/codec_reflect.go | 1 + .../protobuf/internal/impl/codec_unsafe.go | 1 + .../protobuf/internal/impl/decode.go | 8 + .../protobuf/internal/impl/pointer_reflect.go | 1 + .../protobuf/internal/impl/pointer_unsafe.go | 1 + .../protobuf/internal/strs/strings_pure.go | 1 + .../protobuf/internal/strs/strings_unsafe.go | 1 + .../protobuf/internal/version/version.go | 4 +- .../protobuf/proto/decode.go | 17 +- .../protobuf/proto/proto_methods.go | 1 + .../protobuf/proto/proto_reflect.go | 1 + .../protobuf/reflect/protoreflect/methods.go | 1 + .../reflect/protoreflect/value_pure.go | 1 + .../reflect/protoreflect/value_union.go | 25 + .../reflect/protoreflect/value_unsafe.go | 1 + .../protobuf/runtime/protoiface/methods.go | 1 + .../types/known/fieldmaskpb/field_mask.pb.go | 591 ++++++ .../types/known/wrapperspb/wrappers.pb.go | 760 +++++++ vendor/modules.txt | 60 +- 236 files changed, 33015 insertions(+), 177 deletions(-) create mode 100644 tracing/client.go create mode 100644 tracing/client_test.go create mode 100644 tracing/tracing.go create mode 100644 tracing/tracing_test.go create mode 100644 vendor/github.com/go-logr/logr/.golangci.yaml create mode 100644 vendor/github.com/go-logr/logr/CHANGELOG.md create mode 100644 vendor/github.com/go-logr/logr/CONTRIBUTING.md create mode 100644 vendor/github.com/go-logr/logr/LICENSE create mode 100644 vendor/github.com/go-logr/logr/README.md create mode 100644 vendor/github.com/go-logr/logr/discard.go create mode 100644 vendor/github.com/go-logr/logr/funcr/funcr.go create mode 100644 vendor/github.com/go-logr/logr/logr.go create mode 100644 vendor/github.com/go-logr/stdr/LICENSE create mode 100644 vendor/github.com/go-logr/stdr/README.md create mode 100644 vendor/github.com/go-logr/stdr/stdr.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/BUILD.bazel create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/compile.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/fuzz.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/parse.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/types.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/BUILD.bazel create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/doc.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/pattern.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/readerfactory.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/trie.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go create mode 100644 vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go create mode 100644 vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go create mode 100644 vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go create mode 100644 vendor/go.opentelemetry.io/contrib/propagators/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/.gitattributes create mode 100644 vendor/go.opentelemetry.io/otel/.gitignore create mode 100644 vendor/go.opentelemetry.io/otel/.gitmodules create mode 100644 vendor/go.opentelemetry.io/otel/.golangci.yml create mode 100644 vendor/go.opentelemetry.io/otel/.markdown-link.json create mode 100644 vendor/go.opentelemetry.io/otel/.markdownlint.yaml create mode 100644 vendor/go.opentelemetry.io/otel/CHANGELOG.md create mode 100644 vendor/go.opentelemetry.io/otel/CODEOWNERS create mode 100644 vendor/go.opentelemetry.io/otel/CONTRIBUTING.md create mode 100644 vendor/go.opentelemetry.io/otel/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/Makefile create mode 100644 vendor/go.opentelemetry.io/otel/README.md create mode 100644 vendor/go.opentelemetry.io/otel/RELEASING.md create mode 100644 vendor/go.opentelemetry.io/otel/VERSIONING.md create mode 100644 vendor/go.opentelemetry.io/otel/attribute/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/encoder.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/iterator.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/key.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/kv.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/set.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/type_string.go create mode 100644 vendor/go.opentelemetry.io/otel/attribute/value.go create mode 100644 vendor/go.opentelemetry.io/otel/baggage/baggage.go create mode 100644 vendor/go.opentelemetry.io/otel/baggage/context.go create mode 100644 vendor/go.opentelemetry.io/otel/baggage/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/codes/codes.go create mode 100644 vendor/go.opentelemetry.io/otel/codes/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/error_handler.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/README.md create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/clients.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/instrumentation.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/resource.go create mode 100644 vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go create mode 100644 vendor/go.opentelemetry.io/otel/get_main_pkgs.sh create mode 100644 vendor/go.opentelemetry.io/otel/handler.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/baggage/context.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/global/propagator.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/global/state.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/global/trace.go create mode 100644 vendor/go.opentelemetry.io/otel/internal/rawhelpers.go create mode 100644 vendor/go.opentelemetry.io/otel/internal_logging.go create mode 100644 vendor/go.opentelemetry.io/otel/propagation.go create mode 100644 vendor/go.opentelemetry.io/otel/propagation/baggage.go create mode 100644 vendor/go.opentelemetry.io/otel/propagation/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/propagation/propagation.go create mode 100644 vendor/go.opentelemetry.io/otel/propagation/trace_context.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/internal/internal.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/auto.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/config.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/container.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/env.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os_release_darwin.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/process.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/resource/resource.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/event.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/link.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/provider.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/span.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/span_exporter.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/span_processor.go create mode 100644 vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go create mode 100644 vendor/go.opentelemetry.io/otel/trace.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/LICENSE create mode 100644 vendor/go.opentelemetry.io/otel/trace/config.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/context.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/nonrecording.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/noop.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/trace.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/tracestate.go create mode 100644 vendor/go.opentelemetry.io/otel/verify_examples.sh create mode 100644 vendor/go.opentelemetry.io/otel/version.go create mode 100644 vendor/go.opentelemetry.io/otel/versions.yaml create mode 100644 vendor/go.opentelemetry.io/proto/otlp/LICENSE create mode 100644 vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_config.pb.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.gw.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service_grpc.pb.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/common/v1/common.pb.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/resource/v1/resource.pb.go create mode 100644 vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go create mode 100644 vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go create mode 100644 vendor/google.golang.org/protobuf/encoding/protojson/decode.go create mode 100644 vendor/google.golang.org/protobuf/encoding/protojson/doc.go create mode 100644 vendor/google.golang.org/protobuf/encoding/protojson/encode.go create mode 100644 vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go create mode 100644 vendor/google.golang.org/protobuf/internal/encoding/json/decode.go create mode 100644 vendor/google.golang.org/protobuf/internal/encoding/json/decode_number.go create mode 100644 vendor/google.golang.org/protobuf/internal/encoding/json/decode_string.go create mode 100644 vendor/google.golang.org/protobuf/internal/encoding/json/decode_token.go create mode 100644 vendor/google.golang.org/protobuf/internal/encoding/json/encode.go create mode 100644 vendor/google.golang.org/protobuf/types/known/fieldmaskpb/field_mask.pb.go create mode 100644 vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go diff --git a/connection/connection.go b/connection/connection.go index 154350a6..ef60a2f9 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -14,6 +14,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" + "github.com/cloudflare/cloudflared/tracing" "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" ) @@ -121,7 +122,7 @@ func (t Type) String() string { // OriginProxy is how data flows from cloudflared to the origin services running behind it. type OriginProxy interface { - ProxyHTTP(w ResponseWriter, req *http.Request, isWebsocket bool) error + ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error ProxyTCP(ctx context.Context, rwa ReadWriteAcker, req *TCPRequest) error } diff --git a/connection/connection_test.go b/connection/connection_test.go index 9e43fee2..63390165 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -12,6 +12,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/assert" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" ) @@ -55,9 +56,10 @@ type mockOriginProxy struct{} func (moc *mockOriginProxy) ProxyHTTP( w ResponseWriter, - req *http.Request, + tr *tracing.TracedRequest, isWebsocket bool, ) error { + req := tr.Request if isWebsocket { switch req.URL.Path { case "/ws/echo": diff --git a/connection/h2mux.go b/connection/h2mux.go index 1c7276ac..0bd35cbe 100644 --- a/connection/h2mux.go +++ b/connection/h2mux.go @@ -12,6 +12,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" ) @@ -233,7 +234,7 @@ func (h *h2muxConnection) ServeStream(stream *h2mux.MuxedStream) error { return err } - err = originProxy.ProxyHTTP(respWriter, req, sourceConnectionType == TypeWebsocket) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), sourceConnectionType == TypeWebsocket) if err != nil { respWriter.WriteErrorResponse() } diff --git a/connection/http2.go b/connection/http2.go index 92104dad..67c67862 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -15,6 +15,7 @@ import ( "github.com/rs/zerolog" "golang.org/x/net/http2" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -130,7 +131,9 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { case TypeWebsocket, TypeHTTP: stripWebsocketUpgradeHeader(r) - if err := originProxy.ProxyHTTP(respWriter, r, connType == TypeWebsocket); err != nil { + // Check for tracing on request + tr := tracing.NewTracedRequest(r) + if err := originProxy.ProxyHTTP(respWriter, tr, connType == TypeWebsocket); err != nil { err := fmt.Errorf("Failed to proxy HTTP: %w", err) c.log.Error().Err(err) respWriter.WriteErrorResponse() diff --git a/connection/quic.go b/connection/quic.go index eb2529b8..822925ae 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -20,6 +20,7 @@ import ( "github.com/cloudflare/cloudflared/datagramsession" "github.com/cloudflare/cloudflared/ingress" quicpogs "github.com/cloudflare/cloudflared/quic" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -181,13 +182,13 @@ func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) } switch connectRequest.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - req, err := buildHTTPRequest(connectRequest, stream) + tracedReq, err := buildHTTPRequest(connectRequest, stream) if err != nil { return err } w := newHTTPResponseAdapter(stream) - return originProxy.ProxyHTTP(w, req, connectRequest.Type == quicpogs.ConnectionTypeWebsocket) + return originProxy.ProxyHTTP(w, tracedReq, connectRequest.Type == quicpogs.ConnectionTypeWebsocket) case quicpogs.ConnectionTypeTCP: rwa := &streamReadWriteAcker{stream} return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest}) @@ -305,7 +306,7 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) { hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } -func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*http.Request, error) { +func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { metadata := connectRequest.MetadataMap() dest := connectRequest.Dest method := metadata[HTTPMethodKey] @@ -345,7 +346,10 @@ func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadClose req.Body = http.NoBody } stripWebsocketUpgradeHeader(req) - return req, err + + // Check for tracing on request + tracedReq := tracing.NewTracedRequest(req) + return tracedReq, err } func setContentLength(req *http.Request) error { diff --git a/connection/quic_test.go b/connection/quic_test.go index 0f42b08d..50f9863e 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -24,6 +24,7 @@ import ( "github.com/cloudflare/cloudflared/datagramsession" quicpogs "github.com/cloudflare/cloudflared/quic" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -219,9 +220,10 @@ func quicServer( type mockOriginProxyWithRequest struct{} -func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, r *http.Request, isWebsocket bool) error { +func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error { // These are a series of crude tests to ensure the headers and http related data is transferred from // metadata. + r := tr.Request if r.Method == "" { return errors.New("method not sent") } @@ -478,7 +480,7 @@ func TestBuildHTTPRequest(t *testing.T) { req, err := buildHTTPRequest(test.connectRequest, test.body) assert.NoError(t, err) test.req = test.req.WithContext(req.Context()) - assert.Equal(t, test.req, req) + assert.Equal(t, test.req, req.Request) }) } } diff --git a/go.mod b/go.mod index 1e571d95..0e8a0932 100644 --- a/go.mod +++ b/go.mod @@ -26,14 +26,21 @@ require ( github.com/prometheus/client_model v0.2.0 github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 github.com/rs/zerolog v1.20.0 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/urfave/cli/v2 v2.3.0 + go.opentelemetry.io/contrib/propagators v0.22.0 + go.opentelemetry.io/otel v1.6.3 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3 + go.opentelemetry.io/otel/sdk v1.6.3 + go.opentelemetry.io/otel/trace v1.6.3 + go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b + google.golang.org/protobuf v1.28.0 gopkg.in/coreos/go-oidc.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.6.0 @@ -58,10 +65,13 @@ require ( github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/gdamore/encoding v1.0.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.0.3 // indirect @@ -89,8 +99,7 @@ require ( golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect - google.golang.org/grpc v1.43.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/grpc v1.45.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index acfd57fa..d4065e58 100644 --- a/go.sum +++ b/go.sum @@ -100,10 +100,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -211,6 +211,11 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= @@ -233,6 +238,8 @@ github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1: github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -283,8 +290,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -336,6 +344,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -512,7 +522,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -561,8 +570,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -587,7 +597,22 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib/propagators v0.22.0 h1:KGdv58M2//veiYLIhb31mofaI2LgkIPXXAZVeYVyfd8= +go.opentelemetry.io/contrib/propagators v0.22.0/go.mod h1:xGOuXr6lLIF9BXipA4pm6UuOSI0M98U6tsI3khbOiwU= +go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM= +go.opentelemetry.io/otel v1.6.3 h1:FLOfo8f9JzFVFVyU+MSRJc2HdEAXQgm7pIv2uFKRSZE= +go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3 h1:4/UjHWMVVc5VwX/KAtqJOHErKigMCH8NexChMuanb/o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3/go.mod h1:UJmXdiVVBaZ63umRUTwJuCMAV//GCMvDiQwn703/GoY= +go.opentelemetry.io/otel/sdk v1.6.3 h1:prSHYdwCQOX5DrsEzxowH3nLhoAzEBdZhvrR79scfLs= +go.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ= +go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4= +go.opentelemetry.io/otel/trace v1.6.3 h1:IqN4L+5b0mPNjdXIiZ90Ni4Bl5BRkDQywePLWemd9bc= +go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0 h1:h0bKrvdrT/9sBwEJ6iWUqT/N/xPcS66bL4u3isneJ6w= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= @@ -789,6 +814,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1029,8 +1055,10 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1044,8 +1072,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DataDog/dd-trace-go.v1 v1.34.0/go.mod h1:HtrC65fyJ6lWazShCC9rlOeiTSZJ0XtZhkwjZM2WpC4= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 5afcb8d9..c543d3b6 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -21,6 +21,7 @@ import ( "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/proxy" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -353,7 +354,7 @@ func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Respo return nil, err } - err = originProxy.ProxyHTTP(respWriter, req, false) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), false) if err != nil { return nil, err } @@ -604,7 +605,7 @@ func TestPersistentConnection(t *testing.T) { respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket, &log) require.NoError(t, err) - err = originProxy.ProxyHTTP(respWriter, req, true) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), true) require.NoError(t, err) }() diff --git a/proxy/proxy.go b/proxy/proxy.go index 3ab3c370..76e11fe1 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -10,11 +10,13 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" + "go.opentelemetry.io/otel/attribute" "github.com/cloudflare/cloudflared/carrier" "github.com/cloudflare/cloudflared/cfio" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/ingress" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/websocket" ) @@ -59,16 +61,18 @@ func NewOriginProxy( // a simple roundtrip or a tcp/websocket dial depending on ingres rule setup. func (p *Proxy) ProxyHTTP( w connection.ResponseWriter, - req *http.Request, + tr *tracing.TracedRequest, isWebsocket bool, ) error { incrementRequests() defer decrementConcurrentRequests() + req := tr.Request cfRay := connection.FindCfRayHeader(req) lbProbe := connection.IsLBProbeRequest(req) p.appendTagHeaders(req) + _, ruleSpan := tr.Tracer().Start(req.Context(), "ingress_match") rule, ruleNum := p.ingressRules.FindMatchingRule(req.Host, req.URL.Path) logFields := logFields{ cfRay: cfRay, @@ -76,6 +80,8 @@ func (p *Proxy) ProxyHTTP( rule: ruleNum, } p.logRequest(req, logFields) + ruleSpan.SetAttributes(attribute.Int("rule-num", ruleNum)) + ruleSpan.End() switch originProxy := rule.Service.(type) { case ingress.HTTPOriginProxy: @@ -92,7 +98,6 @@ func (p *Proxy) ProxyHTTP( return err } return nil - case ingress.StreamBasedOriginProxy: dest, err := getDestFromRule(rule, req) if err != nil { diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 7bc3e21b..9f0461ce 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -27,6 +27,7 @@ import ( "github.com/cloudflare/cloudflared/hello" "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/logger" + "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -151,7 +152,7 @@ func testProxyHTTP(proxy connection.OriginProxy) func(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, req, false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) for _, tag := range testTags { assert.Equal(t, tag.Value, req.Header.Get(TagHeaderNamePrefix+tag.Name)) @@ -178,7 +179,7 @@ func testProxyWebsocket(proxy connection.OriginProxy) func(t *testing.T) { errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { - err = proxy.ProxyHTTP(responseWriter, req, true) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), true) require.NoError(t, err) require.Equal(t, http.StatusSwitchingProtocols, responseWriter.Code) @@ -239,7 +240,7 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - err = proxy.ProxyHTTP(responseWriter, req, false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) require.Equal(t, http.StatusOK, responseWriter.Code) @@ -351,7 +352,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat req, err := http.NewRequest(http.MethodGet, test.url, nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, req, false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) assert.Equal(t, test.expectedStatus, responseWriter.Code) @@ -398,7 +399,7 @@ func TestProxyError(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil) assert.NoError(t, err) - assert.Error(t, proxy.ProxyHTTP(responseWriter, req, false)) + assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false)) } type replayer struct { @@ -676,7 +677,7 @@ func TestConnections(t *testing.T) { rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { - err = proxy.ProxyHTTP(respWriter, req, test.args.connectionType == connection.TypeWebsocket) + err = proxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), test.args.connectionType == connection.TypeWebsocket) } cancel() diff --git a/tracing/client.go b/tracing/client.go new file mode 100644 index 00000000..6783912e --- /dev/null +++ b/tracing/client.go @@ -0,0 +1,90 @@ +package tracing + +import ( + "context" + "encoding/base64" + "errors" + "sync" + + coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1" + tracepb "go.opentelemetry.io/proto/otlp/trace/v1" + "google.golang.org/protobuf/proto" +) + +const ( + maxTraceAmount = 20 +) + +var ( + errNoTraces = errors.New("no traces recorded to be exported") +) + +type InMemoryClient interface { + // Spans returns a copy of the list of in-memory stored spans as a base64 + // encoded otlp protobuf string. + Spans() (string, error) +} + +// InMemoryOtlpClient is a client implementation for otlptrace.Client +type InMemoryOtlpClient struct { + mu sync.Mutex + spans []*tracepb.ResourceSpans +} + +func (mc *InMemoryOtlpClient) Start(_ context.Context) error { + return nil +} + +func (mc *InMemoryOtlpClient) Stop(_ context.Context) error { + return nil +} + +// UploadTraces adds the provided list of spans to the in-memory list. +func (mc *InMemoryOtlpClient) UploadTraces(_ context.Context, protoSpans []*tracepb.ResourceSpans) error { + mc.mu.Lock() + defer mc.mu.Unlock() + // Catch to make sure too many traces aren't being added to response header. + // Returning nil makes sure we don't fail to send the traces we already recorded. + if len(mc.spans)+len(protoSpans) > maxTraceAmount { + return nil + } + mc.spans = append(mc.spans, protoSpans...) + return nil +} + +// Spans returns the list of in-memory stored spans as a base64 encoded otlp protobuf string. +func (mc *InMemoryOtlpClient) Spans() (string, error) { + mc.mu.Lock() + defer mc.mu.Unlock() + if len(mc.spans) <= 0 { + return "", errNoTraces + } + pbRequest := &coltracepb.ExportTraceServiceRequest{ + ResourceSpans: mc.spans, + } + data, err := proto.Marshal(pbRequest) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(data), nil +} + +// NoopOtlpClient is a client implementation for otlptrace.Client that does nothing +type NoopOtlpClient struct{} + +func (mc *NoopOtlpClient) Start(_ context.Context) error { + return nil +} + +func (mc *NoopOtlpClient) Stop(_ context.Context) error { + return nil +} + +func (mc *NoopOtlpClient) UploadTraces(_ context.Context, _ []*tracepb.ResourceSpans) error { + return nil +} + +// Spans always returns no traces error +func (mc *NoopOtlpClient) Spans() (string, error) { + return "", errNoTraces +} diff --git a/tracing/client_test.go b/tracing/client_test.go new file mode 100644 index 00000000..946ee7f5 --- /dev/null +++ b/tracing/client_test.go @@ -0,0 +1,161 @@ +package tracing + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" + commonpb "go.opentelemetry.io/proto/otlp/common/v1" + resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" + tracepb "go.opentelemetry.io/proto/otlp/trace/v1" +) + +const ( + resourceSchemaUrl = "http://example.com/custom-resource-schema" + instrumentSchemaUrl = semconv.SchemaURL +) + +var ( + traceId = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} + spanId = []byte{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8} + parentSpanId = []byte{0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08} + startTime = time.Date(2022, 4, 4, 0, 0, 0, 0, time.UTC) + endTime = startTime.Add(5 * time.Second) + + traceState, _ = trace.ParseTraceState("key1=val1,key2=val2") + instrScope = &commonpb.InstrumentationScope{Name: "go.opentelemetry.io/test/otel", Version: "v1.6.0"} + otlpKeyValues = []*commonpb.KeyValue{ + { + Key: "string_key", + Value: &commonpb.AnyValue{ + Value: &commonpb.AnyValue_StringValue{ + StringValue: "string value", + }, + }, + }, + { + Key: "bool_key", + Value: &commonpb.AnyValue{ + Value: &commonpb.AnyValue_BoolValue{ + BoolValue: true, + }, + }, + }, + } + otlpResource = &resourcepb.Resource{ + Attributes: []*commonpb.KeyValue{ + { + Key: "service.name", + Value: &commonpb.AnyValue{ + Value: &commonpb.AnyValue_StringValue{ + StringValue: "service-name", + }, + }, + }, + }, + } +) + +var _ otlptrace.Client = (*InMemoryOtlpClient)(nil) +var _ InMemoryClient = (*InMemoryOtlpClient)(nil) +var _ otlptrace.Client = (*NoopOtlpClient)(nil) +var _ InMemoryClient = (*NoopOtlpClient)(nil) + +func TestUploadTraces(t *testing.T) { + client := &InMemoryOtlpClient{} + spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) + spans2 := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) + err := client.UploadTraces(context.Background(), spans) + assert.NoError(t, err) + err = client.UploadTraces(context.Background(), spans2) + assert.NoError(t, err) + assert.Len(t, client.spans, 2) +} + +func TestSpans(t *testing.T) { + client := &InMemoryOtlpClient{} + spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) + err := client.UploadTraces(context.Background(), spans) + assert.NoError(t, err) + assert.Len(t, client.spans, 1) + enc, err := client.Spans() + assert.NoError(t, err) + expected := "CsECCiAKHgoMc2VydmljZS5uYW1lEg4KDHNlcnZpY2UtbmFtZRLxAQonCh1nby5vcGVudGVsZW1ldHJ5LmlvL3Rlc3Qvb3RlbBIGdjEuNi4wEp0BChAAAQIDBAUGBwgJCgsMDQ4PEgj//v38+/r5+BoTa2V5MT12YWwxLGtleTI9dmFsMiIIDw4NDAsKCQgqCnRyYWNlX25hbWUwATkAANJvaYjiFkEA8teZaojiFkocCgpzdHJpbmdfa2V5Eg4KDHN0cmluZyB2YWx1ZUoOCghib29sX2tleRICEAF6EhIOc3RhdHVzIG1lc3NhZ2UYARomaHR0cHM6Ly9vcGVudGVsZW1ldHJ5LmlvL3NjaGVtYXMvMS43LjAaKWh0dHA6Ly9leGFtcGxlLmNvbS9jdXN0b20tcmVzb3VyY2Utc2NoZW1h" + assert.Equal(t, expected, enc) +} + +func TestSpansEmpty(t *testing.T) { + client := &InMemoryOtlpClient{} + err := client.UploadTraces(context.Background(), []*tracepb.ResourceSpans{}) + assert.NoError(t, err) + assert.Len(t, client.spans, 0) + _, err = client.Spans() + assert.ErrorIs(t, err, errNoTraces) +} + +func TestSpansNil(t *testing.T) { + client := &InMemoryOtlpClient{} + err := client.UploadTraces(context.Background(), nil) + assert.NoError(t, err) + assert.Len(t, client.spans, 0) + _, err = client.Spans() + assert.ErrorIs(t, err, errNoTraces) +} + +func TestSpansTooManySpans(t *testing.T) { + client := &InMemoryOtlpClient{} + for i := 0; i < maxTraceAmount+1; i++ { + spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) + err := client.UploadTraces(context.Background(), spans) + assert.NoError(t, err) + } + assert.Len(t, client.spans, maxTraceAmount) + _, err := client.Spans() + assert.NoError(t, err) +} + +func createResourceSpans(spans []*tracepb.Span) []*tracepb.ResourceSpans { + return []*tracepb.ResourceSpans{createResourceSpan(spans)} +} + +func createResourceSpan(spans []*tracepb.Span) *tracepb.ResourceSpans { + return &tracepb.ResourceSpans{ + Resource: otlpResource, + ScopeSpans: []*tracepb.ScopeSpans{ + { + Scope: instrScope, + Spans: spans, + SchemaUrl: instrumentSchemaUrl, + }, + }, + InstrumentationLibrarySpans: nil, + SchemaUrl: resourceSchemaUrl, + } +} + +func createOtlpSpan(tid []byte) *tracepb.Span { + return &tracepb.Span{ + TraceId: tid, + SpanId: spanId, + TraceState: traceState.String(), + ParentSpanId: parentSpanId, + Name: "trace_name", + Kind: tracepb.Span_SPAN_KIND_INTERNAL, + StartTimeUnixNano: uint64(startTime.UnixNano()), + EndTimeUnixNano: uint64(endTime.UnixNano()), + Attributes: otlpKeyValues, + DroppedAttributesCount: 0, + Events: nil, + DroppedEventsCount: 0, + Links: nil, + DroppedLinksCount: 0, + Status: &tracepb.Status{ + Message: "status message", + Code: tracepb.Status_STATUS_CODE_OK, + }, + } +} diff --git a/tracing/tracing.go b/tracing/tracing.go new file mode 100644 index 00000000..a35d2962 --- /dev/null +++ b/tracing/tracing.go @@ -0,0 +1,116 @@ +package tracing + +import ( + "context" + "errors" + "net/http" + + otelContrib "go.opentelemetry.io/contrib/propagators/Jaeger" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" +) + +const ( + service = "cloudflared" + tracerInstrumentName = "origin" + + tracerContextName = "cf-trace-id" + tracerContextNameOverride = "uber-trace-id" +) + +var ( + Http2TransportAttribute = trace.WithAttributes(TransportAttributeKey.String("http2")) + QuicTransportAttribute = trace.WithAttributes(TransportAttributeKey.String("quic")) + + TransportAttributeKey = attribute.Key("transport") + TrafficAttributeKey = attribute.Key("traffic") + + errNoopTracerProvider = errors.New("noop tracer provider records no spans") +) + +func init() { + // Register the jaeger propagator globally. + otel.SetTextMapPropagator(otelContrib.Jaeger{}) +} + +type TracedRequest struct { + *http.Request + trace.TracerProvider + exporter InMemoryClient +} + +// NewTracedRequest creates a new tracer for the current request context. +func NewTracedRequest(req *http.Request) *TracedRequest { + ctx, exists := extractTrace(req) + if !exists { + return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + } + mc := new(InMemoryOtlpClient) + exp, err := otlptrace.New(req.Context(), mc) + if err != nil { + return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + } + tp := tracesdk.NewTracerProvider( + // We want to dump to in-memory exporter immediately + tracesdk.WithSyncer(exp), + // Record information about this application in a Resource. + tracesdk.WithResource(resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(service), + )), + ) + + return &TracedRequest{req.WithContext(ctx), tp, mc} +} + +func (cft *TracedRequest) Tracer() trace.Tracer { + return cft.TracerProvider.Tracer(tracerInstrumentName) +} + +// Spans returns the spans as base64 encoded protobuf otlp traces. +func (cft *TracedRequest) Spans() (string, error) { + return cft.exporter.Spans() +} + +// EndWithStatus will set a status for the span and then end it. +func EndWithStatus(span trace.Span, code codes.Code, status string) { + if span == nil { + return + } + span.SetStatus(code, status) + span.End() +} + +// extractTrace attempts to check for a cf-trace-id from a request header. +func extractTrace(req *http.Request) (context.Context, bool) { + // Only add tracing for requests with appropriately tagged headers + remoteTraces := req.Header.Values(tracerContextName) + if len(remoteTraces) <= 0 { + // Strip the cf-trace-id header + req.Header.Del(tracerContextName) + return nil, false + } + + traceHeader := make(map[string]string, 1) + for _, t := range remoteTraces { + // Override the 'cf-trace-id' as 'uber-trace-id' so the jaeger propagator can extract it. + // Last entry wins if multiple provided + traceHeader[tracerContextNameOverride] = t + } + + // Strip the cf-trace-id header + req.Header.Del(tracerContextName) + + if traceHeader[tracerContextNameOverride] == "" { + return nil, false + } + remoteCtx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.MapCarrier(traceHeader)) + return remoteCtx, true +} diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go new file mode 100644 index 00000000..50dcb3ff --- /dev/null +++ b/tracing/tracing_test.go @@ -0,0 +1,50 @@ +package tracing + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace" +) + +func TestNewCfTracer(t *testing.T) { + req := httptest.NewRequest("GET", "http://localhost", nil) + req.Header.Add(tracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") + tr := NewTracedRequest(req) + assert.NotNil(t, tr) + assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) + assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) +} + +func TestNewCfTracerMultiple(t *testing.T) { + req := httptest.NewRequest("GET", "http://localhost", nil) + req.Header.Add(tracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") + req.Header.Add(tracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") + tr := NewTracedRequest(req) + assert.NotNil(t, tr) + assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) + assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) +} + +func TestNewCfTracerNilHeader(t *testing.T) { + req := httptest.NewRequest("GET", "http://localhost", nil) + req.Header[http.CanonicalHeaderKey(tracerContextName)] = nil + tr := NewTracedRequest(req) + assert.NotNil(t, tr) + assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) + assert.IsType(t, &NoopOtlpClient{}, tr.exporter) +} + +func TestNewCfTracerInvalidHeaders(t *testing.T) { + req := httptest.NewRequest("GET", "http://localhost", nil) + for _, test := range [][]string{nil, {""}} { + req.Header[http.CanonicalHeaderKey(tracerContextName)] = test + tr := NewTracedRequest(req) + assert.NotNil(t, tr) + assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) + assert.IsType(t, &NoopOtlpClient{}, tr.exporter) + } +} diff --git a/vendor/github.com/go-logr/logr/.golangci.yaml b/vendor/github.com/go-logr/logr/.golangci.yaml new file mode 100644 index 00000000..94ff801d --- /dev/null +++ b/vendor/github.com/go-logr/logr/.golangci.yaml @@ -0,0 +1,29 @@ +run: + timeout: 1m + tests: true + +linters: + disable-all: true + enable: + - asciicheck + - deadcode + - errcheck + - forcetypeassert + - gocritic + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - revive + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + +issues: + exclude-use-default: false + max-issues-per-linter: 0 + max-same-issues: 10 diff --git a/vendor/github.com/go-logr/logr/CHANGELOG.md b/vendor/github.com/go-logr/logr/CHANGELOG.md new file mode 100644 index 00000000..c3569600 --- /dev/null +++ b/vendor/github.com/go-logr/logr/CHANGELOG.md @@ -0,0 +1,6 @@ +# CHANGELOG + +## v1.0.0-rc1 + +This is the first logged release. Major changes (including breaking changes) +have occurred since earlier tags. diff --git a/vendor/github.com/go-logr/logr/CONTRIBUTING.md b/vendor/github.com/go-logr/logr/CONTRIBUTING.md new file mode 100644 index 00000000..5d37e294 --- /dev/null +++ b/vendor/github.com/go-logr/logr/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +Logr is open to pull-requests, provided they fit within the intended scope of +the project. Specifically, this library aims to be VERY small and minimalist, +with no external dependencies. + +## Compatibility + +This project intends to follow [semantic versioning](http://semver.org) and +is very strict about compatibility. Any proposed changes MUST follow those +rules. + +## Performance + +As a logging library, logr must be as light-weight as possible. Any proposed +code change must include results of running the [benchmark](./benchmark) +before and after the change. diff --git a/vendor/github.com/go-logr/logr/LICENSE b/vendor/github.com/go-logr/logr/LICENSE new file mode 100644 index 00000000..8dada3ed --- /dev/null +++ b/vendor/github.com/go-logr/logr/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/go-logr/logr/README.md b/vendor/github.com/go-logr/logr/README.md new file mode 100644 index 00000000..ab593118 --- /dev/null +++ b/vendor/github.com/go-logr/logr/README.md @@ -0,0 +1,282 @@ +# A minimal logging API for Go + +[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr) + +logr offers an(other) opinion on how Go programs and libraries can do logging +without becoming coupled to a particular logging implementation. This is not +an implementation of logging - it is an API. In fact it is two APIs with two +different sets of users. + +The `Logger` type is intended for application and library authors. It provides +a relatively small API which can be used everywhere you want to emit logs. It +defers the actual act of writing logs (to files, to stdout, or whatever) to the +`LogSink` interface. + +The `LogSink` interface is intended for logging library implementers. It is a +pure interface which can be implemented by logging frameworks to provide the actual logging +functionality. + +This decoupling allows application and library developers to write code in +terms of `logr.Logger` (which has very low dependency fan-out) while the +implementation of logging is managed "up stack" (e.g. in or near `main()`.) +Application developers can then switch out implementations as necessary. + +Many people assert that libraries should not be logging, and as such efforts +like this are pointless. Those people are welcome to convince the authors of +the tens-of-thousands of libraries that *DO* write logs that they are all +wrong. In the meantime, logr takes a more practical approach. + +## Typical usage + +Somewhere, early in an application's life, it will make a decision about which +logging library (implementation) it actually wants to use. Something like: + +``` + func main() { + // ... other setup code ... + + // Create the "root" logger. We have chosen the "logimpl" implementation, + // which takes some initial parameters and returns a logr.Logger. + logger := logimpl.New(param1, param2) + + // ... other setup code ... +``` + +Most apps will call into other libraries, create structures to govern the flow, +etc. The `logr.Logger` object can be passed to these other libraries, stored +in structs, or even used as a package-global variable, if needed. For example: + +``` + app := createTheAppObject(logger) + app.Run() +``` + +Outside of this early setup, no other packages need to know about the choice of +implementation. They write logs in terms of the `logr.Logger` that they +received: + +``` + type appObject struct { + // ... other fields ... + logger logr.Logger + // ... other fields ... + } + + func (app *appObject) Run() { + app.logger.Info("starting up", "timestamp", time.Now()) + + // ... app code ... +``` + +## Background + +If the Go standard library had defined an interface for logging, this project +probably would not be needed. Alas, here we are. + +### Inspiration + +Before you consider this package, please read [this blog post by the +inimitable Dave Cheney][warning-makes-no-sense]. We really appreciate what +he has to say, and it largely aligns with our own experiences. + +### Differences from Dave's ideas + +The main differences are: + +1. Dave basically proposes doing away with the notion of a logging API in favor +of `fmt.Printf()`. We disagree, especially when you consider things like output +locations, timestamps, file and line decorations, and structured logging. This +package restricts the logging API to just 2 types of logs: info and error. + +Info logs are things you want to tell the user which are not errors. Error +logs are, well, errors. If your code receives an `error` from a subordinate +function call and is logging that `error` *and not returning it*, use error +logs. + +2. Verbosity-levels on info logs. This gives developers a chance to indicate +arbitrary grades of importance for info logs, without assigning names with +semantic meaning such as "warning", "trace", and "debug." Superficially this +may feel very similar, but the primary difference is the lack of semantics. +Because verbosity is a numerical value, it's safe to assume that an app running +with higher verbosity means more (and less important) logs will be generated. + +## Implementations (non-exhaustive) + +There are implementations for the following logging libraries: + +- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr) +- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr) +- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr) +- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr) +- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting) +- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr) +- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr) +- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr) +- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend) +- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr) +- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr) +- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0) +- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing) + +## FAQ + +### Conceptual + +#### Why structured logging? + +- **Structured logs are more easily queryable**: Since you've got + key-value pairs, it's much easier to query your structured logs for + particular values by filtering on the contents of a particular key -- + think searching request logs for error codes, Kubernetes reconcilers for + the name and namespace of the reconciled object, etc. + +- **Structured logging makes it easier to have cross-referenceable logs**: + Similarly to searchability, if you maintain conventions around your + keys, it becomes easy to gather all log lines related to a particular + concept. + +- **Structured logs allow better dimensions of filtering**: if you have + structure to your logs, you've got more precise control over how much + information is logged -- you might choose in a particular configuration + to log certain keys but not others, only log lines where a certain key + matches a certain value, etc., instead of just having v-levels and names + to key off of. + +- **Structured logs better represent structured data**: sometimes, the + data that you want to log is inherently structured (think tuple-link + objects.) Structured logs allow you to preserve that structure when + outputting. + +#### Why V-levels? + +**V-levels give operators an easy way to control the chattiness of log +operations**. V-levels provide a way for a given package to distinguish +the relative importance or verbosity of a given log message. Then, if +a particular logger or package is logging too many messages, the user +of the package can simply change the v-levels for that library. + +#### Why not named levels, like Info/Warning/Error? + +Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences +from Dave's ideas](#differences-from-daves-ideas). + +#### Why not allow format strings, too? + +**Format strings negate many of the benefits of structured logs**: + +- They're not easily searchable without resorting to fuzzy searching, + regular expressions, etc. + +- They don't store structured data well, since contents are flattened into + a string. + +- They're not cross-referenceable. + +- They don't compress easily, since the message is not constant. + +(Unless you turn positional parameters into key-value pairs with numerical +keys, at which point you've gotten key-value logging with meaningless +keys.) + +### Practical + +#### Why key-value pairs, and not a map? + +Key-value pairs are *much* easier to optimize, especially around +allocations. Zap (a structured logger that inspired logr's interface) has +[performance measurements](https://github.com/uber-go/zap#performance) +that show this quite nicely. + +While the interface ends up being a little less obvious, you get +potentially better performance, plus avoid making users type +`map[string]string{}` every time they want to log. + +#### What if my V-levels differ between libraries? + +That's fine. Control your V-levels on a per-logger basis, and use the +`WithName` method to pass different loggers to different libraries. + +Generally, you should take care to ensure that you have relatively +consistent V-levels within a given logger, however, as this makes deciding +on what verbosity of logs to request easier. + +#### But I really want to use a format string! + +That's not actually a question. Assuming your question is "how do +I convert my mental model of logging with format strings to logging with +constant messages": + +1. Figure out what the error actually is, as you'd write in a TL;DR style, + and use that as a message. + +2. For every place you'd write a format specifier, look to the word before + it, and add that as a key value pair. + +For instance, consider the following examples (all taken from spots in the +Kubernetes codebase): + +- `klog.V(4).Infof("Client is returning errors: code %v, error %v", + responseCode, err)` becomes `logger.Error(err, "client returned an + error", "code", responseCode)` + +- `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v", + seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after + response when requesting url", "attempt", retries, "after + seconds", seconds, "url", url)` + +If you *really* must use a format string, use it in a key's value, and +call `fmt.Sprintf` yourself. For instance: `log.Printf("unable to +reflect over type %T")` becomes `logger.Info("unable to reflect over +type", "type", fmt.Sprintf("%T"))`. In general though, the cases where +this is necessary should be few and far between. + +#### How do I choose my V-levels? + +This is basically the only hard constraint: increase V-levels to denote +more verbose or more debug-y logs. + +Otherwise, you can start out with `0` as "you always want to see this", +`1` as "common logging that you might *possibly* want to turn off", and +`10` as "I would like to performance-test your log collection stack." + +Then gradually choose levels in between as you need them, working your way +down from 10 (for debug and trace style logs) and up from 1 (for chattier +info-type logs.) + +#### How do I choose my keys? + +Keys are fairly flexible, and can hold more or less any string +value. For best compatibility with implementations and consistency +with existing code in other projects, there are a few conventions you +should consider. + +- Make your keys human-readable. +- Constant keys are generally a good idea. +- Be consistent across your codebase. +- Keys should naturally match parts of the message string. +- Use lower case for simple keys and + [lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for + more complex ones. Kubernetes is one example of a project that has + [adopted that + convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments). + +While key names are mostly unrestricted (and spaces are acceptable), +it's generally a good idea to stick to printable ascii characters, or at +least match the general character set of your log lines. + +#### Why should keys be constant values? + +The point of structured logging is to make later log processing easier. Your +keys are, effectively, the schema of each log message. If you use different +keys across instances of the same log line, you will make your structured logs +much harder to use. `Sprintf()` is for values, not for keys! + +#### Why is this not a pure interface? + +The Logger type is implemented as a struct in order to allow the Go compiler to +optimize things like high-V `Info` logs that are not triggered. Not all of +these implementations are implemented yet, but this structure was suggested as +a way to ensure they *can* be implemented. All of the real work is behind the +`LogSink` interface. + +[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging diff --git a/vendor/github.com/go-logr/logr/discard.go b/vendor/github.com/go-logr/logr/discard.go new file mode 100644 index 00000000..9d92a38f --- /dev/null +++ b/vendor/github.com/go-logr/logr/discard.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The logr Authors. + +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 logr + +// Discard returns a Logger that discards all messages logged to it. It can be +// used whenever the caller is not interested in the logs. Logger instances +// produced by this function always compare as equal. +func Discard() Logger { + return Logger{ + level: 0, + sink: discardLogSink{}, + } +} + +// discardLogSink is a LogSink that discards all messages. +type discardLogSink struct{} + +// Verify that it actually implements the interface +var _ LogSink = discardLogSink{} + +func (l discardLogSink) Init(RuntimeInfo) { +} + +func (l discardLogSink) Enabled(int) bool { + return false +} + +func (l discardLogSink) Info(int, string, ...interface{}) { +} + +func (l discardLogSink) Error(error, string, ...interface{}) { +} + +func (l discardLogSink) WithValues(...interface{}) LogSink { + return l +} + +func (l discardLogSink) WithName(string) LogSink { + return l +} diff --git a/vendor/github.com/go-logr/logr/funcr/funcr.go b/vendor/github.com/go-logr/logr/funcr/funcr.go new file mode 100644 index 00000000..7accdb0c --- /dev/null +++ b/vendor/github.com/go-logr/logr/funcr/funcr.go @@ -0,0 +1,787 @@ +/* +Copyright 2021 The logr Authors. + +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 funcr implements formatting of structured log messages and +// optionally captures the call site and timestamp. +// +// The simplest way to use it is via its implementation of a +// github.com/go-logr/logr.LogSink with output through an arbitrary +// "write" function. See New and NewJSON for details. +// +// Custom LogSinks +// +// For users who need more control, a funcr.Formatter can be embedded inside +// your own custom LogSink implementation. This is useful when the LogSink +// needs to implement additional methods, for example. +// +// Formatting +// +// This will respect logr.Marshaler, fmt.Stringer, and error interfaces for +// values which are being logged. When rendering a struct, funcr will use Go's +// standard JSON tags (all except "string"). +package funcr + +import ( + "bytes" + "encoding" + "fmt" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + "time" + + "github.com/go-logr/logr" +) + +// New returns a logr.Logger which is implemented by an arbitrary function. +func New(fn func(prefix, args string), opts Options) logr.Logger { + return logr.New(newSink(fn, NewFormatter(opts))) +} + +// NewJSON returns a logr.Logger which is implemented by an arbitrary function +// and produces JSON output. +func NewJSON(fn func(obj string), opts Options) logr.Logger { + fnWrapper := func(_, obj string) { + fn(obj) + } + return logr.New(newSink(fnWrapper, NewFormatterJSON(opts))) +} + +// Underlier exposes access to the underlying logging function. Since +// callers only have a logr.Logger, they have to know which +// implementation is in use, so this interface is less of an +// abstraction and more of a way to test type conversion. +type Underlier interface { + GetUnderlying() func(prefix, args string) +} + +func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink { + l := &fnlogger{ + Formatter: formatter, + write: fn, + } + // For skipping fnlogger.Info and fnlogger.Error. + l.Formatter.AddCallDepth(1) + return l +} + +// Options carries parameters which influence the way logs are generated. +type Options struct { + // LogCaller tells funcr to add a "caller" key to some or all log lines. + // This has some overhead, so some users might not want it. + LogCaller MessageClass + + // LogCallerFunc tells funcr to also log the calling function name. This + // has no effect if caller logging is not enabled (see Options.LogCaller). + LogCallerFunc bool + + // LogTimestamp tells funcr to add a "ts" key to log lines. This has some + // overhead, so some users might not want it. + LogTimestamp bool + + // TimestampFormat tells funcr how to render timestamps when LogTimestamp + // is enabled. If not specified, a default format will be used. For more + // details, see docs for Go's time.Layout. + TimestampFormat string + + // Verbosity tells funcr which V logs to produce. Higher values enable + // more logs. Info logs at or below this level will be written, while logs + // above this level will be discarded. + Verbosity int + + // RenderBuiltinsHook allows users to mutate the list of key-value pairs + // while a log line is being rendered. The kvList argument follows logr + // conventions - each pair of slice elements is comprised of a string key + // and an arbitrary value (verified and sanitized before calling this + // hook). The value returned must follow the same conventions. This hook + // can be used to audit or modify logged data. For example, you might want + // to prefix all of funcr's built-in keys with some string. This hook is + // only called for built-in (provided by funcr itself) key-value pairs. + // Equivalent hooks are offered for key-value pairs saved via + // logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and + // for user-provided pairs (see RenderArgsHook). + RenderBuiltinsHook func(kvList []interface{}) []interface{} + + // RenderValuesHook is the same as RenderBuiltinsHook, except that it is + // only called for key-value pairs saved via logr.Logger.WithValues. See + // RenderBuiltinsHook for more details. + RenderValuesHook func(kvList []interface{}) []interface{} + + // RenderArgsHook is the same as RenderBuiltinsHook, except that it is only + // called for key-value pairs passed directly to Info and Error. See + // RenderBuiltinsHook for more details. + RenderArgsHook func(kvList []interface{}) []interface{} + + // MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct + // that contains a struct, etc.) it may log. Every time it finds a struct, + // slice, array, or map the depth is increased by one. When the maximum is + // reached, the value will be converted to a string indicating that the max + // depth has been exceeded. If this field is not specified, a default + // value will be used. + MaxLogDepth int +} + +// MessageClass indicates which category or categories of messages to consider. +type MessageClass int + +const ( + // None ignores all message classes. + None MessageClass = iota + // All considers all message classes. + All + // Info only considers info messages. + Info + // Error only considers error messages. + Error +) + +// fnlogger inherits some of its LogSink implementation from Formatter +// and just needs to add some glue code. +type fnlogger struct { + Formatter + write func(prefix, args string) +} + +func (l fnlogger) WithName(name string) logr.LogSink { + l.Formatter.AddName(name) + return &l +} + +func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink { + l.Formatter.AddValues(kvList) + return &l +} + +func (l fnlogger) WithCallDepth(depth int) logr.LogSink { + l.Formatter.AddCallDepth(depth) + return &l +} + +func (l fnlogger) Info(level int, msg string, kvList ...interface{}) { + prefix, args := l.FormatInfo(level, msg, kvList) + l.write(prefix, args) +} + +func (l fnlogger) Error(err error, msg string, kvList ...interface{}) { + prefix, args := l.FormatError(err, msg, kvList) + l.write(prefix, args) +} + +func (l fnlogger) GetUnderlying() func(prefix, args string) { + return l.write +} + +// Assert conformance to the interfaces. +var _ logr.LogSink = &fnlogger{} +var _ logr.CallDepthLogSink = &fnlogger{} +var _ Underlier = &fnlogger{} + +// NewFormatter constructs a Formatter which emits a JSON-like key=value format. +func NewFormatter(opts Options) Formatter { + return newFormatter(opts, outputKeyValue) +} + +// NewFormatterJSON constructs a Formatter which emits strict JSON. +func NewFormatterJSON(opts Options) Formatter { + return newFormatter(opts, outputJSON) +} + +// Defaults for Options. +const defaultTimestampFormat = "2006-01-02 15:04:05.000000" +const defaultMaxLogDepth = 16 + +func newFormatter(opts Options, outfmt outputFormat) Formatter { + if opts.TimestampFormat == "" { + opts.TimestampFormat = defaultTimestampFormat + } + if opts.MaxLogDepth == 0 { + opts.MaxLogDepth = defaultMaxLogDepth + } + f := Formatter{ + outputFormat: outfmt, + prefix: "", + values: nil, + depth: 0, + opts: opts, + } + return f +} + +// Formatter is an opaque struct which can be embedded in a LogSink +// implementation. It should be constructed with NewFormatter. Some of +// its methods directly implement logr.LogSink. +type Formatter struct { + outputFormat outputFormat + prefix string + values []interface{} + valuesStr string + depth int + opts Options +} + +// outputFormat indicates which outputFormat to use. +type outputFormat int + +const ( + // outputKeyValue emits a JSON-like key=value format, but not strict JSON. + outputKeyValue outputFormat = iota + // outputJSON emits strict JSON. + outputJSON +) + +// PseudoStruct is a list of key-value pairs that gets logged as a struct. +type PseudoStruct []interface{} + +// render produces a log line, ready to use. +func (f Formatter) render(builtins, args []interface{}) string { + // Empirically bytes.Buffer is faster than strings.Builder for this. + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + if f.outputFormat == outputJSON { + buf.WriteByte('{') + } + vals := builtins + if hook := f.opts.RenderBuiltinsHook; hook != nil { + vals = hook(f.sanitize(vals)) + } + f.flatten(buf, vals, false, false) // keys are ours, no need to escape + continuing := len(builtins) > 0 + if len(f.valuesStr) > 0 { + if continuing { + if f.outputFormat == outputJSON { + buf.WriteByte(',') + } else { + buf.WriteByte(' ') + } + } + continuing = true + buf.WriteString(f.valuesStr) + } + vals = args + if hook := f.opts.RenderArgsHook; hook != nil { + vals = hook(f.sanitize(vals)) + } + f.flatten(buf, vals, continuing, true) // escape user-provided keys + if f.outputFormat == outputJSON { + buf.WriteByte('}') + } + return buf.String() +} + +// flatten renders a list of key-value pairs into a buffer. If continuing is +// true, it assumes that the buffer has previous values and will emit a +// separator (which depends on the output format) before the first pair it +// writes. If escapeKeys is true, the keys are assumed to have +// non-JSON-compatible characters in them and must be evaluated for escapes. +// +// This function returns a potentially modified version of kvList, which +// ensures that there is a value for every key (adding a value if needed) and +// that each key is a string (substituting a key if needed). +func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} { + // This logic overlaps with sanitize() but saves one type-cast per key, + // which can be measurable. + if len(kvList)%2 != 0 { + kvList = append(kvList, noValue) + } + for i := 0; i < len(kvList); i += 2 { + k, ok := kvList[i].(string) + if !ok { + k = f.nonStringKey(kvList[i]) + kvList[i] = k + } + v := kvList[i+1] + + if i > 0 || continuing { + if f.outputFormat == outputJSON { + buf.WriteByte(',') + } else { + // In theory the format could be something we don't understand. In + // practice, we control it, so it won't be. + buf.WriteByte(' ') + } + } + + if escapeKeys { + buf.WriteString(prettyString(k)) + } else { + // this is faster + buf.WriteByte('"') + buf.WriteString(k) + buf.WriteByte('"') + } + if f.outputFormat == outputJSON { + buf.WriteByte(':') + } else { + buf.WriteByte('=') + } + buf.WriteString(f.pretty(v)) + } + return kvList +} + +func (f Formatter) pretty(value interface{}) string { + return f.prettyWithFlags(value, 0, 0) +} + +const ( + flagRawStruct = 0x1 // do not print braces on structs +) + +// TODO: This is not fast. Most of the overhead goes here. +func (f Formatter) prettyWithFlags(value interface{}, flags uint32, depth int) string { + if depth > f.opts.MaxLogDepth { + return `""` + } + + // Handle types that take full control of logging. + if v, ok := value.(logr.Marshaler); ok { + // Replace the value with what the type wants to get logged. + // That then gets handled below via reflection. + value = invokeMarshaler(v) + } + + // Handle types that want to format themselves. + switch v := value.(type) { + case fmt.Stringer: + value = invokeStringer(v) + case error: + value = invokeError(v) + } + + // Handling the most common types without reflect is a small perf win. + switch v := value.(type) { + case bool: + return strconv.FormatBool(v) + case string: + return prettyString(v) + case int: + return strconv.FormatInt(int64(v), 10) + case int8: + return strconv.FormatInt(int64(v), 10) + case int16: + return strconv.FormatInt(int64(v), 10) + case int32: + return strconv.FormatInt(int64(v), 10) + case int64: + return strconv.FormatInt(int64(v), 10) + case uint: + return strconv.FormatUint(uint64(v), 10) + case uint8: + return strconv.FormatUint(uint64(v), 10) + case uint16: + return strconv.FormatUint(uint64(v), 10) + case uint32: + return strconv.FormatUint(uint64(v), 10) + case uint64: + return strconv.FormatUint(v, 10) + case uintptr: + return strconv.FormatUint(uint64(v), 10) + case float32: + return strconv.FormatFloat(float64(v), 'f', -1, 32) + case float64: + return strconv.FormatFloat(v, 'f', -1, 64) + case complex64: + return `"` + strconv.FormatComplex(complex128(v), 'f', -1, 64) + `"` + case complex128: + return `"` + strconv.FormatComplex(v, 'f', -1, 128) + `"` + case PseudoStruct: + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + v = f.sanitize(v) + if flags&flagRawStruct == 0 { + buf.WriteByte('{') + } + for i := 0; i < len(v); i += 2 { + if i > 0 { + buf.WriteByte(',') + } + k, _ := v[i].(string) // sanitize() above means no need to check success + // arbitrary keys might need escaping + buf.WriteString(prettyString(k)) + buf.WriteByte(':') + buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1)) + } + if flags&flagRawStruct == 0 { + buf.WriteByte('}') + } + return buf.String() + } + + buf := bytes.NewBuffer(make([]byte, 0, 256)) + t := reflect.TypeOf(value) + if t == nil { + return "null" + } + v := reflect.ValueOf(value) + switch t.Kind() { + case reflect.Bool: + return strconv.FormatBool(v.Bool()) + case reflect.String: + return prettyString(v.String()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(int64(v.Int()), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(uint64(v.Uint()), 10) + case reflect.Float32: + return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32) + case reflect.Float64: + return strconv.FormatFloat(v.Float(), 'f', -1, 64) + case reflect.Complex64: + return `"` + strconv.FormatComplex(complex128(v.Complex()), 'f', -1, 64) + `"` + case reflect.Complex128: + return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"` + case reflect.Struct: + if flags&flagRawStruct == 0 { + buf.WriteByte('{') + } + for i := 0; i < t.NumField(); i++ { + fld := t.Field(i) + if fld.PkgPath != "" { + // reflect says this field is only defined for non-exported fields. + continue + } + if !v.Field(i).CanInterface() { + // reflect isn't clear exactly what this means, but we can't use it. + continue + } + name := "" + omitempty := false + if tag, found := fld.Tag.Lookup("json"); found { + if tag == "-" { + continue + } + if comma := strings.Index(tag, ","); comma != -1 { + if n := tag[:comma]; n != "" { + name = n + } + rest := tag[comma:] + if strings.Contains(rest, ",omitempty,") || strings.HasSuffix(rest, ",omitempty") { + omitempty = true + } + } else { + name = tag + } + } + if omitempty && isEmpty(v.Field(i)) { + continue + } + if i > 0 { + buf.WriteByte(',') + } + if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" { + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1)) + continue + } + if name == "" { + name = fld.Name + } + // field names can't contain characters which need escaping + buf.WriteByte('"') + buf.WriteString(name) + buf.WriteByte('"') + buf.WriteByte(':') + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1)) + } + if flags&flagRawStruct == 0 { + buf.WriteByte('}') + } + return buf.String() + case reflect.Slice, reflect.Array: + buf.WriteByte('[') + for i := 0; i < v.Len(); i++ { + if i > 0 { + buf.WriteByte(',') + } + e := v.Index(i) + buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1)) + } + buf.WriteByte(']') + return buf.String() + case reflect.Map: + buf.WriteByte('{') + // This does not sort the map keys, for best perf. + it := v.MapRange() + i := 0 + for it.Next() { + if i > 0 { + buf.WriteByte(',') + } + // If a map key supports TextMarshaler, use it. + keystr := "" + if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok { + txt, err := m.MarshalText() + if err != nil { + keystr = fmt.Sprintf("", err.Error()) + } else { + keystr = string(txt) + } + keystr = prettyString(keystr) + } else { + // prettyWithFlags will produce already-escaped values + keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1) + if t.Key().Kind() != reflect.String { + // JSON only does string keys. Unlike Go's standard JSON, we'll + // convert just about anything to a string. + keystr = prettyString(keystr) + } + } + buf.WriteString(keystr) + buf.WriteByte(':') + buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1)) + i++ + } + buf.WriteByte('}') + return buf.String() + case reflect.Ptr, reflect.Interface: + if v.IsNil() { + return "null" + } + return f.prettyWithFlags(v.Elem().Interface(), 0, depth) + } + return fmt.Sprintf(`""`, t.Kind().String()) +} + +func prettyString(s string) string { + // Avoid escaping (which does allocations) if we can. + if needsEscape(s) { + return strconv.Quote(s) + } + b := bytes.NewBuffer(make([]byte, 0, 1024)) + b.WriteByte('"') + b.WriteString(s) + b.WriteByte('"') + return b.String() +} + +// needsEscape determines whether the input string needs to be escaped or not, +// without doing any allocations. +func needsEscape(s string) bool { + for _, r := range s { + if !strconv.IsPrint(r) || r == '\\' || r == '"' { + return true + } + } + return false +} + +func isEmpty(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func invokeMarshaler(m logr.Marshaler) (ret interface{}) { + defer func() { + if r := recover(); r != nil { + ret = fmt.Sprintf("", r) + } + }() + return m.MarshalLog() +} + +func invokeStringer(s fmt.Stringer) (ret string) { + defer func() { + if r := recover(); r != nil { + ret = fmt.Sprintf("", r) + } + }() + return s.String() +} + +func invokeError(e error) (ret string) { + defer func() { + if r := recover(); r != nil { + ret = fmt.Sprintf("", r) + } + }() + return e.Error() +} + +// Caller represents the original call site for a log line, after considering +// logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper. The File and +// Line fields will always be provided, while the Func field is optional. +// Users can set the render hook fields in Options to examine logged key-value +// pairs, one of which will be {"caller", Caller} if the Options.LogCaller +// field is enabled for the given MessageClass. +type Caller struct { + // File is the basename of the file for this call site. + File string `json:"file"` + // Line is the line number in the file for this call site. + Line int `json:"line"` + // Func is the function name for this call site, or empty if + // Options.LogCallerFunc is not enabled. + Func string `json:"function,omitempty"` +} + +func (f Formatter) caller() Caller { + // +1 for this frame, +1 for Info/Error. + pc, file, line, ok := runtime.Caller(f.depth + 2) + if !ok { + return Caller{"", 0, ""} + } + fn := "" + if f.opts.LogCallerFunc { + if fp := runtime.FuncForPC(pc); fp != nil { + fn = fp.Name() + } + } + + return Caller{filepath.Base(file), line, fn} +} + +const noValue = "" + +func (f Formatter) nonStringKey(v interface{}) string { + return fmt.Sprintf("", f.snippet(v)) +} + +// snippet produces a short snippet string of an arbitrary value. +func (f Formatter) snippet(v interface{}) string { + const snipLen = 16 + + snip := f.pretty(v) + if len(snip) > snipLen { + snip = snip[:snipLen] + } + return snip +} + +// sanitize ensures that a list of key-value pairs has a value for every key +// (adding a value if needed) and that each key is a string (substituting a key +// if needed). +func (f Formatter) sanitize(kvList []interface{}) []interface{} { + if len(kvList)%2 != 0 { + kvList = append(kvList, noValue) + } + for i := 0; i < len(kvList); i += 2 { + _, ok := kvList[i].(string) + if !ok { + kvList[i] = f.nonStringKey(kvList[i]) + } + } + return kvList +} + +// Init configures this Formatter from runtime info, such as the call depth +// imposed by logr itself. +// Note that this receiver is a pointer, so depth can be saved. +func (f *Formatter) Init(info logr.RuntimeInfo) { + f.depth += info.CallDepth +} + +// Enabled checks whether an info message at the given level should be logged. +func (f Formatter) Enabled(level int) bool { + return level <= f.opts.Verbosity +} + +// GetDepth returns the current depth of this Formatter. This is useful for +// implementations which do their own caller attribution. +func (f Formatter) GetDepth() int { + return f.depth +} + +// FormatInfo renders an Info log message into strings. The prefix will be +// empty when no names were set (via AddNames), or when the output is +// configured for JSON. +func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) { + args := make([]interface{}, 0, 64) // using a constant here impacts perf + prefix = f.prefix + if f.outputFormat == outputJSON { + args = append(args, "logger", prefix) + prefix = "" + } + if f.opts.LogTimestamp { + args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat)) + } + if policy := f.opts.LogCaller; policy == All || policy == Info { + args = append(args, "caller", f.caller()) + } + args = append(args, "level", level, "msg", msg) + return prefix, f.render(args, kvList) +} + +// FormatError renders an Error log message into strings. The prefix will be +// empty when no names were set (via AddNames), or when the output is +// configured for JSON. +func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) { + args := make([]interface{}, 0, 64) // using a constant here impacts perf + prefix = f.prefix + if f.outputFormat == outputJSON { + args = append(args, "logger", prefix) + prefix = "" + } + if f.opts.LogTimestamp { + args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat)) + } + if policy := f.opts.LogCaller; policy == All || policy == Error { + args = append(args, "caller", f.caller()) + } + args = append(args, "msg", msg) + var loggableErr interface{} + if err != nil { + loggableErr = err.Error() + } + args = append(args, "error", loggableErr) + return f.prefix, f.render(args, kvList) +} + +// AddName appends the specified name. funcr uses '/' characters to separate +// name elements. Callers should not pass '/' in the provided name string, but +// this library does not actually enforce that. +func (f *Formatter) AddName(name string) { + if len(f.prefix) > 0 { + f.prefix += "/" + } + f.prefix += name +} + +// AddValues adds key-value pairs to the set of saved values to be logged with +// each log line. +func (f *Formatter) AddValues(kvList []interface{}) { + // Three slice args forces a copy. + n := len(f.values) + f.values = append(f.values[:n:n], kvList...) + + vals := f.values + if hook := f.opts.RenderValuesHook; hook != nil { + vals = hook(f.sanitize(vals)) + } + + // Pre-render values, so we don't have to do it on each Info/Error call. + buf := bytes.NewBuffer(make([]byte, 0, 1024)) + f.flatten(buf, vals, false, true) // escape user-provided keys + f.valuesStr = buf.String() +} + +// AddCallDepth increases the number of stack-frames to skip when attributing +// the log line to a file and line. +func (f *Formatter) AddCallDepth(depth int) { + f.depth += depth +} diff --git a/vendor/github.com/go-logr/logr/logr.go b/vendor/github.com/go-logr/logr/logr.go new file mode 100644 index 00000000..c3b56b3d --- /dev/null +++ b/vendor/github.com/go-logr/logr/logr.go @@ -0,0 +1,510 @@ +/* +Copyright 2019 The logr Authors. + +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. +*/ + +// This design derives from Dave Cheney's blog: +// http://dave.cheney.net/2015/11/05/lets-talk-about-logging + +// Package logr defines a general-purpose logging API and abstract interfaces +// to back that API. Packages in the Go ecosystem can depend on this package, +// while callers can implement logging with whatever backend is appropriate. +// +// Usage +// +// Logging is done using a Logger instance. Logger is a concrete type with +// methods, which defers the actual logging to a LogSink interface. The main +// methods of Logger are Info() and Error(). Arguments to Info() and Error() +// are key/value pairs rather than printf-style formatted strings, emphasizing +// "structured logging". +// +// With Go's standard log package, we might write: +// log.Printf("setting target value %s", targetValue) +// +// With logr's structured logging, we'd write: +// logger.Info("setting target", "value", targetValue) +// +// Errors are much the same. Instead of: +// log.Printf("failed to open the pod bay door for user %s: %v", user, err) +// +// We'd write: +// logger.Error(err, "failed to open the pod bay door", "user", user) +// +// Info() and Error() are very similar, but they are separate methods so that +// LogSink implementations can choose to do things like attach additional +// information (such as stack traces) on calls to Error(). Error() messages are +// always logged, regardless of the current verbosity. If there is no error +// instance available, passing nil is valid. +// +// Verbosity +// +// Often we want to log information only when the application in "verbose +// mode". To write log lines that are more verbose, Logger has a V() method. +// The higher the V-level of a log line, the less critical it is considered. +// Log-lines with V-levels that are not enabled (as per the LogSink) will not +// be written. Level V(0) is the default, and logger.V(0).Info() has the same +// meaning as logger.Info(). Negative V-levels have the same meaning as V(0). +// Error messages do not have a verbosity level and are always logged. +// +// Where we might have written: +// if flVerbose >= 2 { +// log.Printf("an unusual thing happened") +// } +// +// We can write: +// logger.V(2).Info("an unusual thing happened") +// +// Logger Names +// +// Logger instances can have name strings so that all messages logged through +// that instance have additional context. For example, you might want to add +// a subsystem name: +// +// logger.WithName("compactor").Info("started", "time", time.Now()) +// +// The WithName() method returns a new Logger, which can be passed to +// constructors or other functions for further use. Repeated use of WithName() +// will accumulate name "segments". These name segments will be joined in some +// way by the LogSink implementation. It is strongly recommended that name +// segments contain simple identifiers (letters, digits, and hyphen), and do +// not contain characters that could muddle the log output or confuse the +// joining operation (e.g. whitespace, commas, periods, slashes, brackets, +// quotes, etc). +// +// Saved Values +// +// Logger instances can store any number of key/value pairs, which will be +// logged alongside all messages logged through that instance. For example, +// you might want to create a Logger instance per managed object: +// +// With the standard log package, we might write: +// log.Printf("decided to set field foo to value %q for object %s/%s", +// targetValue, object.Namespace, object.Name) +// +// With logr we'd write: +// // Elsewhere: set up the logger to log the object name. +// obj.logger = mainLogger.WithValues( +// "name", obj.name, "namespace", obj.namespace) +// +// // later on... +// obj.logger.Info("setting foo", "value", targetValue) +// +// Best Practices +// +// Logger has very few hard rules, with the goal that LogSink implementations +// might have a lot of freedom to differentiate. There are, however, some +// things to consider. +// +// The log message consists of a constant message attached to the log line. +// This should generally be a simple description of what's occurring, and should +// never be a format string. Variable information can then be attached using +// named values. +// +// Keys are arbitrary strings, but should generally be constant values. Values +// may be any Go value, but how the value is formatted is determined by the +// LogSink implementation. +// +// Logger instances are meant to be passed around by value. Code that receives +// such a value can call its methods without having to check whether the +// instance is ready for use. +// +// Calling methods with the null logger (Logger{}) as instance will crash +// because it has no LogSink. Therefore this null logger should never be passed +// around. For cases where passing a logger is optional, a pointer to Logger +// should be used. +// +// Key Naming Conventions +// +// Keys are not strictly required to conform to any specification or regex, but +// it is recommended that they: +// * be human-readable and meaningful (not auto-generated or simple ordinals) +// * be constant (not dependent on input data) +// * contain only printable characters +// * not contain whitespace or punctuation +// * use lower case for simple keys and lowerCamelCase for more complex ones +// +// These guidelines help ensure that log data is processed properly regardless +// of the log implementation. For example, log implementations will try to +// output JSON data or will store data for later database (e.g. SQL) queries. +// +// While users are generally free to use key names of their choice, it's +// generally best to avoid using the following keys, as they're frequently used +// by implementations: +// * "caller": the calling information (file/line) of a particular log line +// * "error": the underlying error value in the `Error` method +// * "level": the log level +// * "logger": the name of the associated logger +// * "msg": the log message +// * "stacktrace": the stack trace associated with a particular log line or +// error (often from the `Error` message) +// * "ts": the timestamp for a log line +// +// Implementations are encouraged to make use of these keys to represent the +// above concepts, when necessary (for example, in a pure-JSON output form, it +// would be necessary to represent at least message and timestamp as ordinary +// named values). +// +// Break Glass +// +// Implementations may choose to give callers access to the underlying +// logging implementation. The recommended pattern for this is: +// // Underlier exposes access to the underlying logging implementation. +// // Since callers only have a logr.Logger, they have to know which +// // implementation is in use, so this interface is less of an abstraction +// // and more of way to test type conversion. +// type Underlier interface { +// GetUnderlying() +// } +// +// Logger grants access to the sink to enable type assertions like this: +// func DoSomethingWithImpl(log logr.Logger) { +// if underlier, ok := log.GetSink()(impl.Underlier) { +// implLogger := underlier.GetUnderlying() +// ... +// } +// } +// +// Custom `With*` functions can be implemented by copying the complete +// Logger struct and replacing the sink in the copy: +// // WithFooBar changes the foobar parameter in the log sink and returns a +// // new logger with that modified sink. It does nothing for loggers where +// // the sink doesn't support that parameter. +// func WithFoobar(log logr.Logger, foobar int) logr.Logger { +// if foobarLogSink, ok := log.GetSink()(FoobarSink); ok { +// log = log.WithSink(foobarLogSink.WithFooBar(foobar)) +// } +// return log +// } +// +// Don't use New to construct a new Logger with a LogSink retrieved from an +// existing Logger. Source code attribution might not work correctly and +// unexported fields in Logger get lost. +// +// Beware that the same LogSink instance may be shared by different logger +// instances. Calling functions that modify the LogSink will affect all of +// those. +package logr + +import ( + "context" +) + +// New returns a new Logger instance. This is primarily used by libraries +// implementing LogSink, rather than end users. +func New(sink LogSink) Logger { + logger := Logger{} + logger.setSink(sink) + sink.Init(runtimeInfo) + return logger +} + +// setSink stores the sink and updates any related fields. It mutates the +// logger and thus is only safe to use for loggers that are not currently being +// used concurrently. +func (l *Logger) setSink(sink LogSink) { + l.sink = sink +} + +// GetSink returns the stored sink. +func (l Logger) GetSink() LogSink { + return l.sink +} + +// WithSink returns a copy of the logger with the new sink. +func (l Logger) WithSink(sink LogSink) Logger { + l.setSink(sink) + return l +} + +// Logger is an interface to an abstract logging implementation. This is a +// concrete type for performance reasons, but all the real work is passed on to +// a LogSink. Implementations of LogSink should provide their own constructors +// that return Logger, not LogSink. +// +// The underlying sink can be accessed through GetSink and be modified through +// WithSink. This enables the implementation of custom extensions (see "Break +// Glass" in the package documentation). Normally the sink should be used only +// indirectly. +type Logger struct { + sink LogSink + level int +} + +// Enabled tests whether this Logger is enabled. For example, commandline +// flags might be used to set the logging verbosity and disable some info logs. +func (l Logger) Enabled() bool { + return l.sink.Enabled(l.level) +} + +// Info logs a non-error message with the given key/value pairs as context. +// +// The msg argument should be used to add some constant description to the log +// line. The key/value pairs can then be used to add additional variable +// information. The key/value pairs must alternate string keys and arbitrary +// values. +func (l Logger) Info(msg string, keysAndValues ...interface{}) { + if l.Enabled() { + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + withHelper.GetCallStackHelper()() + } + l.sink.Info(l.level, msg, keysAndValues...) + } +} + +// Error logs an error, with the given message and key/value pairs as context. +// It functions similarly to Info, but may have unique behavior, and should be +// preferred for logging errors (see the package documentations for more +// information). The log message will always be emitted, regardless of +// verbosity level. +// +// The msg argument should be used to add context to any underlying error, +// while the err argument should be used to attach the actual error that +// triggered this log line, if present. The err parameter is optional +// and nil may be passed instead of an error instance. +func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + withHelper.GetCallStackHelper()() + } + l.sink.Error(err, msg, keysAndValues...) +} + +// V returns a new Logger instance for a specific verbosity level, relative to +// this Logger. In other words, V-levels are additive. A higher verbosity +// level means a log message is less important. Negative V-levels are treated +// as 0. +func (l Logger) V(level int) Logger { + if level < 0 { + level = 0 + } + l.level += level + return l +} + +// WithValues returns a new Logger instance with additional key/value pairs. +// See Info for documentation on how key/value pairs work. +func (l Logger) WithValues(keysAndValues ...interface{}) Logger { + l.setSink(l.sink.WithValues(keysAndValues...)) + return l +} + +// WithName returns a new Logger instance with the specified name element added +// to the Logger's name. Successive calls with WithName append additional +// suffixes to the Logger's name. It's strongly recommended that name segments +// contain only letters, digits, and hyphens (see the package documentation for +// more information). +func (l Logger) WithName(name string) Logger { + l.setSink(l.sink.WithName(name)) + return l +} + +// WithCallDepth returns a Logger instance that offsets the call stack by the +// specified number of frames when logging call site information, if possible. +// This is useful for users who have helper functions between the "real" call +// site and the actual calls to Logger methods. If depth is 0 the attribution +// should be to the direct caller of this function. If depth is 1 the +// attribution should skip 1 call frame, and so on. Successive calls to this +// are additive. +// +// If the underlying log implementation supports a WithCallDepth(int) method, +// it will be called and the result returned. If the implementation does not +// support CallDepthLogSink, the original Logger will be returned. +// +// To skip one level, WithCallStackHelper() should be used instead of +// WithCallDepth(1) because it works with implementions that support the +// CallDepthLogSink and/or CallStackHelperLogSink interfaces. +func (l Logger) WithCallDepth(depth int) Logger { + if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { + l.setSink(withCallDepth.WithCallDepth(depth)) + } + return l +} + +// WithCallStackHelper returns a new Logger instance that skips the direct +// caller when logging call site information, if possible. This is useful for +// users who have helper functions between the "real" call site and the actual +// calls to Logger methods and want to support loggers which depend on marking +// each individual helper function, like loggers based on testing.T. +// +// In addition to using that new logger instance, callers also must call the +// returned function. +// +// If the underlying log implementation supports a WithCallDepth(int) method, +// WithCallDepth(1) will be called to produce a new logger. If it supports a +// WithCallStackHelper() method, that will be also called. If the +// implementation does not support either of these, the original Logger will be +// returned. +func (l Logger) WithCallStackHelper() (func(), Logger) { + var helper func() + if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { + l.setSink(withCallDepth.WithCallDepth(1)) + } + if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { + helper = withHelper.GetCallStackHelper() + } else { + helper = func() {} + } + return helper, l +} + +// contextKey is how we find Loggers in a context.Context. +type contextKey struct{} + +// FromContext returns a Logger from ctx or an error if no Logger is found. +func FromContext(ctx context.Context) (Logger, error) { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v, nil + } + + return Logger{}, notFoundError{} +} + +// notFoundError exists to carry an IsNotFound method. +type notFoundError struct{} + +func (notFoundError) Error() string { + return "no logr.Logger was present" +} + +func (notFoundError) IsNotFound() bool { + return true +} + +// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this +// returns a Logger that discards all log messages. +func FromContextOrDiscard(ctx context.Context) Logger { + if v, ok := ctx.Value(contextKey{}).(Logger); ok { + return v + } + + return Discard() +} + +// NewContext returns a new Context, derived from ctx, which carries the +// provided Logger. +func NewContext(ctx context.Context, logger Logger) context.Context { + return context.WithValue(ctx, contextKey{}, logger) +} + +// RuntimeInfo holds information that the logr "core" library knows which +// LogSinks might want to know. +type RuntimeInfo struct { + // CallDepth is the number of call frames the logr library adds between the + // end-user and the LogSink. LogSink implementations which choose to print + // the original logging site (e.g. file & line) should climb this many + // additional frames to find it. + CallDepth int +} + +// runtimeInfo is a static global. It must not be changed at run time. +var runtimeInfo = RuntimeInfo{ + CallDepth: 1, +} + +// LogSink represents a logging implementation. End-users will generally not +// interact with this type. +type LogSink interface { + // Init receives optional information about the logr library for LogSink + // implementations that need it. + Init(info RuntimeInfo) + + // Enabled tests whether this LogSink is enabled at the specified V-level. + // For example, commandline flags might be used to set the logging + // verbosity and disable some info logs. + Enabled(level int) bool + + // Info logs a non-error message with the given key/value pairs as context. + // The level argument is provided for optional logging. This method will + // only be called when Enabled(level) is true. See Logger.Info for more + // details. + Info(level int, msg string, keysAndValues ...interface{}) + + // Error logs an error, with the given message and key/value pairs as + // context. See Logger.Error for more details. + Error(err error, msg string, keysAndValues ...interface{}) + + // WithValues returns a new LogSink with additional key/value pairs. See + // Logger.WithValues for more details. + WithValues(keysAndValues ...interface{}) LogSink + + // WithName returns a new LogSink with the specified name appended. See + // Logger.WithName for more details. + WithName(name string) LogSink +} + +// CallDepthLogSink represents a Logger that knows how to climb the call stack +// to identify the original call site and can offset the depth by a specified +// number of frames. This is useful for users who have helper functions +// between the "real" call site and the actual calls to Logger methods. +// Implementations that log information about the call site (such as file, +// function, or line) would otherwise log information about the intermediate +// helper functions. +// +// This is an optional interface and implementations are not required to +// support it. +type CallDepthLogSink interface { + // WithCallDepth returns a LogSink that will offset the call + // stack by the specified number of frames when logging call + // site information. + // + // If depth is 0, the LogSink should skip exactly the number + // of call frames defined in RuntimeInfo.CallDepth when Info + // or Error are called, i.e. the attribution should be to the + // direct caller of Logger.Info or Logger.Error. + // + // If depth is 1 the attribution should skip 1 call frame, and so on. + // Successive calls to this are additive. + WithCallDepth(depth int) LogSink +} + +// CallStackHelperLogSink represents a Logger that knows how to climb +// the call stack to identify the original call site and can skip +// intermediate helper functions if they mark themselves as +// helper. Go's testing package uses that approach. +// +// This is useful for users who have helper functions between the +// "real" call site and the actual calls to Logger methods. +// Implementations that log information about the call site (such as +// file, function, or line) would otherwise log information about the +// intermediate helper functions. +// +// This is an optional interface and implementations are not required +// to support it. Implementations that choose to support this must not +// simply implement it as WithCallDepth(1), because +// Logger.WithCallStackHelper will call both methods if they are +// present. This should only be implemented for LogSinks that actually +// need it, as with testing.T. +type CallStackHelperLogSink interface { + // GetCallStackHelper returns a function that must be called + // to mark the direct caller as helper function when logging + // call site information. + GetCallStackHelper() func() +} + +// Marshaler is an optional interface that logged values may choose to +// implement. Loggers with structured output, such as JSON, should +// log the object return by the MarshalLog method instead of the +// original value. +type Marshaler interface { + // MarshalLog can be used to: + // - ensure that structs are not logged as strings when the original + // value has a String method: return a different type without a + // String method + // - select which fields of a complex type should get logged: + // return a simpler struct with fewer fields + // - log unexported fields: return a different struct + // with exported fields + // + // It may return any value of any type. + MarshalLog() interface{} +} diff --git a/vendor/github.com/go-logr/stdr/LICENSE b/vendor/github.com/go-logr/stdr/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/github.com/go-logr/stdr/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/go-logr/stdr/README.md b/vendor/github.com/go-logr/stdr/README.md new file mode 100644 index 00000000..51586678 --- /dev/null +++ b/vendor/github.com/go-logr/stdr/README.md @@ -0,0 +1,6 @@ +# Minimal Go logging using logr and Go's standard library + +[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/stdr.svg)](https://pkg.go.dev/github.com/go-logr/stdr) + +This package implements the [logr interface](https://github.com/go-logr/logr) +in terms of Go's standard log package(https://pkg.go.dev/log). diff --git a/vendor/github.com/go-logr/stdr/stdr.go b/vendor/github.com/go-logr/stdr/stdr.go new file mode 100644 index 00000000..93a8aab5 --- /dev/null +++ b/vendor/github.com/go-logr/stdr/stdr.go @@ -0,0 +1,170 @@ +/* +Copyright 2019 The logr Authors. + +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 stdr implements github.com/go-logr/logr.Logger in terms of +// Go's standard log package. +package stdr + +import ( + "log" + "os" + + "github.com/go-logr/logr" + "github.com/go-logr/logr/funcr" +) + +// The global verbosity level. See SetVerbosity(). +var globalVerbosity int + +// SetVerbosity sets the global level against which all info logs will be +// compared. If this is greater than or equal to the "V" of the logger, the +// message will be logged. A higher value here means more logs will be written. +// The previous verbosity value is returned. This is not concurrent-safe - +// callers must be sure to call it from only one goroutine. +func SetVerbosity(v int) int { + old := globalVerbosity + globalVerbosity = v + return old +} + +// New returns a logr.Logger which is implemented by Go's standard log package, +// or something like it. If std is nil, this will use a default logger +// instead. +// +// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) +func New(std StdLogger) logr.Logger { + return NewWithOptions(std, Options{}) +} + +// NewWithOptions returns a logr.Logger which is implemented by Go's standard +// log package, or something like it. See New for details. +func NewWithOptions(std StdLogger, opts Options) logr.Logger { + if std == nil { + // Go's log.Default() is only available in 1.16 and higher. + std = log.New(os.Stderr, "", log.LstdFlags) + } + + if opts.Depth < 0 { + opts.Depth = 0 + } + + fopts := funcr.Options{ + LogCaller: funcr.MessageClass(opts.LogCaller), + } + + sl := &logger{ + Formatter: funcr.NewFormatter(fopts), + std: std, + } + + // For skipping our own logger.Info/Error. + sl.Formatter.AddCallDepth(1 + opts.Depth) + + return logr.New(sl) +} + +// Options carries parameters which influence the way logs are generated. +type Options struct { + // Depth biases the assumed number of call frames to the "true" caller. + // This is useful when the calling code calls a function which then calls + // stdr (e.g. a logging shim to another API). Values less than zero will + // be treated as zero. + Depth int + + // LogCaller tells stdr to add a "caller" key to some or all log lines. + // Go's log package has options to log this natively, too. + LogCaller MessageClass + + // TODO: add an option to log the date/time +} + +// MessageClass indicates which category or categories of messages to consider. +type MessageClass int + +const ( + // None ignores all message classes. + None MessageClass = iota + // All considers all message classes. + All + // Info only considers info messages. + Info + // Error only considers error messages. + Error +) + +// StdLogger is the subset of the Go stdlib log.Logger API that is needed for +// this adapter. +type StdLogger interface { + // Output is the same as log.Output and log.Logger.Output. + Output(calldepth int, logline string) error +} + +type logger struct { + funcr.Formatter + std StdLogger +} + +var _ logr.LogSink = &logger{} +var _ logr.CallDepthLogSink = &logger{} + +func (l logger) Enabled(level int) bool { + return globalVerbosity >= level +} + +func (l logger) Info(level int, msg string, kvList ...interface{}) { + prefix, args := l.FormatInfo(level, msg, kvList) + if prefix != "" { + args = prefix + ": " + args + } + _ = l.std.Output(l.Formatter.GetDepth()+1, args) +} + +func (l logger) Error(err error, msg string, kvList ...interface{}) { + prefix, args := l.FormatError(err, msg, kvList) + if prefix != "" { + args = prefix + ": " + args + } + _ = l.std.Output(l.Formatter.GetDepth()+1, args) +} + +func (l logger) WithName(name string) logr.LogSink { + l.Formatter.AddName(name) + return &l +} + +func (l logger) WithValues(kvList ...interface{}) logr.LogSink { + l.Formatter.AddValues(kvList) + return &l +} + +func (l logger) WithCallDepth(depth int) logr.LogSink { + l.Formatter.AddCallDepth(depth) + return &l +} + +// Underlier exposes access to the underlying logging implementation. Since +// callers only have a logr.Logger, they have to know which implementation is +// in use, so this interface is less of an abstraction and more of way to test +// type conversion. +type Underlier interface { + GetUnderlying() StdLogger +} + +// GetUnderlying returns the StdLogger underneath this logger. Since StdLogger +// is itself an interface, the result may or may not be a Go log.Logger. +func (l logger) GetUnderlying() StdLogger { + return l.std +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt new file mode 100644 index 00000000..36451625 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2015, Gengo, Inc. +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 Gengo, 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/grpc-ecosystem/grpc-gateway/v2/internal/httprule/BUILD.bazel b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/BUILD.bazel new file mode 100644 index 00000000..f694f3c0 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package(default_visibility = ["//visibility:public"]) + +go_library( + name = "httprule", + srcs = [ + "compile.go", + "parse.go", + "types.go", + ], + importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule", + deps = ["//utilities"], +) + +go_test( + name = "httprule_test", + size = "small", + srcs = [ + "compile_test.go", + "parse_test.go", + "types_test.go", + ], + embed = [":httprule"], + deps = [ + "//utilities", + "@com_github_golang_glog//:glog", + ], +) + +alias( + name = "go_default_library", + actual = ":httprule", + visibility = ["//:__subpackages__"], +) diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/compile.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/compile.go new file mode 100644 index 00000000..3cd93729 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/compile.go @@ -0,0 +1,121 @@ +package httprule + +import ( + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" +) + +const ( + opcodeVersion = 1 +) + +// Template is a compiled representation of path templates. +type Template struct { + // Version is the version number of the format. + Version int + // OpCodes is a sequence of operations. + OpCodes []int + // Pool is a constant pool + Pool []string + // Verb is a VERB part in the template. + Verb string + // Fields is a list of field paths bound in this template. + Fields []string + // Original template (example: /v1/a_bit_of_everything) + Template string +} + +// Compiler compiles utilities representation of path templates into marshallable operations. +// They can be unmarshalled by runtime.NewPattern. +type Compiler interface { + Compile() Template +} + +type op struct { + // code is the opcode of the operation + code utilities.OpCode + + // str is a string operand of the code. + // num is ignored if str is not empty. + str string + + // num is a numeric operand of the code. + num int +} + +func (w wildcard) compile() []op { + return []op{ + {code: utilities.OpPush}, + } +} + +func (w deepWildcard) compile() []op { + return []op{ + {code: utilities.OpPushM}, + } +} + +func (l literal) compile() []op { + return []op{ + { + code: utilities.OpLitPush, + str: string(l), + }, + } +} + +func (v variable) compile() []op { + var ops []op + for _, s := range v.segments { + ops = append(ops, s.compile()...) + } + ops = append(ops, op{ + code: utilities.OpConcatN, + num: len(v.segments), + }, op{ + code: utilities.OpCapture, + str: v.path, + }) + + return ops +} + +func (t template) Compile() Template { + var rawOps []op + for _, s := range t.segments { + rawOps = append(rawOps, s.compile()...) + } + + var ( + ops []int + pool []string + fields []string + ) + consts := make(map[string]int) + for _, op := range rawOps { + ops = append(ops, int(op.code)) + if op.str == "" { + ops = append(ops, op.num) + } else { + // eof segment literal represents the "/" path pattern + if op.str == eof { + op.str = "" + } + if _, ok := consts[op.str]; !ok { + consts[op.str] = len(pool) + pool = append(pool, op.str) + } + ops = append(ops, consts[op.str]) + } + if op.code == utilities.OpCapture { + fields = append(fields, op.str) + } + } + return Template{ + Version: opcodeVersion, + OpCodes: ops, + Pool: pool, + Verb: t.verb, + Fields: fields, + Template: t.template, + } +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/fuzz.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/fuzz.go new file mode 100644 index 00000000..138f7c12 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/fuzz.go @@ -0,0 +1,11 @@ +// +build gofuzz + +package httprule + +func Fuzz(data []byte) int { + _, err := Parse(string(data)) + if err != nil { + return 0 + } + return 0 +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/parse.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/parse.go new file mode 100644 index 00000000..5edd784e --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/parse.go @@ -0,0 +1,368 @@ +package httprule + +import ( + "fmt" + "strings" +) + +// InvalidTemplateError indicates that the path template is not valid. +type InvalidTemplateError struct { + tmpl string + msg string +} + +func (e InvalidTemplateError) Error() string { + return fmt.Sprintf("%s: %s", e.msg, e.tmpl) +} + +// Parse parses the string representation of path template +func Parse(tmpl string) (Compiler, error) { + if !strings.HasPrefix(tmpl, "/") { + return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"} + } + tokens, verb := tokenize(tmpl[1:]) + + p := parser{tokens: tokens} + segs, err := p.topLevelSegments() + if err != nil { + return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()} + } + + return template{ + segments: segs, + verb: verb, + template: tmpl, + }, nil +} + +func tokenize(path string) (tokens []string, verb string) { + if path == "" { + return []string{eof}, "" + } + + const ( + init = iota + field + nested + ) + st := init + for path != "" { + var idx int + switch st { + case init: + idx = strings.IndexAny(path, "/{") + case field: + idx = strings.IndexAny(path, ".=}") + case nested: + idx = strings.IndexAny(path, "/}") + } + if idx < 0 { + tokens = append(tokens, path) + break + } + switch r := path[idx]; r { + case '/', '.': + case '{': + st = field + case '=': + st = nested + case '}': + st = init + } + if idx == 0 { + tokens = append(tokens, path[idx:idx+1]) + } else { + tokens = append(tokens, path[:idx], path[idx:idx+1]) + } + path = path[idx+1:] + } + + l := len(tokens) + // See + // https://github.com/grpc-ecosystem/grpc-gateway/pull/1947#issuecomment-774523693 ; + // although normal and backwards-compat logic here is to use the last index + // of a colon, if the final segment is a variable followed by a colon, the + // part following the colon must be a verb. Hence if the previous token is + // an end var marker, we switch the index we're looking for to Index instead + // of LastIndex, so that we correctly grab the remaining part of the path as + // the verb. + var penultimateTokenIsEndVar bool + switch l { + case 0, 1: + // Not enough to be variable so skip this logic and don't result in an + // invalid index + default: + penultimateTokenIsEndVar = tokens[l-2] == "}" + } + t := tokens[l-1] + var idx int + if penultimateTokenIsEndVar { + idx = strings.Index(t, ":") + } else { + idx = strings.LastIndex(t, ":") + } + if idx == 0 { + tokens, verb = tokens[:l-1], t[1:] + } else if idx > 0 { + tokens[l-1], verb = t[:idx], t[idx+1:] + } + tokens = append(tokens, eof) + return tokens, verb +} + +// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto. +type parser struct { + tokens []string + accepted []string +} + +// topLevelSegments is the target of this parser. +func (p *parser) topLevelSegments() ([]segment, error) { + if _, err := p.accept(typeEOF); err == nil { + p.tokens = p.tokens[:0] + return []segment{literal(eof)}, nil + } + segs, err := p.segments() + if err != nil { + return nil, err + } + if _, err := p.accept(typeEOF); err != nil { + return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, "")) + } + return segs, nil +} + +func (p *parser) segments() ([]segment, error) { + s, err := p.segment() + if err != nil { + return nil, err + } + + segs := []segment{s} + for { + if _, err := p.accept("/"); err != nil { + return segs, nil + } + s, err := p.segment() + if err != nil { + return segs, err + } + segs = append(segs, s) + } +} + +func (p *parser) segment() (segment, error) { + if _, err := p.accept("*"); err == nil { + return wildcard{}, nil + } + if _, err := p.accept("**"); err == nil { + return deepWildcard{}, nil + } + if l, err := p.literal(); err == nil { + return l, nil + } + + v, err := p.variable() + if err != nil { + return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err) + } + return v, err +} + +func (p *parser) literal() (segment, error) { + lit, err := p.accept(typeLiteral) + if err != nil { + return nil, err + } + return literal(lit), nil +} + +func (p *parser) variable() (segment, error) { + if _, err := p.accept("{"); err != nil { + return nil, err + } + + path, err := p.fieldPath() + if err != nil { + return nil, err + } + + var segs []segment + if _, err := p.accept("="); err == nil { + segs, err = p.segments() + if err != nil { + return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err) + } + } else { + segs = []segment{wildcard{}} + } + + if _, err := p.accept("}"); err != nil { + return nil, fmt.Errorf("unterminated variable segment: %s", path) + } + return variable{ + path: path, + segments: segs, + }, nil +} + +func (p *parser) fieldPath() (string, error) { + c, err := p.accept(typeIdent) + if err != nil { + return "", err + } + components := []string{c} + for { + if _, err = p.accept("."); err != nil { + return strings.Join(components, "."), nil + } + c, err := p.accept(typeIdent) + if err != nil { + return "", fmt.Errorf("invalid field path component: %v", err) + } + components = append(components, c) + } +} + +// A termType is a type of terminal symbols. +type termType string + +// These constants define some of valid values of termType. +// They improve readability of parse functions. +// +// You can also use "/", "*", "**", "." or "=" as valid values. +const ( + typeIdent = termType("ident") + typeLiteral = termType("literal") + typeEOF = termType("$") +) + +const ( + // eof is the terminal symbol which always appears at the end of token sequence. + eof = "\u0000" +) + +// accept tries to accept a token in "p". +// This function consumes a token and returns it if it matches to the specified "term". +// If it doesn't match, the function does not consume any tokens and return an error. +func (p *parser) accept(term termType) (string, error) { + t := p.tokens[0] + switch term { + case "/", "*", "**", ".", "=", "{", "}": + if t != string(term) && t != "/" { + return "", fmt.Errorf("expected %q but got %q", term, t) + } + case typeEOF: + if t != eof { + return "", fmt.Errorf("expected EOF but got %q", t) + } + case typeIdent: + if err := expectIdent(t); err != nil { + return "", err + } + case typeLiteral: + if err := expectPChars(t); err != nil { + return "", err + } + default: + return "", fmt.Errorf("unknown termType %q", term) + } + p.tokens = p.tokens[1:] + p.accepted = append(p.accepted, t) + return t, nil +} + +// expectPChars determines if "t" consists of only pchars defined in RFC3986. +// +// https://www.ietf.org/rfc/rfc3986.txt, P.49 +// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" +// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" +// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" +// / "*" / "+" / "," / ";" / "=" +// pct-encoded = "%" HEXDIG HEXDIG +func expectPChars(t string) error { + const ( + init = iota + pct1 + pct2 + ) + st := init + for _, r := range t { + if st != init { + if !isHexDigit(r) { + return fmt.Errorf("invalid hexdigit: %c(%U)", r, r) + } + switch st { + case pct1: + st = pct2 + case pct2: + st = init + } + continue + } + + // unreserved + switch { + case 'A' <= r && r <= 'Z': + continue + case 'a' <= r && r <= 'z': + continue + case '0' <= r && r <= '9': + continue + } + switch r { + case '-', '.', '_', '~': + // unreserved + case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': + // sub-delims + case ':', '@': + // rest of pchar + case '%': + // pct-encoded + st = pct1 + default: + return fmt.Errorf("invalid character in path segment: %q(%U)", r, r) + } + } + if st != init { + return fmt.Errorf("invalid percent-encoding in %q", t) + } + return nil +} + +// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*). +func expectIdent(ident string) error { + if ident == "" { + return fmt.Errorf("empty identifier") + } + for pos, r := range ident { + switch { + case '0' <= r && r <= '9': + if pos == 0 { + return fmt.Errorf("identifier starting with digit: %s", ident) + } + continue + case 'A' <= r && r <= 'Z': + continue + case 'a' <= r && r <= 'z': + continue + case r == '_': + continue + default: + return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident) + } + } + return nil +} + +func isHexDigit(r rune) bool { + switch { + case '0' <= r && r <= '9': + return true + case 'A' <= r && r <= 'F': + return true + case 'a' <= r && r <= 'f': + return true + } + return false +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/types.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/types.go new file mode 100644 index 00000000..5a814a00 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/types.go @@ -0,0 +1,60 @@ +package httprule + +import ( + "fmt" + "strings" +) + +type template struct { + segments []segment + verb string + template string +} + +type segment interface { + fmt.Stringer + compile() (ops []op) +} + +type wildcard struct{} + +type deepWildcard struct{} + +type literal string + +type variable struct { + path string + segments []segment +} + +func (wildcard) String() string { + return "*" +} + +func (deepWildcard) String() string { + return "**" +} + +func (l literal) String() string { + return string(l) +} + +func (v variable) String() string { + var segs []string + for _, s := range v.segments { + segs = append(segs, s.String()) + } + return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/")) +} + +func (t template) String() string { + var segs []string + for _, s := range t.segments { + segs = append(segs, s.String()) + } + str := strings.Join(segs, "/") + if t.verb != "" { + str = fmt.Sprintf("%s:%s", str, t.verb) + } + return "/" + str +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel new file mode 100644 index 00000000..95f867a5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel @@ -0,0 +1,91 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package(default_visibility = ["//visibility:public"]) + +go_library( + name = "runtime", + srcs = [ + "context.go", + "convert.go", + "doc.go", + "errors.go", + "fieldmask.go", + "handler.go", + "marshal_httpbodyproto.go", + "marshal_json.go", + "marshal_jsonpb.go", + "marshal_proto.go", + "marshaler.go", + "marshaler_registry.go", + "mux.go", + "pattern.go", + "proto2_convert.go", + "query.go", + ], + importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/runtime", + deps = [ + "//internal/httprule", + "//utilities", + "@go_googleapis//google/api:httpbody_go_proto", + "@io_bazel_rules_go//proto/wkt:field_mask_go_proto", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//grpclog", + "@org_golang_google_grpc//metadata", + "@org_golang_google_grpc//status", + "@org_golang_google_protobuf//encoding/protojson", + "@org_golang_google_protobuf//proto", + "@org_golang_google_protobuf//reflect/protoreflect", + "@org_golang_google_protobuf//reflect/protoregistry", + "@org_golang_google_protobuf//types/known/durationpb", + "@org_golang_google_protobuf//types/known/timestamppb", + "@org_golang_google_protobuf//types/known/wrapperspb", + ], +) + +go_test( + name = "runtime_test", + size = "small", + srcs = [ + "context_test.go", + "convert_test.go", + "errors_test.go", + "fieldmask_test.go", + "handler_test.go", + "marshal_httpbodyproto_test.go", + "marshal_json_test.go", + "marshal_jsonpb_test.go", + "marshal_proto_test.go", + "marshaler_registry_test.go", + "mux_test.go", + "pattern_test.go", + "query_test.go", + ], + embed = [":runtime"], + deps = [ + "//runtime/internal/examplepb", + "//utilities", + "@com_github_google_go_cmp//cmp", + "@com_github_google_go_cmp//cmp/cmpopts", + "@go_googleapis//google/api:httpbody_go_proto", + "@go_googleapis//google/rpc:errdetails_go_proto", + "@go_googleapis//google/rpc:status_go_proto", + "@io_bazel_rules_go//proto/wkt:field_mask_go_proto", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//metadata", + "@org_golang_google_grpc//status", + "@org_golang_google_protobuf//encoding/protojson", + "@org_golang_google_protobuf//proto", + "@org_golang_google_protobuf//testing/protocmp", + "@org_golang_google_protobuf//types/known/durationpb", + "@org_golang_google_protobuf//types/known/emptypb", + "@org_golang_google_protobuf//types/known/structpb", + "@org_golang_google_protobuf//types/known/timestamppb", + "@org_golang_google_protobuf//types/known/wrapperspb", + ], +) + +alias( + name = "go_default_library", + actual = ":runtime", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go new file mode 100644 index 00000000..fb57b936 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go @@ -0,0 +1,345 @@ +package runtime + +import ( + "context" + "encoding/base64" + "fmt" + "net" + "net/http" + "net/textproto" + "strconv" + "strings" + "sync" + "time" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// MetadataHeaderPrefix is the http prefix that represents custom metadata +// parameters to or from a gRPC call. +const MetadataHeaderPrefix = "Grpc-Metadata-" + +// MetadataPrefix is prepended to permanent HTTP header keys (as specified +// by the IANA) when added to the gRPC context. +const MetadataPrefix = "grpcgateway-" + +// MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to +// HTTP headers in a response handled by grpc-gateway +const MetadataTrailerPrefix = "Grpc-Trailer-" + +const metadataGrpcTimeout = "Grpc-Timeout" +const metadataHeaderBinarySuffix = "-Bin" + +const xForwardedFor = "X-Forwarded-For" +const xForwardedHost = "X-Forwarded-Host" + +var ( + // DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound + // header isn't present. If the value is 0 the sent `context` will not have a timeout. + DefaultContextTimeout = 0 * time.Second +) + +type ( + rpcMethodKey struct{} + httpPathPatternKey struct{} + + AnnotateContextOption func(ctx context.Context) context.Context +) + +func WithHTTPPathPattern(pattern string) AnnotateContextOption { + return func(ctx context.Context) context.Context { + return withHTTPPathPattern(ctx, pattern) + } +} + +func decodeBinHeader(v string) ([]byte, error) { + if len(v)%4 == 0 { + // Input was padded, or padding was not necessary. + return base64.StdEncoding.DecodeString(v) + } + return base64.RawStdEncoding.DecodeString(v) +} + +/* +AnnotateContext adds context information such as metadata from the request. + +At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For", +except that the forwarded destination is not another HTTP service but rather +a gRPC service. +*/ +func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string, options ...AnnotateContextOption) (context.Context, error) { + ctx, md, err := annotateContext(ctx, mux, req, rpcMethodName, options...) + if err != nil { + return nil, err + } + if md == nil { + return ctx, nil + } + + return metadata.NewOutgoingContext(ctx, md), nil +} + +// AnnotateIncomingContext adds context information such as metadata from the request. +// Attach metadata as incoming context. +func AnnotateIncomingContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string, options ...AnnotateContextOption) (context.Context, error) { + ctx, md, err := annotateContext(ctx, mux, req, rpcMethodName, options...) + if err != nil { + return nil, err + } + if md == nil { + return ctx, nil + } + + return metadata.NewIncomingContext(ctx, md), nil +} + +func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string, options ...AnnotateContextOption) (context.Context, metadata.MD, error) { + ctx = withRPCMethod(ctx, rpcMethodName) + for _, o := range options { + ctx = o(ctx) + } + var pairs []string + timeout := DefaultContextTimeout + if tm := req.Header.Get(metadataGrpcTimeout); tm != "" { + var err error + timeout, err = timeoutDecode(tm) + if err != nil { + return nil, nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm) + } + } + + for key, vals := range req.Header { + key = textproto.CanonicalMIMEHeaderKey(key) + for _, val := range vals { + // For backwards-compatibility, pass through 'authorization' header with no prefix. + if key == "Authorization" { + pairs = append(pairs, "authorization", val) + } + if h, ok := mux.incomingHeaderMatcher(key); ok { + // Handles "-bin" metadata in grpc, since grpc will do another base64 + // encode before sending to server, we need to decode it first. + if strings.HasSuffix(key, metadataHeaderBinarySuffix) { + b, err := decodeBinHeader(val) + if err != nil { + return nil, nil, status.Errorf(codes.InvalidArgument, "invalid binary header %s: %s", key, err) + } + + val = string(b) + } + pairs = append(pairs, h, val) + } + } + } + if host := req.Header.Get(xForwardedHost); host != "" { + pairs = append(pairs, strings.ToLower(xForwardedHost), host) + } else if req.Host != "" { + pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host) + } + + if addr := req.RemoteAddr; addr != "" { + if remoteIP, _, err := net.SplitHostPort(addr); err == nil { + if fwd := req.Header.Get(xForwardedFor); fwd == "" { + pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIP) + } else { + pairs = append(pairs, strings.ToLower(xForwardedFor), fmt.Sprintf("%s, %s", fwd, remoteIP)) + } + } + } + + if timeout != 0 { + //nolint:govet // The context outlives this function + ctx, _ = context.WithTimeout(ctx, timeout) + } + if len(pairs) == 0 { + return ctx, nil, nil + } + md := metadata.Pairs(pairs...) + for _, mda := range mux.metadataAnnotators { + md = metadata.Join(md, mda(ctx, req)) + } + return ctx, md, nil +} + +// ServerMetadata consists of metadata sent from gRPC server. +type ServerMetadata struct { + HeaderMD metadata.MD + TrailerMD metadata.MD +} + +type serverMetadataKey struct{} + +// NewServerMetadataContext creates a new context with ServerMetadata +func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context { + return context.WithValue(ctx, serverMetadataKey{}, md) +} + +// ServerMetadataFromContext returns the ServerMetadata in ctx +func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) { + md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata) + return +} + +// ServerTransportStream implements grpc.ServerTransportStream. +// It should only be used by the generated files to support grpc.SendHeader +// outside of gRPC server use. +type ServerTransportStream struct { + mu sync.Mutex + header metadata.MD + trailer metadata.MD +} + +// Method returns the method for the stream. +func (s *ServerTransportStream) Method() string { + return "" +} + +// Header returns the header metadata of the stream. +func (s *ServerTransportStream) Header() metadata.MD { + s.mu.Lock() + defer s.mu.Unlock() + return s.header.Copy() +} + +// SetHeader sets the header metadata. +func (s *ServerTransportStream) SetHeader(md metadata.MD) error { + if md.Len() == 0 { + return nil + } + + s.mu.Lock() + s.header = metadata.Join(s.header, md) + s.mu.Unlock() + return nil +} + +// SendHeader sets the header metadata. +func (s *ServerTransportStream) SendHeader(md metadata.MD) error { + return s.SetHeader(md) +} + +// Trailer returns the cached trailer metadata. +func (s *ServerTransportStream) Trailer() metadata.MD { + s.mu.Lock() + defer s.mu.Unlock() + return s.trailer.Copy() +} + +// SetTrailer sets the trailer metadata. +func (s *ServerTransportStream) SetTrailer(md metadata.MD) error { + if md.Len() == 0 { + return nil + } + + s.mu.Lock() + s.trailer = metadata.Join(s.trailer, md) + s.mu.Unlock() + return nil +} + +func timeoutDecode(s string) (time.Duration, error) { + size := len(s) + if size < 2 { + return 0, fmt.Errorf("timeout string is too short: %q", s) + } + d, ok := timeoutUnitToDuration(s[size-1]) + if !ok { + return 0, fmt.Errorf("timeout unit is not recognized: %q", s) + } + t, err := strconv.ParseInt(s[:size-1], 10, 64) + if err != nil { + return 0, err + } + return d * time.Duration(t), nil +} + +func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) { + switch u { + case 'H': + return time.Hour, true + case 'M': + return time.Minute, true + case 'S': + return time.Second, true + case 'm': + return time.Millisecond, true + case 'u': + return time.Microsecond, true + case 'n': + return time.Nanosecond, true + default: + } + return +} + +// isPermanentHTTPHeader checks whether hdr belongs to the list of +// permanent request headers maintained by IANA. +// http://www.iana.org/assignments/message-headers/message-headers.xml +func isPermanentHTTPHeader(hdr string) bool { + switch hdr { + case + "Accept", + "Accept-Charset", + "Accept-Language", + "Accept-Ranges", + "Authorization", + "Cache-Control", + "Content-Type", + "Cookie", + "Date", + "Expect", + "From", + "Host", + "If-Match", + "If-Modified-Since", + "If-None-Match", + "If-Schedule-Tag-Match", + "If-Unmodified-Since", + "Max-Forwards", + "Origin", + "Pragma", + "Referer", + "User-Agent", + "Via", + "Warning": + return true + } + return false +} + +// RPCMethod returns the method string for the server context. The returned +// string is in the format of "/package.service/method". +func RPCMethod(ctx context.Context) (string, bool) { + m := ctx.Value(rpcMethodKey{}) + if m == nil { + return "", false + } + ms, ok := m.(string) + if !ok { + return "", false + } + return ms, true +} + +func withRPCMethod(ctx context.Context, rpcMethodName string) context.Context { + return context.WithValue(ctx, rpcMethodKey{}, rpcMethodName) +} + +// HTTPPathPattern returns the HTTP path pattern string relating to the HTTP handler, if one exists. +// The format of the returned string is defined by the google.api.http path template type. +func HTTPPathPattern(ctx context.Context) (string, bool) { + m := ctx.Value(httpPathPatternKey{}) + if m == nil { + return "", false + } + ms, ok := m.(string) + if !ok { + return "", false + } + return ms, true +} + +func withHTTPPathPattern(ctx context.Context, httpPathPattern string) context.Context { + return context.WithValue(ctx, httpPathPatternKey{}, httpPathPattern) +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go new file mode 100644 index 00000000..e6bc4e6c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go @@ -0,0 +1,322 @@ +package runtime + +import ( + "encoding/base64" + "fmt" + "strconv" + "strings" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// String just returns the given string. +// It is just for compatibility to other types. +func String(val string) (string, error) { + return val, nil +} + +// StringSlice converts 'val' where individual strings are separated by +// 'sep' into a string slice. +func StringSlice(val, sep string) ([]string, error) { + return strings.Split(val, sep), nil +} + +// Bool converts the given string representation of a boolean value into bool. +func Bool(val string) (bool, error) { + return strconv.ParseBool(val) +} + +// BoolSlice converts 'val' where individual booleans are separated by +// 'sep' into a bool slice. +func BoolSlice(val, sep string) ([]bool, error) { + s := strings.Split(val, sep) + values := make([]bool, len(s)) + for i, v := range s { + value, err := Bool(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Float64 converts the given string representation into representation of a floating point number into float64. +func Float64(val string) (float64, error) { + return strconv.ParseFloat(val, 64) +} + +// Float64Slice converts 'val' where individual floating point numbers are separated by +// 'sep' into a float64 slice. +func Float64Slice(val, sep string) ([]float64, error) { + s := strings.Split(val, sep) + values := make([]float64, len(s)) + for i, v := range s { + value, err := Float64(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Float32 converts the given string representation of a floating point number into float32. +func Float32(val string) (float32, error) { + f, err := strconv.ParseFloat(val, 32) + if err != nil { + return 0, err + } + return float32(f), nil +} + +// Float32Slice converts 'val' where individual floating point numbers are separated by +// 'sep' into a float32 slice. +func Float32Slice(val, sep string) ([]float32, error) { + s := strings.Split(val, sep) + values := make([]float32, len(s)) + for i, v := range s { + value, err := Float32(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Int64 converts the given string representation of an integer into int64. +func Int64(val string) (int64, error) { + return strconv.ParseInt(val, 0, 64) +} + +// Int64Slice converts 'val' where individual integers are separated by +// 'sep' into a int64 slice. +func Int64Slice(val, sep string) ([]int64, error) { + s := strings.Split(val, sep) + values := make([]int64, len(s)) + for i, v := range s { + value, err := Int64(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Int32 converts the given string representation of an integer into int32. +func Int32(val string) (int32, error) { + i, err := strconv.ParseInt(val, 0, 32) + if err != nil { + return 0, err + } + return int32(i), nil +} + +// Int32Slice converts 'val' where individual integers are separated by +// 'sep' into a int32 slice. +func Int32Slice(val, sep string) ([]int32, error) { + s := strings.Split(val, sep) + values := make([]int32, len(s)) + for i, v := range s { + value, err := Int32(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Uint64 converts the given string representation of an integer into uint64. +func Uint64(val string) (uint64, error) { + return strconv.ParseUint(val, 0, 64) +} + +// Uint64Slice converts 'val' where individual integers are separated by +// 'sep' into a uint64 slice. +func Uint64Slice(val, sep string) ([]uint64, error) { + s := strings.Split(val, sep) + values := make([]uint64, len(s)) + for i, v := range s { + value, err := Uint64(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Uint32 converts the given string representation of an integer into uint32. +func Uint32(val string) (uint32, error) { + i, err := strconv.ParseUint(val, 0, 32) + if err != nil { + return 0, err + } + return uint32(i), nil +} + +// Uint32Slice converts 'val' where individual integers are separated by +// 'sep' into a uint32 slice. +func Uint32Slice(val, sep string) ([]uint32, error) { + s := strings.Split(val, sep) + values := make([]uint32, len(s)) + for i, v := range s { + value, err := Uint32(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Bytes converts the given string representation of a byte sequence into a slice of bytes +// A bytes sequence is encoded in URL-safe base64 without padding +func Bytes(val string) ([]byte, error) { + b, err := base64.StdEncoding.DecodeString(val) + if err != nil { + b, err = base64.URLEncoding.DecodeString(val) + if err != nil { + return nil, err + } + } + return b, nil +} + +// BytesSlice converts 'val' where individual bytes sequences, encoded in URL-safe +// base64 without padding, are separated by 'sep' into a slice of bytes slices slice. +func BytesSlice(val, sep string) ([][]byte, error) { + s := strings.Split(val, sep) + values := make([][]byte, len(s)) + for i, v := range s { + value, err := Bytes(v) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +// Timestamp converts the given RFC3339 formatted string into a timestamp.Timestamp. +func Timestamp(val string) (*timestamppb.Timestamp, error) { + var r timestamppb.Timestamp + val = strconv.Quote(strings.Trim(val, `"`)) + unmarshaler := &protojson.UnmarshalOptions{} + err := unmarshaler.Unmarshal([]byte(val), &r) + if err != nil { + return nil, err + } + return &r, nil +} + +// Duration converts the given string into a timestamp.Duration. +func Duration(val string) (*durationpb.Duration, error) { + var r durationpb.Duration + val = strconv.Quote(strings.Trim(val, `"`)) + unmarshaler := &protojson.UnmarshalOptions{} + err := unmarshaler.Unmarshal([]byte(val), &r) + if err != nil { + return nil, err + } + return &r, nil +} + +// Enum converts the given string into an int32 that should be type casted into the +// correct enum proto type. +func Enum(val string, enumValMap map[string]int32) (int32, error) { + e, ok := enumValMap[val] + if ok { + return e, nil + } + + i, err := Int32(val) + if err != nil { + return 0, fmt.Errorf("%s is not valid", val) + } + for _, v := range enumValMap { + if v == i { + return i, nil + } + } + return 0, fmt.Errorf("%s is not valid", val) +} + +// EnumSlice converts 'val' where individual enums are separated by 'sep' +// into a int32 slice. Each individual int32 should be type casted into the +// correct enum proto type. +func EnumSlice(val, sep string, enumValMap map[string]int32) ([]int32, error) { + s := strings.Split(val, sep) + values := make([]int32, len(s)) + for i, v := range s { + value, err := Enum(v, enumValMap) + if err != nil { + return values, err + } + values[i] = value + } + return values, nil +} + +/* + Support fot google.protobuf.wrappers on top of primitive types +*/ + +// StringValue well-known type support as wrapper around string type +func StringValue(val string) (*wrapperspb.StringValue, error) { + return &wrapperspb.StringValue{Value: val}, nil +} + +// FloatValue well-known type support as wrapper around float32 type +func FloatValue(val string) (*wrapperspb.FloatValue, error) { + parsedVal, err := Float32(val) + return &wrapperspb.FloatValue{Value: parsedVal}, err +} + +// DoubleValue well-known type support as wrapper around float64 type +func DoubleValue(val string) (*wrapperspb.DoubleValue, error) { + parsedVal, err := Float64(val) + return &wrapperspb.DoubleValue{Value: parsedVal}, err +} + +// BoolValue well-known type support as wrapper around bool type +func BoolValue(val string) (*wrapperspb.BoolValue, error) { + parsedVal, err := Bool(val) + return &wrapperspb.BoolValue{Value: parsedVal}, err +} + +// Int32Value well-known type support as wrapper around int32 type +func Int32Value(val string) (*wrapperspb.Int32Value, error) { + parsedVal, err := Int32(val) + return &wrapperspb.Int32Value{Value: parsedVal}, err +} + +// UInt32Value well-known type support as wrapper around uint32 type +func UInt32Value(val string) (*wrapperspb.UInt32Value, error) { + parsedVal, err := Uint32(val) + return &wrapperspb.UInt32Value{Value: parsedVal}, err +} + +// Int64Value well-known type support as wrapper around int64 type +func Int64Value(val string) (*wrapperspb.Int64Value, error) { + parsedVal, err := Int64(val) + return &wrapperspb.Int64Value{Value: parsedVal}, err +} + +// UInt64Value well-known type support as wrapper around uint64 type +func UInt64Value(val string) (*wrapperspb.UInt64Value, error) { + parsedVal, err := Uint64(val) + return &wrapperspb.UInt64Value{Value: parsedVal}, err +} + +// BytesValue well-known type support as wrapper around bytes[] type +func BytesValue(val string) (*wrapperspb.BytesValue, error) { + parsedVal, err := Bytes(val) + return &wrapperspb.BytesValue{Value: parsedVal}, err +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go new file mode 100644 index 00000000..b6e5ddf7 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go @@ -0,0 +1,5 @@ +/* +Package runtime contains runtime helper functions used by +servers which protoc-gen-grpc-gateway generates. +*/ +package runtime diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go new file mode 100644 index 00000000..d9e0013c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go @@ -0,0 +1,180 @@ +package runtime + +import ( + "context" + "errors" + "io" + "net/http" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// ErrorHandlerFunc is the signature used to configure error handling. +type ErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error) + +// StreamErrorHandlerFunc is the signature used to configure stream error handling. +type StreamErrorHandlerFunc func(context.Context, error) *status.Status + +// RoutingErrorHandlerFunc is the signature used to configure error handling for routing errors. +type RoutingErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, int) + +// HTTPStatusError is the error to use when needing to provide a different HTTP status code for an error +// passed to the DefaultRoutingErrorHandler. +type HTTPStatusError struct { + HTTPStatus int + Err error +} + +func (e *HTTPStatusError) Error() string { + return e.Err.Error() +} + +// HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status. +// See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto +func HTTPStatusFromCode(code codes.Code) int { + switch code { + case codes.OK: + return http.StatusOK + case codes.Canceled: + return http.StatusRequestTimeout + case codes.Unknown: + return http.StatusInternalServerError + case codes.InvalidArgument: + return http.StatusBadRequest + case codes.DeadlineExceeded: + return http.StatusGatewayTimeout + case codes.NotFound: + return http.StatusNotFound + case codes.AlreadyExists: + return http.StatusConflict + case codes.PermissionDenied: + return http.StatusForbidden + case codes.Unauthenticated: + return http.StatusUnauthorized + case codes.ResourceExhausted: + return http.StatusTooManyRequests + case codes.FailedPrecondition: + // Note, this deliberately doesn't translate to the similarly named '412 Precondition Failed' HTTP response status. + return http.StatusBadRequest + case codes.Aborted: + return http.StatusConflict + case codes.OutOfRange: + return http.StatusBadRequest + case codes.Unimplemented: + return http.StatusNotImplemented + case codes.Internal: + return http.StatusInternalServerError + case codes.Unavailable: + return http.StatusServiceUnavailable + case codes.DataLoss: + return http.StatusInternalServerError + } + + grpclog.Infof("Unknown gRPC error code: %v", code) + return http.StatusInternalServerError +} + +// HTTPError uses the mux-configured error handler. +func HTTPError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) { + mux.errorHandler(ctx, mux, marshaler, w, r, err) +} + +// DefaultHTTPErrorHandler is the default error handler. +// If "err" is a gRPC Status, the function replies with the status code mapped by HTTPStatusFromCode. +// If "err" is a HTTPStatusError, the function replies with the status code provide by that struct. This is +// intended to allow passing through of specific statuses via the function set via WithRoutingErrorHandler +// for the ServeMux constructor to handle edge cases which the standard mappings in HTTPStatusFromCode +// are insufficient for. +// If otherwise, it replies with http.StatusInternalServerError. +// +// The response body written by this function is a Status message marshaled by the Marshaler. +func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) { + // return Internal when Marshal failed + const fallback = `{"code": 13, "message": "failed to marshal error message"}` + + var customStatus *HTTPStatusError + if errors.As(err, &customStatus) { + err = customStatus.Err + } + + s := status.Convert(err) + pb := s.Proto() + + w.Header().Del("Trailer") + w.Header().Del("Transfer-Encoding") + + contentType := marshaler.ContentType(pb) + w.Header().Set("Content-Type", contentType) + + if s.Code() == codes.Unauthenticated { + w.Header().Set("WWW-Authenticate", s.Message()) + } + + buf, merr := marshaler.Marshal(pb) + if merr != nil { + grpclog.Infof("Failed to marshal error message %q: %v", s, merr) + w.WriteHeader(http.StatusInternalServerError) + if _, err := io.WriteString(w, fallback); err != nil { + grpclog.Infof("Failed to write response: %v", err) + } + return + } + + md, ok := ServerMetadataFromContext(ctx) + if !ok { + grpclog.Infof("Failed to extract ServerMetadata from context") + } + + handleForwardResponseServerMetadata(w, mux, md) + + // RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2 + // Unless the request includes a TE header field indicating "trailers" + // is acceptable, as described in Section 4.3, a server SHOULD NOT + // generate trailer fields that it believes are necessary for the user + // agent to receive. + doForwardTrailers := requestAcceptsTrailers(r) + + if doForwardTrailers { + handleForwardResponseTrailerHeader(w, md) + w.Header().Set("Transfer-Encoding", "chunked") + } + + st := HTTPStatusFromCode(s.Code()) + if customStatus != nil { + st = customStatus.HTTPStatus + } + + w.WriteHeader(st) + if _, err := w.Write(buf); err != nil { + grpclog.Infof("Failed to write response: %v", err) + } + + if doForwardTrailers { + handleForwardResponseTrailer(w, md) + } +} + +func DefaultStreamErrorHandler(_ context.Context, err error) *status.Status { + return status.Convert(err) +} + +// DefaultRoutingErrorHandler is our default handler for routing errors. +// By default http error codes mapped on the following error codes: +// NotFound -> grpc.NotFound +// StatusBadRequest -> grpc.InvalidArgument +// MethodNotAllowed -> grpc.Unimplemented +// Other -> grpc.Internal, method is not expecting to be called for anything else +func DefaultRoutingErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) { + sterr := status.Error(codes.Internal, "Unexpected routing error") + switch httpStatus { + case http.StatusBadRequest: + sterr = status.Error(codes.InvalidArgument, http.StatusText(httpStatus)) + case http.StatusMethodNotAllowed: + sterr = status.Error(codes.Unimplemented, http.StatusText(httpStatus)) + case http.StatusNotFound: + sterr = status.Error(codes.NotFound, http.StatusText(httpStatus)) + } + mux.errorHandler(ctx, mux, marshaler, w, r, sterr) +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go new file mode 100644 index 00000000..0138ed2f --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go @@ -0,0 +1,165 @@ +package runtime + +import ( + "encoding/json" + "fmt" + "io" + "sort" + + "google.golang.org/genproto/protobuf/field_mask" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +func getFieldByName(fields protoreflect.FieldDescriptors, name string) protoreflect.FieldDescriptor { + fd := fields.ByName(protoreflect.Name(name)) + if fd != nil { + return fd + } + + return fields.ByJSONName(name) +} + +// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body. +func FieldMaskFromRequestBody(r io.Reader, msg proto.Message) (*field_mask.FieldMask, error) { + fm := &field_mask.FieldMask{} + var root interface{} + + if err := json.NewDecoder(r).Decode(&root); err != nil { + if err == io.EOF { + return fm, nil + } + return nil, err + } + + queue := []fieldMaskPathItem{{node: root, msg: msg.ProtoReflect()}} + for len(queue) > 0 { + // dequeue an item + item := queue[0] + queue = queue[1:] + + m, ok := item.node.(map[string]interface{}) + switch { + case ok: + // if the item is an object, then enqueue all of its children + for k, v := range m { + if item.msg == nil { + return nil, fmt.Errorf("JSON structure did not match request type") + } + + fd := getFieldByName(item.msg.Descriptor().Fields(), k) + if fd == nil { + return nil, fmt.Errorf("could not find field %q in %q", k, item.msg.Descriptor().FullName()) + } + + if isDynamicProtoMessage(fd.Message()) { + for _, p := range buildPathsBlindly(k, v) { + newPath := p + if item.path != "" { + newPath = item.path + "." + newPath + } + queue = append(queue, fieldMaskPathItem{path: newPath}) + } + continue + } + + if isProtobufAnyMessage(fd.Message()) { + _, hasTypeField := v.(map[string]interface{})["@type"] + if hasTypeField { + queue = append(queue, fieldMaskPathItem{path: k}) + continue + } else { + return nil, fmt.Errorf("could not find field @type in %q in message %q", k, item.msg.Descriptor().FullName()) + } + + } + + child := fieldMaskPathItem{ + node: v, + } + if item.path == "" { + child.path = string(fd.FullName().Name()) + } else { + child.path = item.path + "." + string(fd.FullName().Name()) + } + + switch { + case fd.IsList(), fd.IsMap(): + // As per: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/field_mask.proto#L85-L86 + // Do not recurse into repeated fields. The repeated field goes on the end of the path and we stop. + fm.Paths = append(fm.Paths, child.path) + case fd.Message() != nil: + child.msg = item.msg.Get(fd).Message() + fallthrough + default: + queue = append(queue, child) + } + } + case len(item.path) > 0: + // otherwise, it's a leaf node so print its path + fm.Paths = append(fm.Paths, item.path) + } + } + + // Sort for deterministic output in the presence + // of repeated fields. + sort.Strings(fm.Paths) + + return fm, nil +} + +func isProtobufAnyMessage(md protoreflect.MessageDescriptor) bool { + return md != nil && (md.FullName() == "google.protobuf.Any") +} + +func isDynamicProtoMessage(md protoreflect.MessageDescriptor) bool { + return md != nil && (md.FullName() == "google.protobuf.Struct" || md.FullName() == "google.protobuf.Value") +} + +// buildPathsBlindly does not attempt to match proto field names to the +// json value keys. Instead it relies completely on the structure of +// the unmarshalled json contained within in. +// Returns a slice containing all subpaths with the root at the +// passed in name and json value. +func buildPathsBlindly(name string, in interface{}) []string { + m, ok := in.(map[string]interface{}) + if !ok { + return []string{name} + } + + var paths []string + queue := []fieldMaskPathItem{{path: name, node: m}} + for len(queue) > 0 { + cur := queue[0] + queue = queue[1:] + + m, ok := cur.node.(map[string]interface{}) + if !ok { + // This should never happen since we should always check that we only add + // nodes of type map[string]interface{} to the queue. + continue + } + for k, v := range m { + if mi, ok := v.(map[string]interface{}); ok { + queue = append(queue, fieldMaskPathItem{path: cur.path + "." + k, node: mi}) + } else { + // This is not a struct, so there are no more levels to descend. + curPath := cur.path + "." + k + paths = append(paths, curPath) + } + } + } + return paths +} + +// fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask +type fieldMaskPathItem struct { + // the list of prior fields leading up to node connected by dots + path string + + // a generic decoded json object the current item to inspect for further path extraction + node interface{} + + // parent message + msg protoreflect.Message +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go new file mode 100644 index 00000000..d1e21df4 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go @@ -0,0 +1,223 @@ +package runtime + +import ( + "context" + "fmt" + "io" + "net/http" + "net/textproto" + "strings" + + "google.golang.org/genproto/googleapis/api/httpbody" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// ForwardResponseStream forwards the stream from gRPC server to REST client. +func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) { + f, ok := w.(http.Flusher) + if !ok { + grpclog.Infof("Flush not supported in %T", w) + http.Error(w, "unexpected type of web server", http.StatusInternalServerError) + return + } + + md, ok := ServerMetadataFromContext(ctx) + if !ok { + grpclog.Infof("Failed to extract ServerMetadata from context") + http.Error(w, "unexpected error", http.StatusInternalServerError) + return + } + handleForwardResponseServerMetadata(w, mux, md) + + w.Header().Set("Transfer-Encoding", "chunked") + if err := handleForwardResponseOptions(ctx, w, nil, opts); err != nil { + HTTPError(ctx, mux, marshaler, w, req, err) + return + } + + var delimiter []byte + if d, ok := marshaler.(Delimited); ok { + delimiter = d.Delimiter() + } else { + delimiter = []byte("\n") + } + + var wroteHeader bool + for { + resp, err := recv() + if err == io.EOF { + return + } + if err != nil { + handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err) + return + } + if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil { + handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err) + return + } + + if !wroteHeader { + w.Header().Set("Content-Type", marshaler.ContentType(resp)) + } + + var buf []byte + httpBody, isHTTPBody := resp.(*httpbody.HttpBody) + switch { + case resp == nil: + buf, err = marshaler.Marshal(errorChunk(status.New(codes.Internal, "empty response"))) + case isHTTPBody: + buf = httpBody.GetData() + default: + result := map[string]interface{}{"result": resp} + if rb, ok := resp.(responseBody); ok { + result["result"] = rb.XXX_ResponseBody() + } + + buf, err = marshaler.Marshal(result) + } + + if err != nil { + grpclog.Infof("Failed to marshal response chunk: %v", err) + handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err) + return + } + if _, err = w.Write(buf); err != nil { + grpclog.Infof("Failed to send response chunk: %v", err) + return + } + wroteHeader = true + if _, err = w.Write(delimiter); err != nil { + grpclog.Infof("Failed to send delimiter chunk: %v", err) + return + } + f.Flush() + } +} + +func handleForwardResponseServerMetadata(w http.ResponseWriter, mux *ServeMux, md ServerMetadata) { + for k, vs := range md.HeaderMD { + if h, ok := mux.outgoingHeaderMatcher(k); ok { + for _, v := range vs { + w.Header().Add(h, v) + } + } + } +} + +func handleForwardResponseTrailerHeader(w http.ResponseWriter, md ServerMetadata) { + for k := range md.TrailerMD { + tKey := textproto.CanonicalMIMEHeaderKey(fmt.Sprintf("%s%s", MetadataTrailerPrefix, k)) + w.Header().Add("Trailer", tKey) + } +} + +func handleForwardResponseTrailer(w http.ResponseWriter, md ServerMetadata) { + for k, vs := range md.TrailerMD { + tKey := fmt.Sprintf("%s%s", MetadataTrailerPrefix, k) + for _, v := range vs { + w.Header().Add(tKey, v) + } + } +} + +// responseBody interface contains method for getting field for marshaling to the response body +// this method is generated for response struct from the value of `response_body` in the `google.api.HttpRule` +type responseBody interface { + XXX_ResponseBody() interface{} +} + +// ForwardResponseMessage forwards the message "resp" from gRPC server to REST client. +func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) { + md, ok := ServerMetadataFromContext(ctx) + if !ok { + grpclog.Infof("Failed to extract ServerMetadata from context") + } + + handleForwardResponseServerMetadata(w, mux, md) + + // RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2 + // Unless the request includes a TE header field indicating "trailers" + // is acceptable, as described in Section 4.3, a server SHOULD NOT + // generate trailer fields that it believes are necessary for the user + // agent to receive. + doForwardTrailers := requestAcceptsTrailers(req) + + if doForwardTrailers { + handleForwardResponseTrailerHeader(w, md) + w.Header().Set("Transfer-Encoding", "chunked") + } + + handleForwardResponseTrailerHeader(w, md) + + contentType := marshaler.ContentType(resp) + w.Header().Set("Content-Type", contentType) + + if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil { + HTTPError(ctx, mux, marshaler, w, req, err) + return + } + var buf []byte + var err error + if rb, ok := resp.(responseBody); ok { + buf, err = marshaler.Marshal(rb.XXX_ResponseBody()) + } else { + buf, err = marshaler.Marshal(resp) + } + if err != nil { + grpclog.Infof("Marshal error: %v", err) + HTTPError(ctx, mux, marshaler, w, req, err) + return + } + + if _, err = w.Write(buf); err != nil { + grpclog.Infof("Failed to write response: %v", err) + } + + if doForwardTrailers { + handleForwardResponseTrailer(w, md) + } +} + +func requestAcceptsTrailers(req *http.Request) bool { + te := req.Header.Get("TE") + return strings.Contains(strings.ToLower(te), "trailers") +} + +func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, resp proto.Message, opts []func(context.Context, http.ResponseWriter, proto.Message) error) error { + if len(opts) == 0 { + return nil + } + for _, opt := range opts { + if err := opt(ctx, w, resp); err != nil { + grpclog.Infof("Error handling ForwardResponseOptions: %v", err) + return err + } + } + return nil +} + +func handleForwardResponseStreamError(ctx context.Context, wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, req *http.Request, mux *ServeMux, err error) { + st := mux.streamErrorHandler(ctx, err) + msg := errorChunk(st) + if !wroteHeader { + w.Header().Set("Content-Type", marshaler.ContentType(msg)) + w.WriteHeader(HTTPStatusFromCode(st.Code())) + } + buf, merr := marshaler.Marshal(msg) + if merr != nil { + grpclog.Infof("Failed to marshal an error: %v", merr) + return + } + if _, werr := w.Write(buf); werr != nil { + grpclog.Infof("Failed to notify error to client: %v", werr) + return + } +} + +func errorChunk(st *status.Status) map[string]proto.Message { + return map[string]proto.Message{"error": st.Proto()} +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go new file mode 100644 index 00000000..b86135c8 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go @@ -0,0 +1,32 @@ +package runtime + +import ( + "google.golang.org/genproto/googleapis/api/httpbody" +) + +// HTTPBodyMarshaler is a Marshaler which supports marshaling of a +// google.api.HttpBody message as the full response body if it is +// the actual message used as the response. If not, then this will +// simply fallback to the Marshaler specified as its default Marshaler. +type HTTPBodyMarshaler struct { + Marshaler +} + +// ContentType returns its specified content type in case v is a +// google.api.HttpBody message, otherwise it will fall back to the default Marshalers +// content type. +func (h *HTTPBodyMarshaler) ContentType(v interface{}) string { + if httpBody, ok := v.(*httpbody.HttpBody); ok { + return httpBody.GetContentType() + } + return h.Marshaler.ContentType(v) +} + +// Marshal marshals "v" by returning the body bytes if v is a +// google.api.HttpBody message, otherwise it falls back to the default Marshaler. +func (h *HTTPBodyMarshaler) Marshal(v interface{}) ([]byte, error) { + if httpBody, ok := v.(*httpbody.HttpBody); ok { + return httpBody.Data, nil + } + return h.Marshaler.Marshal(v) +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go new file mode 100644 index 00000000..d6aa8257 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go @@ -0,0 +1,45 @@ +package runtime + +import ( + "encoding/json" + "io" +) + +// JSONBuiltin is a Marshaler which marshals/unmarshals into/from JSON +// with the standard "encoding/json" package of Golang. +// Although it is generally faster for simple proto messages than JSONPb, +// it does not support advanced features of protobuf, e.g. map, oneof, .... +// +// The NewEncoder and NewDecoder types return *json.Encoder and +// *json.Decoder respectively. +type JSONBuiltin struct{} + +// ContentType always Returns "application/json". +func (*JSONBuiltin) ContentType(_ interface{}) string { + return "application/json" +} + +// Marshal marshals "v" into JSON +func (j *JSONBuiltin) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +// Unmarshal unmarshals JSON data into "v". +func (j *JSONBuiltin) Unmarshal(data []byte, v interface{}) error { + return json.Unmarshal(data, v) +} + +// NewDecoder returns a Decoder which reads JSON stream from "r". +func (j *JSONBuiltin) NewDecoder(r io.Reader) Decoder { + return json.NewDecoder(r) +} + +// NewEncoder returns an Encoder which writes JSON stream into "w". +func (j *JSONBuiltin) NewEncoder(w io.Writer) Encoder { + return json.NewEncoder(w) +} + +// Delimiter for newline encoded JSON streams. +func (j *JSONBuiltin) Delimiter() []byte { + return []byte("\n") +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go new file mode 100644 index 00000000..7387c8e3 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go @@ -0,0 +1,344 @@ +package runtime + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "reflect" + "strconv" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" +) + +// JSONPb is a Marshaler which marshals/unmarshals into/from JSON +// with the "google.golang.org/protobuf/encoding/protojson" marshaler. +// It supports the full functionality of protobuf unlike JSONBuiltin. +// +// The NewDecoder method returns a DecoderWrapper, so the underlying +// *json.Decoder methods can be used. +type JSONPb struct { + protojson.MarshalOptions + protojson.UnmarshalOptions +} + +// ContentType always returns "application/json". +func (*JSONPb) ContentType(_ interface{}) string { + return "application/json" +} + +// Marshal marshals "v" into JSON. +func (j *JSONPb) Marshal(v interface{}) ([]byte, error) { + if _, ok := v.(proto.Message); !ok { + return j.marshalNonProtoField(v) + } + + var buf bytes.Buffer + if err := j.marshalTo(&buf, v); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error { + p, ok := v.(proto.Message) + if !ok { + buf, err := j.marshalNonProtoField(v) + if err != nil { + return err + } + _, err = w.Write(buf) + return err + } + b, err := j.MarshalOptions.Marshal(p) + if err != nil { + return err + } + + _, err = w.Write(b) + return err +} + +var ( + // protoMessageType is stored to prevent constant lookup of the same type at runtime. + protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem() +) + +// marshalNonProto marshals a non-message field of a protobuf message. +// This function does not correctly marshal arbitrary data structures into JSON, +// it is only capable of marshaling non-message field values of protobuf, +// i.e. primitive types, enums; pointers to primitives or enums; maps from +// integer/string types to primitives/enums/pointers to messages. +func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) { + if v == nil { + return []byte("null"), nil + } + rv := reflect.ValueOf(v) + for rv.Kind() == reflect.Ptr { + if rv.IsNil() { + return []byte("null"), nil + } + rv = rv.Elem() + } + + if rv.Kind() == reflect.Slice { + if rv.IsNil() { + if j.EmitUnpopulated { + return []byte("[]"), nil + } + return []byte("null"), nil + } + + if rv.Type().Elem().Implements(protoMessageType) { + var buf bytes.Buffer + err := buf.WriteByte('[') + if err != nil { + return nil, err + } + for i := 0; i < rv.Len(); i++ { + if i != 0 { + err = buf.WriteByte(',') + if err != nil { + return nil, err + } + } + if err = j.marshalTo(&buf, rv.Index(i).Interface().(proto.Message)); err != nil { + return nil, err + } + } + err = buf.WriteByte(']') + if err != nil { + return nil, err + } + + return buf.Bytes(), nil + } + + if rv.Type().Elem().Implements(typeProtoEnum) { + var buf bytes.Buffer + err := buf.WriteByte('[') + if err != nil { + return nil, err + } + for i := 0; i < rv.Len(); i++ { + if i != 0 { + err = buf.WriteByte(',') + if err != nil { + return nil, err + } + } + if j.UseEnumNumbers { + _, err = buf.WriteString(strconv.FormatInt(rv.Index(i).Int(), 10)) + } else { + _, err = buf.WriteString("\"" + rv.Index(i).Interface().(protoEnum).String() + "\"") + } + if err != nil { + return nil, err + } + } + err = buf.WriteByte(']') + if err != nil { + return nil, err + } + + return buf.Bytes(), nil + } + } + + if rv.Kind() == reflect.Map { + m := make(map[string]*json.RawMessage) + for _, k := range rv.MapKeys() { + buf, err := j.Marshal(rv.MapIndex(k).Interface()) + if err != nil { + return nil, err + } + m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf) + } + if j.Indent != "" { + return json.MarshalIndent(m, "", j.Indent) + } + return json.Marshal(m) + } + if enum, ok := rv.Interface().(protoEnum); ok && !j.UseEnumNumbers { + return json.Marshal(enum.String()) + } + return json.Marshal(rv.Interface()) +} + +// Unmarshal unmarshals JSON "data" into "v" +func (j *JSONPb) Unmarshal(data []byte, v interface{}) error { + return unmarshalJSONPb(data, j.UnmarshalOptions, v) +} + +// NewDecoder returns a Decoder which reads JSON stream from "r". +func (j *JSONPb) NewDecoder(r io.Reader) Decoder { + d := json.NewDecoder(r) + return DecoderWrapper{ + Decoder: d, + UnmarshalOptions: j.UnmarshalOptions, + } +} + +// DecoderWrapper is a wrapper around a *json.Decoder that adds +// support for protos to the Decode method. +type DecoderWrapper struct { + *json.Decoder + protojson.UnmarshalOptions +} + +// Decode wraps the embedded decoder's Decode method to support +// protos using a jsonpb.Unmarshaler. +func (d DecoderWrapper) Decode(v interface{}) error { + return decodeJSONPb(d.Decoder, d.UnmarshalOptions, v) +} + +// NewEncoder returns an Encoder which writes JSON stream into "w". +func (j *JSONPb) NewEncoder(w io.Writer) Encoder { + return EncoderFunc(func(v interface{}) error { + if err := j.marshalTo(w, v); err != nil { + return err + } + // mimic json.Encoder by adding a newline (makes output + // easier to read when it contains multiple encoded items) + _, err := w.Write(j.Delimiter()) + return err + }) +} + +func unmarshalJSONPb(data []byte, unmarshaler protojson.UnmarshalOptions, v interface{}) error { + d := json.NewDecoder(bytes.NewReader(data)) + return decodeJSONPb(d, unmarshaler, v) +} + +func decodeJSONPb(d *json.Decoder, unmarshaler protojson.UnmarshalOptions, v interface{}) error { + p, ok := v.(proto.Message) + if !ok { + return decodeNonProtoField(d, unmarshaler, v) + } + + // Decode into bytes for marshalling + var b json.RawMessage + err := d.Decode(&b) + if err != nil { + return err + } + + return unmarshaler.Unmarshal([]byte(b), p) +} + +func decodeNonProtoField(d *json.Decoder, unmarshaler protojson.UnmarshalOptions, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return fmt.Errorf("%T is not a pointer", v) + } + for rv.Kind() == reflect.Ptr { + if rv.IsNil() { + rv.Set(reflect.New(rv.Type().Elem())) + } + if rv.Type().ConvertibleTo(typeProtoMessage) { + // Decode into bytes for marshalling + var b json.RawMessage + err := d.Decode(&b) + if err != nil { + return err + } + + return unmarshaler.Unmarshal([]byte(b), rv.Interface().(proto.Message)) + } + rv = rv.Elem() + } + if rv.Kind() == reflect.Map { + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + conv, ok := convFromType[rv.Type().Key().Kind()] + if !ok { + return fmt.Errorf("unsupported type of map field key: %v", rv.Type().Key()) + } + + m := make(map[string]*json.RawMessage) + if err := d.Decode(&m); err != nil { + return err + } + for k, v := range m { + result := conv.Call([]reflect.Value{reflect.ValueOf(k)}) + if err := result[1].Interface(); err != nil { + return err.(error) + } + bk := result[0] + bv := reflect.New(rv.Type().Elem()) + if v == nil { + null := json.RawMessage("null") + v = &null + } + if err := unmarshalJSONPb([]byte(*v), unmarshaler, bv.Interface()); err != nil { + return err + } + rv.SetMapIndex(bk, bv.Elem()) + } + return nil + } + if rv.Kind() == reflect.Slice { + var sl []json.RawMessage + if err := d.Decode(&sl); err != nil { + return err + } + if sl != nil { + rv.Set(reflect.MakeSlice(rv.Type(), 0, 0)) + } + for _, item := range sl { + bv := reflect.New(rv.Type().Elem()) + if err := unmarshalJSONPb([]byte(item), unmarshaler, bv.Interface()); err != nil { + return err + } + rv.Set(reflect.Append(rv, bv.Elem())) + } + return nil + } + if _, ok := rv.Interface().(protoEnum); ok { + var repr interface{} + if err := d.Decode(&repr); err != nil { + return err + } + switch v := repr.(type) { + case string: + // TODO(yugui) Should use proto.StructProperties? + return fmt.Errorf("unmarshaling of symbolic enum %q not supported: %T", repr, rv.Interface()) + case float64: + rv.Set(reflect.ValueOf(int32(v)).Convert(rv.Type())) + return nil + default: + return fmt.Errorf("cannot assign %#v into Go type %T", repr, rv.Interface()) + } + } + return d.Decode(v) +} + +type protoEnum interface { + fmt.Stringer + EnumDescriptor() ([]byte, []int) +} + +var typeProtoEnum = reflect.TypeOf((*protoEnum)(nil)).Elem() + +var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem() + +// Delimiter for newline encoded JSON streams. +func (j *JSONPb) Delimiter() []byte { + return []byte("\n") +} + +var ( + convFromType = map[reflect.Kind]reflect.Value{ + reflect.String: reflect.ValueOf(String), + reflect.Bool: reflect.ValueOf(Bool), + reflect.Float64: reflect.ValueOf(Float64), + reflect.Float32: reflect.ValueOf(Float32), + reflect.Int64: reflect.ValueOf(Int64), + reflect.Int32: reflect.ValueOf(Int32), + reflect.Uint64: reflect.ValueOf(Uint64), + reflect.Uint32: reflect.ValueOf(Uint32), + reflect.Slice: reflect.ValueOf(Bytes), + } +) diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go new file mode 100644 index 00000000..007f8f1a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go @@ -0,0 +1,63 @@ +package runtime + +import ( + "io" + + "errors" + "io/ioutil" + + "google.golang.org/protobuf/proto" +) + +// ProtoMarshaller is a Marshaller which marshals/unmarshals into/from serialize proto bytes +type ProtoMarshaller struct{} + +// ContentType always returns "application/octet-stream". +func (*ProtoMarshaller) ContentType(_ interface{}) string { + return "application/octet-stream" +} + +// Marshal marshals "value" into Proto +func (*ProtoMarshaller) Marshal(value interface{}) ([]byte, error) { + message, ok := value.(proto.Message) + if !ok { + return nil, errors.New("unable to marshal non proto field") + } + return proto.Marshal(message) +} + +// Unmarshal unmarshals proto "data" into "value" +func (*ProtoMarshaller) Unmarshal(data []byte, value interface{}) error { + message, ok := value.(proto.Message) + if !ok { + return errors.New("unable to unmarshal non proto field") + } + return proto.Unmarshal(data, message) +} + +// NewDecoder returns a Decoder which reads proto stream from "reader". +func (marshaller *ProtoMarshaller) NewDecoder(reader io.Reader) Decoder { + return DecoderFunc(func(value interface{}) error { + buffer, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + return marshaller.Unmarshal(buffer, value) + }) +} + +// NewEncoder returns an Encoder which writes proto stream into "writer". +func (marshaller *ProtoMarshaller) NewEncoder(writer io.Writer) Encoder { + return EncoderFunc(func(value interface{}) error { + buffer, err := marshaller.Marshal(value) + if err != nil { + return err + } + _, err = writer.Write(buffer) + if err != nil { + return err + } + + return nil + }) +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go new file mode 100644 index 00000000..2c0d25ff --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go @@ -0,0 +1,50 @@ +package runtime + +import ( + "io" +) + +// Marshaler defines a conversion between byte sequence and gRPC payloads / fields. +type Marshaler interface { + // Marshal marshals "v" into byte sequence. + Marshal(v interface{}) ([]byte, error) + // Unmarshal unmarshals "data" into "v". + // "v" must be a pointer value. + Unmarshal(data []byte, v interface{}) error + // NewDecoder returns a Decoder which reads byte sequence from "r". + NewDecoder(r io.Reader) Decoder + // NewEncoder returns an Encoder which writes bytes sequence into "w". + NewEncoder(w io.Writer) Encoder + // ContentType returns the Content-Type which this marshaler is responsible for. + // The parameter describes the type which is being marshalled, which can sometimes + // affect the content type returned. + ContentType(v interface{}) string +} + +// Decoder decodes a byte sequence +type Decoder interface { + Decode(v interface{}) error +} + +// Encoder encodes gRPC payloads / fields into byte sequence. +type Encoder interface { + Encode(v interface{}) error +} + +// DecoderFunc adapts an decoder function into Decoder. +type DecoderFunc func(v interface{}) error + +// Decode delegates invocations to the underlying function itself. +func (f DecoderFunc) Decode(v interface{}) error { return f(v) } + +// EncoderFunc adapts an encoder function into Encoder +type EncoderFunc func(v interface{}) error + +// Encode delegates invocations to the underlying function itself. +func (f EncoderFunc) Encode(v interface{}) error { return f(v) } + +// Delimited defines the streaming delimiter. +type Delimited interface { + // Delimiter returns the record separator for the stream. + Delimiter() []byte +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go new file mode 100644 index 00000000..a714de02 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go @@ -0,0 +1,109 @@ +package runtime + +import ( + "errors" + "mime" + "net/http" + + "google.golang.org/grpc/grpclog" + "google.golang.org/protobuf/encoding/protojson" +) + +// MIMEWildcard is the fallback MIME type used for requests which do not match +// a registered MIME type. +const MIMEWildcard = "*" + +var ( + acceptHeader = http.CanonicalHeaderKey("Accept") + contentTypeHeader = http.CanonicalHeaderKey("Content-Type") + + defaultMarshaler = &HTTPBodyMarshaler{ + Marshaler: &JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + EmitUnpopulated: true, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }, + } +) + +// MarshalerForRequest returns the inbound/outbound marshalers for this request. +// It checks the registry on the ServeMux for the MIME type set by the Content-Type header. +// If it isn't set (or the request Content-Type is empty), checks for "*". +// If there are multiple Content-Type headers set, choose the first one that it can +// exactly match in the registry. +// Otherwise, it follows the above logic for "*"/InboundMarshaler/OutboundMarshaler. +func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, outbound Marshaler) { + for _, acceptVal := range r.Header[acceptHeader] { + if m, ok := mux.marshalers.mimeMap[acceptVal]; ok { + outbound = m + break + } + } + + for _, contentTypeVal := range r.Header[contentTypeHeader] { + contentType, _, err := mime.ParseMediaType(contentTypeVal) + if err != nil { + grpclog.Infof("Failed to parse Content-Type %s: %v", contentTypeVal, err) + continue + } + if m, ok := mux.marshalers.mimeMap[contentType]; ok { + inbound = m + break + } + } + + if inbound == nil { + inbound = mux.marshalers.mimeMap[MIMEWildcard] + } + if outbound == nil { + outbound = inbound + } + + return inbound, outbound +} + +// marshalerRegistry is a mapping from MIME types to Marshalers. +type marshalerRegistry struct { + mimeMap map[string]Marshaler +} + +// add adds a marshaler for a case-sensitive MIME type string ("*" to match any +// MIME type). +func (m marshalerRegistry) add(mime string, marshaler Marshaler) error { + if len(mime) == 0 { + return errors.New("empty MIME type") + } + + m.mimeMap[mime] = marshaler + + return nil +} + +// makeMarshalerMIMERegistry returns a new registry of marshalers. +// It allows for a mapping of case-sensitive Content-Type MIME type string to runtime.Marshaler interfaces. +// +// For example, you could allow the client to specify the use of the runtime.JSONPb marshaler +// with a "application/jsonpb" Content-Type and the use of the runtime.JSONBuiltin marshaler +// with a "application/json" Content-Type. +// "*" can be used to match any Content-Type. +// This can be attached to a ServerMux with the marshaler option. +func makeMarshalerMIMERegistry() marshalerRegistry { + return marshalerRegistry{ + mimeMap: map[string]Marshaler{ + MIMEWildcard: defaultMarshaler, + }, + } +} + +// WithMarshalerOption returns a ServeMuxOption which associates inbound and outbound +// Marshalers to a MIME type in mux. +func WithMarshalerOption(mime string, marshaler Marshaler) ServeMuxOption { + return func(mux *ServeMux) { + if err := mux.marshalers.add(mime, marshaler); err != nil { + panic(err) + } + } +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go new file mode 100644 index 00000000..46a4aaba --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go @@ -0,0 +1,356 @@ +package runtime + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/textproto" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// UnescapingMode defines the behavior of ServeMux when unescaping path parameters. +type UnescapingMode int + +const ( + // UnescapingModeLegacy is the default V2 behavior, which escapes the entire + // path string before doing any routing. + UnescapingModeLegacy UnescapingMode = iota + + // EscapingTypeExceptReserved unescapes all path parameters except RFC 6570 + // reserved characters. + UnescapingModeAllExceptReserved + + // EscapingTypeExceptSlash unescapes URL path parameters except path + // seperators, which will be left as "%2F". + UnescapingModeAllExceptSlash + + // URL path parameters will be fully decoded. + UnescapingModeAllCharacters + + // UnescapingModeDefault is the default escaping type. + // TODO(v3): default this to UnescapingModeAllExceptReserved per grpc-httpjson-transcoding's + // reference implementation + UnescapingModeDefault = UnescapingModeLegacy +) + +// A HandlerFunc handles a specific pair of path pattern and HTTP method. +type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) + +// ServeMux is a request multiplexer for grpc-gateway. +// It matches http requests to patterns and invokes the corresponding handler. +type ServeMux struct { + // handlers maps HTTP method to a list of handlers. + handlers map[string][]handler + forwardResponseOptions []func(context.Context, http.ResponseWriter, proto.Message) error + marshalers marshalerRegistry + incomingHeaderMatcher HeaderMatcherFunc + outgoingHeaderMatcher HeaderMatcherFunc + metadataAnnotators []func(context.Context, *http.Request) metadata.MD + errorHandler ErrorHandlerFunc + streamErrorHandler StreamErrorHandlerFunc + routingErrorHandler RoutingErrorHandlerFunc + disablePathLengthFallback bool + unescapingMode UnescapingMode +} + +// ServeMuxOption is an option that can be given to a ServeMux on construction. +type ServeMuxOption func(*ServeMux) + +// WithForwardResponseOption returns a ServeMuxOption representing the forwardResponseOption. +// +// forwardResponseOption is an option that will be called on the relevant context.Context, +// http.ResponseWriter, and proto.Message before every forwarded response. +// +// The message may be nil in the case where just a header is being sent. +func WithForwardResponseOption(forwardResponseOption func(context.Context, http.ResponseWriter, proto.Message) error) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.forwardResponseOptions = append(serveMux.forwardResponseOptions, forwardResponseOption) + } +} + +// WithEscapingType sets the escaping type. See the definitions of UnescapingMode +// for more information. +func WithUnescapingMode(mode UnescapingMode) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.unescapingMode = mode + } +} + +// SetQueryParameterParser sets the query parameter parser, used to populate message from query parameters. +// Configuring this will mean the generated OpenAPI output is no longer correct, and it should be +// done with careful consideration. +func SetQueryParameterParser(queryParameterParser QueryParameterParser) ServeMuxOption { + return func(serveMux *ServeMux) { + currentQueryParser = queryParameterParser + } +} + +// HeaderMatcherFunc checks whether a header key should be forwarded to/from gRPC context. +type HeaderMatcherFunc func(string) (string, bool) + +// DefaultHeaderMatcher is used to pass http request headers to/from gRPC context. This adds permanent HTTP header +// keys (as specified by the IANA) to gRPC context with grpcgateway- prefix. HTTP headers that start with +// 'Grpc-Metadata-' are mapped to gRPC metadata after removing prefix 'Grpc-Metadata-'. +func DefaultHeaderMatcher(key string) (string, bool) { + key = textproto.CanonicalMIMEHeaderKey(key) + if isPermanentHTTPHeader(key) { + return MetadataPrefix + key, true + } else if strings.HasPrefix(key, MetadataHeaderPrefix) { + return key[len(MetadataHeaderPrefix):], true + } + return "", false +} + +// WithIncomingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for incoming request to gateway. +// +// This matcher will be called with each header in http.Request. If matcher returns true, that header will be +// passed to gRPC context. To transform the header before passing to gRPC context, matcher should return modified header. +func WithIncomingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption { + return func(mux *ServeMux) { + mux.incomingHeaderMatcher = fn + } +} + +// WithOutgoingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for outgoing response from gateway. +// +// This matcher will be called with each header in response header metadata. If matcher returns true, that header will be +// passed to http response returned from gateway. To transform the header before passing to response, +// matcher should return modified header. +func WithOutgoingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption { + return func(mux *ServeMux) { + mux.outgoingHeaderMatcher = fn + } +} + +// WithMetadata returns a ServeMuxOption for passing metadata to a gRPC context. +// +// This can be used by services that need to read from http.Request and modify gRPC context. A common use case +// is reading token from cookie and adding it in gRPC context. +func WithMetadata(annotator func(context.Context, *http.Request) metadata.MD) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.metadataAnnotators = append(serveMux.metadataAnnotators, annotator) + } +} + +// WithErrorHandler returns a ServeMuxOption for configuring a custom error handler. +// +// This can be used to configure a custom error response. +func WithErrorHandler(fn ErrorHandlerFunc) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.errorHandler = fn + } +} + +// WithStreamErrorHandler returns a ServeMuxOption that will use the given custom stream +// error handler, which allows for customizing the error trailer for server-streaming +// calls. +// +// For stream errors that occur before any response has been written, the mux's +// ErrorHandler will be invoked. However, once data has been written, the errors must +// be handled differently: they must be included in the response body. The response body's +// final message will include the error details returned by the stream error handler. +func WithStreamErrorHandler(fn StreamErrorHandlerFunc) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.streamErrorHandler = fn + } +} + +// WithRoutingErrorHandler returns a ServeMuxOption for configuring a custom error handler to handle http routing errors. +// +// Method called for errors which can happen before gRPC route selected or executed. +// The following error codes: StatusMethodNotAllowed StatusNotFound StatusBadRequest +func WithRoutingErrorHandler(fn RoutingErrorHandlerFunc) ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.routingErrorHandler = fn + } +} + +// WithDisablePathLengthFallback returns a ServeMuxOption for disable path length fallback. +func WithDisablePathLengthFallback() ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.disablePathLengthFallback = true + } +} + +// NewServeMux returns a new ServeMux whose internal mapping is empty. +func NewServeMux(opts ...ServeMuxOption) *ServeMux { + serveMux := &ServeMux{ + handlers: make(map[string][]handler), + forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0), + marshalers: makeMarshalerMIMERegistry(), + errorHandler: DefaultHTTPErrorHandler, + streamErrorHandler: DefaultStreamErrorHandler, + routingErrorHandler: DefaultRoutingErrorHandler, + unescapingMode: UnescapingModeDefault, + } + + for _, opt := range opts { + opt(serveMux) + } + + if serveMux.incomingHeaderMatcher == nil { + serveMux.incomingHeaderMatcher = DefaultHeaderMatcher + } + + if serveMux.outgoingHeaderMatcher == nil { + serveMux.outgoingHeaderMatcher = func(key string) (string, bool) { + return fmt.Sprintf("%s%s", MetadataHeaderPrefix, key), true + } + } + + return serveMux +} + +// Handle associates "h" to the pair of HTTP method and path pattern. +func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) { + s.handlers[meth] = append([]handler{{pat: pat, h: h}}, s.handlers[meth]...) +} + +// HandlePath allows users to configure custom path handlers. +// refer: https://grpc-ecosystem.github.io/grpc-gateway/docs/operations/inject_router/ +func (s *ServeMux) HandlePath(meth string, pathPattern string, h HandlerFunc) error { + compiler, err := httprule.Parse(pathPattern) + if err != nil { + return fmt.Errorf("parsing path pattern: %w", err) + } + tp := compiler.Compile() + pattern, err := NewPattern(tp.Version, tp.OpCodes, tp.Pool, tp.Verb) + if err != nil { + return fmt.Errorf("creating new pattern: %w", err) + } + s.Handle(meth, pattern, h) + return nil +} + +// ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.Path. +func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + path := r.URL.Path + if !strings.HasPrefix(path, "/") { + _, outboundMarshaler := MarshalerForRequest(s, r) + s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusBadRequest) + return + } + + // TODO(v3): remove UnescapingModeLegacy + if s.unescapingMode != UnescapingModeLegacy && r.URL.RawPath != "" { + path = r.URL.RawPath + } + + components := strings.Split(path[1:], "/") + + if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) { + r.Method = strings.ToUpper(override) + if err := r.ParseForm(); err != nil { + _, outboundMarshaler := MarshalerForRequest(s, r) + sterr := status.Error(codes.InvalidArgument, err.Error()) + s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr) + return + } + } + + // Verb out here is to memoize for the fallback case below + var verb string + + for _, h := range s.handlers[r.Method] { + // If the pattern has a verb, explicitly look for a suffix in the last + // component that matches a colon plus the verb. This allows us to + // handle some cases that otherwise can't be correctly handled by the + // former LastIndex case, such as when the verb literal itself contains + // a colon. This should work for all cases that have run through the + // parser because we know what verb we're looking for, however, there + // are still some cases that the parser itself cannot disambiguate. See + // the comment there if interested. + patVerb := h.pat.Verb() + l := len(components) + lastComponent := components[l-1] + var idx int = -1 + if patVerb != "" && strings.HasSuffix(lastComponent, ":"+patVerb) { + idx = len(lastComponent) - len(patVerb) - 1 + } + if idx == 0 { + _, outboundMarshaler := MarshalerForRequest(s, r) + s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusNotFound) + return + } + if idx > 0 { + components[l-1], verb = lastComponent[:idx], lastComponent[idx+1:] + } + + pathParams, err := h.pat.MatchAndEscape(components, verb, s.unescapingMode) + if err != nil { + var mse MalformedSequenceError + if ok := errors.As(err, &mse); ok { + _, outboundMarshaler := MarshalerForRequest(s, r) + s.errorHandler(ctx, s, outboundMarshaler, w, r, &HTTPStatusError{ + HTTPStatus: http.StatusBadRequest, + Err: mse, + }) + } + continue + } + h.h(w, r, pathParams) + return + } + + // lookup other methods to handle fallback from GET to POST and + // to determine if it is NotImplemented or NotFound. + for m, handlers := range s.handlers { + if m == r.Method { + continue + } + for _, h := range handlers { + pathParams, err := h.pat.MatchAndEscape(components, verb, s.unescapingMode) + if err != nil { + var mse MalformedSequenceError + if ok := errors.As(err, &mse); ok { + _, outboundMarshaler := MarshalerForRequest(s, r) + s.errorHandler(ctx, s, outboundMarshaler, w, r, &HTTPStatusError{ + HTTPStatus: http.StatusBadRequest, + Err: mse, + }) + } + continue + } + // X-HTTP-Method-Override is optional. Always allow fallback to POST. + if s.isPathLengthFallback(r) { + if err := r.ParseForm(); err != nil { + _, outboundMarshaler := MarshalerForRequest(s, r) + sterr := status.Error(codes.InvalidArgument, err.Error()) + s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr) + return + } + h.h(w, r, pathParams) + return + } + _, outboundMarshaler := MarshalerForRequest(s, r) + s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusMethodNotAllowed) + return + } + } + + _, outboundMarshaler := MarshalerForRequest(s, r) + s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusNotFound) +} + +// GetForwardResponseOptions returns the ForwardResponseOptions associated with this ServeMux. +func (s *ServeMux) GetForwardResponseOptions() []func(context.Context, http.ResponseWriter, proto.Message) error { + return s.forwardResponseOptions +} + +func (s *ServeMux) isPathLengthFallback(r *http.Request) bool { + return !s.disablePathLengthFallback && r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" +} + +type handler struct { + pat Pattern + h HandlerFunc +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go new file mode 100644 index 00000000..df7cb814 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go @@ -0,0 +1,383 @@ +package runtime + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc/grpclog" +) + +var ( + // ErrNotMatch indicates that the given HTTP request path does not match to the pattern. + ErrNotMatch = errors.New("not match to the path pattern") + // ErrInvalidPattern indicates that the given definition of Pattern is not valid. + ErrInvalidPattern = errors.New("invalid pattern") + // ErrMalformedSequence indicates that an escape sequence was malformed. + ErrMalformedSequence = errors.New("malformed escape sequence") +) + +type MalformedSequenceError string + +func (e MalformedSequenceError) Error() string { + return "malformed path escape " + strconv.Quote(string(e)) +} + +type op struct { + code utilities.OpCode + operand int +} + +// Pattern is a template pattern of http request paths defined in +// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto +type Pattern struct { + // ops is a list of operations + ops []op + // pool is a constant pool indexed by the operands or vars. + pool []string + // vars is a list of variables names to be bound by this pattern + vars []string + // stacksize is the max depth of the stack + stacksize int + // tailLen is the length of the fixed-size segments after a deep wildcard + tailLen int + // verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part. + verb string +} + +// NewPattern returns a new Pattern from the given definition values. +// "ops" is a sequence of op codes. "pool" is a constant pool. +// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part. +// "version" must be 1 for now. +// It returns an error if the given definition is invalid. +func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) { + if version != 1 { + grpclog.Infof("unsupported version: %d", version) + return Pattern{}, ErrInvalidPattern + } + + l := len(ops) + if l%2 != 0 { + grpclog.Infof("odd number of ops codes: %d", l) + return Pattern{}, ErrInvalidPattern + } + + var ( + typedOps []op + stack, maxstack int + tailLen int + pushMSeen bool + vars []string + ) + for i := 0; i < l; i += 2 { + op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]} + switch op.code { + case utilities.OpNop: + continue + case utilities.OpPush: + if pushMSeen { + tailLen++ + } + stack++ + case utilities.OpPushM: + if pushMSeen { + grpclog.Infof("pushM appears twice") + return Pattern{}, ErrInvalidPattern + } + pushMSeen = true + stack++ + case utilities.OpLitPush: + if op.operand < 0 || len(pool) <= op.operand { + grpclog.Infof("negative literal index: %d", op.operand) + return Pattern{}, ErrInvalidPattern + } + if pushMSeen { + tailLen++ + } + stack++ + case utilities.OpConcatN: + if op.operand <= 0 { + grpclog.Infof("negative concat size: %d", op.operand) + return Pattern{}, ErrInvalidPattern + } + stack -= op.operand + if stack < 0 { + grpclog.Info("stack underflow") + return Pattern{}, ErrInvalidPattern + } + stack++ + case utilities.OpCapture: + if op.operand < 0 || len(pool) <= op.operand { + grpclog.Infof("variable name index out of bound: %d", op.operand) + return Pattern{}, ErrInvalidPattern + } + v := pool[op.operand] + op.operand = len(vars) + vars = append(vars, v) + stack-- + if stack < 0 { + grpclog.Infof("stack underflow") + return Pattern{}, ErrInvalidPattern + } + default: + grpclog.Infof("invalid opcode: %d", op.code) + return Pattern{}, ErrInvalidPattern + } + + if maxstack < stack { + maxstack = stack + } + typedOps = append(typedOps, op) + } + return Pattern{ + ops: typedOps, + pool: pool, + vars: vars, + stacksize: maxstack, + tailLen: tailLen, + verb: verb, + }, nil +} + +// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization. +func MustPattern(p Pattern, err error) Pattern { + if err != nil { + grpclog.Fatalf("Pattern initialization failed: %v", err) + } + return p +} + +// MatchAndEscape examines components to determine if they match to a Pattern. +// MatchAndEscape will return an error if no Patterns matched or if a pattern +// matched but contained malformed escape sequences. If successful, the function +// returns a mapping from field paths to their captured values. +func (p Pattern) MatchAndEscape(components []string, verb string, unescapingMode UnescapingMode) (map[string]string, error) { + if p.verb != verb { + if p.verb != "" { + return nil, ErrNotMatch + } + if len(components) == 0 { + components = []string{":" + verb} + } else { + components = append([]string{}, components...) + components[len(components)-1] += ":" + verb + } + } + + var pos int + stack := make([]string, 0, p.stacksize) + captured := make([]string, len(p.vars)) + l := len(components) + for _, op := range p.ops { + var err error + + switch op.code { + case utilities.OpNop: + continue + case utilities.OpPush, utilities.OpLitPush: + if pos >= l { + return nil, ErrNotMatch + } + c := components[pos] + if op.code == utilities.OpLitPush { + if lit := p.pool[op.operand]; c != lit { + return nil, ErrNotMatch + } + } else if op.code == utilities.OpPush { + if c, err = unescape(c, unescapingMode, false); err != nil { + return nil, err + } + } + stack = append(stack, c) + pos++ + case utilities.OpPushM: + end := len(components) + if end < pos+p.tailLen { + return nil, ErrNotMatch + } + end -= p.tailLen + c := strings.Join(components[pos:end], "/") + if c, err = unescape(c, unescapingMode, true); err != nil { + return nil, err + } + stack = append(stack, c) + pos = end + case utilities.OpConcatN: + n := op.operand + l := len(stack) - n + stack = append(stack[:l], strings.Join(stack[l:], "/")) + case utilities.OpCapture: + n := len(stack) - 1 + captured[op.operand] = stack[n] + stack = stack[:n] + } + } + if pos < l { + return nil, ErrNotMatch + } + bindings := make(map[string]string) + for i, val := range captured { + bindings[p.vars[i]] = val + } + return bindings, nil +} + +// MatchAndEscape examines components to determine if they match to a Pattern. +// It will never perform per-component unescaping (see: UnescapingModeLegacy). +// MatchAndEscape will return an error if no Patterns matched. If successful, +// the function returns a mapping from field paths to their captured values. +// +// Deprecated: Use MatchAndEscape. +func (p Pattern) Match(components []string, verb string) (map[string]string, error) { + return p.MatchAndEscape(components, verb, UnescapingModeDefault) +} + +// Verb returns the verb part of the Pattern. +func (p Pattern) Verb() string { return p.verb } + +func (p Pattern) String() string { + var stack []string + for _, op := range p.ops { + switch op.code { + case utilities.OpNop: + continue + case utilities.OpPush: + stack = append(stack, "*") + case utilities.OpLitPush: + stack = append(stack, p.pool[op.operand]) + case utilities.OpPushM: + stack = append(stack, "**") + case utilities.OpConcatN: + n := op.operand + l := len(stack) - n + stack = append(stack[:l], strings.Join(stack[l:], "/")) + case utilities.OpCapture: + n := len(stack) - 1 + stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n]) + } + } + segs := strings.Join(stack, "/") + if p.verb != "" { + return fmt.Sprintf("/%s:%s", segs, p.verb) + } + return "/" + segs +} + +/* + * The following code is adopted and modified from Go's standard library + * and carries the attached license. + * + * 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 LICENSE file. + */ + +// ishex returns whether or not the given byte is a valid hex character +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} + +func isRFC6570Reserved(c byte) bool { + switch c { + case '!', '#', '$', '&', '\'', '(', ')', '*', + '+', ',', '/', ':', ';', '=', '?', '@', '[', ']': + return true + default: + return false + } +} + +// unhex converts a hex point to the bit representation +func unhex(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0' + case 'a' <= c && c <= 'f': + return c - 'a' + 10 + case 'A' <= c && c <= 'F': + return c - 'A' + 10 + } + return 0 +} + +// shouldUnescapeWithMode returns true if the character is escapable with the +// given mode +func shouldUnescapeWithMode(c byte, mode UnescapingMode) bool { + switch mode { + case UnescapingModeAllExceptReserved: + if isRFC6570Reserved(c) { + return false + } + case UnescapingModeAllExceptSlash: + if c == '/' { + return false + } + case UnescapingModeAllCharacters: + return true + } + return true +} + +// unescape unescapes a path string using the provided mode +func unescape(s string, mode UnescapingMode, multisegment bool) (string, error) { + // TODO(v3): remove UnescapingModeLegacy + if mode == UnescapingModeLegacy { + return s, nil + } + + if !multisegment { + mode = UnescapingModeAllCharacters + } + + // Count %, check that they're well-formed. + n := 0 + for i := 0; i < len(s); { + if s[i] == '%' { + n++ + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { + s = s[i:] + if len(s) > 3 { + s = s[:3] + } + + return "", MalformedSequenceError(s) + } + i += 3 + } else { + i++ + } + } + + if n == 0 { + return s, nil + } + + var t strings.Builder + t.Grow(len(s)) + for i := 0; i < len(s); i++ { + switch s[i] { + case '%': + c := unhex(s[i+1])<<4 | unhex(s[i+2]) + if shouldUnescapeWithMode(c, mode) { + t.WriteByte(c) + i += 2 + continue + } + fallthrough + default: + t.WriteByte(s[i]) + } + } + + return t.String(), nil +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go new file mode 100644 index 00000000..d549407f --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go @@ -0,0 +1,80 @@ +package runtime + +import ( + "google.golang.org/protobuf/proto" +) + +// StringP returns a pointer to a string whose pointee is same as the given string value. +func StringP(val string) (*string, error) { + return proto.String(val), nil +} + +// BoolP parses the given string representation of a boolean value, +// and returns a pointer to a bool whose value is same as the parsed value. +func BoolP(val string) (*bool, error) { + b, err := Bool(val) + if err != nil { + return nil, err + } + return proto.Bool(b), nil +} + +// Float64P parses the given string representation of a floating point number, +// and returns a pointer to a float64 whose value is same as the parsed number. +func Float64P(val string) (*float64, error) { + f, err := Float64(val) + if err != nil { + return nil, err + } + return proto.Float64(f), nil +} + +// Float32P parses the given string representation of a floating point number, +// and returns a pointer to a float32 whose value is same as the parsed number. +func Float32P(val string) (*float32, error) { + f, err := Float32(val) + if err != nil { + return nil, err + } + return proto.Float32(f), nil +} + +// Int64P parses the given string representation of an integer +// and returns a pointer to a int64 whose value is same as the parsed integer. +func Int64P(val string) (*int64, error) { + i, err := Int64(val) + if err != nil { + return nil, err + } + return proto.Int64(i), nil +} + +// Int32P parses the given string representation of an integer +// and returns a pointer to a int32 whose value is same as the parsed integer. +func Int32P(val string) (*int32, error) { + i, err := Int32(val) + if err != nil { + return nil, err + } + return proto.Int32(i), err +} + +// Uint64P parses the given string representation of an integer +// and returns a pointer to a uint64 whose value is same as the parsed integer. +func Uint64P(val string) (*uint64, error) { + i, err := Uint64(val) + if err != nil { + return nil, err + } + return proto.Uint64(i), err +} + +// Uint32P parses the given string representation of an integer +// and returns a pointer to a uint32 whose value is same as the parsed integer. +func Uint32P(val string) (*uint32, error) { + i, err := Uint32(val) + if err != nil { + return nil, err + } + return proto.Uint32(i), err +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go new file mode 100644 index 00000000..fb0c84ef --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go @@ -0,0 +1,329 @@ +package runtime + +import ( + "encoding/base64" + "errors" + "fmt" + "net/url" + "regexp" + "strconv" + "strings" + "time" + + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/genproto/protobuf/field_mask" + "google.golang.org/grpc/grpclog" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var valuesKeyRegexp = regexp.MustCompile(`^(.*)\[(.*)\]$`) + +var currentQueryParser QueryParameterParser = &defaultQueryParser{} + +// QueryParameterParser defines interface for all query parameter parsers +type QueryParameterParser interface { + Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error +} + +// PopulateQueryParameters parses query parameters +// into "msg" using current query parser +func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error { + return currentQueryParser.Parse(msg, values, filter) +} + +type defaultQueryParser struct{} + +// Parse populates "values" into "msg". +// A value is ignored if its key starts with one of the elements in "filter". +func (*defaultQueryParser) Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error { + for key, values := range values { + match := valuesKeyRegexp.FindStringSubmatch(key) + if len(match) == 3 { + key = match[1] + values = append([]string{match[2]}, values...) + } + fieldPath := strings.Split(key, ".") + if filter.HasCommonPrefix(fieldPath) { + continue + } + if err := populateFieldValueFromPath(msg.ProtoReflect(), fieldPath, values); err != nil { + return err + } + } + return nil +} + +// PopulateFieldFromPath sets a value in a nested Protobuf structure. +func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error { + fieldPath := strings.Split(fieldPathString, ".") + return populateFieldValueFromPath(msg.ProtoReflect(), fieldPath, []string{value}) +} + +func populateFieldValueFromPath(msgValue protoreflect.Message, fieldPath []string, values []string) error { + if len(fieldPath) < 1 { + return errors.New("no field path") + } + if len(values) < 1 { + return errors.New("no value provided") + } + + var fieldDescriptor protoreflect.FieldDescriptor + for i, fieldName := range fieldPath { + fields := msgValue.Descriptor().Fields() + + // Get field by name + fieldDescriptor = fields.ByName(protoreflect.Name(fieldName)) + if fieldDescriptor == nil { + fieldDescriptor = fields.ByJSONName(fieldName) + if fieldDescriptor == nil { + // We're not returning an error here because this could just be + // an extra query parameter that isn't part of the request. + grpclog.Infof("field not found in %q: %q", msgValue.Descriptor().FullName(), strings.Join(fieldPath, ".")) + return nil + } + } + + // If this is the last element, we're done + if i == len(fieldPath)-1 { + break + } + + // Only singular message fields are allowed + if fieldDescriptor.Message() == nil || fieldDescriptor.Cardinality() == protoreflect.Repeated { + return fmt.Errorf("invalid path: %q is not a message", fieldName) + } + + // Get the nested message + msgValue = msgValue.Mutable(fieldDescriptor).Message() + } + + // Check if oneof already set + if of := fieldDescriptor.ContainingOneof(); of != nil { + if f := msgValue.WhichOneof(of); f != nil { + return fmt.Errorf("field already set for oneof %q", of.FullName().Name()) + } + } + + switch { + case fieldDescriptor.IsList(): + return populateRepeatedField(fieldDescriptor, msgValue.Mutable(fieldDescriptor).List(), values) + case fieldDescriptor.IsMap(): + return populateMapField(fieldDescriptor, msgValue.Mutable(fieldDescriptor).Map(), values) + } + + if len(values) > 1 { + return fmt.Errorf("too many values for field %q: %s", fieldDescriptor.FullName().Name(), strings.Join(values, ", ")) + } + + return populateField(fieldDescriptor, msgValue, values[0]) +} + +func populateField(fieldDescriptor protoreflect.FieldDescriptor, msgValue protoreflect.Message, value string) error { + v, err := parseField(fieldDescriptor, value) + if err != nil { + return fmt.Errorf("parsing field %q: %w", fieldDescriptor.FullName().Name(), err) + } + + msgValue.Set(fieldDescriptor, v) + return nil +} + +func populateRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List, values []string) error { + for _, value := range values { + v, err := parseField(fieldDescriptor, value) + if err != nil { + return fmt.Errorf("parsing list %q: %w", fieldDescriptor.FullName().Name(), err) + } + list.Append(v) + } + + return nil +} + +func populateMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map, values []string) error { + if len(values) != 2 { + return fmt.Errorf("more than one value provided for key %q in map %q", values[0], fieldDescriptor.FullName()) + } + + key, err := parseField(fieldDescriptor.MapKey(), values[0]) + if err != nil { + return fmt.Errorf("parsing map key %q: %w", fieldDescriptor.FullName().Name(), err) + } + + value, err := parseField(fieldDescriptor.MapValue(), values[1]) + if err != nil { + return fmt.Errorf("parsing map value %q: %w", fieldDescriptor.FullName().Name(), err) + } + + mp.Set(key.MapKey(), value) + + return nil +} + +func parseField(fieldDescriptor protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) { + switch fieldDescriptor.Kind() { + case protoreflect.BoolKind: + v, err := strconv.ParseBool(value) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfBool(v), nil + case protoreflect.EnumKind: + enum, err := protoregistry.GlobalTypes.FindEnumByName(fieldDescriptor.Enum().FullName()) + switch { + case errors.Is(err, protoregistry.NotFound): + return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fieldDescriptor.Enum().FullName()) + case err != nil: + return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err) + } + // Look for enum by name + v := enum.Descriptor().Values().ByName(protoreflect.Name(value)) + if v == nil { + i, err := strconv.Atoi(value) + if err != nil { + return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) + } + // Look for enum by number + v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i)) + if v == nil { + return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) + } + } + return protoreflect.ValueOfEnum(v.Number()), nil + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfInt32(int32(v)), nil + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfInt64(v), nil + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + v, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfUint32(uint32(v)), nil + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfUint64(v), nil + case protoreflect.FloatKind: + v, err := strconv.ParseFloat(value, 32) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfFloat32(float32(v)), nil + case protoreflect.DoubleKind: + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfFloat64(v), nil + case protoreflect.StringKind: + return protoreflect.ValueOfString(value), nil + case protoreflect.BytesKind: + v, err := base64.URLEncoding.DecodeString(value) + if err != nil { + return protoreflect.Value{}, err + } + return protoreflect.ValueOfBytes(v), nil + case protoreflect.MessageKind, protoreflect.GroupKind: + return parseMessage(fieldDescriptor.Message(), value) + default: + panic(fmt.Sprintf("unknown field kind: %v", fieldDescriptor.Kind())) + } +} + +func parseMessage(msgDescriptor protoreflect.MessageDescriptor, value string) (protoreflect.Value, error) { + var msg proto.Message + switch msgDescriptor.FullName() { + case "google.protobuf.Timestamp": + if value == "null" { + break + } + t, err := time.Parse(time.RFC3339Nano, value) + if err != nil { + return protoreflect.Value{}, err + } + msg = timestamppb.New(t) + case "google.protobuf.Duration": + if value == "null" { + break + } + d, err := time.ParseDuration(value) + if err != nil { + return protoreflect.Value{}, err + } + msg = durationpb.New(d) + case "google.protobuf.DoubleValue": + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.DoubleValue{Value: v} + case "google.protobuf.FloatValue": + v, err := strconv.ParseFloat(value, 32) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.FloatValue{Value: float32(v)} + case "google.protobuf.Int64Value": + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.Int64Value{Value: v} + case "google.protobuf.Int32Value": + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.Int32Value{Value: int32(v)} + case "google.protobuf.UInt64Value": + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.UInt64Value{Value: v} + case "google.protobuf.UInt32Value": + v, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.UInt32Value{Value: uint32(v)} + case "google.protobuf.BoolValue": + v, err := strconv.ParseBool(value) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.BoolValue{Value: v} + case "google.protobuf.StringValue": + msg = &wrapperspb.StringValue{Value: value} + case "google.protobuf.BytesValue": + v, err := base64.URLEncoding.DecodeString(value) + if err != nil { + return protoreflect.Value{}, err + } + msg = &wrapperspb.BytesValue{Value: v} + case "google.protobuf.FieldMask": + fm := &field_mask.FieldMask{} + fm.Paths = append(fm.Paths, strings.Split(value, ",")...) + msg = fm + default: + return protoreflect.Value{}, fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName())) + } + + return protoreflect.ValueOfMessage(msg.ProtoReflect()), nil +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/BUILD.bazel b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/BUILD.bazel new file mode 100644 index 00000000..5d8d12bc --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package(default_visibility = ["//visibility:public"]) + +go_library( + name = "utilities", + srcs = [ + "doc.go", + "pattern.go", + "readerfactory.go", + "trie.go", + ], + importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/utilities", +) + +go_test( + name = "utilities_test", + size = "small", + srcs = ["trie_test.go"], + deps = [":utilities"], +) + +alias( + name = "go_default_library", + actual = ":utilities", + visibility = ["//visibility:public"], +) diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/doc.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/doc.go new file mode 100644 index 00000000..cf79a4d5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/doc.go @@ -0,0 +1,2 @@ +// Package utilities provides members for internal use in grpc-gateway. +package utilities diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/pattern.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/pattern.go new file mode 100644 index 00000000..dfe7de48 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/pattern.go @@ -0,0 +1,22 @@ +package utilities + +// An OpCode is a opcode of compiled path patterns. +type OpCode int + +// These constants are the valid values of OpCode. +const ( + // OpNop does nothing + OpNop = OpCode(iota) + // OpPush pushes a component to stack + OpPush + // OpLitPush pushes a component to stack if it matches to the literal + OpLitPush + // OpPushM concatenates the remaining components and pushes it to stack + OpPushM + // OpConcatN pops N items from stack, concatenates them and pushes it back to stack + OpConcatN + // OpCapture pops an item and binds it to the variable + OpCapture + // OpEnd is the least positive invalid opcode. + OpEnd +) diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/readerfactory.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/readerfactory.go new file mode 100644 index 00000000..6dd38546 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/readerfactory.go @@ -0,0 +1,20 @@ +package utilities + +import ( + "bytes" + "io" + "io/ioutil" +) + +// IOReaderFactory takes in an io.Reader and returns a function that will allow you to create a new reader that begins +// at the start of the stream +func IOReaderFactory(r io.Reader) (func() io.Reader, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return func() io.Reader { + return bytes.NewReader(b) + }, nil +} diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/trie.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/trie.go new file mode 100644 index 00000000..af3b703d --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/utilities/trie.go @@ -0,0 +1,174 @@ +package utilities + +import ( + "sort" +) + +// DoubleArray is a Double Array implementation of trie on sequences of strings. +type DoubleArray struct { + // Encoding keeps an encoding from string to int + Encoding map[string]int + // Base is the base array of Double Array + Base []int + // Check is the check array of Double Array + Check []int +} + +// NewDoubleArray builds a DoubleArray from a set of sequences of strings. +func NewDoubleArray(seqs [][]string) *DoubleArray { + da := &DoubleArray{Encoding: make(map[string]int)} + if len(seqs) == 0 { + return da + } + + encoded := registerTokens(da, seqs) + sort.Sort(byLex(encoded)) + + root := node{row: -1, col: -1, left: 0, right: len(encoded)} + addSeqs(da, encoded, 0, root) + + for i := len(da.Base); i > 0; i-- { + if da.Check[i-1] != 0 { + da.Base = da.Base[:i] + da.Check = da.Check[:i] + break + } + } + return da +} + +func registerTokens(da *DoubleArray, seqs [][]string) [][]int { + var result [][]int + for _, seq := range seqs { + var encoded []int + for _, token := range seq { + if _, ok := da.Encoding[token]; !ok { + da.Encoding[token] = len(da.Encoding) + } + encoded = append(encoded, da.Encoding[token]) + } + result = append(result, encoded) + } + for i := range result { + result[i] = append(result[i], len(da.Encoding)) + } + return result +} + +type node struct { + row, col int + left, right int +} + +func (n node) value(seqs [][]int) int { + return seqs[n.row][n.col] +} + +func (n node) children(seqs [][]int) []*node { + var result []*node + lastVal := int(-1) + last := new(node) + for i := n.left; i < n.right; i++ { + if lastVal == seqs[i][n.col+1] { + continue + } + last.right = i + last = &node{ + row: i, + col: n.col + 1, + left: i, + } + result = append(result, last) + } + last.right = n.right + return result +} + +func addSeqs(da *DoubleArray, seqs [][]int, pos int, n node) { + ensureSize(da, pos) + + children := n.children(seqs) + var i int + for i = 1; ; i++ { + ok := func() bool { + for _, child := range children { + code := child.value(seqs) + j := i + code + ensureSize(da, j) + if da.Check[j] != 0 { + return false + } + } + return true + }() + if ok { + break + } + } + da.Base[pos] = i + for _, child := range children { + code := child.value(seqs) + j := i + code + da.Check[j] = pos + 1 + } + terminator := len(da.Encoding) + for _, child := range children { + code := child.value(seqs) + if code == terminator { + continue + } + j := i + code + addSeqs(da, seqs, j, *child) + } +} + +func ensureSize(da *DoubleArray, i int) { + for i >= len(da.Base) { + da.Base = append(da.Base, make([]int, len(da.Base)+1)...) + da.Check = append(da.Check, make([]int, len(da.Check)+1)...) + } +} + +type byLex [][]int + +func (l byLex) Len() int { return len(l) } +func (l byLex) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l byLex) Less(i, j int) bool { + si := l[i] + sj := l[j] + var k int + for k = 0; k < len(si) && k < len(sj); k++ { + if si[k] < sj[k] { + return true + } + if si[k] > sj[k] { + return false + } + } + return k < len(sj) +} + +// HasCommonPrefix determines if any sequence in the DoubleArray is a prefix of the given sequence. +func (da *DoubleArray) HasCommonPrefix(seq []string) bool { + if len(da.Base) == 0 { + return false + } + + var i int + for _, t := range seq { + code, ok := da.Encoding[t] + if !ok { + break + } + j := da.Base[i] + code + if len(da.Check) <= j || da.Check[j] != i+1 { + break + } + i = j + } + j := da.Base[i] + len(da.Encoding) + if len(da.Check) <= j || da.Check[j] != i+1 { + return false + } + return true +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 41649d26..3bb22a97 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -3,6 +3,7 @@ package assert import ( "fmt" "reflect" + "time" ) type CompareType int @@ -30,6 +31,8 @@ var ( float64Type = reflect.TypeOf(float64(1)) stringType = reflect.TypeOf("") + + timeType = reflect.TypeOf(time.Time{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { @@ -299,6 +302,27 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return compareLess, true } } + // Check for known struct types we can check for compare results. + case reflect.Struct: + { + // All structs enter here. We're not interested in most types. + if !canConvert(obj1Value, timeType) { + break + } + + // time.Time can compared! + timeObj1, ok := obj1.(time.Time) + if !ok { + timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) + } + + timeObj2, ok := obj2.(time.Time) + if !ok { + timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) + } + + return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + } } return compareEqual, false @@ -310,7 +334,10 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -320,7 +347,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second @@ -329,7 +359,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -339,7 +372,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive @@ -347,8 +383,11 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // assert.Positive(t, 1) // assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative @@ -356,8 +395,11 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // assert.Negative(t, -1) // assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go new file mode 100644 index 00000000..df22c47f --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go @@ -0,0 +1,16 @@ +//go:build go1.17 +// +build go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_legacy.go + +package assert + +import "reflect" + +// Wrapper around reflect.Value.CanConvert, for compatability +// reasons. +func canConvert(value reflect.Value, to reflect.Type) bool { + return value.CanConvert(to) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go new file mode 100644 index 00000000..1701af2a --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go @@ -0,0 +1,16 @@ +//go:build !go1.17 +// +build !go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_can_convert.go + +package assert + +import "reflect" + +// Older versions of Go does not have the reflect.Value.CanConvert +// method. +func canConvert(value reflect.Value, to reflect.Type) bool { + return false +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 4dfd1229..27e2420e 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -123,6 +123,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) +} + // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 25337a6f..d9ea368d 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -222,6 +222,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 1c3b4718..75944878 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing @@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing @@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing @@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index bcac4401..0357b223 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -718,10 +718,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (false, false) if impossible. // return (true, false) if element was not found. // return (true, true) if element was found. -func includeElement(list interface{}, element interface{}) (ok, found bool) { +func containsElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) - listKind := reflect.TypeOf(list).Kind() + listType := reflect.TypeOf(list) + if listType == nil { + return false, false + } + listKind := listType.Kind() defer func() { if e := recover(); e != nil { ok = false @@ -764,7 +768,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } @@ -787,7 +791,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) } @@ -831,7 +835,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -852,7 +856,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) h.Helper() } if subset == nil { - return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) + return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } subsetValue := reflect.ValueOf(subset) @@ -875,7 +879,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -1000,27 +1004,21 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { type PanicTestFunc func() // didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (bool, interface{}, string) { - - didPanic := false - var message interface{} - var stack string - func() { - - defer func() { - if message = recover(); message != nil { - didPanic = true - stack = string(debug.Stack()) - } - }() - - // call the target function - f() +func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { + didPanic = true + defer func() { + message = recover() + if didPanic { + stack = string(debug.Stack()) + } }() - return didPanic, message, stack + // call the target function + f() + didPanic = false + return } // Panics asserts that the code inside the specified PanicTestFunc panics. @@ -1161,11 +1159,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs bf, bok := toFloat(actual) if !aok || !bok { - return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + return Fail(t, "Parameters must be numerical", msgAndArgs...) + } + + if math.IsNaN(af) && math.IsNaN(bf) { + return true } if math.IsNaN(af) { - return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...) + return Fail(t, "Expected must not be NaN", msgAndArgs...) } if math.IsNaN(bf) { @@ -1188,7 +1190,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1250,8 +1252,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m func calcRelativeError(expected, actual interface{}) (float64, error) { af, aok := toFloat(expected) - if !aok { - return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + bf, bok := toFloat(actual) + if !aok || !bok { + return 0, fmt.Errorf("Parameters must be numerical") + } + if math.IsNaN(af) && math.IsNaN(bf) { + return 0, nil } if math.IsNaN(af) { return 0, errors.New("expected value must not be NaN") @@ -1259,10 +1265,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { if af == 0 { return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") } - bf, bok := toFloat(actual) - if !bok { - return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) - } if math.IsNaN(bf) { return 0, errors.New("actual value must not be NaN") } @@ -1298,7 +1300,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1375,6 +1377,27 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte return true } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + + actual := theError.Error() + if !strings.Contains(actual, contains) { + return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + } + + return true +} + // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { @@ -1588,12 +1611,17 @@ func diff(expected interface{}, actual interface{}) string { } var e, a string - if et != reflect.TypeOf("") { - e = spewConfig.Sdump(expected) - a = spewConfig.Sdump(actual) - } else { + + switch et { + case reflect.TypeOf(""): e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() + case reflect.TypeOf(time.Time{}): + e = spewConfigStringerEnabled.Sdump(expected) + a = spewConfigStringerEnabled.Sdump(actual) + default: + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ @@ -1625,6 +1653,14 @@ var spewConfig = spew.ConfigState{ MaxDepth: 10, } +var spewConfigStringerEnabled = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + MaxDepth: 10, +} + type tHelper interface { Helper() } diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 51820df2..59c48277 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -280,6 +280,36 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index ed54a9d8..5bb07c89 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -223,6 +223,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go new file mode 100644 index 00000000..a11fa8c8 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// +// 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 jaeger + +import "context" + +type jaegerKeyType int + +const ( + debugKey jaegerKeyType = iota +) + +// withDebug returns a copy of parent with debug set as the debug flag value . +func withDebug(parent context.Context, debug bool) context.Context { + return context.WithValue(parent, debugKey, debug) +} + +// debugFromContext returns the debug value stored in ctx. +// +// If no debug value is stored in ctx false is returned. +func debugFromContext(ctx context.Context) bool { + if ctx == nil { + return false + } + if debug, ok := ctx.Value(debugKey).(bool); ok { + return debug + } + return false +} diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go new file mode 100644 index 00000000..29f5bbd3 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go @@ -0,0 +1,17 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +// This package implements the Jaeger propagator specification as defined +// at https://www.jaegertracing.io/docs/1.18/client-libraries/#propagation-format +package jaeger // import "go.opentelemetry.io/contrib/propagators/jaeger" diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go new file mode 100644 index 00000000..ab230536 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go @@ -0,0 +1,161 @@ +// Copyright The OpenTelemetry Authors +// +// 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 jaeger + +import ( + "context" + "errors" + "fmt" + "strconv" + "strings" + + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +const ( + jaegerHeader = "uber-trace-id" + separator = ":" + traceID64bitsWidth = 64 / 4 + traceID128bitsWidth = 128 / 4 + spanIDWidth = 64 / 4 + + traceIDPadding = "0000000000000000" + + flagsDebug = 0x02 + flagsSampled = 0x01 + flagsNotSampled = 0x00 + + deprecatedParentSpanID = "0" +) + +var ( + empty = trace.SpanContext{} + + errMalformedTraceContextVal = errors.New("header value of uber-trace-id should contain four different part separated by : ") + errInvalidTraceIDLength = errors.New("invalid trace id length, must be either 16 or 32") + errMalformedTraceID = errors.New("cannot decode trace id from header, should be a string of hex, lowercase trace id can't be all zero") + errInvalidSpanIDLength = errors.New("invalid span id length, must be 16") + errMalformedSpanID = errors.New("cannot decode span id from header, should be a string of hex, lowercase span id can't be all zero") + errMalformedFlag = errors.New("cannot decode flag") +) + +// Jaeger propagator serializes SpanContext to/from Jaeger Headers +// +// Jaeger format: +// +// uber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags} +type Jaeger struct{} + +var _ propagation.TextMapPropagator = &Jaeger{} + +// Inject injects a context to the carrier following jaeger format. +// The parent span ID is set to an dummy parent span id as the most implementations do. +func (jaeger Jaeger) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { + sc := trace.SpanFromContext(ctx).SpanContext() + headers := []string{} + if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() { + return + } + headers = append(headers, sc.TraceID().String(), sc.SpanID().String(), deprecatedParentSpanID) + if debugFromContext(ctx) { + headers = append(headers, fmt.Sprintf("%x", flagsDebug|flagsSampled)) + } else if sc.IsSampled() { + headers = append(headers, fmt.Sprintf("%x", flagsSampled)) + } else { + headers = append(headers, fmt.Sprintf("%x", flagsNotSampled)) + } + + carrier.Set(jaegerHeader, strings.Join(headers, separator)) +} + +// Extract extracts a context from the carrier if it contains Jaeger headers. +func (jaeger Jaeger) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { + // extract tracing information + if h := carrier.Get(jaegerHeader); h != "" { + ctx, sc, err := extract(ctx, h) + if err == nil && sc.IsValid() { + return trace.ContextWithRemoteSpanContext(ctx, sc) + } + } + + return ctx +} + +func extract(ctx context.Context, headerVal string) (context.Context, trace.SpanContext, error) { + var ( + scc = trace.SpanContextConfig{} + err error + ) + + parts := strings.Split(headerVal, separator) + if len(parts) != 4 { + return ctx, empty, errMalformedTraceContextVal + } + + // extract trace ID + if parts[0] != "" { + id := parts[0] + if len(id) != traceID128bitsWidth && len(id) != traceID64bitsWidth { + return ctx, empty, errInvalidTraceIDLength + } + // padding when length is 16 + if len(id) == traceID64bitsWidth { + id = traceIDPadding + id + } + scc.TraceID, err = trace.TraceIDFromHex(id) + if err != nil { + return ctx, empty, errMalformedTraceID + } + } + + // extract span ID + if parts[1] != "" { + id := parts[1] + if len(id) != spanIDWidth { + return ctx, empty, errInvalidSpanIDLength + } + scc.SpanID, err = trace.SpanIDFromHex(id) + if err != nil { + return ctx, empty, errMalformedSpanID + } + } + + // skip third part as it is deprecated + + // extract flag + if parts[3] != "" { + flagStr := parts[3] + flag, err := strconv.ParseInt(flagStr, 16, 64) + if err != nil { + return ctx, empty, errMalformedFlag + } + if flag&flagsSampled == flagsSampled { + // if sample bit is set, we check if debug bit is also set + if flag&flagsDebug == flagsDebug { + scc.TraceFlags |= trace.FlagsSampled + ctx = withDebug(ctx, true) + } else { + scc.TraceFlags |= trace.FlagsSampled + } + } + // ignore other bit, including firehose since we don't have corresponding flag in trace context. + } + return ctx, trace.NewSpanContext(scc), nil +} + +func (jaeger Jaeger) Fields() []string { + return []string{jaegerHeader} +} diff --git a/vendor/go.opentelemetry.io/contrib/propagators/LICENSE b/vendor/go.opentelemetry.io/contrib/propagators/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/propagators/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/go.opentelemetry.io/otel/.gitattributes b/vendor/go.opentelemetry.io/otel/.gitattributes new file mode 100644 index 00000000..314766e9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf diff --git a/vendor/go.opentelemetry.io/otel/.gitignore b/vendor/go.opentelemetry.io/otel/.gitignore new file mode 100644 index 00000000..99230bae --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.gitignore @@ -0,0 +1,20 @@ +.DS_Store +Thumbs.db + +.tools/ +.idea/ +.vscode/ +*.iml +*.so +coverage.* + +gen/ + +/example/fib/fib +/example/jaeger/jaeger +/example/namedtracer/namedtracer +/example/opencensus/opencensus +/example/passthrough/passthrough +/example/prometheus/prometheus +/example/zipkin/zipkin +/example/otel-collector/otel-collector diff --git a/vendor/go.opentelemetry.io/otel/.gitmodules b/vendor/go.opentelemetry.io/otel/.gitmodules new file mode 100644 index 00000000..38a1f569 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.gitmodules @@ -0,0 +1,3 @@ +[submodule "opentelemetry-proto"] + path = exporters/otlp/internal/opentelemetry-proto + url = https://github.com/open-telemetry/opentelemetry-proto diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml new file mode 100644 index 00000000..7a5fdc07 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.golangci.yml @@ -0,0 +1,47 @@ +# See https://github.com/golangci/golangci-lint#config-file +run: + issues-exit-code: 1 #Default + tests: true #Default + +linters: + # Disable everything by default so upgrades to not include new "default + # enabled" linters. + disable-all: true + # Specifically enable linters we want to use. + enable: + - deadcode + - errcheck + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - revive + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + + +issues: + exclude-rules: + # helpers in tests often (rightfully) pass a *testing.T as their first argument + - path: _test\.go + text: "context.Context should be the first parameter of a function" + linters: + - revive + # Yes, they are, but it's okay in a test + - path: _test\.go + text: "exported func.*returns unexported type.*which can be annoying to use" + linters: + - revive + +linters-settings: + misspell: + locale: US + ignore-words: + - cancelled + goimports: + local-prefixes: go.opentelemetry.io diff --git a/vendor/go.opentelemetry.io/otel/.markdown-link.json b/vendor/go.opentelemetry.io/otel/.markdown-link.json new file mode 100644 index 00000000..bd786f03 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.markdown-link.json @@ -0,0 +1,20 @@ +{ + "ignorePatterns": [ + { + "pattern": "^http(s)?://localhost" + } + ], + "replacementPatterns": [ + { + "pattern": "^/registry", + "replacement": "https://opentelemetry.io/registry" + }, + { + "pattern": "^/docs/", + "replacement": "https://opentelemetry.io/docs/" + } + ], + "retryOn429": true, + "retryCount": 5, + "fallbackRetryDelay": "30s" +} diff --git a/vendor/go.opentelemetry.io/otel/.markdownlint.yaml b/vendor/go.opentelemetry.io/otel/.markdownlint.yaml new file mode 100644 index 00000000..3202496c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/.markdownlint.yaml @@ -0,0 +1,29 @@ +# Default state for all rules +default: true + +# ul-style +MD004: false + +# hard-tabs +MD010: false + +# line-length +MD013: false + +# no-duplicate-header +MD024: + siblings_only: true + +#single-title +MD025: false + +# ol-prefix +MD029: + style: ordered + +# no-inline-html +MD033: false + +# fenced-code-language +MD040: false + diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md new file mode 100644 index 00000000..381ce2e3 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -0,0 +1,1860 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- The metrics global package was added back into several test files. (#2764) +- The `Meter` function is added back to the `go.opentelemetry.io/otel/metric/global` package. + This function is a convenience function equivalent to calling `global.MeterProvider().Meter(...)`. (#2750) + +### Removed + +- Removed module the `go.opentelemetry.io/otel/sdk/export/metric`. + Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2720) + +### Changed + +- Don't panic anymore when setting a global MeterProvider to itself. (#2749) +- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` from `v0.12.1` to `v0.15.0`. + This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibraryMetrics` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeMetrics`. (#2748) + +## [1.6.3] - 2022-04-07 + +### Fixed + +- Allow non-comparable global `MeterProvider`, `TracerProvider`, and `TextMapPropagator` types to be set. (#2772, #2773) + +## [1.6.2] - 2022-04-06 + +### Changed + +- Don't panic anymore when setting a global TracerProvider or TextMapPropagator to itself. (#2749) +- Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from `v0.12.1` to `v0.15.0`. + This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibrarySpans` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeSpans`. (#2748) + +## [1.6.1] - 2022-03-28 + +### Fixed + +- The `go.opentelemetry.io/otel/schema/*` packages now use the correct schema URL for their `SchemaURL` constant. + Instead of using `"https://opentelemetry.io/schemas/v"` they now use the correct URL without a `v` prefix, `"https://opentelemetry.io/schemas/"`. (#2743, #2744) + +### Security + +- Upgrade `go.opentelemetry.io/proto/otlp` from `v0.12.0` to `v0.12.1`. + This includes an indirect upgrade of `github.com/grpc-ecosystem/grpc-gateway` which resolves [a vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2019-11254) from `gopkg.in/yaml.v2` in version `v2.2.3`. (#2724, #2728) + +## [1.6.0/0.28.0] - 2022-03-23 + +### ⚠️ Notice ⚠️ + +This update is a breaking change of the unstable Metrics API. +Code instrumented with the `go.opentelemetry.io/otel/metric` will need to be modified. + +### Added + +- Add metrics exponential histogram support. + New mapping functions have been made available in `sdk/metric/aggregator/exponential/mapping` for other OpenTelemetry projects to take dependencies on. (#2502) +- Add Go 1.18 to our compatibility tests. (#2679) +- Allow configuring the Sampler with the `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` environment variables. (#2305, #2517) +- Add the `metric/global` for obtaining and setting the global `MeterProvider`. (#2660) + +### Changed + +- The metrics API has been significantly changed to match the revised OpenTelemetry specification. + High-level changes include: + + - Synchronous and asynchronous instruments are now handled by independent `InstrumentProvider`s. + These `InstrumentProvider`s are managed with a `Meter`. + - Synchronous and asynchronous instruments are grouped into their own packages based on value types. + - Asynchronous callbacks can now be registered with a `Meter`. + + Be sure to check out the metric module documentation for more information on how to use the revised API. (#2587, #2660) + +### Fixed + +- Fallback to general attribute limits when span specific ones are not set in the environment. (#2675, #2677) + +## [1.5.0] - 2022-03-16 + +### Added + +- Log the Exporters configuration in the TracerProviders message. (#2578) +- Added support to configure the span limits with environment variables. + The following environment variables are supported. (#2606, #2637) + - `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` + - `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` + - `OTEL_SPAN_EVENT_COUNT_LIMIT` + - `OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT` + - `OTEL_SPAN_LINK_COUNT_LIMIT` + - `OTEL_LINK_ATTRIBUTE_COUNT_LIMIT` + + If the provided environment variables are invalid (negative), the default values would be used. +- Rename the `gc` runtime name to `go` (#2560) +- Add resource container ID detection. (#2418) +- Add span attribute value length limit. + The new `AttributeValueLengthLimit` field is added to the `"go.opentelemetry.io/otel/sdk/trace".SpanLimits` type to configure this limit for a `TracerProvider`. + The default limit for this resource is "unlimited". (#2637) +- Add the `WithRawSpanLimits` option to `go.opentelemetry.io/otel/sdk/trace`. + This option replaces the `WithSpanLimits` option. + Zero or negative values will not be changed to the default value like `WithSpanLimits` does. + Setting a limit to zero will effectively disable the related resource it limits and setting to a negative value will mean that resource is unlimited. + Consequentially, limits should be constructed using `NewSpanLimits` and updated accordingly. (#2637) + +### Changed + +- Drop oldest tracestate `Member` when capacity is reached. (#2592) +- Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601) +- Unify path cleaning functionally in the `otlpmetric` and `otlptrace` configuration. (#2639) +- Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640) +- Introduce new internal `envconfig` package for OTLP exporters. (#2608) +- If `http.Request.Host` is empty, fall back to use `URL.Host` when populating `http.host` in the `semconv` packages. (#2661) + +### Fixed + +- Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616) +- Default to port `4318` instead of `4317` for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625) +- Unlimited span limits are now supported (negative values). (#2636, #2637) + +### Deprecated + +- Deprecated `"go.opentelemetry.io/otel/sdk/trace".WithSpanLimits`. + Use `WithRawSpanLimits` instead. + That option allows setting unlimited and zero limits, this option does not. + This option will be kept until the next major version incremented release. (#2637) + +## [1.4.1] - 2022-02-16 + +### Fixed + +- Fix race condition in reading the dropped spans number for the `BatchSpanProcessor`. (#2615) + +## [1.4.0] - 2022-02-11 + +### Added + +- Use `OTEL_EXPORTER_ZIPKIN_ENDPOINT` environment variable to specify zipkin collector endpoint. (#2490) +- Log the configuration of `TracerProvider`s, and `Tracer`s for debugging. + To enable use a logger with Verbosity (V level) `>=1`. (#2500) +- Added support to configure the batch span-processor with environment variables. + The following environment variables are used. (#2515) + - `OTEL_BSP_SCHEDULE_DELAY` + - `OTEL_BSP_EXPORT_TIMEOUT` + - `OTEL_BSP_MAX_QUEUE_SIZE`. + - `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` + +### Changed + +- Zipkin exporter exports `Resource` attributes in the `Tags` field. (#2589) + +### Deprecated + +- Deprecate module the `go.opentelemetry.io/otel/sdk/export/metric`. + Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2382) +- Deprecate `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets`. (#2445) + +### Fixed + +- Fixed the instrument kind for noop async instruments to correctly report an implementation. (#2461) +- Fix UDP packets overflowing with Jaeger payloads. (#2489, #2512) +- Change the `otlpmetric.Client` interface's `UploadMetrics` method to accept a single `ResourceMetrics` instead of a slice of them. (#2491) +- Specify explicit buckets in Prometheus example, fixing issue where example only has `+inf` bucket. (#2419, #2493) +- W3C baggage will now decode urlescaped values. (#2529) +- Baggage members are now only validated once, when calling `NewMember` and not also when adding it to the baggage itself. (#2522) +- The order attributes are dropped from spans in the `go.opentelemetry.io/otel/sdk/trace` package when capacity is reached is fixed to be in compliance with the OpenTelemetry specification. + Instead of dropping the least-recently-used attribute, the last added attribute is dropped. + This drop order still only applies to attributes with unique keys not already contained in the span. + If an attribute is added with a key already contained in the span, that attribute is updated to the new value being added. (#2576) + +### Removed + +- Updated `go.opentelemetry.io/proto/otlp` from `v0.11.0` to `v0.12.0`. This version removes a number of deprecated methods. (#2546) + - [`Metric.GetIntGauge()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntGauge) + - [`Metric.GetIntHistogram()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntHistogram) + - [`Metric.GetIntSum()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntSum) + +## [1.3.0] - 2021-12-10 + +### ⚠️ Notice ⚠️ + +We have updated the project minimum supported Go version to 1.16 + +### Added + +- Added an internal Logger. + This can be used by the SDK and API to provide users with feedback of the internal state. + To enable verbose logs configure the logger which will print V(1) logs. For debugging information configure to print V(5) logs. (#2343) +- Add the `WithRetry` `Option` and the `RetryConfig` type to the `go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp` package to specify retry behavior consistently. (#2425) +- Add `SpanStatusFromHTTPStatusCodeAndSpanKind` to all `semconv` packages to return a span status code similar to `SpanStatusFromHTTPStatusCode`, but exclude `4XX` HTTP errors as span errors if the span is of server kind. (#2296) + +### Changed + +- The `"go.opentelemetry.io/otel/exporter/otel/otlptrace/otlptracegrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2329) +- The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2425) +- The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".RetrySettings` type is renamed to `RetryConfig`. (#2425) +- The `go.opentelemetry.io/otel/exporter/otel/*` gRPC exporters now default to using the host's root CA set if none are provided by the user and `WithInsecure` is not specified. (#2432) +- Change `resource.Default` to be evaluated the first time it is called, rather than on import. This allows the caller the option to update `OTEL_RESOURCE_ATTRIBUTES` first, such as with `os.Setenv`. (#2371) + +### Fixed + +- The `go.opentelemetry.io/otel/exporter/otel/*` exporters are updated to handle per-signal and universal endpoints according to the OpenTelemetry specification. + Any per-signal endpoint set via an `OTEL_EXPORTER_OTLP__ENDPOINT` environment variable is now used without modification of the path. + When `OTEL_EXPORTER_OTLP_ENDPOINT` is set, if it contains a path, that path is used as a base path which per-signal paths are appended to. (#2433) +- Basic metric controller updated to use sync.Map to avoid blocking calls (#2381) +- The `go.opentelemetry.io/otel/exporter/jaeger` correctly sets the `otel.status_code` value to be a string of `ERROR` or `OK` instead of an integer code. (#2439, #2440) + +### Deprecated + +- Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithMaxAttempts` `Option`, use the new `WithRetry` `Option` instead. (#2425) +- Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithBackoff` `Option`, use the new `WithRetry` `Option` instead. (#2425) + +### Removed + +- Remove the metric Processor's ability to convert cumulative to delta aggregation temporality. (#2350) +- Remove the metric Bound Instruments interface and implementations. (#2399) +- Remove the metric MinMaxSumCount kind aggregation and the corresponding OTLP export path. (#2423) +- Metric SDK removes the "exact" aggregator for histogram instruments, as it performed a non-standard aggregation for OTLP export (creating repeated Gauge points) and worked its way into a number of confusing examples. (#2348) + +## [1.2.0] - 2021-11-12 + +### Changed + +- Metric SDK `export.ExportKind`, `export.ExportKindSelector` types have been renamed to `aggregation.Temporality` and `aggregation.TemporalitySelector` respectively to keep in line with current specification and protocol along with built-in selectors (e.g., `aggregation.CumulativeTemporalitySelector`, ...). (#2274) +- The Metric `Exporter` interface now requires a `TemporalitySelector` method instead of an `ExportKindSelector`. (#2274) +- Metrics API cleanup. The `metric/sdkapi` package has been created to relocate the API-to-SDK interface: + - The following interface types simply moved from `metric` to `metric/sdkapi`: `Descriptor`, `MeterImpl`, `InstrumentImpl`, `SyncImpl`, `BoundSyncImpl`, `AsyncImpl`, `AsyncRunner`, `AsyncSingleRunner`, and `AsyncBatchRunner` + - The following struct types moved and are replaced with type aliases, since they are exposed to the user: `Observation`, `Measurement`. + - The No-op implementations of sync and async instruments are no longer exported, new functions `sdkapi.NewNoopAsyncInstrument()` and `sdkapi.NewNoopSyncInstrument()` are provided instead. (#2271) +- Update the SDK `BatchSpanProcessor` to export all queued spans when `ForceFlush` is called. (#2080, #2335) + +### Added + +- Add the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) +- Added a new `schema` module to help parse Schema Files in OTEP 0152 format. (#2267) +- Added a new `MapCarrier` to the `go.opentelemetry.io/otel/propagation` package to hold propagated cross-cutting concerns as a `map[string]string` held in memory. (#2334) + +## [1.1.0] - 2021-10-27 + +### Added + +- Add the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) +- Add the `go.opentelemetry.io/otel/semconv/v1.7.0` package. + The package contains semantic conventions from the `v1.7.0` version of the OpenTelemetry specification. (#2320) +- Add the `go.opentelemetry.io/otel/semconv/v1.6.1` package. + The package contains semantic conventions from the `v1.6.1` version of the OpenTelemetry specification. (#2321) +- Add the `go.opentelemetry.io/otel/semconv/v1.5.0` package. + The package contains semantic conventions from the `v1.5.0` version of the OpenTelemetry specification. (#2322) + - When upgrading from the `semconv/v1.4.0` package note the following name changes: + - `K8SReplicasetUIDKey` -> `K8SReplicaSetUIDKey` + - `K8SReplicasetNameKey` -> `K8SReplicaSetNameKey` + - `K8SStatefulsetUIDKey` -> `K8SStatefulSetUIDKey` + - `k8SStatefulsetNameKey` -> `K8SStatefulSetNameKey` + - `K8SDaemonsetUIDKey` -> `K8SDaemonSetUIDKey` + - `K8SDaemonsetNameKey` -> `K8SDaemonSetNameKey` + +### Changed + +- Links added to a span will be dropped by the SDK if they contain an invalid span context (#2275). + +### Fixed + +- The `"go.opentelemetry.io/otel/semconv/v1.4.0".HTTPServerAttributesFromHTTPRequest` now correctly only sets the HTTP client IP attribute even if the connection was routed with proxies and there are multiple addresses in the `X-Forwarded-For` header. (#2282, #2284) +- The `"go.opentelemetry.io/otel/semconv/v1.4.0".NetAttributesFromHTTPRequest` function correctly handles IPv6 addresses as IP addresses and sets the correct net peer IP instead of the net peer hostname attribute. (#2283, #2285) +- The simple span processor shutdown method deterministically returns the exporter error status if it simultaneously finishes when the deadline is reached. (#2290, #2289) + +## [1.0.1] - 2021-10-01 + +### Fixed + +- json stdout exporter no longer crashes due to concurrency bug. (#2265) + +## [Metrics 0.24.0] - 2021-10-01 + +### Changed + +- NoopMeterProvider is now private and NewNoopMeterProvider must be used to obtain a noopMeterProvider. (#2237) +- The Metric SDK `Export()` function takes a new two-level reader interface for iterating over results one instrumentation library at a time. (#2197) + - The former `"go.opentelemetry.io/otel/sdk/export/metric".CheckpointSet` is renamed `Reader`. + - The new interface is named `"go.opentelemetry.io/otel/sdk/export/metric".InstrumentationLibraryReader`. + +## [1.0.0] - 2021-09-20 + +This is the first stable release for the project. +This release includes an API and SDK for the tracing signal that will comply with the stability guarantees defined by the projects [versioning policy](./VERSIONING.md). + +### Added + +- OTLP trace exporter now sets the `SchemaURL` field in the exported telemetry if the Tracer has `WithSchemaURL` option. (#2242) + +### Fixed + +- Slice-valued attributes can correctly be used as map keys. (#2223) + +### Removed + +- Removed the `"go.opentelemetry.io/otel/exporters/zipkin".WithSDKOptions` function. (#2248) +- Removed the deprecated package `go.opentelemetry.io/otel/oteltest`. (#2234) +- Removed the deprecated package `go.opentelemetry.io/otel/bridge/opencensus/utils`. (#2233) +- Removed deprecated functions, types, and methods from `go.opentelemetry.io/otel/attribute` package. + Use the typed functions and methods added to the package instead. (#2235) + - The `Key.Array` method is removed. + - The `Array` function is removed. + - The `Any` function is removed. + - The `ArrayValue` function is removed. + - The `AsArray` function is removed. + +## [1.0.0-RC3] - 2021-09-02 + +### Added + +- Added `ErrorHandlerFunc` to use a function as an `"go.opentelemetry.io/otel".ErrorHandler`. (#2149) +- Added `"go.opentelemetry.io/otel/trace".WithStackTrace` option to add a stack trace when using `span.RecordError` or when panic is handled in `span.End`. (#2163) +- Added typed slice attribute types and functionality to the `go.opentelemetry.io/otel/attribute` package to replace the existing array type and functions. (#2162) + - `BoolSlice`, `IntSlice`, `Int64Slice`, `Float64Slice`, and `StringSlice` replace the use of the `Array` function in the package. +- Added the `go.opentelemetry.io/otel/example/fib` example package. + Included is an example application that computes Fibonacci numbers. (#2203) + +### Changed + +- Metric instruments have been renamed to match the (feature-frozen) metric API specification: + - ValueRecorder becomes Histogram + - ValueObserver becomes Gauge + - SumObserver becomes CounterObserver + - UpDownSumObserver becomes UpDownCounterObserver + The API exported from this project is still considered experimental. (#2202) +- Metric SDK/API implementation type `InstrumentKind` moves into `sdkapi` sub-package. (#2091) +- The Metrics SDK export record no longer contains a Resource pointer, the SDK `"go.opentelemetry.io/otel/sdk/trace/export/metric".Exporter.Export()` function for push-based exporters now takes a single Resource argument, pull-based exporters use `"go.opentelemetry.io/otel/sdk/metric/controller/basic".Controller.Resource()`. (#2120) +- The JSON output of the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` is harmonized now such that the output is "plain" JSON objects after each other of the form `{ ... } { ... } { ... }`. Earlier the JSON objects describing a span were wrapped in a slice for each `Exporter.ExportSpans` call, like `[ { ... } ][ { ... } { ... } ]`. Outputting JSON object directly after each other is consistent with JSON loggers, and a bit easier to parse and read. (#2196) +- Update the `NewTracerConfig`, `NewSpanStartConfig`, `NewSpanEndConfig`, and `NewEventConfig` function in the `go.opentelemetry.io/otel/trace` package to return their respective configurations as structs instead of pointers to the struct. (#2212) + +### Deprecated + +- The `go.opentelemetry.io/otel/bridge/opencensus/utils` package is deprecated. + All functionality from this package now exists in the `go.opentelemetry.io/otel/bridge/opencensus` package. + The functions from that package should be used instead. (#2166) +- The `"go.opentelemetry.io/otel/attribute".Array` function and the related `ARRAY` value type is deprecated. + Use the typed `*Slice` functions and types added to the package instead. (#2162) +- The `"go.opentelemetry.io/otel/attribute".Any` function is deprecated. + Use the typed functions instead. (#2181) +- The `go.opentelemetry.io/otel/oteltest` package is deprecated. + The `"go.opentelemetry.io/otel/sdk/trace/tracetest".SpanRecorder` can be registered with the default SDK (`go.opentelemetry.io/otel/sdk/trace`) as a `SpanProcessor` and used as a replacement for this deprecated package. (#2188) + +### Removed + +- Removed metrics test package `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#2105) + +### Fixed + +- The `fromEnv` detector no longer throws an error when `OTEL_RESOURCE_ATTRIBUTES` environment variable is not set or empty. (#2138) +- Setting the global `ErrorHandler` with `"go.opentelemetry.io/otel".SetErrorHandler` multiple times is now supported. (#2160, #2140) +- The `"go.opentelemetry.io/otel/attribute".Any` function now supports `int32` values. (#2169) +- Multiple calls to `"go.opentelemetry.io/otel/sdk/metric/controller/basic".WithResource()` are handled correctly, and when no resources are provided `"go.opentelemetry.io/otel/sdk/resource".Default()` is used. (#2120) +- The `WithoutTimestamps` option for the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter causes the exporter to correctly ommit timestamps. (#2195) +- Fixed typos in resources.go. (#2201) + +## [1.0.0-RC2] - 2021-07-26 + +### Added + +- Added `WithOSDescription` resource configuration option to set OS (Operating System) description resource attribute (`os.description`). (#1840) +- Added `WithOS` resource configuration option to set all OS (Operating System) resource attributes at once. (#1840) +- Added the `WithRetry` option to the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. + This option is a replacement for the removed `WithMaxAttempts` and `WithBackoff` options. (#2095) +- Added API `LinkFromContext` to return Link which encapsulates SpanContext from provided context and also encapsulates attributes. (#2115) +- Added a new `Link` type under the SDK `otel/sdk/trace` package that counts the number of attributes that were dropped for surpassing the `AttributePerLinkCountLimit` configured in the Span's `SpanLimits`. + This new type replaces the equal-named API `Link` type found in the `otel/trace` package for most usages within the SDK. + For example, instances of this type are now returned by the `Links()` function of `ReadOnlySpan`s provided in places like the `OnEnd` function of `SpanProcessor` implementations. (#2118) +- Added the `SpanRecorder` type to the `go.opentelemetry.io/otel/skd/trace/tracetest` package. + This type can be used with the default SDK as a `SpanProcessor` during testing. (#2132) + +### Changed + +- The `SpanModels` function is now exported from the `go.opentelemetry.io/otel/exporters/zipkin` package to convert OpenTelemetry spans into Zipkin model spans. (#2027) +- Rename the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".RetrySettings` to `RetryConfig`. (#2095) + +### Deprecated + +- The `TextMapCarrier` and `TextMapPropagator` from the `go.opentelemetry.io/otel/oteltest` package and their associated creation functions (`TextMapCarrier`, `NewTextMapPropagator`) are deprecated. (#2114) +- The `Harness` type from the `go.opentelemetry.io/otel/oteltest` package and its associated creation function, `NewHarness` are deprecated and will be removed in the next release. (#2123) +- The `TraceStateFromKeyValues` function from the `go.opentelemetry.io/otel/oteltest` package is deprecated. + Use the `trace.ParseTraceState` function instead. (#2122) + +### Removed + +- Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/jaeger`. (#2020) +- Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/zipkin`. (#2020) +- Removed the `"go.opentelemetry.io/otel/sdk/resource".WithBuiltinDetectors` function. + The explicit `With*` options for every built-in detector should be used instead. (#2026 #2097) +- Removed the `WithMaxAttempts` and `WithBackoff` options from the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. + The retry logic of the package has been updated to match the `otlptracegrpc` package and accordingly a `WithRetry` option is added that should be used instead. (#2095) +- Removed `DroppedAttributeCount` field from `otel/trace.Link` struct. (#2118) + +### Fixed + +- When using WithNewRoot, don't use the parent context for making sampling decisions. (#2032) +- `oteltest.Tracer` now creates a valid `SpanContext` when using `WithNewRoot`. (#2073) +- OS type detector now sets the correct `dragonflybsd` value for DragonFly BSD. (#2092) +- The OTel span status is correctly transformed into the OTLP status in the `go.opentelemetry.io/otel/exporters/otlp/otlptrace` package. + This fix will by default set the status to `Unset` if it is not explicitly set to `Ok` or `Error`. (#2099 #2102) +- The `Inject` method for the `"go.opentelemetry.io/otel/propagation".TraceContext` type no longer injects empty `tracestate` values. (#2108) +- Use `6831` as default Jaeger agent port instead of `6832`. (#2131) + +## [Experimental Metrics v0.22.0] - 2021-07-19 + +### Added + +- Adds HTTP support for OTLP metrics exporter. (#2022) + +### Removed + +- Removed the deprecated package `go.opentelemetry.io/otel/exporters/metric/prometheus`. (#2020) + +## [1.0.0-RC1] / 0.21.0 - 2021-06-18 + +With this release we are introducing a split in module versions. The tracing API and SDK are entering the `v1.0.0` Release Candidate phase with `v1.0.0-RC1` +while the experimental metrics API and SDK continue with `v0.x` releases at `v0.21.0`. Modules at major version 1 or greater will not depend on modules +with major version 0. + +### Added + +- Adds `otlpgrpc.WithRetry`option for configuring the retry policy for transient errors on the otlp/gRPC exporter. (#1832) + - The following status codes are defined as transient errors: + | gRPC Status Code | Description | + | ---------------- | ----------- | + | 1 | Cancelled | + | 4 | Deadline Exceeded | + | 8 | Resource Exhausted | + | 10 | Aborted | + | 10 | Out of Range | + | 14 | Unavailable | + | 15 | Data Loss | +- Added `Status` type to the `go.opentelemetry.io/otel/sdk/trace` package to represent the status of a span. (#1874) +- Added `SpanStub` type and its associated functions to the `go.opentelemetry.io/otel/sdk/trace/tracetest` package. + This type can be used as a testing replacement for the `SpanSnapshot` that was removed from the `go.opentelemetry.io/otel/sdk/trace` package. (#1873) +- Adds support for scheme in `OTEL_EXPORTER_OTLP_ENDPOINT` according to the spec. (#1886) +- Adds `trace.WithSchemaURL` option for configuring the tracer with a Schema URL. (#1889) +- Added an example of using OpenTelemetry Go as a trace context forwarder. (#1912) +- `ParseTraceState` is added to the `go.opentelemetry.io/otel/trace` package. + It can be used to decode a `TraceState` from a `tracestate` header string value. (#1937) +- Added `Len` method to the `TraceState` type in the `go.opentelemetry.io/otel/trace` package. + This method returns the number of list-members the `TraceState` holds. (#1937) +- Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace` that defines a trace exporter that uses a `otlptrace.Client` to send data. + Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` implementing a gRPC `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing .(#1922) +- Added `Baggage`, `Member`, and `Property` types to the `go.opentelemetry.io/otel/baggage` package along with their related functions. (#1967) +- Added `ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext` functions to the `go.opentelemetry.io/otel/baggage` package. + These functions replace the `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions from that package and directly work with the new `Baggage` type. (#1967) +- The `OTEL_SERVICE_NAME` environment variable is the preferred source for `service.name`, used by the environment resource detector if a service name is present both there and in `OTEL_RESOURCE_ATTRIBUTES`. (#1969) +- Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` implementing an HTTP `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing. (#1963) +- Changes `go.opentelemetry.io/otel/sdk/resource.NewWithAttributes` to require a schema URL. The old function is still available as `resource.NewSchemaless`. This is a breaking change. (#1938) +- Several builtin resource detectors now correctly populate the schema URL. (#1938) +- Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` that defines a metrics exporter that uses a `otlpmetric.Client` to send data. +- Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` implementing a gRPC `otlpmetric.Client` and offers convenience functions, `New` and `NewUnstarted`, to create an `otlpmetric.Exporter`.(#1991) +- Added `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter. (#2005) +- Added `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` exporter. (#2005) +- Added a `TracerProvider()` method to the `"go.opentelemetry.io/otel/trace".Span` interface. This can be used to obtain a `TracerProvider` from a given span that utilizes the same trace processing pipeline. (#2009) + +### Changed + +- Make `NewSplitDriver` from `go.opentelemetry.io/otel/exporters/otlp` take variadic arguments instead of a `SplitConfig` item. + `NewSplitDriver` now automatically implements an internal `noopDriver` for `SplitConfig` fields that are not initialized. (#1798) +- `resource.New()` now creates a Resource without builtin detectors. Previous behavior is now achieved by using `WithBuiltinDetectors` Option. (#1810) +- Move the `Event` type from the `go.opentelemetry.io/otel` package to the `go.opentelemetry.io/otel/sdk/trace` package. (#1846) +- CI builds validate against last two versions of Go, dropping 1.14 and adding 1.16. (#1865) +- BatchSpanProcessor now report export failures when calling `ForceFlush()` method. (#1860) +- `Set.Encoded(Encoder)` no longer caches the result of an encoding. (#1855) +- Renamed `CloudZoneKey` to `CloudAvailabilityZoneKey` in Resource semantic conventions according to spec. (#1871) +- The `StatusCode` and `StatusMessage` methods of the `ReadOnlySpan` interface and the `Span` produced by the `go.opentelemetry.io/otel/sdk/trace` package have been replaced with a single `Status` method. + This method returns the status of a span using the new `Status` type. (#1874) +- Updated `ExportSpans` method of the`SpanExporter` interface type to accept `ReadOnlySpan`s instead of the removed `SpanSnapshot`. + This brings the export interface into compliance with the specification in that it now accepts an explicitly immutable type instead of just an implied one. (#1873) +- Unembed `SpanContext` in `Link`. (#1877) +- Generate Semantic conventions from the specification YAML. (#1891) +- Spans created by the global `Tracer` obtained from `go.opentelemetry.io/otel`, prior to a functioning `TracerProvider` being set, now propagate the span context from their parent if one exists. (#1901) +- The `"go.opentelemetry.io/otel".Tracer` function now accepts tracer options. (#1902) +- Move the `go.opentelemetry.io/otel/unit` package to `go.opentelemetry.io/otel/metric/unit`. (#1903) +- Changed `go.opentelemetry.io/otel/trace.TracerConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config.) (#1921) +- Changed `go.opentelemetry.io/otel/trace.SpanConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) +- Changed `span.End()` now only accepts Options that are allowed at `End()`. (#1921) +- Changed `go.opentelemetry.io/otel/metric.InstrumentConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) +- Changed `go.opentelemetry.io/otel/metric.MeterConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) +- Refactored option types according to the contribution style guide. (#1882) +- Move the `go.opentelemetry.io/otel/trace.TraceStateFromKeyValues` function to the `go.opentelemetry.io/otel/oteltest` package. + This function is preserved for testing purposes where it may be useful to create a `TraceState` from `attribute.KeyValue`s, but it is not intended for production use. + The new `ParseTraceState` function should be used to create a `TraceState`. (#1931) +- Updated `MarshalJSON` method of the `go.opentelemetry.io/otel/trace.TraceState` type to marshal the type into the string representation of the `TraceState`. (#1931) +- The `TraceState.Delete` method from the `go.opentelemetry.io/otel/trace` package no longer returns an error in addition to a `TraceState`. (#1931) +- Updated `Get` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) +- Updated `Insert` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a pair of `string`s instead of an `attribute.KeyValue` type. (#1931) +- Updated `Delete` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) +- Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/stdout` package. (#1985) +- Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/metric/prometheus` package. (#1985) +- Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1985) +- Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1985) +- Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) +- Renamed `NewUnstartedExporter` to `NewUnstarted` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) +- The `go.opentelemetry.io/otel/semconv` package has been moved to `go.opentelemetry.io/otel/semconv/v1.4.0` to allow for multiple [telemetry schema](https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md) versions to be used concurrently. (#1987) +- Metrics test helpers in `go.opentelemetry.io/otel/oteltest` have been moved to `go.opentelemetry.io/otel/metric/metrictest`. (#1988) + +### Deprecated + +- The `go.opentelemetry.io/otel/exporters/metric/prometheus` is deprecated, use `go.opentelemetry.io/otel/exporters/prometheus` instead. (#1993) +- The `go.opentelemetry.io/otel/exporters/trace/jaeger` is deprecated, use `go.opentelemetry.io/otel/exporters/jaeger` instead. (#1993) +- The `go.opentelemetry.io/otel/exporters/trace/zipkin` is deprecated, use `go.opentelemetry.io/otel/exporters/zipkin` instead. (#1993) + +### Removed + +- Removed `resource.WithoutBuiltin()`. Use `resource.New()`. (#1810) +- Unexported types `resource.FromEnv`, `resource.Host`, and `resource.TelemetrySDK`, Use the corresponding `With*()` to use individually. (#1810) +- Removed the `Tracer` and `IsRecording` method from the `ReadOnlySpan` in the `go.opentelemetry.io/otel/sdk/trace`. + The `Tracer` method is not a required to be included in this interface and given the mutable nature of the tracer that is associated with a span, this method is not appropriate. + The `IsRecording` method returns if the span is recording or not. + A read-only span value does not need to know if updates to it will be recorded or not. + By definition, it cannot be updated so there is no point in communicating if an update is recorded. (#1873) +- Removed the `SpanSnapshot` type from the `go.opentelemetry.io/otel/sdk/trace` package. + The use of this type has been replaced with the use of the explicitly immutable `ReadOnlySpan` type. + When a concrete representation of a read-only span is needed for testing, the newly added `SpanStub` in the `go.opentelemetry.io/otel/sdk/trace/tracetest` package should be used. (#1873) +- Removed the `Tracer` method from the `Span` interface in the `go.opentelemetry.io/otel/trace` package. + Using the same tracer that created a span introduces the error where an instrumentation library's `Tracer` is used by other code instead of their own. + The `"go.opentelemetry.io/otel".Tracer` function or a `TracerProvider` should be used to acquire a library specific `Tracer` instead. (#1900) + - The `TracerProvider()` method on the `Span` interface may also be used to obtain a `TracerProvider` using the same trace processing pipeline. (#2009) +- The `http.url` attribute generated by `HTTPClientAttributesFromHTTPRequest` will no longer include username or password information. (#1919) +- Removed `IsEmpty` method of the `TraceState` type in the `go.opentelemetry.io/otel/trace` package in favor of using the added `TraceState.Len` method. (#1931) +- Removed `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions in the `go.opentelemetry.io/otel/baggage` package. + Handling of baggage is now done using the added `Baggage` type and related context functions (`ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext`) in that package. (#1967) +- The `InstallNewPipeline` and `NewExportPipeline` creation functions in all the exporters (prometheus, otlp, stdout, jaeger, and zipkin) have been removed. + These functions were deemed premature attempts to provide convenience that did not achieve this aim. (#1985) +- The `go.opentelemetry.io/otel/exporters/otlp` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/otlp/otlptrace` instead. (#1990) +- The `go.opentelemetry.io/otel/exporters/stdout` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` or `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` instead. (#2005) + +### Fixed + +- Only report errors from the `"go.opentelemetry.io/otel/sdk/resource".Environment` function when they are not `nil`. (#1850, #1851) +- The `Shutdown` method of the simple `SpanProcessor` in the `go.opentelemetry.io/otel/sdk/trace` package now honors the context deadline or cancellation. (#1616, #1856) +- BatchSpanProcessor now drops span batches that failed to be exported. (#1860) +- Use `http://localhost:14268/api/traces` as default Jaeger collector endpoint instead of `http://localhost:14250`. (#1898) +- Allow trailing and leading whitespace in the parsing of a `tracestate` header. (#1931) +- Add logic to determine if the channel is closed to fix Jaeger exporter test panic with close closed channel. (#1870, #1973) +- Avoid transport security when OTLP endpoint is a Unix socket. (#2001) + +### Security + +## [0.20.0] - 2021-04-23 + +### Added + +- The OTLP exporter now has two new convenience functions, `NewExportPipeline` and `InstallNewPipeline`, setup and install the exporter in tracing and metrics pipelines. (#1373) +- Adds semantic conventions for exceptions. (#1492) +- Added Jaeger Environment variables: `OTEL_EXPORTER_JAEGER_AGENT_HOST`, `OTEL_EXPORTER_JAEGER_AGENT_PORT` + These environment variables can be used to override Jaeger agent hostname and port (#1752) +- Option `ExportTimeout` was added to batch span processor. (#1755) +- `trace.TraceFlags` is now a defined type over `byte` and `WithSampled(bool) TraceFlags` and `IsSampled() bool` methods have been added to it. (#1770) +- The `Event` and `Link` struct types from the `go.opentelemetry.io/otel` package now include a `DroppedAttributeCount` field to record the number of attributes that were not recorded due to configured limits being reached. (#1771) +- The Jaeger exporter now reports dropped attributes for a Span event in the exported log. (#1771) +- Adds test to check BatchSpanProcessor ignores `OnEnd` and `ForceFlush` post `Shutdown`. (#1772) +- Extract resource attributes from the `OTEL_RESOURCE_ATTRIBUTES` environment variable and merge them with the `resource.Default` resource as well as resources provided to the `TracerProvider` and metric `Controller`. (#1785) +- Added `WithOSType` resource configuration option to set OS (Operating System) type resource attribute (`os.type`). (#1788) +- Added `WithProcess*` resource configuration options to set Process resource attributes. (#1788) + - `process.pid` + - `process.executable.name` + - `process.executable.path` + - `process.command_args` + - `process.owner` + - `process.runtime.name` + - `process.runtime.version` + - `process.runtime.description` +- Adds `k8s.node.name` and `k8s.node.uid` attribute keys to the `semconv` package. (#1789) +- Added support for configuring OTLP/HTTP and OTLP/gRPC Endpoints, TLS Certificates, Headers, Compression and Timeout via Environment Variables. (#1758, #1769 and #1811) + - `OTEL_EXPORTER_OTLP_ENDPOINT` + - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` + - `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` + - `OTEL_EXPORTER_OTLP_HEADERS` + - `OTEL_EXPORTER_OTLP_TRACES_HEADERS` + - `OTEL_EXPORTER_OTLP_METRICS_HEADERS` + - `OTEL_EXPORTER_OTLP_COMPRESSION` + - `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` + - `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` + - `OTEL_EXPORTER_OTLP_TIMEOUT` + - `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` + - `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` + - `OTEL_EXPORTER_OTLP_CERTIFICATE` + - `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` + - `OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` +- Adds `otlpgrpc.WithTimeout` option for configuring timeout to the otlp/gRPC exporter. (#1821) +- Adds `jaeger.WithMaxPacketSize` option for configuring maximum UDP packet size used when connecting to the Jaeger agent. (#1853) + +### Fixed + +- The `Span.IsRecording` implementation from `go.opentelemetry.io/otel/sdk/trace` always returns false when not being sampled. (#1750) +- The Jaeger exporter now correctly sets tags for the Span status code and message. + This means it uses the correct tag keys (`"otel.status_code"`, `"otel.status_description"`) and does not set the status message as a tag unless it is set on the span. (#1761) +- The Jaeger exporter now correctly records Span event's names using the `"event"` key for a tag. + Additionally, this tag is overridden, as specified in the OTel specification, if the event contains an attribute with that key. (#1768) +- Zipkin Exporter: Ensure mapping between OTel and Zipkin span data complies with the specification. (#1688) +- Fixed typo for default service name in Jaeger Exporter. (#1797) +- Fix flaky OTLP for the reconnnection of the client connection. (#1527, #1814) +- Fix Jaeger exporter dropping of span batches that exceed the UDP packet size limit. + Instead, the exporter now splits the batch into smaller sendable batches. (#1828) + +### Changed + +- Span `RecordError` now records an `exception` event to comply with the semantic convention specification. (#1492) +- Jaeger exporter was updated to use thrift v0.14.1. (#1712) +- Migrate from using internally built and maintained version of the OTLP to the one hosted at `go.opentelemetry.io/proto/otlp`. (#1713) +- Migrate from using `github.com/gogo/protobuf` to `google.golang.org/protobuf` to match `go.opentelemetry.io/proto/otlp`. (#1713) +- The storage of a local or remote Span in a `context.Context` using its SpanContext is unified to store just the current Span. + The Span's SpanContext can now self-identify as being remote or not. + This means that `"go.opentelemetry.io/otel/trace".ContextWithRemoteSpanContext` will now overwrite any existing current Span, not just existing remote Spans, and make it the current Span in a `context.Context`. (#1731) +- Improve OTLP/gRPC exporter connection errors. (#1737) +- Information about a parent span context in a `"go.opentelemetry.io/otel/export/trace".SpanSnapshot` is unified in a new `Parent` field. + The existing `ParentSpanID` and `HasRemoteParent` fields are removed in favor of this. (#1748) +- The `ParentContext` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is updated to hold a `context.Context` containing the parent span. + This changes it to make `SamplingParameters` conform with the OpenTelemetry specification. (#1749) +- Updated Jaeger Environment Variables: `JAEGER_ENDPOINT`, `JAEGER_USER`, `JAEGER_PASSWORD` + to `OTEL_EXPORTER_JAEGER_ENDPOINT`, `OTEL_EXPORTER_JAEGER_USER`, `OTEL_EXPORTER_JAEGER_PASSWORD` in compliance with OTel specification. (#1752) +- Modify `BatchSpanProcessor.ForceFlush` to abort after timeout/cancellation. (#1757) +- The `DroppedAttributeCount` field of the `Span` in the `go.opentelemetry.io/otel` package now only represents the number of attributes dropped for the span itself. + It no longer is a conglomerate of itself, events, and link attributes that have been dropped. (#1771) +- Make `ExportSpans` in Jaeger Exporter honor context deadline. (#1773) +- Modify Zipkin Exporter default service name, use default resource's serviceName instead of empty. (#1777) +- The `go.opentelemetry.io/otel/sdk/export/trace` package is merged into the `go.opentelemetry.io/otel/sdk/trace` package. (#1778) +- The prometheus.InstallNewPipeline example is moved from comment to example test (#1796) +- The convenience functions for the stdout exporter have been updated to return the `TracerProvider` implementation and enable the shutdown of the exporter. (#1800) +- Replace the flush function returned from the Jaeger exporter's convenience creation functions (`InstallNewPipeline` and `NewExportPipeline`) with the `TracerProvider` implementation they create. + This enables the caller to shutdown and flush using the related `TracerProvider` methods. (#1822) +- Updated the Jaeger exporter to have a default endpoint, `http://localhost:14250`, for the collector. (#1824) +- Changed the function `WithCollectorEndpoint` in the Jaeger exporter to no longer accept an endpoint as an argument. + The endpoint can be passed with the `CollectorEndpointOption` using the `WithEndpoint` function or by setting the `OTEL_EXPORTER_JAEGER_ENDPOINT` environment variable value appropriately. (#1824) +- The Jaeger exporter no longer batches exported spans itself, instead it relies on the SDK's `BatchSpanProcessor` for this functionality. (#1830) +- The Jaeger exporter creation functions (`NewRawExporter`, `NewExportPipeline`, and `InstallNewPipeline`) no longer accept the removed `Option` type as a variadic argument. (#1830) + +### Removed + +- Removed Jaeger Environment variables: `JAEGER_SERVICE_NAME`, `JAEGER_DISABLED`, `JAEGER_TAGS` + These environment variables will no longer be used to override values of the Jaeger exporter (#1752) +- No longer set the links for a `Span` in `go.opentelemetry.io/otel/sdk/trace` that is configured to be a new root. + This is unspecified behavior that the OpenTelemetry community plans to standardize in the future. + To prevent backwards incompatible changes when it is specified, these links are removed. (#1726) +- Setting error status while recording error with Span from oteltest package. (#1729) +- The concept of a remote and local Span stored in a context is unified to just the current Span. + Because of this `"go.opentelemetry.io/otel/trace".RemoteSpanContextFromContext` is removed as it is no longer needed. + Instead, `"go.opentelemetry.io/otel/trace".SpanContextFromContex` can be used to return the current Span. + If needed, that Span's `SpanContext.IsRemote()` can then be used to determine if it is remote or not. (#1731) +- The `HasRemoteParent` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is removed. + This field is redundant to the information returned from the `Remote` method of the `SpanContext` held in the `ParentContext` field. (#1749) +- The `trace.FlagsDebug` and `trace.FlagsDeferred` constants have been removed and will be localized to the B3 propagator. (#1770) +- Remove `Process` configuration, `WithProcessFromEnv` and `ProcessFromEnv`, and type from the Jaeger exporter package. + The information that could be configured in the `Process` struct should be configured in a `Resource` instead. (#1776, #1804) +- Remove the `WithDisabled` option from the Jaeger exporter. + To disable the exporter unregister it from the `TracerProvider` or use a no-operation `TracerProvider`. (#1806) +- Removed the functions `CollectorEndpointFromEnv` and `WithCollectorEndpointOptionFromEnv` from the Jaeger exporter. + These functions for retrieving specific environment variable values are redundant of other internal functions and + are not intended for end user use. (#1824) +- Removed the Jaeger exporter `WithSDKOptions` `Option`. + This option was used to set SDK options for the exporter creation convenience functions. + These functions are provided as a way to easily setup or install the exporter with what are deemed reasonable SDK settings for common use cases. + If the SDK needs to be configured differently, the `NewRawExporter` function and direct setup of the SDK with the desired settings should be used. (#1825) +- The `WithBufferMaxCount` and `WithBatchMaxCount` `Option`s from the Jaeger exporter are removed. + The exporter no longer batches exports, instead relying on the SDK's `BatchSpanProcessor` for this functionality. (#1830) +- The Jaeger exporter `Option` type is removed. + The type is no longer used by the exporter to configure anything. + All the previous configurations these options provided were duplicates of SDK configuration. + They have been removed in favor of using the SDK configuration and focuses the exporter configuration to be only about the endpoints it will send telemetry to. (#1830) + +## [0.19.0] - 2021-03-18 + +### Added + +- Added `Marshaler` config option to `otlphttp` to enable otlp over json or protobufs. (#1586) +- A `ForceFlush` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` to flush all registered `SpanProcessor`s. (#1608) +- Added `WithSampler` and `WithSpanLimits` to tracer provider. (#1633, #1702) +- `"go.opentelemetry.io/otel/trace".SpanContext` now has a `remote` property, and `IsRemote()` predicate, that is true when the `SpanContext` has been extracted from remote context data. (#1701) +- A `Valid` method to the `"go.opentelemetry.io/otel/attribute".KeyValue` type. (#1703) + +### Changed + +- `trace.SpanContext` is now immutable and has no exported fields. (#1573) + - `trace.NewSpanContext()` can be used in conjunction with the `trace.SpanContextConfig` struct to initialize a new `SpanContext` where all values are known. +- Update the `ForceFlush` method signature to the `"go.opentelemetry.io/otel/sdk/trace".SpanProcessor` to accept a `context.Context` and return an error. (#1608) +- Update the `Shutdown` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` return an error on shutdown failure. (#1608) +- The SimpleSpanProcessor will now shut down the enclosed `SpanExporter` and gracefully ignore subsequent calls to `OnEnd` after `Shutdown` is called. (#1612) +- `"go.opentelemetry.io/sdk/metric/controller.basic".WithPusher` is replaced with `WithExporter` to provide consistent naming across project. (#1656) +- Added non-empty string check for trace `Attribute` keys. (#1659) +- Add `description` to SpanStatus only when `StatusCode` is set to error. (#1662) +- Jaeger exporter falls back to `resource.Default`'s `service.name` if the exported Span does not have one. (#1673) +- Jaeger exporter populates Jaeger's Span Process from Resource. (#1673) +- Renamed the `LabelSet` method of `"go.opentelemetry.io/otel/sdk/resource".Resource` to `Set`. (#1692) +- Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1693) +- Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1693) + +### Removed + +- Removed `serviceName` parameter from Zipkin exporter and uses resource instead. (#1549) +- Removed `WithConfig` from tracer provider to avoid overriding configuration. (#1633) +- Removed the exported `SimpleSpanProcessor` and `BatchSpanProcessor` structs. + These are now returned as a SpanProcessor interface from their respective constructors. (#1638) +- Removed `WithRecord()` from `trace.SpanOption` when creating a span. (#1660) +- Removed setting status to `Error` while recording an error as a span event in `RecordError`. (#1663) +- Removed `jaeger.WithProcess` configuration option. (#1673) +- Removed `ApplyConfig` method from `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` and the now unneeded `Config` struct. (#1693) + +### Fixed + +- Jaeger Exporter: Ensure mapping between OTEL and Jaeger span data complies with the specification. (#1626) +- `SamplingResult.TraceState` is correctly propagated to a newly created span's `SpanContext`. (#1655) +- The `otel-collector` example now correctly flushes metric events prior to shutting down the exporter. (#1678) +- Do not set span status message in `SpanStatusFromHTTPStatusCode` if it can be inferred from `http.status_code`. (#1681) +- Synchronization issues in global trace delegate implementation. (#1686) +- Reduced excess memory usage by global `TracerProvider`. (#1687) + +## [0.18.0] - 2021-03-03 + +### Added + +- Added `resource.Default()` for use with meter and tracer providers. (#1507) +- `AttributePerEventCountLimit` and `AttributePerLinkCountLimit` for `SpanLimits`. (#1535) +- Added `Keys()` method to `propagation.TextMapCarrier` and `propagation.HeaderCarrier` to adapt `http.Header` to this interface. (#1544) +- Added `code` attributes to `go.opentelemetry.io/otel/semconv` package. (#1558) +- Compatibility testing suite in the CI system for the following systems. (#1567) + | OS | Go Version | Architecture | + | ------- | ---------- | ------------ | + | Ubuntu | 1.15 | amd64 | + | Ubuntu | 1.14 | amd64 | + | Ubuntu | 1.15 | 386 | + | Ubuntu | 1.14 | 386 | + | MacOS | 1.15 | amd64 | + | MacOS | 1.14 | amd64 | + | Windows | 1.15 | amd64 | + | Windows | 1.14 | amd64 | + | Windows | 1.15 | 386 | + | Windows | 1.14 | 386 | + +### Changed + +- Replaced interface `oteltest.SpanRecorder` with its existing implementation + `StandardSpanRecorder`. (#1542) +- Default span limit values to 128. (#1535) +- Rename `MaxEventsPerSpan`, `MaxAttributesPerSpan` and `MaxLinksPerSpan` to `EventCountLimit`, `AttributeCountLimit` and `LinkCountLimit`, and move these fields into `SpanLimits`. (#1535) +- Renamed the `otel/label` package to `otel/attribute`. (#1541) +- Vendor the Jaeger exporter's dependency on Apache Thrift. (#1551) +- Parallelize the CI linting and testing. (#1567) +- Stagger timestamps in exact aggregator tests. (#1569) +- Changed all examples to use `WithBatchTimeout(5 * time.Second)` rather than `WithBatchTimeout(5)`. (#1621) +- Prevent end-users from implementing some interfaces (#1575) + + ``` + "otel/exporters/otlp/otlphttp".Option + "otel/exporters/stdout".Option + "otel/oteltest".Option + "otel/trace".TracerOption + "otel/trace".SpanOption + "otel/trace".EventOption + "otel/trace".LifeCycleOption + "otel/trace".InstrumentationOption + "otel/sdk/resource".Option + "otel/sdk/trace".ParentBasedSamplerOption + "otel/sdk/trace".ReadOnlySpan + "otel/sdk/trace".ReadWriteSpan + ``` + +### Removed + +- Removed attempt to resample spans upon changing the span name with `span.SetName()`. (#1545) +- The `test-benchmark` is no longer a dependency of the `precommit` make target. (#1567) +- Removed the `test-386` make target. + This was replaced with a full compatibility testing suite (i.e. multi OS/arch) in the CI system. (#1567) + +### Fixed + +- The sequential timing check of timestamps in the stdout exporter are now setup explicitly to be sequential (#1571). (#1572) +- Windows build of Jaeger tests now compiles with OS specific functions (#1576). (#1577) +- The sequential timing check of timestamps of go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue are now setup explicitly to be sequential (#1578). (#1579) +- Validate tracestate header keys with vendors according to the W3C TraceContext specification (#1475). (#1581) +- The OTLP exporter includes related labels for translations of a GaugeArray (#1563). (#1570) + +## [0.17.0] - 2021-02-12 + +### Changed + +- Rename project default branch from `master` to `main`. (#1505) +- Reverse order in which `Resource` attributes are merged, per change in spec. (#1501) +- Add tooling to maintain "replace" directives in go.mod files automatically. (#1528) +- Create new modules: otel/metric, otel/trace, otel/oteltest, otel/sdk/export/metric, otel/sdk/metric (#1528) +- Move metric-related public global APIs from otel to otel/metric/global. (#1528) + +## Fixed + +- Fixed otlpgrpc reconnection issue. +- The example code in the README.md of `go.opentelemetry.io/otel/exporters/otlp` is moved to a compiled example test and used the new `WithAddress` instead of `WithEndpoint`. (#1513) +- The otel-collector example now uses the default OTLP receiver port of the collector. + +## [0.16.0] - 2021-01-13 + +### Added + +- Add the `ReadOnlySpan` and `ReadWriteSpan` interfaces to provide better control for accessing span data. (#1360) +- `NewGRPCDriver` function returns a `ProtocolDriver` that maintains a single gRPC connection to the collector. (#1369) +- Added documentation about the project's versioning policy. (#1388) +- Added `NewSplitDriver` for OTLP exporter that allows sending traces and metrics to different endpoints. (#1418) +- Added codeql worfklow to GitHub Actions (#1428) +- Added Gosec workflow to GitHub Actions (#1429) +- Add new HTTP driver for OTLP exporter in `exporters/otlp/otlphttp`. Currently it only supports the binary protobuf payloads. (#1420) +- Add an OpenCensus exporter bridge. (#1444) + +### Changed + +- Rename `internal/testing` to `internal/internaltest`. (#1449) +- Rename `export.SpanData` to `export.SpanSnapshot` and use it only for exporting spans. (#1360) +- Store the parent's full `SpanContext` rather than just its span ID in the `span` struct. (#1360) +- Improve span duration accuracy. (#1360) +- Migrated CI/CD from CircleCI to GitHub Actions (#1382) +- Remove duplicate checkout from GitHub Actions workflow (#1407) +- Metric `array` aggregator renamed `exact` to match its `aggregation.Kind` (#1412) +- Metric `exact` aggregator includes per-point timestamps (#1412) +- Metric stdout exporter uses MinMaxSumCount aggregator for ValueRecorder instruments (#1412) +- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369) +- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369) +- Unify endpoint API that related to OTel exporter. (#1401) +- Optimize metric histogram aggregator to re-use its slice of buckets. (#1435) +- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430) +- Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434) +- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432) +- Moved gRPC driver for OTLP exporter to `exporters/otlp/otlpgrpc`. (#1420) +- The `TraceContext` propagator now correctly propagates `TraceState` through the `SpanContext`. (#1447) +- Metric Push and Pull Controller components are combined into a single "basic" Controller: + - `WithExporter()` and `Start()` to configure Push behavior + - `Start()` is optional; use `Collect()` and `ForEach()` for Pull behavior + - `Start()` and `Stop()` accept Context. (#1378) +- The `Event` type is moved from the `otel/sdk/export/trace` package to the `otel/trace` API package. (#1452) + +### Removed + +- Remove `errUninitializedSpan` as its only usage is now obsolete. (#1360) +- Remove Metric export functionality related to quantiles and summary data points: this is not specified (#1412) +- Remove DDSketch metric aggregator; our intention is to re-introduce this as an option of the histogram aggregator after [new OTLP histogram data types](https://github.com/open-telemetry/opentelemetry-proto/pull/226) are released (#1412) + +### Fixed + +- `BatchSpanProcessor.Shutdown()` will now shutdown underlying `export.SpanExporter`. (#1443) + +## [0.15.0] - 2020-12-10 + +### Added + +- The `WithIDGenerator` `TracerProviderOption` is added to the `go.opentelemetry.io/otel/trace` package to configure an `IDGenerator` for the `TracerProvider`. (#1363) + +### Changed + +- The Zipkin exporter now uses the Span status code to determine. (#1328) +- `NewExporter` and `Start` functions in `go.opentelemetry.io/otel/exporters/otlp` now receive `context.Context` as a first parameter. (#1357) +- Move the OpenCensus example into `example` directory. (#1359) +- Moved the SDK's `internal.IDGenerator` interface in to the `sdk/trace` package to enable support for externally-defined ID generators. (#1363) +- Bump `github.com/google/go-cmp` from 0.5.3 to 0.5.4 (#1374) +- Bump `github.com/golangci/golangci-lint` in `/internal/tools` (#1375) + +### Fixed + +- Metric SDK `SumObserver` and `UpDownSumObserver` instruments correctness fixes. (#1381) + +## [0.14.0] - 2020-11-19 + +### Added + +- An `EventOption` and the related `NewEventConfig` function are added to the `go.opentelemetry.io/otel` package to configure Span events. (#1254) +- A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test `TextMap` type propagators and their use. (#1259) +- `SpanContextFromContext` returns `SpanContext` from context. (#1255) +- `TraceState` has been added to `SpanContext`. (#1340) +- `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323) +- Add an OpenCensus to OpenTelemetry tracing bridge. (#1305) +- Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333) +- Add missing tests for `sdk/trace/attributes_map.go`. (#1337) + +### Changed + +- Move the `go.opentelemetry.io/otel/api/trace` package into `go.opentelemetry.io/otel/trace` with the following changes. (#1229) (#1307) + - `ID` has been renamed to `TraceID`. + - `IDFromHex` has been renamed to `TraceIDFromHex`. + - `EmptySpanContext` is removed. +- Move the `go.opentelemetry.io/otel/api/trace/tracetest` package into `go.opentelemetry.io/otel/oteltest`. (#1229) +- OTLP Exporter updates: + - supports OTLP v0.6.0 (#1230, #1354) + - supports configurable aggregation temporality (default: Cumulative, optional: Stateless). (#1296) +- The Sampler is now called on local child spans. (#1233) +- The `Kind` type from the `go.opentelemetry.io/otel/api/metric` package was renamed to `InstrumentKind` to more specifically describe what it is and avoid semantic ambiguity. (#1240) +- The `MetricKind` method of the `Descriptor` type in the `go.opentelemetry.io/otel/api/metric` package was renamed to `Descriptor.InstrumentKind`. + This matches the returned type and fixes misuse of the term metric. (#1240) +- Move test harness from the `go.opentelemetry.io/otel/api/apitest` package into `go.opentelemetry.io/otel/oteltest`. (#1241) +- Move the `go.opentelemetry.io/otel/api/metric/metrictest` package into `go.opentelemetry.io/oteltest` as part of #964. (#1252) +- Move the `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric` as part of #1303. (#1321) +- Move the `go.opentelemetry.io/otel/api/metric/registry` package into `go.opentelemetry.io/otel/metric/registry` as a part of #1303. (#1316) +- Move the `Number` type (together with related functions) from `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric/number` as a part of #1303. (#1316) +- The function signature of the Span `AddEvent` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required name and a variable number of `EventOption`s. (#1254) +- The function signature of the Span `RecordError` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required error value and a variable number of `EventOption`s. (#1254) +- Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel`. (#1262) (#1330) +- Move the `Version` function from `go.opentelemetry.io/otel/sdk` to `go.opentelemetry.io/otel`. (#1330) +- Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267) +- Fix `Code.UnmarshalJSON` to work with valid JSON only. (#1276) +- The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and + `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235) +- The Prometheus exporter now exports non-monotonic counters (i.e. `UpDownCounter`s) as gauges. (#1210) +- Correct the `Span.End` method documentation in the `otel` API to state updates are not allowed on a span after it has ended. (#1310) +- Updated span collection limits for attribute, event and link counts to 1000 (#1318) +- Renamed `semconv.HTTPUrlKey` to `semconv.HTTPURLKey`. (#1338) + +### Removed + +- The `ErrInvalidHexID`, `ErrInvalidTraceIDLength`, `ErrInvalidSpanIDLength`, `ErrInvalidSpanIDLength`, or `ErrNilSpanID` from the `go.opentelemetry.io/otel` package are unexported now. (#1243) +- The `AddEventWithTimestamp` method on the `Span` interface in `go.opentelemetry.io/otel` is removed due to its redundancy. + It is replaced by using the `AddEvent` method with a `WithTimestamp` option. (#1254) +- The `MockSpan` and `MockTracer` types are removed from `go.opentelemetry.io/otel/oteltest`. + `Tracer` and `Span` from the same module should be used in their place instead. (#1306) +- `WorkerCount` option is removed from `go.opentelemetry.io/otel/exporters/otlp`. (#1350) +- Remove the following labels types: INT32, UINT32, UINT64 and FLOAT32. (#1314) + +### Fixed + +- Rename `MergeItererator` to `MergeIterator` in the `go.opentelemetry.io/otel/label` package. (#1244) +- The `go.opentelemetry.io/otel/api/global` packages global TextMapPropagator now delegates functionality to a globally set delegate for all previously returned propagators. (#1258) +- Fix condition in `label.Any`. (#1299) +- Fix global `TracerProvider` to pass options to its configured provider. (#1329) +- Fix missing handler for `ExactKind` aggregator in OTLP metrics transformer (#1309) + +## [0.13.0] - 2020-10-08 + +### Added + +- OTLP Metric exporter supports Histogram aggregation. (#1209) +- The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214) +- A Baggage API to implement the OpenTelemetry specification. (#1217) +- Add Shutdown method to sdk/trace/provider, shutdown processors in the order they were registered. (#1227) + +### Changed + +- Set default propagator to no-op propagator. (#1184) +- The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel/propagation` package. (#1212) (#1325) +- The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212) +- The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification. + They now are `Unset`, `Error`, and `Ok`. + They no longer track the gRPC codes. (#1214) +- The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214) +- Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/baggage`. (#1217) (#1325) +- A `Shutdown` method of `SpanProcessor` and all its implementations receives a context and returns an error. (#1264) + +### Fixed + +- Copies of data from arrays and slices passed to `go.opentelemetry.io/otel/label.ArrayValue()` are now used in the returned `Value` instead of using the mutable data itself. (#1226) + +### Removed + +- The `ExtractHTTP` and `InjectHTTP` functions from the `go.opentelemetry.io/otel/api/propagation` package were removed. (#1212) +- The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification. + The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212) +- The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216) +- The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217) +- Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219) +- Nested array/slice support has been removed. (#1226) + +## [0.12.0] - 2020-09-24 + +### Added + +- A `SpanConfigure` function in `go.opentelemetry.io/otel/api/trace` to create a new `SpanConfig` from `SpanOption`s. (#1108) +- In the `go.opentelemetry.io/otel/api/trace` package, `NewTracerConfig` was added to construct new `TracerConfig`s. + This addition was made to conform with our project option conventions. (#1155) +- Instrumentation library information was added to the Zipkin exporter. (#1119) +- The `SpanProcessor` interface now has a `ForceFlush()` method. (#1166) +- More semantic conventions for k8s as resource attributes. (#1167) + +### Changed + +- Add reconnecting udp connection type to Jaeger exporter. + This change adds a new optional implementation of the udp conn interface used to detect changes to an agent's host dns record. + It then adopts the new destination address to ensure the exporter doesn't get stuck. This change was ported from jaegertracing/jaeger-client-go#520. (#1063) +- Replace `StartOption` and `EndOption` in `go.opentelemetry.io/otel/api/trace` with `SpanOption`. + This change is matched by replacing the `StartConfig` and `EndConfig` with a unified `SpanConfig`. (#1108) +- Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`. + This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108) +- The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109) +- Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package. + This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118) +- Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119) +- Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115) +- Move `tools` package under `internal`. (#1141) +- Move `go.opentelemetry.io/otel/api/correlation` package to `go.opentelemetry.io/otel/api/baggage`. (#1142) + The `correlation.CorrelationContext` propagator has been renamed `baggage.Baggage`. Other exported functions and types are unchanged. +- Rename `ParentOrElse` sampler to `ParentBased` and allow setting samplers depending on parent span. (#1153) +- In the `go.opentelemetry.io/otel/api/trace` package, `SpanConfigure` was renamed to `NewSpanConfig`. (#1155) +- Change `dependabot.yml` to add a `Skip Changelog` label to dependabot-sourced PRs. (#1161) +- The [configuration style guide](https://github.com/open-telemetry/opentelemetry-go/blob/master/CONTRIBUTING.md#config) has been updated to + recommend the use of `newConfig()` instead of `configure()`. (#1163) +- The `otlp.Config` type has been unexported and changed to `otlp.config`, along with its initializer. (#1163) +- Ensure exported interface types include parameter names and update the + Style Guide to reflect this styling rule. (#1172) +- Don't consider unset environment variable for resource detection to be an error. (#1170) +- Rename `go.opentelemetry.io/otel/api/metric.ConfigureInstrument` to `NewInstrumentConfig` and + `go.opentelemetry.io/otel/api/metric.ConfigureMeter` to `NewMeterConfig`. +- ValueObserver instruments use LastValue aggregator by default. (#1165) +- OTLP Metric exporter supports LastValue aggregation. (#1165) +- Move the `go.opentelemetry.io/otel/api/unit` package to `go.opentelemetry.io/otel/unit`. (#1185) +- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) +- Rename `NoopProvider` to `NoopMeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) +- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metric/metrictest` package. (#1190) +- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric/registry` package. (#1190) +- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metri/registryc` package. (#1190) +- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) +- Rename `NoopProvider` to `NoopTracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) +- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) +- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) +- Rename `WrapperProvider` to `WrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) +- Rename `NewWrapperProvider` to `NewWrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) +- Rename `Provider` method of the pull controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/pull` package. (#1190) +- Rename `Provider` method of the push controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/push` package. (#1190) +- Rename `ProviderOptions` to `TracerProviderConfig` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) +- Rename `ProviderOption` to `TracerProviderOption` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) +- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) +- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) +- Renamed `SamplingDecision` values to comply with OpenTelemetry specification change. (#1192) +- Renamed Zipkin attribute names from `ot.status_code & ot.status_description` to `otel.status_code & otel.status_description`. (#1201) +- The default SDK now invokes registered `SpanProcessor`s in the order they were registered with the `TracerProvider`. (#1195) +- Add test of spans being processed by the `SpanProcessor`s in the order they were registered. (#1203) + +### Removed + +- Remove the B3 propagator from `go.opentelemetry.io/otel/propagators`. It is now located in the + `go.opentelemetry.io/contrib/propagators/` module. (#1191) +- Remove the semantic convention for HTTP status text, `HTTPStatusTextKey` from package `go.opentelemetry.io/otel/semconv`. (#1194) + +### Fixed + +- Zipkin example no longer mentions `ParentSampler`, corrected to `ParentBased`. (#1171) +- Fix missing shutdown processor in otel-collector example. (#1186) +- Fix missing shutdown processor in basic and namedtracer examples. (#1197) + +## [0.11.0] - 2020-08-24 + +### Added + +- Support for exporting array-valued attributes via OTLP. (#992) +- `Noop` and `InMemory` `SpanBatcher` implementations to help with testing integrations. (#994) +- Support for filtering metric label sets. (#1047) +- A dimensionality-reducing metric Processor. (#1057) +- Integration tests for more OTel Collector Attribute types. (#1062) +- A new `WithSpanProcessor` `ProviderOption` is added to the `go.opentelemetry.io/otel/sdk/trace` package to create a `Provider` and automatically register the `SpanProcessor`. (#1078) + +### Changed + +- Rename `sdk/metric/processor/test` to `sdk/metric/processor/processortest`. (#1049) +- Rename `sdk/metric/controller/test` to `sdk/metric/controller/controllertest`. (#1049) +- Rename `api/testharness` to `api/apitest`. (#1049) +- Rename `api/trace/testtrace` to `api/trace/tracetest`. (#1049) +- Change Metric Processor to merge multiple observations. (#1024) +- The `go.opentelemetry.io/otel/bridge/opentracing` bridge package has been made into its own module. + This removes the package dependencies of this bridge from the rest of the OpenTelemetry based project. (#1038) +- Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016) +- The environment variable used for resource detection has been changed from `OTEL_RESOURCE_LABELS` to `OTEL_RESOURCE_ATTRIBUTES` (#1042) +- Replace `WithSyncer` with `WithBatcher` in examples. (#1044) +- Replace the `google.golang.org/grpc/codes` dependency in the API with an equivalent `go.opentelemetry.io/otel/codes` package. (#1046) +- Merge the `go.opentelemetry.io/otel/api/label` and `go.opentelemetry.io/otel/api/kv` into the new `go.opentelemetry.io/otel/label` package. (#1060) +- Unify Callback Function Naming. + Rename `*Callback` with `*Func`. (#1061) +- CI builds validate against last two versions of Go, dropping 1.13 and adding 1.15. (#1064) +- The `go.opentelemetry.io/otel/sdk/export/trace` interfaces `SpanSyncer` and `SpanBatcher` have been replaced with a specification compliant `Exporter` interface. + This interface still supports the export of `SpanData`, but only as a slice. + Implementation are also required now to return any error from `ExportSpans` if one occurs as well as implement a `Shutdown` method for exporter clean-up. (#1078) +- The `go.opentelemetry.io/otel/sdk/trace` `NewBatchSpanProcessor` function no longer returns an error. + If a `nil` exporter is passed as an argument to this function, instead of it returning an error, it now returns a `BatchSpanProcessor` that handles the export of `SpanData` by not taking any action. (#1078) +- The `go.opentelemetry.io/otel/sdk/trace` `NewProvider` function to create a `Provider` no longer returns an error, instead only a `*Provider`. + This change is related to `NewBatchSpanProcessor` not returning an error which was the only error this function would return. (#1078) + +### Removed + +- Duplicate, unused API sampler interface. (#999) + Use the [`Sampler` interface](https://github.com/open-telemetry/opentelemetry-go/blob/v0.11.0/sdk/trace/sampling.go) provided by the SDK instead. +- The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository. + This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027) +- The `WithSpan` method of the `Tracer` interface. + The functionality this method provided was limited compared to what a user can provide themselves. + It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043) +- The `RegisterSpanProcessor` and `UnregisterSpanProcessor` functions. + These were holdovers from an approach prior to the TracerProvider design. They were not used anymore. (#1077) +- The `oterror` package. (#1026) +- The `othttp` and `httptrace` instrumentations were moved to `go.opentelemetry.io/contrib`. (#1032) + +### Fixed + +- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031) +- Correct instrumentation version tag in Jaeger exporter. (#1037) +- The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043) +- Move internally generated protobuf code from the `go.opentelemetry.io/otel` to the OTLP exporter to reduce dependency overhead. (#1050) +- The `otel-collector` example referenced outdated collector processors. (#1006) + +## [0.10.0] - 2020-07-29 + +This release migrates the default OpenTelemetry SDK into its own Go module, decoupling the SDK from the API and reducing dependencies for instrumentation packages. + +### Added + +- The Zipkin exporter now has `NewExportPipeline` and `InstallNewPipeline` constructor functions to match the common pattern. + These function build a new exporter with default SDK options and register the exporter with the `global` package respectively. (#944) +- Add propagator option for gRPC instrumentation. (#986) +- The `testtrace` package now tracks the `trace.SpanKind` for each span. (#987) + +### Changed + +- Replace the `RegisterGlobal` `Option` in the Jaeger exporter with an `InstallNewPipeline` constructor function. + This matches the other exporter constructor patterns and will register a new exporter after building it with default configuration. (#944) +- The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`. + This new exporter was made into its own Go module to follow the pattern of all exporters and decouple it from the `go.opentelemetry.io/otel` module. (#956, #963) +- Move the `go.opentelemetry.io/otel/exporters/test` test package to `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#962) +- The `go.opentelemetry.io/otel/api/kv/value` package was merged into the parent `go.opentelemetry.io/otel/api/kv` package. (#968) + - `value.Bool` was replaced with `kv.BoolValue`. + - `value.Int64` was replaced with `kv.Int64Value`. + - `value.Uint64` was replaced with `kv.Uint64Value`. + - `value.Float64` was replaced with `kv.Float64Value`. + - `value.Int32` was replaced with `kv.Int32Value`. + - `value.Uint32` was replaced with `kv.Uint32Value`. + - `value.Float32` was replaced with `kv.Float32Value`. + - `value.String` was replaced with `kv.StringValue`. + - `value.Int` was replaced with `kv.IntValue`. + - `value.Uint` was replaced with `kv.UintValue`. + - `value.Array` was replaced with `kv.ArrayValue`. +- Rename `Infer` to `Any` in the `go.opentelemetry.io/otel/api/kv` package. (#972) +- Change `othttp` to use the `httpsnoop` package to wrap the `ResponseWriter` so that optional interfaces (`http.Hijacker`, `http.Flusher`, etc.) that are implemented by the original `ResponseWriter`are also implemented by the wrapped `ResponseWriter`. (#979) +- Rename `go.opentelemetry.io/otel/sdk/metric/aggregator/test` package to `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest`. (#980) +- Make the SDK into its own Go module called `go.opentelemetry.io/otel/sdk`. (#985) +- Changed the default trace `Sampler` from `AlwaysOn` to `ParentOrElse(AlwaysOn)`. (#989) + +### Removed + +- The `IndexedAttribute` function from the `go.opentelemetry.io/otel/api/label` package was removed in favor of `IndexedLabel` which it was synonymous with. (#970) + +### Fixed + +- Bump github.com/golangci/golangci-lint from 1.28.3 to 1.29.0 in /tools. (#953) +- Bump github.com/google/go-cmp from 0.5.0 to 0.5.1. (#957) +- Use `global.Handle` for span export errors in the OTLP exporter. (#946) +- Correct Go language formatting in the README documentation. (#961) +- Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977) +- Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983) +- Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984) + +## [0.9.0] - 2020-07-20 + +### Added + +- A new Resource Detector interface is included to allow resources to be automatically detected and included. (#939) +- A Detector to automatically detect resources from an environment variable. (#939) +- Github action to generate protobuf Go bindings locally in `internal/opentelemetry-proto-gen`. (#938) +- OTLP .proto files from `open-telemetry/opentelemetry-proto` imported as a git submodule under `internal/opentelemetry-proto`. + References to `github.com/open-telemetry/opentelemetry-proto` changed to `go.opentelemetry.io/otel/internal/opentelemetry-proto-gen`. (#942) + +### Changed + +- Non-nil value `struct`s for key-value pairs will be marshalled using JSON rather than `Sprintf`. (#948) + +### Removed + +- Removed dependency on `github.com/open-telemetry/opentelemetry-collector`. (#943) + +## [0.8.0] - 2020-07-09 + +### Added + +- The `B3Encoding` type to represent the B3 encoding(s) the B3 propagator can inject. + A value for HTTP supported encodings (Multiple Header: `MultipleHeader`, Single Header: `SingleHeader`) are included. (#882) +- The `FlagsDeferred` trace flag to indicate if the trace sampling decision has been deferred. (#882) +- The `FlagsDebug` trace flag to indicate if the trace is a debug trace. (#882) +- Add `peer.service` semantic attribute. (#898) +- Add database-specific semantic attributes. (#899) +- Add semantic convention for `faas.coldstart` and `container.id`. (#909) +- Add http content size semantic conventions. (#905) +- Include `http.request_content_length` in HTTP request basic attributes. (#905) +- Add semantic conventions for operating system process resource attribute keys. (#919) +- The Jaeger exporter now has a `WithBatchMaxCount` option to specify the maximum number of spans sent in a batch. (#931) + +### Changed + +- Update `CONTRIBUTING.md` to ask for updates to `CHANGELOG.md` with each pull request. (#879) +- Use lowercase header names for B3 Multiple Headers. (#881) +- The B3 propagator `SingleHeader` field has been replaced with `InjectEncoding`. + This new field can be set to combinations of the `B3Encoding` bitmasks and will inject trace information in these encodings. + If no encoding is set, the propagator will default to `MultipleHeader` encoding. (#882) +- The B3 propagator now extracts from either HTTP encoding of B3 (Single Header or Multiple Header) based on what is contained in the header. + Preference is given to Single Header encoding with Multiple Header being the fallback if Single Header is not found or is invalid. + This behavior change is made to dynamically support all correctly encoded traces received instead of having to guess the expected encoding prior to receiving. (#882) +- Extend semantic conventions for RPC. (#900) +- To match constant naming conventions in the `api/standard` package, the `FaaS*` key names are appended with a suffix of `Key`. (#920) + - `"api/standard".FaaSName` -> `FaaSNameKey` + - `"api/standard".FaaSID` -> `FaaSIDKey` + - `"api/standard".FaaSVersion` -> `FaaSVersionKey` + - `"api/standard".FaaSInstance` -> `FaaSInstanceKey` + +### Removed + +- The `FlagsUnused` trace flag is removed. + The purpose of this flag was to act as the inverse of `FlagsSampled`, the inverse of `FlagsSampled` is used instead. (#882) +- The B3 header constants (`B3SingleHeader`, `B3DebugFlagHeader`, `B3TraceIDHeader`, `B3SpanIDHeader`, `B3SampledHeader`, `B3ParentSpanIDHeader`) are removed. + If B3 header keys are needed [the authoritative OpenZipkin package constants](https://pkg.go.dev/github.com/openzipkin/zipkin-go@v0.2.2/propagation/b3?tab=doc#pkg-constants) should be used instead. (#882) + +### Fixed + +- The B3 Single Header name is now correctly `b3` instead of the previous `X-B3`. (#881) +- The B3 propagator now correctly supports sampling only values (`b3: 0`, `b3: 1`, or `b3: d`) for a Single B3 Header. (#882) +- The B3 propagator now propagates the debug flag. + This removes the behavior of changing the debug flag into a set sampling bit. + Instead, this now follow the B3 specification and omits the `X-B3-Sampling` header. (#882) +- The B3 propagator now tracks "unset" sampling state (meaning "defer the decision") and does not set the `X-B3-Sampling` header when injecting. (#882) +- Bump github.com/itchyny/gojq from 0.10.3 to 0.10.4 in /tools. (#883) +- Bump github.com/opentracing/opentracing-go from v1.1.1-0.20190913142402-a7454ce5950e to v1.2.0. (#885) +- The tracing time conversion for OTLP spans is now correctly set to `UnixNano`. (#896) +- Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908) +- Ensure `httptrace.clientTracer` closes `http.headers` span. (#912) +- Prometheus exporter will not apply stale updates or forget inactive metrics. (#903) +- Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905) +- Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913) +- Update otel-colector example to use the v0.5.0 collector. (#915) +- The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922) +- The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922) +- The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists. + This is in accordance with OpenTelemetry semantic conventions. (#922) +- Correlation Context extractor will no longer insert an empty map into the returned context when no valid values are extracted. (#923) +- Bump google.golang.org/api from 0.28.0 to 0.29.0 in /exporters/trace/jaeger. (#925) +- Bump github.com/itchyny/gojq from 0.10.4 to 0.11.0 in /tools. (#926) +- Bump github.com/golangci/golangci-lint from 1.28.1 to 1.28.2 in /tools. (#930) + +## [0.7.0] - 2020-06-26 + +This release implements the v0.5.0 version of the OpenTelemetry specification. + +### Added + +- The othttp instrumentation now includes default metrics. (#861) +- This CHANGELOG file to track all changes in the project going forward. +- Support for array type attributes. (#798) +- Apply transitive dependabot go.mod dependency updates as part of a new automatic Github workflow. (#844) +- Timestamps are now passed to exporters for each export. (#835) +- Add new `Accumulation` type to metric SDK to transport telemetry from `Accumulator`s to `Processor`s. + This replaces the prior `Record` `struct` use for this purpose. (#835) +- New dependabot integration to automate package upgrades. (#814) +- `Meter` and `Tracer` implementations accept instrumentation version version as an optional argument. + This instrumentation version is passed on to exporters. (#811) (#805) (#802) +- The OTLP exporter includes the instrumentation version in telemetry it exports. (#811) +- Environment variables for Jaeger exporter are supported. (#796) +- New `aggregation.Kind` in the export metric API. (#808) +- New example that uses OTLP and the collector. (#790) +- Handle errors in the span `SetName` during span initialization. (#791) +- Default service config to enable retries for retry-able failed requests in the OTLP exporter and an option to override this default. (#777) +- New `go.opentelemetry.io/otel/api/oterror` package to uniformly support error handling and definitions for the project. (#778) +- New `global` default implementation of the `go.opentelemetry.io/otel/api/oterror.Handler` interface to be used to handle errors prior to an user defined `Handler`. + There is also functionality for the user to register their `Handler` as well as a convenience function `Handle` to handle an error with this global `Handler`(#778) +- Options to specify propagators for httptrace and grpctrace instrumentation. (#784) +- The required `application/json` header for the Zipkin exporter is included in all exports. (#774) +- Integrate HTTP semantics helpers from the contrib repository into the `api/standard` package. #769 + +### Changed + +- Rename `Integrator` to `Processor` in the metric SDK. (#863) +- Rename `AggregationSelector` to `AggregatorSelector`. (#859) +- Rename `SynchronizedCopy` to `SynchronizedMove`. (#858) +- Rename `simple` integrator to `basic` integrator. (#857) +- Merge otlp collector examples. (#841) +- Change the metric SDK to support cumulative, delta, and pass-through exporters directly. + With these changes, cumulative and delta specific exporters are able to request the correct kind of aggregation from the SDK. (#840) +- The `Aggregator.Checkpoint` API is renamed to `SynchronizedCopy` and adds an argument, a different `Aggregator` into which the copy is stored. (#812) +- The `export.Aggregator` contract is that `Update()` and `SynchronizedCopy()` are synchronized with each other. + All the aggregation interfaces (`Sum`, `LastValue`, ...) are not meant to be synchronized, as the caller is expected to synchronize aggregators at a higher level after the `Accumulator`. + Some of the `Aggregators` used unnecessary locking and that has been cleaned up. (#812) +- Use of `metric.Number` was replaced by `int64` now that we use `sync.Mutex` in the `MinMaxSumCount` and `Histogram` `Aggregators`. (#812) +- Replace `AlwaysParentSample` with `ParentSample(fallback)` to match the OpenTelemetry v0.5.0 specification. (#810) +- Rename `sdk/export/metric/aggregator` to `sdk/export/metric/aggregation`. #808 +- Send configured headers with every request in the OTLP exporter, instead of just on connection creation. (#806) +- Update error handling for any one off error handlers, replacing, instead, with the `global.Handle` function. (#791) +- Rename `plugin` directory to `instrumentation` to match the OpenTelemetry specification. (#779) +- Makes the argument order to Histogram and DDSketch `New()` consistent. (#781) + +### Removed + +- `Uint64NumberKind` and related functions from the API. (#864) +- Context arguments from `Aggregator.Checkpoint` and `Integrator.Process` as they were unused. (#803) +- `SpanID` is no longer included in parameters for sampling decision to match the OpenTelemetry specification. (#775) + +### Fixed + +- Upgrade OTLP exporter to opentelemetry-proto matching the opentelemetry-collector v0.4.0 release. (#866) +- Allow changes to `go.sum` and `go.mod` when running dependabot tidy-up. (#871) +- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1. (#824) +- Bump github.com/prometheus/client_golang from 1.7.0 to 1.7.1 in /exporters/metric/prometheus. (#867) +- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/jaeger. (#853) +- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/zipkin. (#854) +- Bumps github.com/golang/protobuf from 1.3.2 to 1.4.2 (#848) +- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/otlp (#817) +- Bump github.com/golangci/golangci-lint from 1.25.1 to 1.27.0 in /tools (#828) +- Bump github.com/prometheus/client_golang from 1.5.0 to 1.7.0 in /exporters/metric/prometheus (#838) +- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/jaeger (#829) +- Bump github.com/benbjohnson/clock from 1.0.0 to 1.0.3 (#815) +- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/zipkin (#823) +- Bump github.com/itchyny/gojq from 0.10.1 to 0.10.3 in /tools (#830) +- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/metric/prometheus (#822) +- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/zipkin (#820) +- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/jaeger (#831) +- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 (#836) +- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/trace/jaeger (#837) +- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/otlp (#839) +- Bump google.golang.org/api from 0.20.0 to 0.28.0 in /exporters/trace/jaeger (#843) +- Set span status from HTTP status code in the othttp instrumentation. (#832) +- Fixed typo in push controller comment. (#834) +- The `Aggregator` testing has been updated and cleaned. (#812) +- `metric.Number(0)` expressions are replaced by `0` where possible. (#812) +- Fixed `global` `handler_test.go` test failure. #804 +- Fixed `BatchSpanProcessor.Shutdown` to wait until all spans are processed. (#766) +- Fixed OTLP example's accidental early close of exporter. (#807) +- Ensure zipkin exporter reads and closes response body. (#788) +- Update instrumentation to use `api/standard` keys instead of custom keys. (#782) +- Clean up tools and RELEASING documentation. (#762) + +## [0.6.0] - 2020-05-21 + +### Added + +- Support for `Resource`s in the prometheus exporter. (#757) +- New pull controller. (#751) +- New `UpDownSumObserver` instrument. (#750) +- OpenTelemetry collector demo. (#711) +- New `SumObserver` instrument. (#747) +- New `UpDownCounter` instrument. (#745) +- New timeout `Option` and configuration function `WithTimeout` to the push controller. (#742) +- New `api/standards` package to implement semantic conventions and standard key-value generation. (#731) + +### Changed + +- Rename `Register*` functions in the metric API to `New*` for all `Observer` instruments. (#761) +- Use `[]float64` for histogram boundaries, not `[]metric.Number`. (#758) +- Change OTLP example to use exporter as a trace `Syncer` instead of as an unneeded `Batcher`. (#756) +- Replace `WithResourceAttributes()` with `WithResource()` in the trace SDK. (#754) +- The prometheus exporter now uses the new pull controller. (#751) +- Rename `ScheduleDelayMillis` to `BatchTimeout` in the trace `BatchSpanProcessor`.(#752) +- Support use of synchronous instruments in asynchronous callbacks (#725) +- Move `Resource` from the `Export` method parameter into the metric export `Record`. (#739) +- Rename `Observer` instrument to `ValueObserver`. (#734) +- The push controller now has a method (`Provider()`) to return a `metric.Provider` instead of the old `Meter` method that acted as a `metric.Provider`. (#738) +- Replace `Measure` instrument by `ValueRecorder` instrument. (#732) +- Rename correlation context header from `"Correlation-Context"` to `"otcorrelations"` to match the OpenTelemetry specification. (#727) + +### Fixed + +- Ensure gRPC `ClientStream` override methods do not panic in grpctrace package. (#755) +- Disable parts of `BatchSpanProcessor` test until a fix is found. (#743) +- Fix `string` case in `kv` `Infer` function. (#746) +- Fix panic in grpctrace client interceptors. (#740) +- Refactor the `api/metrics` push controller and add `CheckpointSet` synchronization. (#737) +- Rewrite span batch process queue batching logic. (#719) +- Remove the push controller named Meter map. (#738) +- Fix Histogram aggregator initial state (fix #735). (#736) +- Ensure golang alpine image is running `golang-1.14` for examples. (#733) +- Added test for grpctrace `UnaryInterceptorClient`. (#695) +- Rearrange `api/metric` code layout. (#724) + +## [0.5.0] - 2020-05-13 + +### Added + +- Batch `Observer` callback support. (#717) +- Alias `api` types to root package of project. (#696) +- Create basic `othttp.Transport` for simple client instrumentation. (#678) +- `SetAttribute(string, interface{})` to the trace API. (#674) +- Jaeger exporter option that allows user to specify custom http client. (#671) +- `Stringer` and `Infer` methods to `key`s. (#662) + +### Changed + +- Rename `NewKey` in the `kv` package to just `Key`. (#721) +- Move `core` and `key` to `kv` package. (#720) +- Make the metric API `Meter` a `struct` so the abstract `MeterImpl` can be passed and simplify implementation. (#709) +- Rename SDK `Batcher` to `Integrator` to match draft OpenTelemetry SDK specification. (#710) +- Rename SDK `Ungrouped` integrator to `simple.Integrator` to match draft OpenTelemetry SDK specification. (#710) +- Rename SDK `SDK` `struct` to `Accumulator` to match draft OpenTelemetry SDK specification. (#710) +- Move `Number` from `core` to `api/metric` package. (#706) +- Move `SpanContext` from `core` to `trace` package. (#692) +- Change traceparent header from `Traceparent` to `traceparent` to implement the W3C specification. (#681) + +### Fixed + +- Update tooling to run generators in all submodules. (#705) +- gRPC interceptor regexp to match methods without a service name. (#683) +- Use a `const` for padding 64-bit B3 trace IDs. (#701) +- Update `mockZipkin` listen address from `:0` to `127.0.0.1:0`. (#700) +- Left-pad 64-bit B3 trace IDs with zero. (#698) +- Propagate at least the first W3C tracestate header. (#694) +- Remove internal `StateLocker` implementation. (#688) +- Increase instance size CI system uses. (#690) +- Add a `key` benchmark and use reflection in `key.Infer()`. (#679) +- Fix internal `global` test by using `global.Meter` with `RecordBatch()`. (#680) +- Reimplement histogram using mutex instead of `StateLocker`. (#669) +- Switch `MinMaxSumCount` to a mutex lock implementation instead of `StateLocker`. (#667) +- Update documentation to not include any references to `WithKeys`. (#672) +- Correct misspelling. (#668) +- Fix clobbering of the span context if extraction fails. (#656) +- Bump `golangci-lint` and work around the corrupting bug. (#666) (#670) + +## [0.4.3] - 2020-04-24 + +### Added + +- `Dockerfile` and `docker-compose.yml` to run example code. (#635) +- New `grpctrace` package that provides gRPC client and server interceptors for both unary and stream connections. (#621) +- New `api/label` package, providing common label set implementation. (#651) +- Support for JSON marshaling of `Resources`. (#654) +- `TraceID` and `SpanID` implementations for `Stringer` interface. (#642) +- `RemoteAddrKey` in the othttp plugin to include the HTTP client address in top-level spans. (#627) +- `WithSpanFormatter` option to the othttp plugin. (#617) +- Updated README to include section for compatible libraries and include reference to the contrib repository. (#612) +- The prometheus exporter now supports exporting histograms. (#601) +- A `String` method to the `Resource` to return a hashable identifier for a now unique resource. (#613) +- An `Iter` method to the `Resource` to return an array `AttributeIterator`. (#613) +- An `Equal` method to the `Resource` test the equivalence of resources. (#613) +- An iterable structure (`AttributeIterator`) for `Resource` attributes. + +### Changed + +- zipkin export's `NewExporter` now requires a `serviceName` argument to ensure this needed values is provided. (#644) +- Pass `Resources` through the metrics export pipeline. (#659) + +### Removed + +- `WithKeys` option from the metric API. (#639) + +### Fixed + +- Use the `label.Set.Equivalent` value instead of an encoding in the batcher. (#658) +- Correct typo `trace.Exporter` to `trace.SpanSyncer` in comments. (#653) +- Use type names for return values in jaeger exporter. (#648) +- Increase the visibility of the `api/key` package by updating comments and fixing usages locally. (#650) +- `Checkpoint` only after `Update`; Keep records in the `sync.Map` longer. (#647) +- Do not cache `reflect.ValueOf()` in metric Labels. (#649) +- Batch metrics exported from the OTLP exporter based on `Resource` and labels. (#626) +- Add error wrapping to the prometheus exporter. (#631) +- Update the OTLP exporter batching of traces to use a unique `string` representation of an associated `Resource` as the batching key. (#623) +- Update OTLP `SpanData` transform to only include the `ParentSpanID` if one exists. (#614) +- Update `Resource` internal representation to uniquely and reliably identify resources. (#613) +- Check return value from `CheckpointSet.ForEach` in prometheus exporter. (#622) +- Ensure spans created by httptrace client tracer reflect operation structure. (#618) +- Create a new recorder rather than reuse when multiple observations in same epoch for asynchronous instruments. #610 +- The default port the OTLP exporter uses to connect to the OpenTelemetry collector is updated to match the one the collector listens on by default. (#611) + +## [0.4.2] - 2020-03-31 + +### Fixed + +- Fix `pre_release.sh` to update version in `sdk/opentelemetry.go`. (#607) +- Fix time conversion from internal to OTLP in OTLP exporter. (#606) + +## [0.4.1] - 2020-03-31 + +### Fixed + +- Update `tag.sh` to create signed tags. (#604) + +## [0.4.0] - 2020-03-30 + +### Added + +- New API package `api/metric/registry` that exposes a `MeterImpl` wrapper for use by SDKs to generate unique instruments. (#580) +- Script to verify examples after a new release. (#579) + +### Removed + +- The dogstatsd exporter due to lack of support. + This additionally removes support for statsd. (#591) +- `LabelSet` from the metric API. + This is replaced by a `[]core.KeyValue` slice. (#595) +- `Labels` from the metric API's `Meter` interface. (#595) + +### Changed + +- The metric `export.Labels` became an interface which the SDK implements and the `export` package provides a simple, immutable implementation of this interface intended for testing purposes. (#574) +- Renamed `internal/metric.Meter` to `MeterImpl`. (#580) +- Renamed `api/global/internal.obsImpl` to `asyncImpl`. (#580) + +### Fixed + +- Corrected missing return in mock span. (#582) +- Update License header for all source files to match CNCF guidelines and include a test to ensure it is present. (#586) (#596) +- Update to v0.3.0 of the OTLP in the OTLP exporter. (#588) +- Update pre-release script to be compatible between GNU and BSD based systems. (#592) +- Add a `RecordBatch` benchmark. (#594) +- Moved span transforms of the OTLP exporter to the internal package. (#593) +- Build both go-1.13 and go-1.14 in circleci to test for all supported versions of Go. (#569) +- Removed unneeded allocation on empty labels in OLTP exporter. (#597) +- Update `BatchedSpanProcessor` to process the queue until no data but respect max batch size. (#599) +- Update project documentation godoc.org links to pkg.go.dev. (#602) + +## [0.3.0] - 2020-03-21 + +This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality. +There is still a possibility of breaking changes. + +### Added + +- Add `Observer` metric instrument. (#474) +- Add global `Propagators` functionality to enable deferred initialization for propagators registered before the first Meter SDK is installed. (#494) +- Simplified export setup pipeline for the jaeger exporter to match other exporters. (#459) +- The zipkin trace exporter. (#495) +- The OTLP exporter to export metric and trace telemetry to the OpenTelemetry collector. (#497) (#544) (#545) +- Add `StatusMessage` field to the trace `Span`. (#524) +- Context propagation in OpenTracing bridge in terms of OpenTelemetry context propagation. (#525) +- The `Resource` type was added to the SDK. (#528) +- The global API now supports a `Tracer` and `Meter` function as shortcuts to getting a global `*Provider` and calling these methods directly. (#538) +- The metric API now defines a generic `MeterImpl` interface to support general purpose `Meter` construction. + Additionally, `SyncImpl` and `AsyncImpl` are added to support general purpose instrument construction. (#560) +- A metric `Kind` is added to represent the `MeasureKind`, `ObserverKind`, and `CounterKind`. (#560) +- Scripts to better automate the release process. (#576) + +### Changed + +- Default to to use `AlwaysSampler` instead of `ProbabilitySampler` to match OpenTelemetry specification. (#506) +- Renamed `AlwaysSampleSampler` to `AlwaysOnSampler` in the trace API. (#511) +- Renamed `NeverSampleSampler` to `AlwaysOffSampler` in the trace API. (#511) +- The `Status` field of the `Span` was changed to `StatusCode` to disambiguate with the added `StatusMessage`. (#524) +- Updated the trace `Sampler` interface conform to the OpenTelemetry specification. (#531) +- Rename metric API `Options` to `Config`. (#541) +- Rename metric `Counter` aggregator to be `Sum`. (#541) +- Unify metric options into `Option` from instrument specific options. (#541) +- The trace API's `TraceProvider` now support `Resource`s. (#545) +- Correct error in zipkin module name. (#548) +- The jaeger trace exporter now supports `Resource`s. (#551) +- Metric SDK now supports `Resource`s. + The `WithResource` option was added to configure a `Resource` on creation and the `Resource` method was added to the metric `Descriptor` to return the associated `Resource`. (#552) +- Replace `ErrNoLastValue` and `ErrEmptyDataSet` by `ErrNoData` in the metric SDK. (#557) +- The stdout trace exporter now supports `Resource`s. (#558) +- The metric `Descriptor` is now included at the API instead of the SDK. (#560) +- Replace `Ordered` with an iterator in `export.Labels`. (#567) + +### Removed + +- The vendor specific Stackdriver. It is now hosted on 3rd party vendor infrastructure. (#452) +- The `Unregister` method for metric observers as it is not in the OpenTelemetry specification. (#560) +- `GetDescriptor` from the metric SDK. (#575) +- The `Gauge` instrument from the metric API. (#537) + +### Fixed + +- Make histogram aggregator checkpoint consistent. (#438) +- Update README with import instructions and how to build and test. (#505) +- The default label encoding was updated to be unique. (#508) +- Use `NewRoot` in the othttp plugin for public endpoints. (#513) +- Fix data race in `BatchedSpanProcessor`. (#518) +- Skip test-386 for Mac OS 10.15.x (Catalina and upwards). #521 +- Use a variable-size array to represent ordered labels in maps. (#523) +- Update the OTLP protobuf and update changed import path. (#532) +- Use `StateLocker` implementation in `MinMaxSumCount`. (#546) +- Eliminate goroutine leak in histogram stress test. (#547) +- Update OTLP exporter with latest protobuf. (#550) +- Add filters to the othttp plugin. (#556) +- Provide an implementation of the `Header*` filters that do not depend on Go 1.14. (#565) +- Encode labels once during checkpoint. + The checkpoint function is executed in a single thread so we can do the encoding lazily before passing the encoded version of labels to the exporter. + This is a cheap and quick way to avoid encoding the labels on every collection interval. (#572) +- Run coverage over all packages in `COVERAGE_MOD_DIR`. (#573) + +## [0.2.3] - 2020-03-04 + +### Added + +- `RecordError` method on `Span`s in the trace API to Simplify adding error events to spans. (#473) +- Configurable push frequency for exporters setup pipeline. (#504) + +### Changed + +- Rename the `exporter` directory to `exporters`. + The `go.opentelemetry.io/otel/exporter/trace/jaeger` package was mistakenly released with a `v1.0.0` tag instead of `v0.1.0`. + This resulted in all subsequent releases not becoming the default latest. + A consequence of this was that all `go get`s pulled in the incompatible `v0.1.0` release of that package when pulling in more recent packages from other otel packages. + Renaming the `exporter` directory to `exporters` fixes this issue by renaming the package and therefore clearing any existing dependency tags. + Consequentially, this action also renames *all* exporter packages. (#502) + +### Removed + +- The `CorrelationContextHeader` constant in the `correlation` package is no longer exported. (#503) + +## [0.2.2] - 2020-02-27 + +### Added + +- `HTTPSupplier` interface in the propagation API to specify methods to retrieve and store a single value for a key to be associated with a carrier. (#467) +- `HTTPExtractor` interface in the propagation API to extract information from an `HTTPSupplier` into a context. (#467) +- `HTTPInjector` interface in the propagation API to inject information into an `HTTPSupplier.` (#467) +- `Config` and configuring `Option` to the propagator API. (#467) +- `Propagators` interface in the propagation API to contain the set of injectors and extractors for all supported carrier formats. (#467) +- `HTTPPropagator` interface in the propagation API to inject and extract from an `HTTPSupplier.` (#467) +- `WithInjectors` and `WithExtractors` functions to the propagator API to configure injectors and extractors to use. (#467) +- `ExtractHTTP` and `InjectHTTP` functions to apply configured HTTP extractors and injectors to a passed context. (#467) +- Histogram aggregator. (#433) +- `DefaultPropagator` function and have it return `trace.TraceContext` as the default context propagator. (#456) +- `AlwaysParentSample` sampler to the trace API. (#455) +- `WithNewRoot` option function to the trace API to specify the created span should be considered a root span. (#451) + +### Changed + +- Renamed `WithMap` to `ContextWithMap` in the correlation package. (#481) +- Renamed `FromContext` to `MapFromContext` in the correlation package. (#481) +- Move correlation context propagation to correlation package. (#479) +- Do not default to putting remote span context into links. (#480) +- `Tracer.WithSpan` updated to accept `StartOptions`. (#472) +- Renamed `MetricKind` to `Kind` to not stutter in the type usage. (#432) +- Renamed the `export` package to `metric` to match directory structure. (#432) +- Rename the `api/distributedcontext` package to `api/correlation`. (#444) +- Rename the `api/propagators` package to `api/propagation`. (#444) +- Move the propagators from the `propagators` package into the `trace` API package. (#444) +- Update `Float64Gauge`, `Int64Gauge`, `Float64Counter`, `Int64Counter`, `Float64Measure`, and `Int64Measure` metric methods to use value receivers instead of pointers. (#462) +- Moved all dependencies of tools package to a tools directory. (#466) + +### Removed + +- Binary propagators. (#467) +- NOOP propagator. (#467) + +### Fixed + +- Upgraded `github.com/golangci/golangci-lint` from `v1.21.0` to `v1.23.6` in `tools/`. (#492) +- Fix a possible nil-dereference crash (#478) +- Correct comments for `InstallNewPipeline` in the stdout exporter. (#483) +- Correct comments for `InstallNewPipeline` in the dogstatsd exporter. (#484) +- Correct comments for `InstallNewPipeline` in the prometheus exporter. (#482) +- Initialize `onError` based on `Config` in prometheus exporter. (#486) +- Correct module name in prometheus exporter README. (#475) +- Removed tracer name prefix from span names. (#430) +- Fix `aggregator_test.go` import package comment. (#431) +- Improved detail in stdout exporter. (#436) +- Fix a dependency issue (generate target should depend on stringer, not lint target) in Makefile. (#442) +- Reorders the Makefile targets within `precommit` target so we generate files and build the code before doing linting, so we can get much nicer errors about syntax errors from the compiler. (#442) +- Reword function documentation in gRPC plugin. (#446) +- Send the `span.kind` tag to Jaeger from the jaeger exporter. (#441) +- Fix `metadataSupplier` in the jaeger exporter to overwrite the header if existing instead of appending to it. (#441) +- Upgraded to Go 1.13 in CI. (#465) +- Correct opentelemetry.io URL in trace SDK documentation. (#464) +- Refactored reference counting logic in SDK determination of stale records. (#468) +- Add call to `runtime.Gosched` in instrument `acquireHandle` logic to not block the collector. (#469) + +## [0.2.1.1] - 2020-01-13 + +### Fixed + +- Use stateful batcher on Prometheus exporter fixing regresion introduced in #395. (#428) + +## [0.2.1] - 2020-01-08 + +### Added + +- Global meter forwarding implementation. + This enables deferred initialization for metric instruments registered before the first Meter SDK is installed. (#392) +- Global trace forwarding implementation. + This enables deferred initialization for tracers registered before the first Trace SDK is installed. (#406) +- Standardize export pipeline creation in all exporters. (#395) +- A testing, organization, and comments for 64-bit field alignment. (#418) +- Script to tag all modules in the project. (#414) + +### Changed + +- Renamed `propagation` package to `propagators`. (#362) +- Renamed `B3Propagator` propagator to `B3`. (#362) +- Renamed `TextFormatPropagator` propagator to `TextFormat`. (#362) +- Renamed `BinaryPropagator` propagator to `Binary`. (#362) +- Renamed `BinaryFormatPropagator` propagator to `BinaryFormat`. (#362) +- Renamed `NoopTextFormatPropagator` propagator to `NoopTextFormat`. (#362) +- Renamed `TraceContextPropagator` propagator to `TraceContext`. (#362) +- Renamed `SpanOption` to `StartOption` in the trace API. (#369) +- Renamed `StartOptions` to `StartConfig` in the trace API. (#369) +- Renamed `EndOptions` to `EndConfig` in the trace API. (#369) +- `Number` now has a pointer receiver for its methods. (#375) +- Renamed `CurrentSpan` to `SpanFromContext` in the trace API. (#379) +- Renamed `SetCurrentSpan` to `ContextWithSpan` in the trace API. (#379) +- Renamed `Message` in Event to `Name` in the trace API. (#389) +- Prometheus exporter no longer aggregates metrics, instead it only exports them. (#385) +- Renamed `HandleImpl` to `BoundInstrumentImpl` in the metric API. (#400) +- Renamed `Float64CounterHandle` to `Float64CounterBoundInstrument` in the metric API. (#400) +- Renamed `Int64CounterHandle` to `Int64CounterBoundInstrument` in the metric API. (#400) +- Renamed `Float64GaugeHandle` to `Float64GaugeBoundInstrument` in the metric API. (#400) +- Renamed `Int64GaugeHandle` to `Int64GaugeBoundInstrument` in the metric API. (#400) +- Renamed `Float64MeasureHandle` to `Float64MeasureBoundInstrument` in the metric API. (#400) +- Renamed `Int64MeasureHandle` to `Int64MeasureBoundInstrument` in the metric API. (#400) +- Renamed `Release` method for bound instruments in the metric API to `Unbind`. (#400) +- Renamed `AcquireHandle` method for bound instruments in the metric API to `Bind`. (#400) +- Renamed the `File` option in the stdout exporter to `Writer`. (#404) +- Renamed all `Options` to `Config` for all metric exports where this wasn't already the case. + +### Fixed + +- Aggregator import path corrected. (#421) +- Correct links in README. (#368) +- The README was updated to match latest code changes in its examples. (#374) +- Don't capitalize error statements. (#375) +- Fix ignored errors. (#375) +- Fix ambiguous variable naming. (#375) +- Removed unnecessary type casting. (#375) +- Use named parameters. (#375) +- Updated release schedule. (#378) +- Correct http-stackdriver example module name. (#394) +- Removed the `http.request` span in `httptrace` package. (#397) +- Add comments in the metrics SDK (#399) +- Initialize checkpoint when creating ddsketch aggregator to prevent panic when merging into a empty one. (#402) (#403) +- Add documentation of compatible exporters in the README. (#405) +- Typo fix. (#408) +- Simplify span check logic in SDK tracer implementation. (#419) + +## [0.2.0] - 2019-12-03 + +### Added + +- Unary gRPC tracing example. (#351) +- Prometheus exporter. (#334) +- Dogstatsd metrics exporter. (#326) + +### Changed + +- Rename `MaxSumCount` aggregation to `MinMaxSumCount` and add the `Min` interface for this aggregation. (#352) +- Rename `GetMeter` to `Meter`. (#357) +- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) +- Rename `HTTPB3Propagator` to `B3Propagator`. (#355) +- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) +- Move `/global` package to `/api/global`. (#356) +- Rename `GetTracer` to `Tracer`. (#347) + +### Removed + +- `SetAttribute` from the `Span` interface in the trace API. (#361) +- `AddLink` from the `Span` interface in the trace API. (#349) +- `Link` from the `Span` interface in the trace API. (#349) + +### Fixed + +- Exclude example directories from coverage report. (#365) +- Lint make target now implements automatic fixes with `golangci-lint` before a second run to report the remaining issues. (#360) +- Drop `GO111MODULE` environment variable in Makefile as Go 1.13 is the project specified minimum version and this is environment variable is not needed for that version of Go. (#359) +- Run the race checker for all test. (#354) +- Redundant commands in the Makefile are removed. (#354) +- Split the `generate` and `lint` targets of the Makefile. (#354) +- Renames `circle-ci` target to more generic `ci` in Makefile. (#354) +- Add example Prometheus binary to gitignore. (#358) +- Support negative numbers with the `MaxSumCount`. (#335) +- Resolve race conditions in `push_test.go` identified in #339. (#340) +- Use `/usr/bin/env bash` as a shebang in scripts rather than `/bin/bash`. (#336) +- Trace benchmark now tests both `AlwaysSample` and `NeverSample`. + Previously it was testing `AlwaysSample` twice. (#325) +- Trace benchmark now uses a `[]byte` for `TraceID` to fix failing test. (#325) +- Added a trace benchmark to test variadic functions in `setAttribute` vs `setAttributes` (#325) +- The `defaultkeys` batcher was only using the encoded label set as its map key while building a checkpoint. + This allowed distinct label sets through, but any metrics sharing a label set could be overwritten or merged incorrectly. + This was corrected. (#333) + +## [0.1.2] - 2019-11-18 + +### Fixed + +- Optimized the `simplelru` map for attributes to reduce the number of allocations. (#328) +- Removed unnecessary unslicing of parameters that are already a slice. (#324) + +## [0.1.1] - 2019-11-18 + +This release contains a Metrics SDK with stdout exporter and supports basic aggregations such as counter, gauges, array, maxsumcount, and ddsketch. + +### Added + +- Metrics stdout export pipeline. (#265) +- Array aggregation for raw measure metrics. (#282) +- The core.Value now have a `MarshalJSON` method. (#281) + +### Removed + +- `WithService`, `WithResources`, and `WithComponent` methods of tracers. (#314) +- Prefix slash in `Tracer.Start()` for the Jaeger example. (#292) + +### Changed + +- Allocation in LabelSet construction to reduce GC overhead. (#318) +- `trace.WithAttributes` to append values instead of replacing (#315) +- Use a formula for tolerance in sampling tests. (#298) +- Move export types into trace and metric-specific sub-directories. (#289) +- `SpanKind` back to being based on an `int` type. (#288) + +### Fixed + +- URL to OpenTelemetry website in README. (#323) +- Name of othttp default tracer. (#321) +- `ExportSpans` for the stackdriver exporter now handles `nil` context. (#294) +- CI modules cache to correctly restore/save from/to the cache. (#316) +- Fix metric SDK race condition between `LoadOrStore` and the assignment `rec.recorder = i.meter.exporter.AggregatorFor(rec)`. (#293) +- README now reflects the new code structure introduced with these changes. (#291) +- Make the basic example work. (#279) + +## [0.1.0] - 2019-11-04 + +This is the first release of open-telemetry go library. +It contains api and sdk for trace and meter. + +### Added + +- Initial OpenTelemetry trace and metric API prototypes. +- Initial OpenTelemetry trace, metric, and export SDK packages. +- A wireframe bridge to support compatibility with OpenTracing. +- Example code for a basic, http-stackdriver, http, jaeger, and named tracer setup. +- Exporters for Jaeger, Stackdriver, and stdout. +- Propagators for binary, B3, and trace-context protocols. +- Project information and guidelines in the form of a README and CONTRIBUTING. +- Tools to build the project and a Makefile to automate the process. +- Apache-2.0 license. +- CircleCI build CI manifest files. +- CODEOWNERS file to track owners of this project. + +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.6.3...HEAD +[1.6.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.3 +[1.6.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.2 +[1.6.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.1 +[1.6.0/0.28.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.0 +[1.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.5.0 +[1.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.1 +[1.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.0 +[1.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.3.0 +[1.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.2.0 +[1.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.1.0 +[1.0.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.1 +[Metrics 0.24.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.24.0 +[1.0.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0 +[1.0.0-RC3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC3 +[1.0.0-RC2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC2 +[Experimental Metrics v0.22.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.22.0 +[1.0.0-RC1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC1 +[0.20.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.20.0 +[0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.19.0 +[0.18.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.18.0 +[0.17.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.17.0 +[0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.16.0 +[0.15.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.15.0 +[0.14.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.14.0 +[0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.13.0 +[0.12.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.12.0 +[0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.11.0 +[0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.10.0 +[0.9.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.9.0 +[0.8.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.8.0 +[0.7.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.7.0 +[0.6.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.6.0 +[0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.5.0 +[0.4.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.3 +[0.4.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.2 +[0.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.1 +[0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.0 +[0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.3.0 +[0.2.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.3 +[0.2.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.2 +[0.2.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1.1 +[0.2.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1 +[0.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.0 +[0.1.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.2 +[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1 +[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0 diff --git a/vendor/go.opentelemetry.io/otel/CODEOWNERS b/vendor/go.opentelemetry.io/otel/CODEOWNERS new file mode 100644 index 00000000..76d959d2 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/CODEOWNERS @@ -0,0 +1,17 @@ +##################################################### +# +# List of approvers for this repository +# +##################################################### +# +# Learn about membership in OpenTelemetry community: +# https://github.com/open-telemetry/community/blob/main/community-membership.md +# +# +# Learn about CODEOWNERS file format: +# https://help.github.com/en/articles/about-code-owners +# + +* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared @hanyuancheung + +CODEOWNERS @MrAlias @Aneurysm9 @MadVikingGod diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md new file mode 100644 index 00000000..098a7c54 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -0,0 +1,522 @@ +# Contributing to opentelemetry-go + +The Go special interest group (SIG) meets regularly. See the +OpenTelemetry +[community](https://github.com/open-telemetry/community#golang-sdk) +repo for information on this and other language SIGs. + +See the [public meeting +notes](https://docs.google.com/document/d/1A63zSWX0x2CyCK_LoNhmQC4rqhLpYXJzXbEPDUQ2n6w/edit#heading=h.9tngw7jdwd6b) +for a summary description of past meetings. To request edit access, +join the meeting or get in touch on +[Slack](https://cloud-native.slack.com/archives/C01NPAXACKT). + +## Development + +You can view and edit the source code by cloning this repository: + +```sh +git clone https://github.com/open-telemetry/opentelemetry-go.git +``` + +Run `make test` to run the tests instead of `go test`. + +There are some generated files checked into the repo. To make sure +that the generated files are up-to-date, run `make` (or `make +precommit` - the `precommit` target is the default). + +The `precommit` target also fixes the formatting of the code and +checks the status of the go module files. + +If after running `make precommit` the output of `git status` contains +`nothing to commit, working tree clean` then it means that everything +is up-to-date and properly formatted. + +## Pull Requests + +### How to Send Pull Requests + +Everyone is welcome to contribute code to `opentelemetry-go` via +GitHub pull requests (PRs). + +To create a new PR, fork the project in GitHub and clone the upstream +repo: + +```sh +go get -d go.opentelemetry.io/otel +``` + +(This may print some warning about "build constraints exclude all Go +files", just ignore it.) + +This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You +can alternatively use `git` directly with: + +```sh +git clone https://github.com/open-telemetry/opentelemetry-go +``` + +(Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name - +that name is a kind of a redirector to GitHub that `go get` can +understand, but `git` does not.) + +This would put the project in the `opentelemetry-go` directory in +current working directory. + +Enter the newly created directory and add your fork as a new remote: + +```sh +git remote add git@github.com:/opentelemetry-go +``` + +Check out a new branch, make modifications, run linters and tests, update +`CHANGELOG.md`, and push the branch to your fork: + +```sh +git checkout -b +# edit files +# update changelog +make precommit +git add -p +git commit +git push +``` + +Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull +request ID to the entry you added to `CHANGELOG.md`. + +### How to Receive Comments + +* If the PR is not ready for review, please put `[WIP]` in the title, + tag it as `work-in-progress`, or mark it as + [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). +* Make sure CLA is signed and CI is clear. + +### How to Get PRs Merged + +A PR is considered to be **ready to merge** when: + +* It has received two approvals from Collaborators/Maintainers (at + different companies). This is not enforced through technical means + and a PR may be **ready to merge** with a single approval if the change + and its approach have been discussed and consensus reached. +* Feedback has been addressed. +* Any substantive changes to your PR will require that you clear any prior + Approval reviews, this includes changes resulting from other feedback. Unless + the approver explicitly stated that their approval will persist across + changes it should be assumed that the PR needs their review again. Other + project members (e.g. approvers, maintainers) can help with this if there are + any questions or if you forget to clear reviews. +* It has been open for review for at least one working day. This gives + people reasonable time to review. +* Trivial changes (typo, cosmetic, doc, etc.) do not have to wait for + one day and may be merged with a single Maintainer's approval. +* `CHANGELOG.md` has been updated to reflect what has been + added, changed, removed, or fixed. +* `README.md` has been updated if necessary. +* Urgent fix can take exception as long as it has been actively + communicated. + +Any Maintainer can merge the PR once it is **ready to merge**. + +## Design Choices + +As with other OpenTelemetry clients, opentelemetry-go follows the +[opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification). + +It's especially valuable to read through the [library +guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/library-guidelines.md). + +### Focus on Capabilities, Not Structure Compliance + +OpenTelemetry is an evolving specification, one where the desires and +use cases are clear, but the method to satisfy those uses cases are +not. + +As such, Contributions should provide functionality and behavior that +conforms to the specification, but the interface and structure is +flexible. + +It is preferable to have contributions follow the idioms of the +language rather than conform to specific API names or argument +patterns in the spec. + +For a deeper discussion, see +[this](https://github.com/open-telemetry/opentelemetry-specification/issues/165). + +## Documentation + +Each non-example Go Module should have its own `README.md` containing: + +- A pkg.go.dev badge which can be generated [here](https://pkg.go.dev/badge/). +- Brief description. +- Installation instructions (and requirements if applicable). +- Hyperlink to an example. Depending on the component the example can be: + - An `example_test.go` like [here](exporters/stdout/stdouttrace/example_test.go). + - A sample Go application with its own `README.md`, like [here](example/zipkin). +- Additional documentation sections such us: + - Configuration, + - Contributing, + - References. + +[Here](exporters/jaeger/README.md) is an example of a concise `README.md`. + +Moreover, it should be possible to navigate to any `README.md` from the +root `README.md`. + +## Style Guide + +One of the primary goals of this project is that it is actually used by +developers. With this goal in mind the project strives to build +user-friendly and idiomatic Go code adhering to the Go community's best +practices. + +For a non-comprehensive but foundational overview of these best practices +the [Effective Go](https://golang.org/doc/effective_go.html) documentation +is an excellent starting place. + +As a convenience for developers building this project the `make precommit` +will format, lint, validate, and in some cases fix the changes you plan to +submit. This check will need to pass for your changes to be able to be +merged. + +In addition to idiomatic Go, the project has adopted certain standards for +implementations of common patterns. These standards should be followed as a +default, and if they are not followed documentation needs to be included as +to the reasons why. + +### Configuration + +When creating an instantiation function for a complex `type T struct`, it is +useful to allow variable number of options to be applied. However, the strong +type system of Go restricts the function design options. There are a few ways +to solve this problem, but we have landed on the following design. + +#### `config` + +Configuration should be held in a `struct` named `config`, or prefixed with +specific type name this Configuration applies to if there are multiple +`config` in the package. This type must contain configuration options. + +```go +// config contains configuration options for a thing. +type config struct { + // options ... +} +``` + +In general the `config` type will not need to be used externally to the +package and should be unexported. If, however, it is expected that the user +will likely want to build custom options for the configuration, the `config` +should be exported. Please, include in the documentation for the `config` +how the user can extend the configuration. + +It is important that internal `config` are not shared across package boundaries. +Meaning a `config` from one package should not be directly used by another. The +one exception is the API packages. The configs from the base API, eg. +`go.opentelemetry.io/otel/trace.TracerConfig` and +`go.opentelemetry.io/otel/metric.InstrumentConfig`, are intended to be consumed +by the SDK therefor it is expected that these are exported. + +When a config is exported we want to maintain forward and backward +compatibility, to achieve this no fields should be exported but should +instead be accessed by methods. + +Optionally, it is common to include a `newConfig` function (with the same +naming scheme). This function wraps any defaults setting and looping over +all options to create a configured `config`. + +```go +// newConfig returns an appropriately configured config. +func newConfig(options ...Option) config { + // Set default values for config. + config := config{/* […] */} + for _, option := range options { + config = option.apply(config) + } + // Preform any validation here. + return config +} +``` + +If validation of the `config` options is also preformed this can return an +error as well that is expected to be handled by the instantiation function +or propagated to the user. + +Given the design goal of not having the user need to work with the `config`, +the `newConfig` function should also be unexported. + +#### `Option` + +To set the value of the options a `config` contains, a corresponding +`Option` interface type should be used. + +```go +type Option interface { + apply(config) config +} +``` + +Having `apply` unexported makes sure that it will not be used externally. +Moreover, the interface becomes sealed so the user cannot easily implement +the interface on its own. + +The `apply` method should return a modified version of the passed config. +This approach, instead of passing a pointer, is used to prevent the config from being allocated to the heap. + +The name of the interface should be prefixed in the same way the +corresponding `config` is (if at all). + +#### Options + +All user configurable options for a `config` must have a related unexported +implementation of the `Option` interface and an exported configuration +function that wraps this implementation. + +The wrapping function name should be prefixed with `With*` (or in the +special case of a boolean options `Without*`) and should have the following +function signature. + +```go +func With*(…) Option { … } +``` + +##### `bool` Options + +```go +type defaultFalseOption bool + +func (o defaultFalseOption) apply(c config) config { + c.Bool = bool(o) + return c +} + +// WithOption sets a T to have an option included. +func WithOption() Option { + return defaultFalseOption(true) +} +``` + +```go +type defaultTrueOption bool + +func (o defaultTrueOption) apply(c config) config { + c.Bool = bool(o) + return c +} + +// WithoutOption sets a T to have Bool option excluded. +func WithoutOption() Option { + return defaultTrueOption(false) +} +``` + +##### Declared Type Options + +```go +type myTypeOption struct { + MyType MyType +} + +func (o myTypeOption) apply(c config) config { + c.MyType = o.MyType + return c +} + +// WithMyType sets T to have include MyType. +func WithMyType(t MyType) Option { + return myTypeOption{t} +} +``` + +##### Functional Options + +```go +type optionFunc func(config) config + +func (fn optionFunc) apply(c config) config { + return fn(c) +} + +// WithMyType sets t as MyType. +func WithMyType(t MyType) Option { + return optionFunc(func(c config) config { + c.MyType = t + return c + }) +} +``` + +#### Instantiation + +Using this configuration pattern to configure instantiation with a `NewT` +function. + +```go +func NewT(options ...Option) T {…} +``` + +Any required parameters can be declared before the variadic `options`. + +#### Dealing with Overlap + +Sometimes there are multiple complex `struct` that share common +configuration and also have distinct configuration. To avoid repeated +portions of `config`s, a common `config` can be used with the union of +options being handled with the `Option` interface. + +For example. + +```go +// config holds options for all animals. +type config struct { + Weight float64 + Color string + MaxAltitude float64 +} + +// DogOption apply Dog specific options. +type DogOption interface { + applyDog(config) config +} + +// BirdOption apply Bird specific options. +type BirdOption interface { + applyBird(config) config +} + +// Option apply options for all animals. +type Option interface { + BirdOption + DogOption +} + +type weightOption float64 + +func (o weightOption) applyDog(c config) config { + c.Weight = float64(o) + return c +} + +func (o weightOption) applyBird(c config) config { + c.Weight = float64(o) + return c +} + +func WithWeight(w float64) Option { return weightOption(w) } + +type furColorOption string + +func (o furColorOption) applyDog(c config) config { + c.Color = string(o) + return c +} + +func WithFurColor(c string) DogOption { return furColorOption(c) } + +type maxAltitudeOption float64 + +func (o maxAltitudeOption) applyBird(c config) config { + c.MaxAltitude = float64(o) + return c +} + +func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } + +func NewDog(name string, o ...DogOption) Dog {…} +func NewBird(name string, o ...BirdOption) Bird {…} +``` + +### Interfaces + +To allow other developers to better comprehend the code, it is important +to ensure it is sufficiently documented. One simple measure that contributes +to this aim is self-documenting by naming method parameters. Therefore, +where appropriate, methods of every exported interface type should have +their parameters appropriately named. + +#### Interface Stability + +All exported stable interfaces that include the following warning in their +doumentation are allowed to be extended with additional methods. + +> Warning: methods may be added to this interface in minor releases. + +Otherwise, stable interfaces MUST NOT be modified. + +If new functionality is needed for an interface that cannot be changed it MUST +be added by including an additional interface. That added interface can be a +simple interface for the specific functionality that you want to add or it can +be a super-set of the original interface. For example, if you wanted to a +`Close` method to the `Exporter` interface: + +```go +type Exporter interface { + Export() +} +``` + +A new interface, `Closer`, can be added: + +```go +type Closer interface { + Close() +} +``` + +Code that is passed the `Exporter` interface can now check to see if the passed +value also satisfies the new interface. E.g. + +```go +func caller(e Exporter) { + /* ... */ + if c, ok := e.(Closer); ok { + c.Close() + } + /* ... */ +} +``` + +Alternatively, a new type that is the super-set of an `Exporter` can be created. + +```go +type ClosingExporter struct { + Exporter + Close() +} +``` + +This new type can be used similar to the simple interface above in that a +passed `Exporter` type can be asserted to satisfy the `ClosingExporter` type +and the `Close` method called. + +This super-set approach can be useful if there is explicit behavior that needs +to be coupled with the original type and passed as a unified type to a new +function, but, because of this coupling, it also limits the applicability of +the added functionality. If there exist other interfaces where this +functionality should be added, each one will need their own super-set +interfaces and will duplicate the pattern. For this reason, the simple targeted +interface that defines the specific functionality should be preferred. + +## Approvers and Maintainers + +Approvers: + +- [Evan Torrie](https://github.com/evantorrie), Verizon Media +- [Josh MacDonald](https://github.com/jmacd), LightStep +- [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics +- [David Ashpole](https://github.com/dashpole), Google +- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep +- [Robert Pająk](https://github.com/pellared), Splunk +- [Chester Cheung](https://github.com/hanyuancheung), Tencent + +Maintainers: + +- [Aaron Clawson](https://github.com/MadVikingGod), LightStep +- [Anthony Mirabella](https://github.com/Aneurysm9), AWS +- [Tyler Yahn](https://github.com/MrAlias), Splunk + +### Become an Approver or a Maintainer + +See the [community membership document in OpenTelemetry community +repo](https://github.com/open-telemetry/community/blob/main/community-membership.md). diff --git a/vendor/go.opentelemetry.io/otel/LICENSE b/vendor/go.opentelemetry.io/otel/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/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/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile new file mode 100644 index 00000000..1b80eb72 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/Makefile @@ -0,0 +1,209 @@ +# Copyright The OpenTelemetry Authors +# +# 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. + +TOOLS_MOD_DIR := ./internal/tools + +ALL_DOCS := $(shell find . -name '*.md' -type f | sort) +ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) +OTEL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(ALL_GO_MOD_DIRS)) +ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort) + +GO = go +TIMEOUT = 60 + +.DEFAULT_GOAL := precommit + +.PHONY: precommit ci +precommit: dependabot-generate license-check misspell go-mod-tidy golangci-lint-fix test-default +ci: dependabot-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage + +# Tools + +TOOLS = $(CURDIR)/.tools + +$(TOOLS): + @mkdir -p $@ +$(TOOLS)/%: | $(TOOLS) + cd $(TOOLS_MOD_DIR) && \ + $(GO) build -o $@ $(PACKAGE) + +MULTIMOD = $(TOOLS)/multimod +$(TOOLS)/multimod: PACKAGE=go.opentelemetry.io/build-tools/multimod + +SEMCONVGEN = $(TOOLS)/semconvgen +$(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen + +CROSSLINK = $(TOOLS)/crosslink +$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink + +DBOTCONF = $(TOOLS)/dbotconf +$(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf + +GOLANGCI_LINT = $(TOOLS)/golangci-lint +$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint + +MISSPELL = $(TOOLS)/misspell +$(TOOLS)/misspell: PACKAGE=github.com/client9/misspell/cmd/misspell + +GOCOVMERGE = $(TOOLS)/gocovmerge +$(TOOLS)/gocovmerge: PACKAGE=github.com/wadey/gocovmerge + +STRINGER = $(TOOLS)/stringer +$(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer + +PORTO = $(TOOLS)/porto +$(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto + +GOJQ = $(TOOLS)/gojq +$(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq + +.PHONY: tools +tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) + +# Build + +.PHONY: generate build + +generate: $(OTEL_GO_MOD_DIRS:%=generate/%) +generate/%: DIR=$* +generate/%: | $(STRINGER) $(PORTO) + @echo "$(GO) generate $(DIR)/..." \ + && cd $(DIR) \ + && PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && $(PORTO) -w . + +build: generate $(OTEL_GO_MOD_DIRS:%=build/%) $(OTEL_GO_MOD_DIRS:%=build-tests/%) +build/%: DIR=$* +build/%: + @echo "$(GO) build $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) build ./... + +build-tests/%: DIR=$* +build-tests/%: + @echo "$(GO) build tests $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null + +# Tests + +TEST_TARGETS := test-default test-bench test-short test-verbose test-race +.PHONY: $(TEST_TARGETS) test +test-default test-race: ARGS=-race +test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. +test-short: ARGS=-short +test-verbose: ARGS=-v -race +$(TEST_TARGETS): test +test: $(OTEL_GO_MOD_DIRS:%=test/%) +test/%: DIR=$* +test/%: + @echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $(DIR)/..." \ + && cd $(DIR) \ + && $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS) + +COVERAGE_MODE = atomic +COVERAGE_PROFILE = coverage.out +.PHONY: test-coverage +test-coverage: | $(GOCOVMERGE) + @set -e; \ + printf "" > coverage.txt; \ + for dir in $(ALL_COVERAGE_MOD_DIRS); do \ + echo "$(GO) test -coverpkg=go.opentelemetry.io/otel/... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" $${dir}/..."; \ + (cd "$${dir}" && \ + $(GO) list ./... \ + | grep -v third_party \ + | xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \ + $(GO) tool cover -html=coverage.out -o coverage.html); \ + done; \ + $(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt + +.PHONY: golangci-lint golangci-lint-fix +golangci-lint-fix: ARGS=--fix +golangci-lint-fix: golangci-lint +golangci-lint: $(OTEL_GO_MOD_DIRS:%=golangci-lint/%) +golangci-lint/%: DIR=$* +golangci-lint/%: | $(GOLANGCI_LINT) + @echo 'golangci-lint $(if $(ARGS),$(ARGS) ,)$(DIR)' \ + && cd $(DIR) \ + && $(GOLANGCI_LINT) run --allow-serial-runners $(ARGS) + +.PHONY: crosslink +crosslink: | $(CROSSLINK) + @echo "cross-linking all go modules" \ + && $(CROSSLINK) + +.PHONY: go-mod-tidy +go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%) +go-mod-tidy/%: DIR=$* +go-mod-tidy/%: | crosslink + @echo "$(GO) mod tidy in $(DIR)" \ + && cd $(DIR) \ + && $(GO) mod tidy + +.PHONY: lint-modules +lint-modules: go-mod-tidy + +.PHONY: lint +lint: misspell lint-modules golangci-lint + +.PHONY: vanity-import-check +vanity-import-check: | $(PORTO) + @$(PORTO) --include-internal -l . + +.PHONY: misspell +misspell: | $(MISSPELL) + @$(MISSPELL) -w $(ALL_DOCS) + +.PHONY: license-check +license-check: + @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; do \ + awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \ + done); \ + if [ -n "$${licRes}" ]; then \ + echo "license header checking failed:"; echo "$${licRes}"; \ + exit 1; \ + fi + +DEPENDABOT_CONFIG = .github/dependabot.yml +.PHONY: dependabot-check +dependabot-check: | $(DBOTCONF) + @$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || echo "(run: make dependabot-generate)" + +.PHONY: dependabot-generate +dependabot-generate: | $(DBOTCONF) + @$(DBOTCONF) generate > $(DEPENDABOT_CONFIG) + +.PHONY: check-clean-work-tree +check-clean-work-tree: + @if ! git diff --quiet; then \ + echo; \ + echo 'Working tree is not clean, did you forget to run "make precommit"?'; \ + echo; \ + git status; \ + exit 1; \ + fi + +.PHONY: prerelease +prerelease: | $(MULTIMOD) + @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) + $(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET} + +COMMIT ?= "HEAD" +.PHONY: add-tags +add-tags: | $(MULTIMOD) + @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) + $(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT} diff --git a/vendor/go.opentelemetry.io/otel/README.md b/vendor/go.opentelemetry.io/otel/README.md new file mode 100644 index 00000000..e7e07497 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/README.md @@ -0,0 +1,108 @@ +# OpenTelemetry-Go + +[![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain) +[![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-go/coverage.svg?branch=main)](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main) +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel) +[![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel) +[![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT) + +OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/). +It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms. + +## Project Status + +| Signal | Status | Project | +| ------- | ---------- | ------- | +| Traces | Stable | N/A | +| Metrics | Alpha | N/A | +| Logs | Frozen [1] | N/A | + +- [1]: The Logs signal development is halted for this project while we develop both Traces and Metrics. + No Logs Pull Requests are currently being accepted. + +Progress and status specific to this repository is tracked in our local +[project boards](https://github.com/open-telemetry/opentelemetry-go/projects) +and +[milestones](https://github.com/open-telemetry/opentelemetry-go/milestones). + +Project versioning information and stability guarantees can be found in the +[versioning documentation](./VERSIONING.md). + +### Compatibility + +OpenTelemetry-Go attempts to track the current supported versions of the +[Go language](https://golang.org/doc/devel/release#policy). The release +schedule after a new minor version of go is as follows: + +- The first release or one month, which ever is sooner, will add build steps for the new go version. +- The first release after three months will remove support for the oldest go version. + +This project is tested on the following systems. + +| OS | Go Version | Architecture | +| ------- | ---------- | ------------ | +| Ubuntu | 1.18 | amd64 | +| Ubuntu | 1.17 | amd64 | +| Ubuntu | 1.16 | amd64 | +| Ubuntu | 1.18 | 386 | +| Ubuntu | 1.17 | 386 | +| Ubuntu | 1.16 | 386 | +| MacOS | 1.18 | amd64 | +| MacOS | 1.17 | amd64 | +| MacOS | 1.16 | amd64 | +| Windows | 1.18 | amd64 | +| Windows | 1.17 | amd64 | +| Windows | 1.16 | amd64 | +| Windows | 1.18 | 386 | +| Windows | 1.17 | 386 | +| Windows | 1.16 | 386 | + +While this project should work for other systems, no compatibility guarantees +are made for those systems currently. + +Go 1.18 was added in March of 2022. +Go 1.16 will be removed around June 2022. + +## Getting Started + +You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/). + +OpenTelemetry's goal is to provide a single set of APIs to capture distributed +traces and metrics from your application and send them to an observability +platform. This project allows you to do just that for applications written in +Go. There are two steps to this process: instrument your application, and +configure an exporter. + +### Instrumentation + +To start capturing distributed traces and metric events from your application +it first needs to be instrumented. The easiest way to do this is by using an +instrumentation library for your code. Be sure to check out [the officially +supported instrumentation +libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation). + +If you need to extend the telemetry an instrumentation library provides or want +to build your own instrumentation for your application directly you will need +to use the +[Go otel](https://pkg.go.dev/go.opentelemetry.io/otel) +package. The included [examples](./example/) are a good way to see some +practical uses of this process. + +### Export + +Now that your application is instrumented to collect telemetry, it needs an +export pipeline to send that telemetry to an observability platform. + +All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters). + +| Exporter | Metrics | Traces | +| :-----------------------------------: | :-----: | :----: | +| [Jaeger](./exporters/jaeger/) | | ✓ | +| [OTLP](./exporters/otlp/) | ✓ | ✓ | +| [Prometheus](./exporters/prometheus/) | ✓ | | +| [stdout](./exporters/stdout/) | ✓ | ✓ | +| [Zipkin](./exporters/zipkin/) | | ✓ | + +## Contributing + +See the [contributing documentation](CONTRIBUTING.md). diff --git a/vendor/go.opentelemetry.io/otel/RELEASING.md b/vendor/go.opentelemetry.io/otel/RELEASING.md new file mode 100644 index 00000000..e3bff66c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/RELEASING.md @@ -0,0 +1,132 @@ +# Release Process + +## Semantic Convention Generation + +If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new +semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility +installed by `make tools` that can be used to generate the a package with the name matching the specification +version number under the `semconv` package. This will ideally be done soon after the specification release is +tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the +generated files match the released semantic conventions. + +There are currently two categories of semantic conventions that must be generated, `resource` and `trace`. + +``` +.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2 +.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2 +``` + +Using default values for all options other than `input` will result in using the `template.j2` template to +generate `resource.go` and `trace.go` in `/path/to/otelgo/repo/semconv/`. + +There are several ancillary files that are not generated and should be copied into the new package from the +prior package, with updates made as appropriate to canonical import path statements and constant values. +These files include: + +* doc.go +* exception.go +* http(_test)?.go +* schema.go + +Uses of the previous schema version in this repository should be updated to use the newly generated version. +No tooling for this exists at present, so use find/replace in your editor of choice or craft a `grep | sed` +pipeline if you like living on the edge. + +## Pre-Release + +First, decide which module sets will be released and update their versions +in `versions.yaml`. Commit this change to a new branch. + +Update go.mod for submodules to depend on the new release which will happen in the next step. + +1. Run the `prerelease` make target. It creates a branch + `prerelease__` that will contain all release changes. + + ``` + make prerelease MODSET= + ``` + +2. Verify the changes. + + ``` + git diff ...prerelease__ + ``` + + This should have changed the version for all modules to be ``. + If these changes look correct, merge them into your pre-release branch: + + ```go + git merge prerelease__ + ``` + +3. Update the [Changelog](./CHANGELOG.md). + - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. + To verify this, you can look directly at the commits since the ``. + + ``` + git --no-pager log --pretty=oneline "..HEAD" + ``` + + - Move all the `Unreleased` changes into a new section following the title scheme (`[] - `). + - Update all the appropriate links at the bottom. + +4. Push the changes to upstream and create a Pull Request on GitHub. + Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description. + +## Tag + +Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit. + +***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step! +Failure to do so will leave things in a broken state. As long as you do not +change `versions.yaml` between pre-release and this step, things should be fine. + +***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189). +It is critical you make sure the version you push upstream is correct. +[Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331). + +1. For each module set that will be released, run the `add-tags` make target + using the `` of the commit on the main branch for the merged Pull Request. + + ``` + make add-tags MODSET= COMMIT= + ``` + + It should only be necessary to provide an explicit `COMMIT` value if the + current `HEAD` of your working directory is not the correct commit. + +2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`). + Make sure you push all sub-modules as well. + + ``` + git push upstream + git push upstream + ... + ``` + +## Release + +Finally create a Release for the new `` on GitHub. +The release body should include all the release notes from the Changelog for this release. + +## Verify Examples + +After releasing verify that examples build outside of the repository. + +``` +./verify_examples.sh +``` + +The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them. +This ensures they build with the published release, not the local copy. + +## Post-Release + +### Contrib Repository + +Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md) that uses this release. + +### Website Documentation + +Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/). +Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate. diff --git a/vendor/go.opentelemetry.io/otel/VERSIONING.md b/vendor/go.opentelemetry.io/otel/VERSIONING.md new file mode 100644 index 00000000..412f1e36 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/VERSIONING.md @@ -0,0 +1,224 @@ +# Versioning + +This document describes the versioning policy for this repository. This policy +is designed so the following goals can be achieved. + +**Users are provided a codebase of value that is stable and secure.** + +## Policy + +* Versioning of this project will be idiomatic of a Go project using [Go + modules](https://github.com/golang/go/wiki/Modules). + * [Semantic import + versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) + will be used. + * Versions will comply with [semver + 2.0](https://semver.org/spec/v2.0.0.html) with the following exceptions. + * New methods may be added to exported API interfaces. All exported + interfaces that fall within this exception will include the following + paragraph in their public documentation. + + > Warning: methods may be added to this interface in minor releases. + + * If a module is version `v2` or higher, the major version of the module + must be included as a `/vN` at the end of the module paths used in + `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require + go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path + (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the + paths used in `go get` commands (e.g., `go get + go.opentelemetry.io/otel/v2@v2.0.1`. Note there is both a `/v2` and a + `@v2.0.1` in that example. One way to think about it is that the module + name now includes the `/v2`, so include `/v2` whenever you are using the + module name). + * If a module is version `v0` or `v1`, do not include the major version in + either the module path or the import path. + * Modules will be used to encapsulate signals and components. + * Experimental modules still under active development will be versioned at + `v0` to imply the stability guarantee defined by + [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). + + > Major version zero (0.y.z) is for initial development. Anything MAY + > change at any time. The public API SHOULD NOT be considered stable. + + * Mature modules for which we guarantee a stable public API will be versioned + with a major version greater than `v0`. + * The decision to make a module stable will be made on a case-by-case + basis by the maintainers of this project. + * Experimental modules will start their versioning at `v0.0.0` and will + increment their minor version when backwards incompatible changes are + released and increment their patch version when backwards compatible + changes are released. + * All stable modules that use the same major version number will use the + same entire version number. + * Stable modules may be released with an incremented minor or patch + version even though that module has not been changed, but rather so + that it will remain at the same version as other stable modules that + did undergo change. + * When an experimental module becomes stable a new stable module version + will be released and will include this now stable module. The new + stable module version will be an increment of the minor version number + and will be applied to all existing stable modules as well as the newly + stable module being released. +* Versioning of the associated [contrib + repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of + this project will be idiomatic of a Go project using [Go + modules](https://github.com/golang/go/wiki/Modules). + * [Semantic import + versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) + will be used. + * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). + * If a module is version `v2` or higher, the + major version of the module must be included as a `/vN` at the end of the + module paths used in `go.mod` files (e.g., `module + go.opentelemetry.io/contrib/instrumentation/host/v2`, `require + go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the + package import path (e.g., `import + "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes + the paths used in `go get` commands (e.g., `go get + go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`. Note there + is both a `/v2` and a `@v2.0.1` in that example. One way to think about + it is that the module name now includes the `/v2`, so include `/v2` + whenever you are using the module name). + * If a module is version `v0` or `v1`, do not include the major version + in either the module path or the import path. + * In addition to public APIs, telemetry produced by stable instrumentation + will remain stable and backwards compatible. This is to avoid breaking + alerts and dashboard. + * Modules will be used to encapsulate instrumentation, detectors, exporters, + propagators, and any other independent sets of related components. + * Experimental modules still under active development will be versioned at + `v0` to imply the stability guarantee defined by + [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). + + > Major version zero (0.y.z) is for initial development. Anything MAY + > change at any time. The public API SHOULD NOT be considered stable. + + * Mature modules for which we guarantee a stable public API and telemetry will + be versioned with a major version greater than `v0`. + * Experimental modules will start their versioning at `v0.0.0` and will + increment their minor version when backwards incompatible changes are + released and increment their patch version when backwards compatible + changes are released. + * Stable contrib modules cannot depend on experimental modules from this + project. + * All stable contrib modules of the same major version with this project + will use the same entire version as this project. + * Stable modules may be released with an incremented minor or patch + version even though that module's code has not been changed. Instead + the only change that will have been included is to have updated that + modules dependency on this project's stable APIs. + * When an experimental module in contrib becomes stable a new stable + module version will be released and will include this now stable + module. The new stable module version will be an increment of the minor + version number and will be applied to all existing stable contrib + modules, this project's modules, and the newly stable module being + released. + * Contrib modules will be kept up to date with this project's releases. + * Due to the dependency contrib modules will implicitly have on this + project's modules the release of stable contrib modules to match the + released version number will be staggered after this project's release. + There is no explicit time guarantee for how long after this projects + release the contrib release will be. Effort should be made to keep them + as close in time as possible. + * No additional stable release in this project can be made until the + contrib repository has a matching stable release. + * No release can be made in the contrib repository after this project's + stable release except for a stable release of the contrib repository. +* GitHub releases will be made for all releases. +* Go modules will be made available at Go package mirrors. + +## Example Versioning Lifecycle + +To better understand the implementation of the above policy the following +example is provided. This project is simplified to include only the following +modules and their versions: + +* `otel`: `v0.14.0` +* `otel/trace`: `v0.14.0` +* `otel/metric`: `v0.14.0` +* `otel/baggage`: `v0.14.0` +* `otel/sdk/trace`: `v0.14.0` +* `otel/sdk/metric`: `v0.14.0` + +These modules have been developed to a point where the `otel/trace`, +`otel/baggage`, and `otel/sdk/trace` modules have reached a point that they +should be considered for a stable release. The `otel/metric` and +`otel/sdk/metric` are still under active development and the `otel` module +depends on both `otel/trace` and `otel/metric`. + +The `otel` package is refactored to remove its dependencies on `otel/metric` so +it can be released as stable as well. With that done the following release +candidates are made: + +* `otel`: `v1.0.0-RC1` +* `otel/trace`: `v1.0.0-RC1` +* `otel/baggage`: `v1.0.0-RC1` +* `otel/sdk/trace`: `v1.0.0-RC1` + +The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`. + +A few minor issues are discovered in the `otel/trace` package. These issues are +resolved with some minor, but backwards incompatible, changes and are released +as a second release candidate: + +* `otel`: `v1.0.0-RC2` +* `otel/trace`: `v1.0.0-RC2` +* `otel/baggage`: `v1.0.0-RC2` +* `otel/sdk/trace`: `v1.0.0-RC2` + +Notice that all module version numbers are incremented to adhere to our +versioning policy. + +After these release candidates have been evaluated to satisfaction, they are +released as version `v1.0.0`. + +* `otel`: `v1.0.0` +* `otel/trace`: `v1.0.0` +* `otel/baggage`: `v1.0.0` +* `otel/sdk/trace`: `v1.0.0` + +Since both the `go` utility and the Go module system support [the semantic +versioning definition of +precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release +will correctly be interpreted as the successor to the previous release +candidates. + +Active development of this project continues. The `otel/metric` module now has +backwards incompatible changes to its API that need to be released and the +`otel/baggage` module has a minor bug fix that needs to be released. The +following release is made: + +* `otel`: `v1.0.1` +* `otel/trace`: `v1.0.1` +* `otel/metric`: `v0.15.0` +* `otel/baggage`: `v1.0.1` +* `otel/sdk/trace`: `v1.0.1` +* `otel/sdk/metric`: `v0.15.0` + +Notice that, again, all stable module versions are incremented in unison and +the `otel/sdk/metric` package, which depends on the `otel/metric` package, also +bumped its version. This bump of the `otel/sdk/metric` package makes sense +given their coupling, though it is not explicitly required by our versioning +policy. + +As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a +point where they should be evaluated for stability. The `otel` module is +reintegrated with the `otel/metric` package and the following release is made: + +* `otel`: `v1.1.0-RC1` +* `otel/trace`: `v1.1.0-RC1` +* `otel/metric`: `v1.1.0-RC1` +* `otel/baggage`: `v1.1.0-RC1` +* `otel/sdk/trace`: `v1.1.0-RC1` +* `otel/sdk/metric`: `v1.1.0-RC1` + +All the modules are evaluated and determined to a viable stable release. They +are then released as version `v1.1.0` (the minor version is incremented to +indicate the addition of new signal). + +* `otel`: `v1.1.0` +* `otel/trace`: `v1.1.0` +* `otel/metric`: `v1.1.0` +* `otel/baggage`: `v1.1.0` +* `otel/sdk/trace`: `v1.1.0` +* `otel/sdk/metric`: `v1.1.0` diff --git a/vendor/go.opentelemetry.io/otel/attribute/doc.go b/vendor/go.opentelemetry.io/otel/attribute/doc.go new file mode 100644 index 00000000..dafe7424 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/doc.go @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute provides key and value attributes. +package attribute // import "go.opentelemetry.io/otel/attribute" diff --git a/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/vendor/go.opentelemetry.io/otel/attribute/encoder.go new file mode 100644 index 00000000..8b940f78 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/encoder.go @@ -0,0 +1,150 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "bytes" + "sync" + "sync/atomic" +) + +type ( + // Encoder is a mechanism for serializing a label set into a + // specific string representation that supports caching, to + // avoid repeated serialization. An example could be an + // exporter encoding the label set into a wire representation. + Encoder interface { + // Encode returns the serialized encoding of the label + // set using its Iterator. This result may be cached + // by a attribute.Set. + Encode(iterator Iterator) string + + // ID returns a value that is unique for each class of + // label encoder. Label encoders allocate these using + // `NewEncoderID`. + ID() EncoderID + } + + // EncoderID is used to identify distinct Encoder + // implementations, for caching encoded results. + EncoderID struct { + value uint64 + } + + // defaultLabelEncoder uses a sync.Pool of buffers to reduce + // the number of allocations used in encoding labels. This + // implementation encodes a comma-separated list of key=value, + // with '/'-escaping of '=', ',', and '\'. + defaultLabelEncoder struct { + // pool is a pool of labelset builders. The buffers in this + // pool grow to a size that most label encodings will not + // allocate new memory. + pool sync.Pool // *bytes.Buffer + } +) + +// escapeChar is used to ensure uniqueness of the label encoding where +// keys or values contain either '=' or ','. Since there is no parser +// needed for this encoding and its only requirement is to be unique, +// this choice is arbitrary. Users will see these in some exporters +// (e.g., stdout), so the backslash ('\') is used as a conventional choice. +const escapeChar = '\\' + +var ( + _ Encoder = &defaultLabelEncoder{} + + // encoderIDCounter is for generating IDs for other label + // encoders. + encoderIDCounter uint64 + + defaultEncoderOnce sync.Once + defaultEncoderID = NewEncoderID() + defaultEncoderInstance *defaultLabelEncoder +) + +// NewEncoderID returns a unique label encoder ID. It should be +// called once per each type of label encoder. Preferably in init() or +// in var definition. +func NewEncoderID() EncoderID { + return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)} +} + +// DefaultEncoder returns a label encoder that encodes labels +// in such a way that each escaped label's key is followed by an equal +// sign and then by an escaped label's value. All key-value pairs are +// separated by a comma. +// +// Escaping is done by prepending a backslash before either a +// backslash, equal sign or a comma. +func DefaultEncoder() Encoder { + defaultEncoderOnce.Do(func() { + defaultEncoderInstance = &defaultLabelEncoder{ + pool: sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, + }, + } + }) + return defaultEncoderInstance +} + +// Encode is a part of an implementation of the LabelEncoder +// interface. +func (d *defaultLabelEncoder) Encode(iter Iterator) string { + buf := d.pool.Get().(*bytes.Buffer) + defer d.pool.Put(buf) + buf.Reset() + + for iter.Next() { + i, keyValue := iter.IndexedLabel() + if i > 0 { + _, _ = buf.WriteRune(',') + } + copyAndEscape(buf, string(keyValue.Key)) + + _, _ = buf.WriteRune('=') + + if keyValue.Value.Type() == STRING { + copyAndEscape(buf, keyValue.Value.AsString()) + } else { + _, _ = buf.WriteString(keyValue.Value.Emit()) + } + } + return buf.String() +} + +// ID is a part of an implementation of the LabelEncoder interface. +func (*defaultLabelEncoder) ID() EncoderID { + return defaultEncoderID +} + +// copyAndEscape escapes `=`, `,` and its own escape character (`\`), +// making the default encoding unique. +func copyAndEscape(buf *bytes.Buffer, val string) { + for _, ch := range val { + switch ch { + case '=', ',', escapeChar: + buf.WriteRune(escapeChar) + } + buf.WriteRune(ch) + } +} + +// Valid returns true if this encoder ID was allocated by +// `NewEncoderID`. Invalid encoder IDs will not be cached. +func (id EncoderID) Valid() bool { + return id.value != 0 +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/iterator.go b/vendor/go.opentelemetry.io/otel/attribute/iterator.go new file mode 100644 index 00000000..e03aabb6 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/iterator.go @@ -0,0 +1,143 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +// Iterator allows iterating over the set of labels in order, +// sorted by key. +type Iterator struct { + storage *Set + idx int +} + +// MergeIterator supports iterating over two sets of labels while +// eliminating duplicate values from the combined set. The first +// iterator value takes precedence. +type MergeIterator struct { + one oneIterator + two oneIterator + current KeyValue +} + +type oneIterator struct { + iter Iterator + done bool + label KeyValue +} + +// Next moves the iterator to the next position. Returns false if there +// are no more labels. +func (i *Iterator) Next() bool { + i.idx++ + return i.idx < i.Len() +} + +// Label returns current KeyValue. Must be called only after Next returns +// true. +func (i *Iterator) Label() KeyValue { + kv, _ := i.storage.Get(i.idx) + return kv +} + +// Attribute is a synonym for Label(). +func (i *Iterator) Attribute() KeyValue { + return i.Label() +} + +// IndexedLabel returns current index and attribute. Must be called only +// after Next returns true. +func (i *Iterator) IndexedLabel() (int, KeyValue) { + return i.idx, i.Label() +} + +// Len returns a number of labels in the iterator's `*Set`. +func (i *Iterator) Len() int { + return i.storage.Len() +} + +// ToSlice is a convenience function that creates a slice of labels +// from the passed iterator. The iterator is set up to start from the +// beginning before creating the slice. +func (i *Iterator) ToSlice() []KeyValue { + l := i.Len() + if l == 0 { + return nil + } + i.idx = -1 + slice := make([]KeyValue, 0, l) + for i.Next() { + slice = append(slice, i.Label()) + } + return slice +} + +// NewMergeIterator returns a MergeIterator for merging two label sets +// Duplicates are resolved by taking the value from the first set. +func NewMergeIterator(s1, s2 *Set) MergeIterator { + mi := MergeIterator{ + one: makeOne(s1.Iter()), + two: makeOne(s2.Iter()), + } + return mi +} + +func makeOne(iter Iterator) oneIterator { + oi := oneIterator{ + iter: iter, + } + oi.advance() + return oi +} + +func (oi *oneIterator) advance() { + if oi.done = !oi.iter.Next(); !oi.done { + oi.label = oi.iter.Label() + } +} + +// Next returns true if there is another label available. +func (m *MergeIterator) Next() bool { + if m.one.done && m.two.done { + return false + } + if m.one.done { + m.current = m.two.label + m.two.advance() + return true + } + if m.two.done { + m.current = m.one.label + m.one.advance() + return true + } + if m.one.label.Key == m.two.label.Key { + m.current = m.one.label // first iterator label value wins + m.one.advance() + m.two.advance() + return true + } + if m.one.label.Key < m.two.label.Key { + m.current = m.one.label + m.one.advance() + return true + } + m.current = m.two.label + m.two.advance() + return true +} + +// Label returns the current value after Next() returns true. +func (m *MergeIterator) Label() KeyValue { + return m.current +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/key.go b/vendor/go.opentelemetry.io/otel/attribute/key.go new file mode 100644 index 00000000..0656a04e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/key.go @@ -0,0 +1,134 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +// Key represents the key part in key-value pairs. It's a string. The +// allowed character set in the key depends on the use of the key. +type Key string + +// Bool creates a KeyValue instance with a BOOL Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Bool(name, value). +func (k Key) Bool(v bool) KeyValue { + return KeyValue{ + Key: k, + Value: BoolValue(v), + } +} + +// BoolSlice creates a KeyValue instance with a BOOLSLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- BoolSlice(name, value). +func (k Key) BoolSlice(v []bool) KeyValue { + return KeyValue{ + Key: k, + Value: BoolSliceValue(v), + } +} + +// Int creates a KeyValue instance with an INT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int(name, value). +func (k Key) Int(v int) KeyValue { + return KeyValue{ + Key: k, + Value: IntValue(v), + } +} + +// IntSlice creates a KeyValue instance with an INT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- IntSlice(name, value). +func (k Key) IntSlice(v []int) KeyValue { + return KeyValue{ + Key: k, + Value: IntSliceValue(v), + } +} + +// Int64 creates a KeyValue instance with an INT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int64(name, value). +func (k Key) Int64(v int64) KeyValue { + return KeyValue{ + Key: k, + Value: Int64Value(v), + } +} + +// Int64Slice creates a KeyValue instance with an INT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Int64Slice(name, value). +func (k Key) Int64Slice(v []int64) KeyValue { + return KeyValue{ + Key: k, + Value: Int64SliceValue(v), + } +} + +// Float64 creates a KeyValue instance with a FLOAT64 Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Float64(name, value). +func (k Key) Float64(v float64) KeyValue { + return KeyValue{ + Key: k, + Value: Float64Value(v), + } +} + +// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Float64(name, value). +func (k Key) Float64Slice(v []float64) KeyValue { + return KeyValue{ + Key: k, + Value: Float64SliceValue(v), + } +} + +// String creates a KeyValue instance with a STRING Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- String(name, value). +func (k Key) String(v string) KeyValue { + return KeyValue{ + Key: k, + Value: StringValue(v), + } +} + +// StringSlice creates a KeyValue instance with a STRINGSLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- StringSlice(name, value). +func (k Key) StringSlice(v []string) KeyValue { + return KeyValue{ + Key: k, + Value: StringSliceValue(v), + } +} + +// Defined returns true for non-empty keys. +func (k Key) Defined() bool { + return len(k) != 0 +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/kv.go b/vendor/go.opentelemetry.io/otel/attribute/kv.go new file mode 100644 index 00000000..1ddf3ce0 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/kv.go @@ -0,0 +1,86 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "fmt" +) + +// KeyValue holds a key and value pair. +type KeyValue struct { + Key Key + Value Value +} + +// Valid returns if kv is a valid OpenTelemetry attribute. +func (kv KeyValue) Valid() bool { + return kv.Key.Defined() && kv.Value.Type() != INVALID +} + +// Bool creates a KeyValue with a BOOL Value type. +func Bool(k string, v bool) KeyValue { + return Key(k).Bool(v) +} + +// BoolSlice creates a KeyValue with a BOOLSLICE Value type. +func BoolSlice(k string, v []bool) KeyValue { + return Key(k).BoolSlice(v) +} + +// Int creates a KeyValue with an INT64 Value type. +func Int(k string, v int) KeyValue { + return Key(k).Int(v) +} + +// IntSlice creates a KeyValue with an INT64SLICE Value type. +func IntSlice(k string, v []int) KeyValue { + return Key(k).IntSlice(v) +} + +// Int64 creates a KeyValue with an INT64 Value type. +func Int64(k string, v int64) KeyValue { + return Key(k).Int64(v) +} + +// Int64Slice creates a KeyValue with an INT64SLICE Value type. +func Int64Slice(k string, v []int64) KeyValue { + return Key(k).Int64Slice(v) +} + +// Float64 creates a KeyValue with a FLOAT64 Value type. +func Float64(k string, v float64) KeyValue { + return Key(k).Float64(v) +} + +// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type. +func Float64Slice(k string, v []float64) KeyValue { + return Key(k).Float64Slice(v) +} + +// String creates a KeyValue with a STRING Value type. +func String(k, v string) KeyValue { + return Key(k).String(v) +} + +// StringSlice creates a KeyValue with a STRINGSLICE Value type. +func StringSlice(k string, v []string) KeyValue { + return Key(k).StringSlice(v) +} + +// Stringer creates a new key-value pair with a passed name and a string +// value generated by the passed Stringer interface. +func Stringer(k string, v fmt.Stringer) KeyValue { + return Key(k).String(v.String()) +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/set.go b/vendor/go.opentelemetry.io/otel/attribute/set.go new file mode 100644 index 00000000..a28f1435 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -0,0 +1,435 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "encoding/json" + "reflect" + "sort" +) + +type ( + // Set is the representation for a distinct label set. It + // manages an immutable set of labels, with an internal cache + // for storing label encodings. + // + // This type supports the `Equivalent` method of comparison + // using values of type `Distinct`. + // + // This type is used to implement: + // 1. Metric labels + // 2. Resource sets + // 3. Correlation map (TODO) + Set struct { + equivalent Distinct + } + + // Distinct wraps a variable-size array of `KeyValue`, + // constructed with keys in sorted order. This can be used as + // a map key or for equality checking between Sets. + Distinct struct { + iface interface{} + } + + // Filter supports removing certain labels from label sets. + // When the filter returns true, the label will be kept in + // the filtered label set. When the filter returns false, the + // label is excluded from the filtered label set, and the + // label instead appears in the `removed` list of excluded labels. + Filter func(KeyValue) bool + + // Sortable implements `sort.Interface`, used for sorting + // `KeyValue`. This is an exported type to support a + // memory optimization. A pointer to one of these is needed + // for the call to `sort.Stable()`, which the caller may + // provide in order to avoid an allocation. See + // `NewSetWithSortable()`. + Sortable []KeyValue +) + +var ( + // keyValueType is used in `computeDistinctReflect`. + keyValueType = reflect.TypeOf(KeyValue{}) + + // emptySet is returned for empty label sets. + emptySet = &Set{ + equivalent: Distinct{ + iface: [0]KeyValue{}, + }, + } +) + +// EmptySet returns a reference to a Set with no elements. +// +// This is a convenience provided for optimized calling utility. +func EmptySet() *Set { + return emptySet +} + +// reflect abbreviates `reflect.ValueOf`. +func (d Distinct) reflect() reflect.Value { + return reflect.ValueOf(d.iface) +} + +// Valid returns true if this value refers to a valid `*Set`. +func (d Distinct) Valid() bool { + return d.iface != nil +} + +// Len returns the number of labels in this set. +func (l *Set) Len() int { + if l == nil || !l.equivalent.Valid() { + return 0 + } + return l.equivalent.reflect().Len() +} + +// Get returns the KeyValue at ordered position `idx` in this set. +func (l *Set) Get(idx int) (KeyValue, bool) { + if l == nil { + return KeyValue{}, false + } + value := l.equivalent.reflect() + + if idx >= 0 && idx < value.Len() { + // Note: The Go compiler successfully avoids an allocation for + // the interface{} conversion here: + return value.Index(idx).Interface().(KeyValue), true + } + + return KeyValue{}, false +} + +// Value returns the value of a specified key in this set. +func (l *Set) Value(k Key) (Value, bool) { + if l == nil { + return Value{}, false + } + rValue := l.equivalent.reflect() + vlen := rValue.Len() + + idx := sort.Search(vlen, func(idx int) bool { + return rValue.Index(idx).Interface().(KeyValue).Key >= k + }) + if idx >= vlen { + return Value{}, false + } + keyValue := rValue.Index(idx).Interface().(KeyValue) + if k == keyValue.Key { + return keyValue.Value, true + } + return Value{}, false +} + +// HasValue tests whether a key is defined in this set. +func (l *Set) HasValue(k Key) bool { + if l == nil { + return false + } + _, ok := l.Value(k) + return ok +} + +// Iter returns an iterator for visiting the labels in this set. +func (l *Set) Iter() Iterator { + return Iterator{ + storage: l, + idx: -1, + } +} + +// ToSlice returns the set of labels belonging to this set, sorted, +// where keys appear no more than once. +func (l *Set) ToSlice() []KeyValue { + iter := l.Iter() + return iter.ToSlice() +} + +// Equivalent returns a value that may be used as a map key. The +// Distinct type guarantees that the result will equal the equivalent +// Distinct value of any label set with the same elements as this, +// where sets are made unique by choosing the last value in the input +// for any given key. +func (l *Set) Equivalent() Distinct { + if l == nil || !l.equivalent.Valid() { + return emptySet.equivalent + } + return l.equivalent +} + +// Equals returns true if the argument set is equivalent to this set. +func (l *Set) Equals(o *Set) bool { + return l.Equivalent() == o.Equivalent() +} + +// Encoded returns the encoded form of this set, according to +// `encoder`. +func (l *Set) Encoded(encoder Encoder) string { + if l == nil || encoder == nil { + return "" + } + + return encoder.Encode(l.Iter()) +} + +func empty() Set { + return Set{ + equivalent: emptySet.equivalent, + } +} + +// NewSet returns a new `Set`. See the documentation for +// `NewSetWithSortableFiltered` for more details. +// +// Except for empty sets, this method adds an additional allocation +// compared with calls that include a `*Sortable`. +func NewSet(kvs ...KeyValue) Set { + // Check for empty set. + if len(kvs) == 0 { + return empty() + } + s, _ := NewSetWithSortableFiltered(kvs, new(Sortable), nil) + return s +} + +// NewSetWithSortable returns a new `Set`. See the documentation for +// `NewSetWithSortableFiltered` for more details. +// +// This call includes a `*Sortable` option as a memory optimization. +func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { + // Check for empty set. + if len(kvs) == 0 { + return empty() + } + s, _ := NewSetWithSortableFiltered(kvs, tmp, nil) + return s +} + +// NewSetWithFiltered returns a new `Set`. See the documentation for +// `NewSetWithSortableFiltered` for more details. +// +// This call includes a `Filter` to include/exclude label keys from +// the return value. Excluded keys are returned as a slice of label +// values. +func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { + // Check for empty set. + if len(kvs) == 0 { + return empty(), nil + } + return NewSetWithSortableFiltered(kvs, new(Sortable), filter) +} + +// NewSetWithSortableFiltered returns a new `Set`. +// +// Duplicate keys are eliminated by taking the last value. This +// re-orders the input slice so that unique last-values are contiguous +// at the end of the slice. +// +// This ensures the following: +// +// - Last-value-wins semantics +// - Caller sees the reordering, but doesn't lose values +// - Repeated call preserve last-value wins. +// +// Note that methods are defined on `*Set`, although this returns `Set`. +// Callers can avoid memory allocations by: +// +// - allocating a `Sortable` for use as a temporary in this method +// - allocating a `Set` for storing the return value of this +// constructor. +// +// The result maintains a cache of encoded labels, by attribute.EncoderID. +// This value should not be copied after its first use. +// +// The second `[]KeyValue` return value is a list of labels that were +// excluded by the Filter (if non-nil). +func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) { + // Check for empty set. + if len(kvs) == 0 { + return empty(), nil + } + + *tmp = kvs + + // Stable sort so the following de-duplication can implement + // last-value-wins semantics. + sort.Stable(tmp) + + *tmp = nil + + position := len(kvs) - 1 + offset := position - 1 + + // The requirements stated above require that the stable + // result be placed in the end of the input slice, while + // overwritten values are swapped to the beginning. + // + // De-duplicate with last-value-wins semantics. Preserve + // duplicate values at the beginning of the input slice. + for ; offset >= 0; offset-- { + if kvs[offset].Key == kvs[position].Key { + continue + } + position-- + kvs[offset], kvs[position] = kvs[position], kvs[offset] + } + if filter != nil { + return filterSet(kvs[position:], filter) + } + return Set{ + equivalent: computeDistinct(kvs[position:]), + }, nil +} + +// filterSet reorders `kvs` so that included keys are contiguous at +// the end of the slice, while excluded keys precede the included keys. +func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) { + var excluded []KeyValue + + // Move labels that do not match the filter so + // they're adjacent before calling computeDistinct(). + distinctPosition := len(kvs) + + // Swap indistinct keys forward and distinct keys toward the + // end of the slice. + offset := len(kvs) - 1 + for ; offset >= 0; offset-- { + if filter(kvs[offset]) { + distinctPosition-- + kvs[offset], kvs[distinctPosition] = kvs[distinctPosition], kvs[offset] + continue + } + } + excluded = kvs[:distinctPosition] + + return Set{ + equivalent: computeDistinct(kvs[distinctPosition:]), + }, excluded +} + +// Filter returns a filtered copy of this `Set`. See the +// documentation for `NewSetWithSortableFiltered` for more details. +func (l *Set) Filter(re Filter) (Set, []KeyValue) { + if re == nil { + return Set{ + equivalent: l.equivalent, + }, nil + } + + // Note: This could be refactored to avoid the temporary slice + // allocation, if it proves to be expensive. + return filterSet(l.ToSlice(), re) +} + +// computeDistinct returns a `Distinct` using either the fixed- or +// reflect-oriented code path, depending on the size of the input. +// The input slice is assumed to already be sorted and de-duplicated. +func computeDistinct(kvs []KeyValue) Distinct { + iface := computeDistinctFixed(kvs) + if iface == nil { + iface = computeDistinctReflect(kvs) + } + return Distinct{ + iface: iface, + } +} + +// computeDistinctFixed computes a `Distinct` for small slices. It +// returns nil if the input is too large for this code path. +func computeDistinctFixed(kvs []KeyValue) interface{} { + switch len(kvs) { + case 1: + ptr := new([1]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 2: + ptr := new([2]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 3: + ptr := new([3]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 4: + ptr := new([4]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 5: + ptr := new([5]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 6: + ptr := new([6]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 7: + ptr := new([7]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 8: + ptr := new([8]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 9: + ptr := new([9]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + case 10: + ptr := new([10]KeyValue) + copy((*ptr)[:], kvs) + return *ptr + default: + return nil + } +} + +// computeDistinctReflect computes a `Distinct` using reflection, +// works for any size input. +func computeDistinctReflect(kvs []KeyValue) interface{} { + at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() + for i, keyValue := range kvs { + *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue + } + return at.Interface() +} + +// MarshalJSON returns the JSON encoding of the `*Set`. +func (l *Set) MarshalJSON() ([]byte, error) { + return json.Marshal(l.equivalent.iface) +} + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (l Set) MarshalLog() interface{} { + kvs := make(map[string]string) + for _, kv := range l.ToSlice() { + kvs[string(kv.Key)] = kv.Value.Emit() + } + return kvs +} + +// Len implements `sort.Interface`. +func (l *Sortable) Len() int { + return len(*l) +} + +// Swap implements `sort.Interface`. +func (l *Sortable) Swap(i, j int) { + (*l)[i], (*l)[j] = (*l)[j], (*l)[i] +} + +// Less implements `sort.Interface`. +func (l *Sortable) Less(i, j int) bool { + return (*l)[i].Key < (*l)[j].Key +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/type_string.go b/vendor/go.opentelemetry.io/otel/attribute/type_string.go new file mode 100644 index 00000000..e584b247 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/type_string.go @@ -0,0 +1,31 @@ +// Code generated by "stringer -type=Type"; DO NOT EDIT. + +package attribute + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[INVALID-0] + _ = x[BOOL-1] + _ = x[INT64-2] + _ = x[FLOAT64-3] + _ = x[STRING-4] + _ = x[BOOLSLICE-5] + _ = x[INT64SLICE-6] + _ = x[FLOAT64SLICE-7] + _ = x[STRINGSLICE-8] +} + +const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE" + +var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/value.go b/vendor/go.opentelemetry.io/otel/attribute/value.go new file mode 100644 index 00000000..6ec5cb29 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -0,0 +1,271 @@ +// Copyright The OpenTelemetry Authors +// +// 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 attribute // import "go.opentelemetry.io/otel/attribute" + +import ( + "encoding/json" + "fmt" + "strconv" + + "go.opentelemetry.io/otel/internal" +) + +//go:generate stringer -type=Type + +// Type describes the type of the data Value holds. +type Type int + +// Value represents the value part in key-value pairs. +type Value struct { + vtype Type + numeric uint64 + stringly string + slice interface{} +} + +const ( + // INVALID is used for a Value with no value set. + INVALID Type = iota + // BOOL is a boolean Type Value. + BOOL + // INT64 is a 64-bit signed integral Type Value. + INT64 + // FLOAT64 is a 64-bit floating point Type Value. + FLOAT64 + // STRING is a string Type Value. + STRING + // BOOLSLICE is a slice of booleans Type Value. + BOOLSLICE + // INT64SLICE is a slice of 64-bit signed integral numbers Type Value. + INT64SLICE + // FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value. + FLOAT64SLICE + // STRINGSLICE is a slice of strings Type Value. + STRINGSLICE +) + +// BoolValue creates a BOOL Value. +func BoolValue(v bool) Value { + return Value{ + vtype: BOOL, + numeric: internal.BoolToRaw(v), + } +} + +// BoolSliceValue creates a BOOLSLICE Value. +func BoolSliceValue(v []bool) Value { + cp := make([]bool, len(v)) + copy(cp, v) + return Value{ + vtype: BOOLSLICE, + slice: &cp, + } +} + +// IntValue creates an INT64 Value. +func IntValue(v int) Value { + return Int64Value(int64(v)) +} + +// IntSliceValue creates an INTSLICE Value. +func IntSliceValue(v []int) Value { + cp := make([]int64, 0, len(v)) + for _, i := range v { + cp = append(cp, int64(i)) + } + return Value{ + vtype: INT64SLICE, + slice: &cp, + } +} + +// Int64Value creates an INT64 Value. +func Int64Value(v int64) Value { + return Value{ + vtype: INT64, + numeric: internal.Int64ToRaw(v), + } +} + +// Int64SliceValue creates an INT64SLICE Value. +func Int64SliceValue(v []int64) Value { + cp := make([]int64, len(v)) + copy(cp, v) + return Value{ + vtype: INT64SLICE, + slice: &cp, + } +} + +// Float64Value creates a FLOAT64 Value. +func Float64Value(v float64) Value { + return Value{ + vtype: FLOAT64, + numeric: internal.Float64ToRaw(v), + } +} + +// Float64SliceValue creates a FLOAT64SLICE Value. +func Float64SliceValue(v []float64) Value { + cp := make([]float64, len(v)) + copy(cp, v) + return Value{ + vtype: FLOAT64SLICE, + slice: &cp, + } +} + +// StringValue creates a STRING Value. +func StringValue(v string) Value { + return Value{ + vtype: STRING, + stringly: v, + } +} + +// StringSliceValue creates a STRINGSLICE Value. +func StringSliceValue(v []string) Value { + cp := make([]string, len(v)) + copy(cp, v) + return Value{ + vtype: STRINGSLICE, + slice: &cp, + } +} + +// Type returns a type of the Value. +func (v Value) Type() Type { + return v.vtype +} + +// AsBool returns the bool value. Make sure that the Value's type is +// BOOL. +func (v Value) AsBool() bool { + return internal.RawToBool(v.numeric) +} + +// AsBoolSlice returns the []bool value. Make sure that the Value's type is +// BOOLSLICE. +func (v Value) AsBoolSlice() []bool { + if s, ok := v.slice.(*[]bool); ok { + return *s + } + return nil +} + +// AsInt64 returns the int64 value. Make sure that the Value's type is +// INT64. +func (v Value) AsInt64() int64 { + return internal.RawToInt64(v.numeric) +} + +// AsInt64Slice returns the []int64 value. Make sure that the Value's type is +// INT64SLICE. +func (v Value) AsInt64Slice() []int64 { + if s, ok := v.slice.(*[]int64); ok { + return *s + } + return nil +} + +// AsFloat64 returns the float64 value. Make sure that the Value's +// type is FLOAT64. +func (v Value) AsFloat64() float64 { + return internal.RawToFloat64(v.numeric) +} + +// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is +// FLOAT64SLICE. +func (v Value) AsFloat64Slice() []float64 { + if s, ok := v.slice.(*[]float64); ok { + return *s + } + return nil +} + +// AsString returns the string value. Make sure that the Value's type +// is STRING. +func (v Value) AsString() string { + return v.stringly +} + +// AsStringSlice returns the []string value. Make sure that the Value's type is +// STRINGSLICE. +func (v Value) AsStringSlice() []string { + if s, ok := v.slice.(*[]string); ok { + return *s + } + return nil +} + +type unknownValueType struct{} + +// AsInterface returns Value's data as interface{}. +func (v Value) AsInterface() interface{} { + switch v.Type() { + case BOOL: + return v.AsBool() + case BOOLSLICE: + return v.AsBoolSlice() + case INT64: + return v.AsInt64() + case INT64SLICE: + return v.AsInt64Slice() + case FLOAT64: + return v.AsFloat64() + case FLOAT64SLICE: + return v.AsFloat64Slice() + case STRING: + return v.stringly + case STRINGSLICE: + return v.AsStringSlice() + } + return unknownValueType{} +} + +// Emit returns a string representation of Value's data. +func (v Value) Emit() string { + switch v.Type() { + case BOOLSLICE: + return fmt.Sprint(*(v.slice.(*[]bool))) + case BOOL: + return strconv.FormatBool(v.AsBool()) + case INT64SLICE: + return fmt.Sprint(*(v.slice.(*[]int64))) + case INT64: + return strconv.FormatInt(v.AsInt64(), 10) + case FLOAT64SLICE: + return fmt.Sprint(*(v.slice.(*[]float64))) + case FLOAT64: + return fmt.Sprint(v.AsFloat64()) + case STRINGSLICE: + return fmt.Sprint(*(v.slice.(*[]string))) + case STRING: + return v.stringly + default: + return "unknown" + } +} + +// MarshalJSON returns the JSON encoding of the Value. +func (v Value) MarshalJSON() ([]byte, error) { + var jsonVal struct { + Type string + Value interface{} + } + jsonVal.Type = v.Type().String() + jsonVal.Value = v.AsInterface() + return json.Marshal(jsonVal) +} diff --git a/vendor/go.opentelemetry.io/otel/baggage/baggage.go b/vendor/go.opentelemetry.io/otel/baggage/baggage.go new file mode 100644 index 00000000..824c67b2 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/baggage/baggage.go @@ -0,0 +1,556 @@ +// Copyright The OpenTelemetry Authors +// +// 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 baggage // import "go.opentelemetry.io/otel/baggage" + +import ( + "errors" + "fmt" + "net/url" + "regexp" + "strings" + + "go.opentelemetry.io/otel/internal/baggage" +) + +const ( + maxMembers = 180 + maxBytesPerMembers = 4096 + maxBytesPerBaggageString = 8192 + + listDelimiter = "," + keyValueDelimiter = "=" + propertyDelimiter = ";" + + keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)` + valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)` + keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*` +) + +var ( + keyRe = regexp.MustCompile(`^` + keyDef + `$`) + valueRe = regexp.MustCompile(`^` + valueDef + `$`) + propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`) +) + +var ( + errInvalidKey = errors.New("invalid key") + errInvalidValue = errors.New("invalid value") + errInvalidProperty = errors.New("invalid baggage list-member property") + errInvalidMember = errors.New("invalid baggage list-member") + errMemberNumber = errors.New("too many list-members in baggage-string") + errMemberBytes = errors.New("list-member too large") + errBaggageBytes = errors.New("baggage-string too large") +) + +// Property is an additional metadata entry for a baggage list-member. +type Property struct { + key, value string + + // hasValue indicates if a zero-value value means the property does not + // have a value or if it was the zero-value. + hasValue bool + + // hasData indicates whether the created property contains data or not. + // Properties that do not contain data are invalid with no other check + // required. + hasData bool +} + +func NewKeyProperty(key string) (Property, error) { + if !keyRe.MatchString(key) { + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) + } + + p := Property{key: key, hasData: true} + return p, nil +} + +func NewKeyValueProperty(key, value string) (Property, error) { + if !keyRe.MatchString(key) { + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) + } + if !valueRe.MatchString(value) { + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value) + } + + p := Property{ + key: key, + value: value, + hasValue: true, + hasData: true, + } + return p, nil +} + +func newInvalidProperty() Property { + return Property{} +} + +// parseProperty attempts to decode a Property from the passed string. It +// returns an error if the input is invalid according to the W3C Baggage +// specification. +func parseProperty(property string) (Property, error) { + if property == "" { + return newInvalidProperty(), nil + } + + match := propertyRe.FindStringSubmatch(property) + if len(match) != 4 { + return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property) + } + + p := Property{hasData: true} + if match[1] != "" { + p.key = match[1] + } else { + p.key = match[2] + p.value = match[3] + p.hasValue = true + } + + return p, nil +} + +// validate ensures p conforms to the W3C Baggage specification, returning an +// error otherwise. +func (p Property) validate() error { + errFunc := func(err error) error { + return fmt.Errorf("invalid property: %w", err) + } + + if !p.hasData { + return errFunc(fmt.Errorf("%w: %q", errInvalidProperty, p)) + } + + if !keyRe.MatchString(p.key) { + return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key)) + } + if p.hasValue && !valueRe.MatchString(p.value) { + return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value)) + } + if !p.hasValue && p.value != "" { + return errFunc(errors.New("inconsistent value")) + } + return nil +} + +// Key returns the Property key. +func (p Property) Key() string { + return p.key +} + +// Value returns the Property value. Additionally a boolean value is returned +// indicating if the returned value is the empty if the Property has a value +// that is empty or if the value is not set. +func (p Property) Value() (string, bool) { + return p.value, p.hasValue +} + +// String encodes Property into a string compliant with the W3C Baggage +// specification. +func (p Property) String() string { + if p.hasValue { + return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value) + } + return p.key +} + +type properties []Property + +func fromInternalProperties(iProps []baggage.Property) properties { + if len(iProps) == 0 { + return nil + } + + props := make(properties, len(iProps)) + for i, p := range iProps { + props[i] = Property{ + key: p.Key, + value: p.Value, + hasValue: p.HasValue, + } + } + return props +} + +func (p properties) asInternal() []baggage.Property { + if len(p) == 0 { + return nil + } + + iProps := make([]baggage.Property, len(p)) + for i, prop := range p { + iProps[i] = baggage.Property{ + Key: prop.key, + Value: prop.value, + HasValue: prop.hasValue, + } + } + return iProps +} + +func (p properties) Copy() properties { + if len(p) == 0 { + return nil + } + + props := make(properties, len(p)) + copy(props, p) + return props +} + +// validate ensures each Property in p conforms to the W3C Baggage +// specification, returning an error otherwise. +func (p properties) validate() error { + for _, prop := range p { + if err := prop.validate(); err != nil { + return err + } + } + return nil +} + +// String encodes properties into a string compliant with the W3C Baggage +// specification. +func (p properties) String() string { + props := make([]string, len(p)) + for i, prop := range p { + props[i] = prop.String() + } + return strings.Join(props, propertyDelimiter) +} + +// Member is a list-member of a baggage-string as defined by the W3C Baggage +// specification. +type Member struct { + key, value string + properties properties + + // hasData indicates whether the created property contains data or not. + // Properties that do not contain data are invalid with no other check + // required. + hasData bool +} + +// NewMember returns a new Member from the passed arguments. An error is +// returned if the created Member would be invalid according to the W3C +// Baggage specification. +func NewMember(key, value string, props ...Property) (Member, error) { + m := Member{ + key: key, + value: value, + properties: properties(props).Copy(), + hasData: true, + } + if err := m.validate(); err != nil { + return newInvalidMember(), err + } + + return m, nil +} + +func newInvalidMember() Member { + return Member{} +} + +// parseMember attempts to decode a Member from the passed string. It returns +// an error if the input is invalid according to the W3C Baggage +// specification. +func parseMember(member string) (Member, error) { + if n := len(member); n > maxBytesPerMembers { + return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n) + } + + var ( + key, value string + props properties + ) + + parts := strings.SplitN(member, propertyDelimiter, 2) + switch len(parts) { + case 2: + // Parse the member properties. + for _, pStr := range strings.Split(parts[1], propertyDelimiter) { + p, err := parseProperty(pStr) + if err != nil { + return newInvalidMember(), err + } + props = append(props, p) + } + fallthrough + case 1: + // Parse the member key/value pair. + + // Take into account a value can contain equal signs (=). + kv := strings.SplitN(parts[0], keyValueDelimiter, 2) + if len(kv) != 2 { + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member) + } + // "Leading and trailing whitespaces are allowed but MUST be trimmed + // when converting the header into a data structure." + key = strings.TrimSpace(kv[0]) + var err error + value, err = url.QueryUnescape(strings.TrimSpace(kv[1])) + if err != nil { + return newInvalidMember(), fmt.Errorf("%w: %q", err, value) + } + if !keyRe.MatchString(key) { + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key) + } + if !valueRe.MatchString(value) { + return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value) + } + default: + // This should never happen unless a developer has changed the string + // splitting somehow. Panic instead of failing silently and allowing + // the bug to slip past the CI checks. + panic("failed to parse baggage member") + } + + return Member{key: key, value: value, properties: props, hasData: true}, nil +} + +// validate ensures m conforms to the W3C Baggage specification, returning an +// error otherwise. +func (m Member) validate() error { + if !m.hasData { + return fmt.Errorf("%w: %q", errInvalidMember, m) + } + + if !keyRe.MatchString(m.key) { + return fmt.Errorf("%w: %q", errInvalidKey, m.key) + } + if !valueRe.MatchString(m.value) { + return fmt.Errorf("%w: %q", errInvalidValue, m.value) + } + return m.properties.validate() +} + +// Key returns the Member key. +func (m Member) Key() string { return m.key } + +// Value returns the Member value. +func (m Member) Value() string { return m.value } + +// Properties returns a copy of the Member properties. +func (m Member) Properties() []Property { return m.properties.Copy() } + +// String encodes Member into a string compliant with the W3C Baggage +// specification. +func (m Member) String() string { + // A key is just an ASCII string, but a value is URL encoded UTF-8. + s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, url.QueryEscape(m.value)) + if len(m.properties) > 0 { + s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String()) + } + return s +} + +// Baggage is a list of baggage members representing the baggage-string as +// defined by the W3C Baggage specification. +type Baggage struct { //nolint:golint + list baggage.List +} + +// New returns a new valid Baggage. It returns an error if it results in a +// Baggage exceeding limits set in that specification. +// +// It expects all the provided members to have already been validated. +func New(members ...Member) (Baggage, error) { + if len(members) == 0 { + return Baggage{}, nil + } + + b := make(baggage.List) + for _, m := range members { + if !m.hasData { + return Baggage{}, errInvalidMember + } + + // OpenTelemetry resolves duplicates by last-one-wins. + b[m.key] = baggage.Item{ + Value: m.value, + Properties: m.properties.asInternal(), + } + } + + // Check member numbers after deduplicating. + if len(b) > maxMembers { + return Baggage{}, errMemberNumber + } + + bag := Baggage{b} + if n := len(bag.String()); n > maxBytesPerBaggageString { + return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) + } + + return bag, nil +} + +// Parse attempts to decode a baggage-string from the passed string. It +// returns an error if the input is invalid according to the W3C Baggage +// specification. +// +// If there are duplicate list-members contained in baggage, the last one +// defined (reading left-to-right) will be the only one kept. This diverges +// from the W3C Baggage specification which allows duplicate list-members, but +// conforms to the OpenTelemetry Baggage specification. +func Parse(bStr string) (Baggage, error) { + if bStr == "" { + return Baggage{}, nil + } + + if n := len(bStr); n > maxBytesPerBaggageString { + return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) + } + + b := make(baggage.List) + for _, memberStr := range strings.Split(bStr, listDelimiter) { + m, err := parseMember(memberStr) + if err != nil { + return Baggage{}, err + } + // OpenTelemetry resolves duplicates by last-one-wins. + b[m.key] = baggage.Item{ + Value: m.value, + Properties: m.properties.asInternal(), + } + } + + // OpenTelemetry does not allow for duplicate list-members, but the W3C + // specification does. Now that we have deduplicated, ensure the baggage + // does not exceed list-member limits. + if len(b) > maxMembers { + return Baggage{}, errMemberNumber + } + + return Baggage{b}, nil +} + +// Member returns the baggage list-member identified by key. +// +// If there is no list-member matching the passed key the returned Member will +// be a zero-value Member. +// The returned member is not validated, as we assume the validation happened +// when it was added to the Baggage. +func (b Baggage) Member(key string) Member { + v, ok := b.list[key] + if !ok { + // We do not need to worry about distiguising between the situation + // where a zero-valued Member is included in the Baggage because a + // zero-valued Member is invalid according to the W3C Baggage + // specification (it has an empty key). + return newInvalidMember() + } + + return Member{ + key: key, + value: v.Value, + properties: fromInternalProperties(v.Properties), + } +} + +// Members returns all the baggage list-members. +// The order of the returned list-members does not have significance. +// +// The returned members are not validated, as we assume the validation happened +// when they were added to the Baggage. +func (b Baggage) Members() []Member { + if len(b.list) == 0 { + return nil + } + + members := make([]Member, 0, len(b.list)) + for k, v := range b.list { + members = append(members, Member{ + key: k, + value: v.Value, + properties: fromInternalProperties(v.Properties), + }) + } + return members +} + +// SetMember returns a copy the Baggage with the member included. If the +// baggage contains a Member with the same key the existing Member is +// replaced. +// +// If member is invalid according to the W3C Baggage specification, an error +// is returned with the original Baggage. +func (b Baggage) SetMember(member Member) (Baggage, error) { + if !member.hasData { + return b, errInvalidMember + } + + n := len(b.list) + if _, ok := b.list[member.key]; !ok { + n++ + } + list := make(baggage.List, n) + + for k, v := range b.list { + // Do not copy if we are just going to overwrite. + if k == member.key { + continue + } + list[k] = v + } + + list[member.key] = baggage.Item{ + Value: member.value, + Properties: member.properties.asInternal(), + } + + return Baggage{list: list}, nil +} + +// DeleteMember returns a copy of the Baggage with the list-member identified +// by key removed. +func (b Baggage) DeleteMember(key string) Baggage { + n := len(b.list) + if _, ok := b.list[key]; ok { + n-- + } + list := make(baggage.List, n) + + for k, v := range b.list { + if k == key { + continue + } + list[k] = v + } + + return Baggage{list: list} +} + +// Len returns the number of list-members in the Baggage. +func (b Baggage) Len() int { + return len(b.list) +} + +// String encodes Baggage into a string compliant with the W3C Baggage +// specification. The returned string will be invalid if the Baggage contains +// any invalid list-members. +func (b Baggage) String() string { + members := make([]string, 0, len(b.list)) + for k, v := range b.list { + members = append(members, Member{ + key: k, + value: v.Value, + properties: fromInternalProperties(v.Properties), + }.String()) + } + return strings.Join(members, listDelimiter) +} diff --git a/vendor/go.opentelemetry.io/otel/baggage/context.go b/vendor/go.opentelemetry.io/otel/baggage/context.go new file mode 100644 index 00000000..24b34b75 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/baggage/context.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// +// 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 baggage // import "go.opentelemetry.io/otel/baggage" + +import ( + "context" + + "go.opentelemetry.io/otel/internal/baggage" +) + +// ContextWithBaggage returns a copy of parent with baggage. +func ContextWithBaggage(parent context.Context, b Baggage) context.Context { + // Delegate so any hooks for the OpenTracing bridge are handled. + return baggage.ContextWithList(parent, b.list) +} + +// ContextWithoutBaggage returns a copy of parent with no baggage. +func ContextWithoutBaggage(parent context.Context) context.Context { + // Delegate so any hooks for the OpenTracing bridge are handled. + return baggage.ContextWithList(parent, nil) +} + +// FromContext returns the baggage contained in ctx. +func FromContext(ctx context.Context) Baggage { + // Delegate so any hooks for the OpenTracing bridge are handled. + return Baggage{list: baggage.ListFromContext(ctx)} +} diff --git a/vendor/go.opentelemetry.io/otel/baggage/doc.go b/vendor/go.opentelemetry.io/otel/baggage/doc.go new file mode 100644 index 00000000..4545100d --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/baggage/doc.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// +// 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 baggage provides functionality for storing and retrieving +baggage items in Go context. For propagating the baggage, see the +go.opentelemetry.io/otel/propagation package. +*/ +package baggage // import "go.opentelemetry.io/otel/baggage" diff --git a/vendor/go.opentelemetry.io/otel/codes/codes.go b/vendor/go.opentelemetry.io/otel/codes/codes.go new file mode 100644 index 00000000..064a9279 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/codes/codes.go @@ -0,0 +1,106 @@ +// Copyright The OpenTelemetry Authors +// +// 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 codes // import "go.opentelemetry.io/otel/codes" + +import ( + "encoding/json" + "fmt" + "strconv" +) + +const ( + // Unset is the default status code. + Unset Code = 0 + // Error indicates the operation contains an error. + Error Code = 1 + // Ok indicates operation has been validated by an Application developers + // or Operator to have completed successfully, or contain no error. + Ok Code = 2 + + maxCode = 3 +) + +// Code is an 32-bit representation of a status state. +type Code uint32 + +var codeToStr = map[Code]string{ + Unset: "Unset", + Error: "Error", + Ok: "Ok", +} + +var strToCode = map[string]Code{ + `"Unset"`: Unset, + `"Error"`: Error, + `"Ok"`: Ok, +} + +// String returns the Code as a string. +func (c Code) String() string { + return codeToStr[c] +} + +// UnmarshalJSON unmarshals b into the Code. +// +// This is based on the functionality in the gRPC codes package: +// https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244 +func (c *Code) UnmarshalJSON(b []byte) error { + // From json.Unmarshaler: By convention, to approximate the behavior of + // Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as + // a no-op. + if string(b) == "null" { + return nil + } + if c == nil { + return fmt.Errorf("nil receiver passed to UnmarshalJSON") + } + + var x interface{} + if err := json.Unmarshal(b, &x); err != nil { + return err + } + switch x.(type) { + case string: + if jc, ok := strToCode[string(b)]; ok { + *c = jc + return nil + } + return fmt.Errorf("invalid code: %q", string(b)) + case float64: + if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil { + if ci >= maxCode { + return fmt.Errorf("invalid code: %q", ci) + } + + *c = Code(ci) + return nil + } + return fmt.Errorf("invalid code: %q", string(b)) + default: + return fmt.Errorf("invalid code: %q", string(b)) + } +} + +// MarshalJSON returns c as the JSON encoding of c. +func (c *Code) MarshalJSON() ([]byte, error) { + if c == nil { + return []byte("null"), nil + } + str, ok := codeToStr[*c] + if !ok { + return nil, fmt.Errorf("invalid code: %d", *c) + } + return []byte(fmt.Sprintf("%q", str)), nil +} diff --git a/vendor/go.opentelemetry.io/otel/codes/doc.go b/vendor/go.opentelemetry.io/otel/codes/doc.go new file mode 100644 index 00000000..df3e0f1b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/codes/doc.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// +// 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 codes defines the canonical error codes used by OpenTelemetry. + +It conforms to [the OpenTelemetry +specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#statuscanonicalcode). +*/ +package codes // import "go.opentelemetry.io/otel/codes" diff --git a/vendor/go.opentelemetry.io/otel/doc.go b/vendor/go.opentelemetry.io/otel/doc.go new file mode 100644 index 00000000..daa36c89 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/doc.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel provides global access to the OpenTelemetry API. The subpackages of +the otel package provide an implementation of the OpenTelemetry API. + +The provided API is used to instrument code and measure data about that code's +performance and operation. The measured data, by default, is not processed or +transmitted anywhere. An implementation of the OpenTelemetry SDK, like the +default SDK implementation (go.opentelemetry.io/otel/sdk), and associated +exporters are used to process and transport this data. + +To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/. + +To read more about tracing, see go.opentelemetry.io/otel/trace. + +To read more about metrics, see go.opentelemetry.io/otel/metric. + +To read more about propagation, see go.opentelemetry.io/otel/propagation and +go.opentelemetry.io/otel/baggage. +*/ +package otel // import "go.opentelemetry.io/otel" diff --git a/vendor/go.opentelemetry.io/otel/error_handler.go b/vendor/go.opentelemetry.io/otel/error_handler.go new file mode 100644 index 00000000..72fad854 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/error_handler.go @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +// ErrorHandler handles irremediable events. +type ErrorHandler interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Handle handles any error deemed irremediable by an OpenTelemetry + // component. + Handle(error) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +// ErrorHandlerFunc is a convenience adapter to allow the use of a function +// as an ErrorHandler. +type ErrorHandlerFunc func(error) + +var _ ErrorHandler = ErrorHandlerFunc(nil) + +// Handle handles the irremediable error by calling the ErrorHandlerFunc itself. +func (f ErrorHandlerFunc) Handle(err error) { + f(err) +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/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/go.opentelemetry.io/otel/exporters/otlp/otlptrace/README.md b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/README.md new file mode 100644 index 00000000..ca91fd4f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/README.md @@ -0,0 +1,51 @@ +# OpenTelemetry-Go OTLP Span Exporter + +[![Go Reference](https://pkg.go.dev/badge/go.opentelemetry.io/otel/exporters/otlp/otlptrace.svg)](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace) + +[OpenTelemetry Protocol Exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.5.0/specification/protocol/exporter.md) implementation. + +## Installation + +``` +go get -u go.opentelemetry.io/otel/exporters/otlp/otlptrace +``` + +## Examples + +- [Exporter setup and examples](./otlptracehttp/example_test.go) +- [Full example sending telemetry to a local collector](../../../example/otel-collector) + +## [`otlptrace`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace) + +The `otlptrace` package provides an exporter implementing the OTel span exporter interface. +This exporter is configured using a client satisfying the `otlptrace.Client` interface. +This client handles the transformation of data into wire format and the transmission of that data to the collector. + +## [`otlptracegrpc`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc) + +The `otlptracegrpc` package implements a client for the span exporter that sends trace telemetry data to the collector using gRPC with protobuf-encoded payloads. + +## [`otlptracehttp`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp) + +The `otlptracehttp` package implements a client for the span exporter that sends trace telemetry data to the collector using HTTP with protobuf-encoded payloads. + +## Configuration + +### Environment Variables + +The following environment variables can be used (instead of options objects) to +override the default configuration. For more information about how each of +these environment variables is interpreted, see [the OpenTelemetry +specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/protocol/exporter.md). + +| Environment variable | Option | Default value | +| ------------------------------------------------------------------------ |------------------------------ | -------------------------------------------------------- | +| `OTEL_EXPORTER_OTLP_ENDPOINT` `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `WithEndpoint` `WithInsecure` | `https://localhost:4317` or `https://localhost:4318`[^1] | +| `OTEL_EXPORTER_OTLP_CERTIFICATE` `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` | `WithTLSClientConfig` | | +| `OTEL_EXPORTER_OTLP_HEADERS` `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `WithHeaders` | | +| `OTEL_EXPORTER_OTLP_COMPRESSION` `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` | `WithCompression` | | +| `OTEL_EXPORTER_OTLP_TIMEOUT` `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `WithTimeout` | `10s` | + +[^1]: The gRPC client defaults to `https://localhost:4317` and the HTTP client `https://localhost:4318`. + +Configuration using options have precedence over the environment variables. diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/clients.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/clients.go new file mode 100644 index 00000000..dbb40cf5 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/clients.go @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + +import ( + "context" + + tracepb "go.opentelemetry.io/proto/otlp/trace/v1" +) + +// Client manages connections to the collector, handles the +// transformation of data into wire format, and the transmission of that +// data to the collector. +type Client interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Start should establish connection(s) to endpoint(s). It is + // called just once by the exporter, so the implementation + // does not need to worry about idempotence and locking. + Start(ctx context.Context) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Stop should close the connections. The function is called + // only once by the exporter, so the implementation does not + // need to worry about idempotence, but it may be called + // concurrently with UploadTraces, so proper + // locking is required. The function serves as a + // synchronization point - after the function returns, the + // process of closing connections is assumed to be finished. + Stop(ctx context.Context) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // UploadTraces should transform the passed traces to the wire + // format and send it to the collector. May be called + // concurrently. + UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go new file mode 100644 index 00000000..c5ee6c09 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/exporter.go @@ -0,0 +1,113 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + +import ( + "context" + "errors" + "sync" + + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" + tracesdk "go.opentelemetry.io/otel/sdk/trace" +) + +var ( + errAlreadyStarted = errors.New("already started") +) + +// Exporter exports trace data in the OTLP wire format. +type Exporter struct { + client Client + + mu sync.RWMutex + started bool + + startOnce sync.Once + stopOnce sync.Once +} + +// ExportSpans exports a batch of spans. +func (e *Exporter) ExportSpans(ctx context.Context, ss []tracesdk.ReadOnlySpan) error { + protoSpans := tracetransform.Spans(ss) + if len(protoSpans) == 0 { + return nil + } + + return e.client.UploadTraces(ctx, protoSpans) +} + +// Start establishes a connection to the receiving endpoint. +func (e *Exporter) Start(ctx context.Context) error { + var err = errAlreadyStarted + e.startOnce.Do(func() { + e.mu.Lock() + e.started = true + e.mu.Unlock() + err = e.client.Start(ctx) + }) + + return err +} + +// Shutdown flushes all exports and closes all connections to the receiving endpoint. +func (e *Exporter) Shutdown(ctx context.Context) error { + e.mu.RLock() + started := e.started + e.mu.RUnlock() + + if !started { + return nil + } + + var err error + + e.stopOnce.Do(func() { + err = e.client.Stop(ctx) + e.mu.Lock() + e.started = false + e.mu.Unlock() + }) + + return err +} + +var _ tracesdk.SpanExporter = (*Exporter)(nil) + +// New constructs a new Exporter and starts it. +func New(ctx context.Context, client Client) (*Exporter, error) { + exp := NewUnstarted(client) + if err := exp.Start(ctx); err != nil { + return nil, err + } + return exp, nil +} + +// NewUnstarted constructs a new Exporter and does not start it. +func NewUnstarted(client Client) *Exporter { + return &Exporter{ + client: client, + } +} + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (e *Exporter) MarshalLog() interface{} { + return struct { + Type string + Client Client + }{ + Type: "otlptrace", + Client: e.client, + } +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go new file mode 100644 index 00000000..d9086a39 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go @@ -0,0 +1,158 @@ +// Copyright The OpenTelemetry Authors +// +// 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 tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" + commonpb "go.opentelemetry.io/proto/otlp/common/v1" +) + +// KeyValues transforms a slice of attribute KeyValues into OTLP key-values. +func KeyValues(attrs []attribute.KeyValue) []*commonpb.KeyValue { + if len(attrs) == 0 { + return nil + } + + out := make([]*commonpb.KeyValue, 0, len(attrs)) + for _, kv := range attrs { + out = append(out, KeyValue(kv)) + } + return out +} + +// Iterator transforms an attribute iterator into OTLP key-values. +func Iterator(iter attribute.Iterator) []*commonpb.KeyValue { + l := iter.Len() + if l == 0 { + return nil + } + + out := make([]*commonpb.KeyValue, 0, l) + for iter.Next() { + out = append(out, KeyValue(iter.Attribute())) + } + return out +} + +// ResourceAttributes transforms a Resource OTLP key-values. +func ResourceAttributes(resource *resource.Resource) []*commonpb.KeyValue { + return Iterator(resource.Iter()) +} + +// KeyValue transforms an attribute KeyValue into an OTLP key-value. +func KeyValue(kv attribute.KeyValue) *commonpb.KeyValue { + return &commonpb.KeyValue{Key: string(kv.Key), Value: Value(kv.Value)} +} + +// Value transforms an attribute Value into an OTLP AnyValue. +func Value(v attribute.Value) *commonpb.AnyValue { + av := new(commonpb.AnyValue) + switch v.Type() { + case attribute.BOOL: + av.Value = &commonpb.AnyValue_BoolValue{ + BoolValue: v.AsBool(), + } + case attribute.BOOLSLICE: + av.Value = &commonpb.AnyValue_ArrayValue{ + ArrayValue: &commonpb.ArrayValue{ + Values: boolSliceValues(v.AsBoolSlice()), + }, + } + case attribute.INT64: + av.Value = &commonpb.AnyValue_IntValue{ + IntValue: v.AsInt64(), + } + case attribute.INT64SLICE: + av.Value = &commonpb.AnyValue_ArrayValue{ + ArrayValue: &commonpb.ArrayValue{ + Values: int64SliceValues(v.AsInt64Slice()), + }, + } + case attribute.FLOAT64: + av.Value = &commonpb.AnyValue_DoubleValue{ + DoubleValue: v.AsFloat64(), + } + case attribute.FLOAT64SLICE: + av.Value = &commonpb.AnyValue_ArrayValue{ + ArrayValue: &commonpb.ArrayValue{ + Values: float64SliceValues(v.AsFloat64Slice()), + }, + } + case attribute.STRING: + av.Value = &commonpb.AnyValue_StringValue{ + StringValue: v.AsString(), + } + case attribute.STRINGSLICE: + av.Value = &commonpb.AnyValue_ArrayValue{ + ArrayValue: &commonpb.ArrayValue{ + Values: stringSliceValues(v.AsStringSlice()), + }, + } + default: + av.Value = &commonpb.AnyValue_StringValue{ + StringValue: "INVALID", + } + } + return av +} + +func boolSliceValues(vals []bool) []*commonpb.AnyValue { + converted := make([]*commonpb.AnyValue, len(vals)) + for i, v := range vals { + converted[i] = &commonpb.AnyValue{ + Value: &commonpb.AnyValue_BoolValue{ + BoolValue: v, + }, + } + } + return converted +} + +func int64SliceValues(vals []int64) []*commonpb.AnyValue { + converted := make([]*commonpb.AnyValue, len(vals)) + for i, v := range vals { + converted[i] = &commonpb.AnyValue{ + Value: &commonpb.AnyValue_IntValue{ + IntValue: v, + }, + } + } + return converted +} + +func float64SliceValues(vals []float64) []*commonpb.AnyValue { + converted := make([]*commonpb.AnyValue, len(vals)) + for i, v := range vals { + converted[i] = &commonpb.AnyValue{ + Value: &commonpb.AnyValue_DoubleValue{ + DoubleValue: v, + }, + } + } + return converted +} + +func stringSliceValues(vals []string) []*commonpb.AnyValue { + converted := make([]*commonpb.AnyValue, len(vals)) + for i, v := range vals { + converted[i] = &commonpb.AnyValue{ + Value: &commonpb.AnyValue_StringValue{ + StringValue: v, + }, + } + } + return converted +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/instrumentation.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/instrumentation.go new file mode 100644 index 00000000..213f9f92 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/instrumentation.go @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// +// 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 tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" + +import ( + "go.opentelemetry.io/otel/sdk/instrumentation" + commonpb "go.opentelemetry.io/proto/otlp/common/v1" +) + +func InstrumentationScope(il instrumentation.Library) *commonpb.InstrumentationScope { + if il == (instrumentation.Library{}) { + return nil + } + return &commonpb.InstrumentationScope{ + Name: il.Name, + Version: il.Version, + } +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/resource.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/resource.go new file mode 100644 index 00000000..05a1f78a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/resource.go @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// +// 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 tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" + +import ( + "go.opentelemetry.io/otel/sdk/resource" + resourcepb "go.opentelemetry.io/proto/otlp/resource/v1" +) + +// Resource transforms a Resource into an OTLP Resource. +func Resource(r *resource.Resource) *resourcepb.Resource { + if r == nil { + return nil + } + return &resourcepb.Resource{Attributes: ResourceAttributes(r)} +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go new file mode 100644 index 00000000..0e8d00a0 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go @@ -0,0 +1,205 @@ +// Copyright The OpenTelemetry Authors +// +// 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 tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform" + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/sdk/instrumentation" + tracesdk "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace" + tracepb "go.opentelemetry.io/proto/otlp/trace/v1" +) + +// Spans transforms a slice of OpenTelemetry spans into a slice of OTLP +// ResourceSpans. +func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans { + if len(sdl) == 0 { + return nil + } + + rsm := make(map[attribute.Distinct]*tracepb.ResourceSpans) + + type key struct { + r attribute.Distinct + il instrumentation.Library + } + ssm := make(map[key]*tracepb.ScopeSpans) + + var resources int + for _, sd := range sdl { + if sd == nil { + continue + } + + rKey := sd.Resource().Equivalent() + k := key{ + r: rKey, + il: sd.InstrumentationLibrary(), + } + scopeSpan, iOk := ssm[k] + if !iOk { + // Either the resource or instrumentation library were unknown. + scopeSpan = &tracepb.ScopeSpans{ + Scope: InstrumentationScope(sd.InstrumentationLibrary()), + Spans: []*tracepb.Span{}, + SchemaUrl: sd.InstrumentationLibrary().SchemaURL, + } + } + scopeSpan.Spans = append(scopeSpan.Spans, span(sd)) + ssm[k] = scopeSpan + + rs, rOk := rsm[rKey] + if !rOk { + resources++ + // The resource was unknown. + rs = &tracepb.ResourceSpans{ + Resource: Resource(sd.Resource()), + ScopeSpans: []*tracepb.ScopeSpans{scopeSpan}, + SchemaUrl: sd.Resource().SchemaURL(), + } + rsm[rKey] = rs + continue + } + + // The resource has been seen before. Check if the instrumentation + // library lookup was unknown because if so we need to add it to the + // ResourceSpans. Otherwise, the instrumentation library has already + // been seen and the append we did above will be included it in the + // ScopeSpans reference. + if !iOk { + rs.ScopeSpans = append(rs.ScopeSpans, scopeSpan) + } + } + + // Transform the categorized map into a slice + rss := make([]*tracepb.ResourceSpans, 0, resources) + for _, rs := range rsm { + rss = append(rss, rs) + } + return rss +} + +// span transforms a Span into an OTLP span. +func span(sd tracesdk.ReadOnlySpan) *tracepb.Span { + if sd == nil { + return nil + } + + tid := sd.SpanContext().TraceID() + sid := sd.SpanContext().SpanID() + + s := &tracepb.Span{ + TraceId: tid[:], + SpanId: sid[:], + TraceState: sd.SpanContext().TraceState().String(), + Status: status(sd.Status().Code, sd.Status().Description), + StartTimeUnixNano: uint64(sd.StartTime().UnixNano()), + EndTimeUnixNano: uint64(sd.EndTime().UnixNano()), + Links: links(sd.Links()), + Kind: spanKind(sd.SpanKind()), + Name: sd.Name(), + Attributes: KeyValues(sd.Attributes()), + Events: spanEvents(sd.Events()), + DroppedAttributesCount: uint32(sd.DroppedAttributes()), + DroppedEventsCount: uint32(sd.DroppedEvents()), + DroppedLinksCount: uint32(sd.DroppedLinks()), + } + + if psid := sd.Parent().SpanID(); psid.IsValid() { + s.ParentSpanId = psid[:] + } + + return s +} + +// status transform a span code and message into an OTLP span status. +func status(status codes.Code, message string) *tracepb.Status { + var c tracepb.Status_StatusCode + switch status { + case codes.Ok: + c = tracepb.Status_STATUS_CODE_OK + case codes.Error: + c = tracepb.Status_STATUS_CODE_ERROR + default: + c = tracepb.Status_STATUS_CODE_UNSET + } + return &tracepb.Status{ + Code: c, + Message: message, + } +} + +// links transforms span Links to OTLP span links. +func links(links []tracesdk.Link) []*tracepb.Span_Link { + if len(links) == 0 { + return nil + } + + sl := make([]*tracepb.Span_Link, 0, len(links)) + for _, otLink := range links { + // This redefinition is necessary to prevent otLink.*ID[:] copies + // being reused -- in short we need a new otLink per iteration. + otLink := otLink + + tid := otLink.SpanContext.TraceID() + sid := otLink.SpanContext.SpanID() + + sl = append(sl, &tracepb.Span_Link{ + TraceId: tid[:], + SpanId: sid[:], + Attributes: KeyValues(otLink.Attributes), + DroppedAttributesCount: uint32(otLink.DroppedAttributeCount), + }) + } + return sl +} + +// spanEvents transforms span Events to an OTLP span events. +func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event { + if len(es) == 0 { + return nil + } + + events := make([]*tracepb.Span_Event, len(es)) + // Transform message events + for i := 0; i < len(es); i++ { + events[i] = &tracepb.Span_Event{ + Name: es[i].Name, + TimeUnixNano: uint64(es[i].Time.UnixNano()), + Attributes: KeyValues(es[i].Attributes), + DroppedAttributesCount: uint32(es[i].DroppedAttributeCount), + } + } + return events +} + +// spanKind transforms a SpanKind to an OTLP span kind. +func spanKind(kind trace.SpanKind) tracepb.Span_SpanKind { + switch kind { + case trace.SpanKindInternal: + return tracepb.Span_SPAN_KIND_INTERNAL + case trace.SpanKindClient: + return tracepb.Span_SPAN_KIND_CLIENT + case trace.SpanKindServer: + return tracepb.Span_SPAN_KIND_SERVER + case trace.SpanKindProducer: + return tracepb.Span_SPAN_KIND_PRODUCER + case trace.SpanKindConsumer: + return tracepb.Span_SPAN_KIND_CONSUMER + default: + return tracepb.Span_SPAN_KIND_UNSPECIFIED + } +} diff --git a/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh b/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh new file mode 100644 index 00000000..9a58fb1d --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright The OpenTelemetry Authors +# +# 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. + +set -euo pipefail + +top_dir='.' +if [[ $# -gt 0 ]]; then + top_dir="${1}" +fi + +p=$(pwd) +mod_dirs=() + +# Note `mapfile` does not exist in older bash versions: +# https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash + +while IFS= read -r line; do + mod_dirs+=("$line") +done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort) + +for mod_dir in "${mod_dirs[@]}"; do + cd "${mod_dir}" + + while IFS= read -r line; do + echo ".${line#${p}}" + done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|') + cd "${p}" +done diff --git a/vendor/go.opentelemetry.io/otel/handler.go b/vendor/go.opentelemetry.io/otel/handler.go new file mode 100644 index 00000000..35263e01 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/handler.go @@ -0,0 +1,98 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "log" + "os" + "sync" +) + +var ( + // globalErrorHandler provides an ErrorHandler that can be used + // throughout an OpenTelemetry instrumented project. When a user + // specified ErrorHandler is registered (`SetErrorHandler`) all calls to + // `Handle` and will be delegated to the registered ErrorHandler. + globalErrorHandler = defaultErrorHandler() + + // Compile-time check that delegator implements ErrorHandler. + _ ErrorHandler = (*delegator)(nil) + // Compile-time check that errLogger implements ErrorHandler. + _ ErrorHandler = (*errLogger)(nil) +) + +type delegator struct { + lock *sync.RWMutex + eh ErrorHandler +} + +func (d *delegator) Handle(err error) { + d.lock.RLock() + defer d.lock.RUnlock() + d.eh.Handle(err) +} + +// setDelegate sets the ErrorHandler delegate. +func (d *delegator) setDelegate(eh ErrorHandler) { + d.lock.Lock() + defer d.lock.Unlock() + d.eh = eh +} + +func defaultErrorHandler() *delegator { + return &delegator{ + lock: &sync.RWMutex{}, + eh: &errLogger{l: log.New(os.Stderr, "", log.LstdFlags)}, + } + +} + +// errLogger logs errors if no delegate is set, otherwise they are delegated. +type errLogger struct { + l *log.Logger +} + +// Handle logs err if no delegate is set, otherwise it is delegated. +func (h *errLogger) Handle(err error) { + h.l.Print(err) +} + +// GetErrorHandler returns the global ErrorHandler instance. +// +// The default ErrorHandler instance returned will log all errors to STDERR +// until an override ErrorHandler is set with SetErrorHandler. All +// ErrorHandler returned prior to this will automatically forward errors to +// the set instance instead of logging. +// +// Subsequent calls to SetErrorHandler after the first will not forward errors +// to the new ErrorHandler for prior returned instances. +func GetErrorHandler() ErrorHandler { + return globalErrorHandler +} + +// SetErrorHandler sets the global ErrorHandler to h. +// +// The first time this is called all ErrorHandler previously returned from +// GetErrorHandler will send errors to h instead of the default logging +// ErrorHandler. Subsequent calls will set the global ErrorHandler, but not +// delegate errors to h. +func SetErrorHandler(h ErrorHandler) { + globalErrorHandler.setDelegate(h) +} + +// Handle is a convenience function for ErrorHandler().Handle(err) +func Handle(err error) { + GetErrorHandler().Handle(err) +} diff --git a/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go b/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go new file mode 100644 index 00000000..b96e5408 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// +// 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 baggage provides base types and functionality to store and retrieve +baggage in Go context. This package exists because the OpenTracing bridge to +OpenTelemetry needs to synchronize state whenever baggage for a context is +modified and that context contains an OpenTracing span. If it were not for +this need this package would not need to exist and the +`go.opentelemetry.io/otel/baggage` package would be the singular place where +W3C baggage is handled. +*/ +package baggage // import "go.opentelemetry.io/otel/internal/baggage" + +// List is the collection of baggage members. The W3C allows for duplicates, +// but OpenTelemetry does not, therefore, this is represented as a map. +type List map[string]Item + +// Item is the value and metadata properties part of a list-member. +type Item struct { + Value string + Properties []Property +} + +// Property is a metadata entry for a list-member. +type Property struct { + Key, Value string + + // HasValue indicates if a zero-value value means the property does not + // have a value or if it was the zero-value. + HasValue bool +} diff --git a/vendor/go.opentelemetry.io/otel/internal/baggage/context.go b/vendor/go.opentelemetry.io/otel/internal/baggage/context.go new file mode 100644 index 00000000..3c2784ee --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/baggage/context.go @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// +// 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 baggage // import "go.opentelemetry.io/otel/internal/baggage" + +import "context" + +type baggageContextKeyType int + +const baggageKey baggageContextKeyType = iota + +// SetHookFunc is a callback called when storing baggage in the context. +type SetHookFunc func(context.Context, List) context.Context + +// GetHookFunc is a callback called when getting baggage from the context. +type GetHookFunc func(context.Context, List) List + +type baggageState struct { + list List + + setHook SetHookFunc + getHook GetHookFunc +} + +// ContextWithSetHook returns a copy of parent with hook configured to be +// invoked every time ContextWithBaggage is called. +// +// Passing nil SetHookFunc creates a context with no set hook to call. +func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context { + var s baggageState + switch v := parent.Value(baggageKey).(type) { + case baggageState: + s = v + } + + s.setHook = hook + return context.WithValue(parent, baggageKey, s) +} + +// ContextWithGetHook returns a copy of parent with hook configured to be +// invoked every time FromContext is called. +// +// Passing nil GetHookFunc creates a context with no get hook to call. +func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context { + var s baggageState + switch v := parent.Value(baggageKey).(type) { + case baggageState: + s = v + } + + s.getHook = hook + return context.WithValue(parent, baggageKey, s) +} + +// ContextWithList returns a copy of parent with baggage. Passing nil list +// returns a context without any baggage. +func ContextWithList(parent context.Context, list List) context.Context { + var s baggageState + switch v := parent.Value(baggageKey).(type) { + case baggageState: + s = v + } + + s.list = list + ctx := context.WithValue(parent, baggageKey, s) + if s.setHook != nil { + ctx = s.setHook(ctx, list) + } + + return ctx +} + +// ListFromContext returns the baggage contained in ctx. +func ListFromContext(ctx context.Context) List { + switch v := ctx.Value(baggageKey).(type) { + case baggageState: + if v.getHook != nil { + return v.getHook(ctx, v.list) + } + return v.list + default: + return nil + } +} diff --git a/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go b/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go new file mode 100644 index 00000000..0a378476 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// +// 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 global // import "go.opentelemetry.io/otel/internal/global" + +import ( + "log" + "os" + "sync" + + "github.com/go-logr/logr" + "github.com/go-logr/stdr" +) + +// globalLogger is the logging interface used within the otel api and sdk provide deatails of the internals. +// +// The default logger uses stdr which is backed by the standard `log.Logger` +// interface. This logger will only show messages at the Error Level. +var globalLogger logr.Logger = stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)) +var globalLoggerLock = &sync.RWMutex{} + +// SetLogger overrides the globalLogger with l. +// +// To see Info messages use a logger with `l.V(1).Enabled() == true` +// To see Debug messages use a logger with `l.V(5).Enabled() == true` +func SetLogger(l logr.Logger) { + globalLoggerLock.Lock() + defer globalLoggerLock.Unlock() + globalLogger = l +} + +// Info prints messages about the general state of the API or SDK. +// This should usually be less then 5 messages a minute +func Info(msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.V(1).Info(msg, keysAndValues...) +} + +// Error prints messages about exceptional states of the API or SDK. +func Error(err error, msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.Error(err, msg, keysAndValues...) +} + +// Debug prints messages about all internal changes in the API or SDK. +func Debug(msg string, keysAndValues ...interface{}) { + globalLoggerLock.RLock() + defer globalLoggerLock.RUnlock() + globalLogger.V(5).Info(msg, keysAndValues...) +} diff --git a/vendor/go.opentelemetry.io/otel/internal/global/propagator.go b/vendor/go.opentelemetry.io/otel/internal/global/propagator.go new file mode 100644 index 00000000..06bac35c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/global/propagator.go @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// +// 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 global // import "go.opentelemetry.io/otel/internal/global" + +import ( + "context" + "sync" + + "go.opentelemetry.io/otel/propagation" +) + +// textMapPropagator is a default TextMapPropagator that delegates calls to a +// registered delegate if one is set, otherwise it defaults to delegating the +// calls to a the default no-op propagation.TextMapPropagator. +type textMapPropagator struct { + mtx sync.Mutex + once sync.Once + delegate propagation.TextMapPropagator + noop propagation.TextMapPropagator +} + +// Compile-time guarantee that textMapPropagator implements the +// propagation.TextMapPropagator interface. +var _ propagation.TextMapPropagator = (*textMapPropagator)(nil) + +func newTextMapPropagator() *textMapPropagator { + return &textMapPropagator{ + noop: propagation.NewCompositeTextMapPropagator(), + } +} + +// SetDelegate sets a delegate propagation.TextMapPropagator that all calls are +// forwarded to. Delegation can only be performed once, all subsequent calls +// perform no delegation. +func (p *textMapPropagator) SetDelegate(delegate propagation.TextMapPropagator) { + if delegate == nil { + return + } + + p.mtx.Lock() + p.once.Do(func() { p.delegate = delegate }) + p.mtx.Unlock() +} + +// effectiveDelegate returns the current delegate of p if one is set, +// otherwise the default noop TextMapPropagator is returned. This method +// can be called concurrently. +func (p *textMapPropagator) effectiveDelegate() propagation.TextMapPropagator { + p.mtx.Lock() + defer p.mtx.Unlock() + if p.delegate != nil { + return p.delegate + } + return p.noop +} + +// Inject set cross-cutting concerns from the Context into the carrier. +func (p *textMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { + p.effectiveDelegate().Inject(ctx, carrier) +} + +// Extract reads cross-cutting concerns from the carrier into a Context. +func (p *textMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { + return p.effectiveDelegate().Extract(ctx, carrier) +} + +// Fields returns the keys whose values are set with Inject. +func (p *textMapPropagator) Fields() []string { + return p.effectiveDelegate().Fields() +} diff --git a/vendor/go.opentelemetry.io/otel/internal/global/state.go b/vendor/go.opentelemetry.io/otel/internal/global/state.go new file mode 100644 index 00000000..837d3c6c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/global/state.go @@ -0,0 +1,127 @@ +// Copyright The OpenTelemetry Authors +// +// 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 global // import "go.opentelemetry.io/otel/internal/global" + +import ( + "errors" + "sync" + "sync/atomic" + "testing" + + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +type ( + tracerProviderHolder struct { + tp trace.TracerProvider + } + + propagatorsHolder struct { + tm propagation.TextMapPropagator + } +) + +var ( + globalTracer = defaultTracerValue() + globalPropagators = defaultPropagatorsValue() + + delegateTraceOnce sync.Once + delegateTextMapPropagatorOnce sync.Once +) + +// TracerProvider is the internal implementation for global.TracerProvider. +func TracerProvider() trace.TracerProvider { + return globalTracer.Load().(tracerProviderHolder).tp +} + +// SetTracerProvider is the internal implementation for global.SetTracerProvider. +func SetTracerProvider(tp trace.TracerProvider) { + current := TracerProvider() + + if _, cOk := current.(*tracerProvider); cOk { + if _, tpOk := tp.(*tracerProvider); tpOk && current == tp { + // Do not assign the default delegating TracerProvider to delegate + // to itself. + Error( + errors.New("no delegate configured in tracer provider"), + "Setting tracer provider to it's current value. No delegate will be configured", + ) + return + } + } + + delegateTraceOnce.Do(func() { + if def, ok := current.(*tracerProvider); ok { + def.setDelegate(tp) + } + }) + globalTracer.Store(tracerProviderHolder{tp: tp}) +} + +// TextMapPropagator is the internal implementation for global.TextMapPropagator. +func TextMapPropagator() propagation.TextMapPropagator { + return globalPropagators.Load().(propagatorsHolder).tm +} + +// SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator. +func SetTextMapPropagator(p propagation.TextMapPropagator) { + current := TextMapPropagator() + + if _, cOk := current.(*textMapPropagator); cOk { + if _, pOk := p.(*textMapPropagator); pOk && current == p { + // Do not assign the default delegating TextMapPropagator to + // delegate to itself. + Error( + errors.New("no delegate configured in text map propagator"), + "Setting text map propagator to it's current value. No delegate will be configured", + ) + return + } + } + + // For the textMapPropagator already returned by TextMapPropagator + // delegate to p. + delegateTextMapPropagatorOnce.Do(func() { + if def, ok := current.(*textMapPropagator); ok { + def.SetDelegate(p) + } + }) + // Return p when subsequent calls to TextMapPropagator are made. + globalPropagators.Store(propagatorsHolder{tm: p}) +} + +func defaultTracerValue() *atomic.Value { + v := &atomic.Value{} + v.Store(tracerProviderHolder{tp: &tracerProvider{}}) + return v +} + +func defaultPropagatorsValue() *atomic.Value { + v := &atomic.Value{} + v.Store(propagatorsHolder{tm: newTextMapPropagator()}) + return v +} + +// ResetForTest configures the test to restores the initial global state during +// its Cleanup step +func ResetForTest(t testing.TB) { + t.Cleanup(func() { + globalTracer = defaultTracerValue() + globalPropagators = defaultPropagatorsValue() + delegateTraceOnce = sync.Once{} + delegateTextMapPropagatorOnce = sync.Once{} + }) +} diff --git a/vendor/go.opentelemetry.io/otel/internal/global/trace.go b/vendor/go.opentelemetry.io/otel/internal/global/trace.go new file mode 100644 index 00000000..5f008d09 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/global/trace.go @@ -0,0 +1,192 @@ +// Copyright The OpenTelemetry Authors +// +// 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 global // import "go.opentelemetry.io/otel/internal/global" + +/* +This file contains the forwarding implementation of the TracerProvider used as +the default global instance. Prior to initialization of an SDK, Tracers +returned by the global TracerProvider will provide no-op functionality. This +means that all Span created prior to initialization are no-op Spans. + +Once an SDK has been initialized, all provided no-op Tracers are swapped for +Tracers provided by the SDK defined TracerProvider. However, any Span started +prior to this initialization does not change its behavior. Meaning, the Span +remains a no-op Span. + +The implementation to track and swap Tracers locks all new Tracer creation +until the swap is complete. This assumes that this operation is not +performance-critical. If that assumption is incorrect, be sure to configure an +SDK prior to any Tracer creation. +*/ + +import ( + "context" + "sync" + "sync/atomic" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +// tracerProvider is a placeholder for a configured SDK TracerProvider. +// +// All TracerProvider functionality is forwarded to a delegate once +// configured. +type tracerProvider struct { + mtx sync.Mutex + tracers map[il]*tracer + delegate trace.TracerProvider +} + +// Compile-time guarantee that tracerProvider implements the TracerProvider +// interface. +var _ trace.TracerProvider = &tracerProvider{} + +// setDelegate configures p to delegate all TracerProvider functionality to +// provider. +// +// All Tracers provided prior to this function call are switched out to be +// Tracers provided by provider. +// +// It is guaranteed by the caller that this happens only once. +func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { + p.mtx.Lock() + defer p.mtx.Unlock() + + p.delegate = provider + + if len(p.tracers) == 0 { + return + } + + for _, t := range p.tracers { + t.setDelegate(provider) + } + + p.tracers = nil +} + +// Tracer implements TracerProvider. +func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { + p.mtx.Lock() + defer p.mtx.Unlock() + + if p.delegate != nil { + return p.delegate.Tracer(name, opts...) + } + + // At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map. + + c := trace.NewTracerConfig(opts...) + key := il{ + name: name, + version: c.InstrumentationVersion(), + } + + if p.tracers == nil { + p.tracers = make(map[il]*tracer) + } + + if val, ok := p.tracers[key]; ok { + return val + } + + t := &tracer{name: name, opts: opts, provider: p} + p.tracers[key] = t + return t +} + +type il struct { + name string + version string +} + +// tracer is a placeholder for a trace.Tracer. +// +// All Tracer functionality is forwarded to a delegate once configured. +// Otherwise, all functionality is forwarded to a NoopTracer. +type tracer struct { + name string + opts []trace.TracerOption + provider *tracerProvider + + delegate atomic.Value +} + +// Compile-time guarantee that tracer implements the trace.Tracer interface. +var _ trace.Tracer = &tracer{} + +// setDelegate configures t to delegate all Tracer functionality to Tracers +// created by provider. +// +// All subsequent calls to the Tracer methods will be passed to the delegate. +// +// It is guaranteed by the caller that this happens only once. +func (t *tracer) setDelegate(provider trace.TracerProvider) { + t.delegate.Store(provider.Tracer(t.name, t.opts...)) +} + +// Start implements trace.Tracer by forwarding the call to t.delegate if +// set, otherwise it forwards the call to a NoopTracer. +func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + delegate := t.delegate.Load() + if delegate != nil { + return delegate.(trace.Tracer).Start(ctx, name, opts...) + } + + s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t} + ctx = trace.ContextWithSpan(ctx, s) + return ctx, s +} + +// nonRecordingSpan is a minimal implementation of a Span that wraps a +// SpanContext. It performs no operations other than to return the wrapped +// SpanContext. +type nonRecordingSpan struct { + sc trace.SpanContext + tracer *tracer +} + +var _ trace.Span = nonRecordingSpan{} + +// SpanContext returns the wrapped SpanContext. +func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc } + +// IsRecording always returns false. +func (nonRecordingSpan) IsRecording() bool { return false } + +// SetStatus does nothing. +func (nonRecordingSpan) SetStatus(codes.Code, string) {} + +// SetError does nothing. +func (nonRecordingSpan) SetError(bool) {} + +// SetAttributes does nothing. +func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} + +// End does nothing. +func (nonRecordingSpan) End(...trace.SpanEndOption) {} + +// RecordError does nothing. +func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} + +// AddEvent does nothing. +func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {} + +// SetName does nothing. +func (nonRecordingSpan) SetName(string) {} + +func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } diff --git a/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go new file mode 100644 index 00000000..ce7afaa1 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// +// 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 internal // import "go.opentelemetry.io/otel/internal" + +import ( + "math" + "unsafe" +) + +func BoolToRaw(b bool) uint64 { + if b { + return 1 + } + return 0 +} + +func RawToBool(r uint64) bool { + return r != 0 +} + +func Int64ToRaw(i int64) uint64 { + return uint64(i) +} + +func RawToInt64(r uint64) int64 { + return int64(r) +} + +func Float64ToRaw(f float64) uint64 { + return math.Float64bits(f) +} + +func RawToFloat64(r uint64) float64 { + return math.Float64frombits(r) +} + +func RawPtrToFloat64Ptr(r *uint64) *float64 { + return (*float64)(unsafe.Pointer(r)) +} + +func RawPtrToInt64Ptr(r *uint64) *int64 { + return (*int64)(unsafe.Pointer(r)) +} diff --git a/vendor/go.opentelemetry.io/otel/internal_logging.go b/vendor/go.opentelemetry.io/otel/internal_logging.go new file mode 100644 index 00000000..c4f8acd5 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/internal_logging.go @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "github.com/go-logr/logr" + + "go.opentelemetry.io/otel/internal/global" +) + +// SetLogger configures the logger used internally to opentelemetry. +func SetLogger(logger logr.Logger) { + global.SetLogger(logger) +} diff --git a/vendor/go.opentelemetry.io/otel/propagation.go b/vendor/go.opentelemetry.io/otel/propagation.go new file mode 100644 index 00000000..d29aaa32 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/propagation.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/propagation" +) + +// GetTextMapPropagator returns the global TextMapPropagator. If none has been +// set, a No-Op TextMapPropagator is returned. +func GetTextMapPropagator() propagation.TextMapPropagator { + return global.TextMapPropagator() +} + +// SetTextMapPropagator sets propagator as the global TextMapPropagator. +func SetTextMapPropagator(propagator propagation.TextMapPropagator) { + global.SetTextMapPropagator(propagator) +} diff --git a/vendor/go.opentelemetry.io/otel/propagation/baggage.go b/vendor/go.opentelemetry.io/otel/propagation/baggage.go new file mode 100644 index 00000000..303cdf1c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/propagation/baggage.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// 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 propagation // import "go.opentelemetry.io/otel/propagation" + +import ( + "context" + + "go.opentelemetry.io/otel/baggage" +) + +const baggageHeader = "baggage" + +// Baggage is a propagator that supports the W3C Baggage format. +// +// This propagates user-defined baggage associated with a trace. The complete +// specification is defined at https://www.w3.org/TR/baggage/. +type Baggage struct{} + +var _ TextMapPropagator = Baggage{} + +// Inject sets baggage key-values from ctx into the carrier. +func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) { + bStr := baggage.FromContext(ctx).String() + if bStr != "" { + carrier.Set(baggageHeader, bStr) + } +} + +// Extract returns a copy of parent with the baggage from the carrier added. +func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context { + bStr := carrier.Get(baggageHeader) + if bStr == "" { + return parent + } + + bag, err := baggage.Parse(bStr) + if err != nil { + return parent + } + return baggage.ContextWithBaggage(parent, bag) +} + +// Fields returns the keys who's values are set with Inject. +func (b Baggage) Fields() []string { + return []string{baggageHeader} +} diff --git a/vendor/go.opentelemetry.io/otel/propagation/doc.go b/vendor/go.opentelemetry.io/otel/propagation/doc.go new file mode 100644 index 00000000..c119eb28 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/propagation/doc.go @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// +// 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 propagation contains OpenTelemetry context propagators. + +OpenTelemetry propagators are used to extract and inject context data from and +into messages exchanged by applications. The propagator supported by this +package is the W3C Trace Context encoding +(https://www.w3.org/TR/trace-context/), and W3C Baggage +(https://www.w3.org/TR/baggage/). +*/ +package propagation // import "go.opentelemetry.io/otel/propagation" diff --git a/vendor/go.opentelemetry.io/otel/propagation/propagation.go b/vendor/go.opentelemetry.io/otel/propagation/propagation.go new file mode 100644 index 00000000..c94438f7 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/propagation/propagation.go @@ -0,0 +1,153 @@ +// Copyright The OpenTelemetry Authors +// +// 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 propagation // import "go.opentelemetry.io/otel/propagation" + +import ( + "context" + "net/http" +) + +// TextMapCarrier is the storage medium used by a TextMapPropagator. +type TextMapCarrier interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Get returns the value associated with the passed key. + Get(key string) string + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Set stores the key-value pair. + Set(key string, value string) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Keys lists the keys stored in this carrier. + Keys() []string + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +// MapCarrier is a TextMapCarrier that uses a map held in memory as a storage +// medium for propagated key-value pairs. +type MapCarrier map[string]string + +// Compile time check that MapCarrier implements the TextMapCarrier. +var _ TextMapCarrier = MapCarrier{} + +// Get returns the value associated with the passed key. +func (c MapCarrier) Get(key string) string { + return c[key] +} + +// Set stores the key-value pair. +func (c MapCarrier) Set(key, value string) { + c[key] = value +} + +// Keys lists the keys stored in this carrier. +func (c MapCarrier) Keys() []string { + keys := make([]string, 0, len(c)) + for k := range c { + keys = append(keys, k) + } + return keys +} + +// HeaderCarrier adapts http.Header to satisfy the TextMapCarrier interface. +type HeaderCarrier http.Header + +// Get returns the value associated with the passed key. +func (hc HeaderCarrier) Get(key string) string { + return http.Header(hc).Get(key) +} + +// Set stores the key-value pair. +func (hc HeaderCarrier) Set(key string, value string) { + http.Header(hc).Set(key, value) +} + +// Keys lists the keys stored in this carrier. +func (hc HeaderCarrier) Keys() []string { + keys := make([]string, 0, len(hc)) + for k := range hc { + keys = append(keys, k) + } + return keys +} + +// TextMapPropagator propagates cross-cutting concerns as key-value text +// pairs within a carrier that travels in-band across process boundaries. +type TextMapPropagator interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Inject set cross-cutting concerns from the Context into the carrier. + Inject(ctx context.Context, carrier TextMapCarrier) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Extract reads cross-cutting concerns from the carrier into a Context. + Extract(ctx context.Context, carrier TextMapCarrier) context.Context + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Fields returns the keys whose values are set with Inject. + Fields() []string + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +type compositeTextMapPropagator []TextMapPropagator + +func (p compositeTextMapPropagator) Inject(ctx context.Context, carrier TextMapCarrier) { + for _, i := range p { + i.Inject(ctx, carrier) + } +} + +func (p compositeTextMapPropagator) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { + for _, i := range p { + ctx = i.Extract(ctx, carrier) + } + return ctx +} + +func (p compositeTextMapPropagator) Fields() []string { + unique := make(map[string]struct{}) + for _, i := range p { + for _, k := range i.Fields() { + unique[k] = struct{}{} + } + } + + fields := make([]string, 0, len(unique)) + for k := range unique { + fields = append(fields, k) + } + return fields +} + +// NewCompositeTextMapPropagator returns a unified TextMapPropagator from the +// group of passed TextMapPropagator. This allows different cross-cutting +// concerns to be propagates in a unified manner. +// +// The returned TextMapPropagator will inject and extract cross-cutting +// concerns in the order the TextMapPropagators were provided. Additionally, +// the Fields method will return a de-duplicated slice of the keys that are +// set with the Inject method. +func NewCompositeTextMapPropagator(p ...TextMapPropagator) TextMapPropagator { + return compositeTextMapPropagator(p) +} diff --git a/vendor/go.opentelemetry.io/otel/propagation/trace_context.go b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go new file mode 100644 index 00000000..902692da --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go @@ -0,0 +1,159 @@ +// Copyright The OpenTelemetry Authors +// +// 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 propagation // import "go.opentelemetry.io/otel/propagation" + +import ( + "context" + "encoding/hex" + "fmt" + "regexp" + + "go.opentelemetry.io/otel/trace" +) + +const ( + supportedVersion = 0 + maxVersion = 254 + traceparentHeader = "traceparent" + tracestateHeader = "tracestate" +) + +// TraceContext is a propagator that supports the W3C Trace Context format +// (https://www.w3.org/TR/trace-context/) +// +// This propagator will propagate the traceparent and tracestate headers to +// guarantee traces are not broken. It is up to the users of this propagator +// to choose if they want to participate in a trace by modifying the +// traceparent header and relevant parts of the tracestate header containing +// their proprietary information. +type TraceContext struct{} + +var _ TextMapPropagator = TraceContext{} +var traceCtxRegExp = regexp.MustCompile("^(?P[0-9a-f]{2})-(?P[a-f0-9]{32})-(?P[a-f0-9]{16})-(?P[a-f0-9]{2})(?:-.*)?$") + +// Inject set tracecontext from the Context into the carrier. +func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) { + sc := trace.SpanContextFromContext(ctx) + if !sc.IsValid() { + return + } + + if ts := sc.TraceState().String(); ts != "" { + carrier.Set(tracestateHeader, ts) + } + + // Clear all flags other than the trace-context supported sampling bit. + flags := sc.TraceFlags() & trace.FlagsSampled + + h := fmt.Sprintf("%.2x-%s-%s-%s", + supportedVersion, + sc.TraceID(), + sc.SpanID(), + flags) + carrier.Set(traceparentHeader, h) +} + +// Extract reads tracecontext from the carrier into a returned Context. +// +// The returned Context will be a copy of ctx and contain the extracted +// tracecontext as the remote SpanContext. If the extracted tracecontext is +// invalid, the passed ctx will be returned directly instead. +func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { + sc := tc.extract(carrier) + if !sc.IsValid() { + return ctx + } + return trace.ContextWithRemoteSpanContext(ctx, sc) +} + +func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext { + h := carrier.Get(traceparentHeader) + if h == "" { + return trace.SpanContext{} + } + + matches := traceCtxRegExp.FindStringSubmatch(h) + + if len(matches) == 0 { + return trace.SpanContext{} + } + + if len(matches) < 5 { // four subgroups plus the overall match + return trace.SpanContext{} + } + + if len(matches[1]) != 2 { + return trace.SpanContext{} + } + ver, err := hex.DecodeString(matches[1]) + if err != nil { + return trace.SpanContext{} + } + version := int(ver[0]) + if version > maxVersion { + return trace.SpanContext{} + } + + if version == 0 && len(matches) != 5 { // four subgroups plus the overall match + return trace.SpanContext{} + } + + if len(matches[2]) != 32 { + return trace.SpanContext{} + } + + var scc trace.SpanContextConfig + + scc.TraceID, err = trace.TraceIDFromHex(matches[2][:32]) + if err != nil { + return trace.SpanContext{} + } + + if len(matches[3]) != 16 { + return trace.SpanContext{} + } + scc.SpanID, err = trace.SpanIDFromHex(matches[3]) + if err != nil { + return trace.SpanContext{} + } + + if len(matches[4]) != 2 { + return trace.SpanContext{} + } + opts, err := hex.DecodeString(matches[4]) + if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) { + return trace.SpanContext{} + } + // Clear all flags other than the trace-context supported sampling bit. + scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled + + // Ignore the error returned here. Failure to parse tracestate MUST NOT + // affect the parsing of traceparent according to the W3C tracecontext + // specification. + scc.TraceState, _ = trace.ParseTraceState(carrier.Get(tracestateHeader)) + scc.Remote = true + + sc := trace.NewSpanContext(scc) + if !sc.IsValid() { + return trace.SpanContext{} + } + + return sc +} + +// Fields returns the keys who's values are set with Inject. +func (tc TraceContext) Fields() []string { + return []string{traceparentHeader, tracestateHeader} +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/LICENSE b/vendor/go.opentelemetry.io/otel/sdk/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/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/go.opentelemetry.io/otel/sdk/instrumentation/library.go b/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go new file mode 100644 index 00000000..6f001616 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/instrumentation/library.go @@ -0,0 +1,33 @@ +// Copyright The OpenTelemetry Authors +// +// 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 instrumentation provides an instrumentation library structure to be +passed to both the OpenTelemetry Tracer and Meter components. + +For more information see +[this](https://github.com/open-telemetry/oteps/blob/main/text/0083-component.md). +*/ +package instrumentation // import "go.opentelemetry.io/otel/sdk/instrumentation" + +// Library represents the instrumentation library. +type Library struct { + // Name is the name of the instrumentation library. This should be the + // Go package name of that library. + Name string + // Version is the version of the instrumentation library. + Version string + // SchemaURL of the telemetry emitted by the library. + SchemaURL string +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go b/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go new file mode 100644 index 00000000..1a03bf7a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go @@ -0,0 +1,186 @@ +// Copyright The OpenTelemetry Authors +// +// 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 env // import "go.opentelemetry.io/otel/sdk/internal/env" + +import ( + "os" + "strconv" + + "go.opentelemetry.io/otel/internal/global" +) + +// Environment variable names +const ( + // BatchSpanProcessorScheduleDelayKey + // Delay interval between two consecutive exports. + // i.e. 5000 + BatchSpanProcessorScheduleDelayKey = "OTEL_BSP_SCHEDULE_DELAY" + // BatchSpanProcessorExportTimeoutKey + // Maximum allowed time to export data. + // i.e. 3000 + BatchSpanProcessorExportTimeoutKey = "OTEL_BSP_EXPORT_TIMEOUT" + // BatchSpanProcessorMaxQueueSizeKey + // Maximum queue size + // i.e. 2048 + BatchSpanProcessorMaxQueueSizeKey = "OTEL_BSP_MAX_QUEUE_SIZE" + // BatchSpanProcessorMaxExportBatchSizeKey + // Maximum batch size + // Note: Must be less than or equal to EnvBatchSpanProcessorMaxQueueSize + // i.e. 512 + BatchSpanProcessorMaxExportBatchSizeKey = "OTEL_BSP_MAX_EXPORT_BATCH_SIZE" + + // AttributeValueLengthKey + // Maximum allowed attribute value size. + AttributeValueLengthKey = "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT" + + // AttributeCountKey + // Maximum allowed span attribute count + AttributeCountKey = "OTEL_ATTRIBUTE_COUNT_LIMIT" + + // SpanAttributeValueLengthKey + // Maximum allowed attribute value size for a span. + SpanAttributeValueLengthKey = "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT" + + // SpanAttributeCountKey + // Maximum allowed span attribute count for a span. + SpanAttributeCountKey = "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT" + + // SpanEventCountKey + // Maximum allowed span event count. + SpanEventCountKey = "OTEL_SPAN_EVENT_COUNT_LIMIT" + + // SpanEventAttributeCountKey + // Maximum allowed attribute per span event count. + SpanEventAttributeCountKey = "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT" + + // SpanLinkCountKey + // Maximum allowed span link count. + SpanLinkCountKey = "OTEL_SPAN_LINK_COUNT_LIMIT" + + // SpanLinkAttributeCountKey + // Maximum allowed attribute per span link count. + SpanLinkAttributeCountKey = "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT" +) + +// firstInt returns the value of the first matching environment variable from +// keys. If the value is not an integer or no match is found, defaultValue is +// returned. +func firstInt(defaultValue int, keys ...string) int { + for _, key := range keys { + value, ok := os.LookupEnv(key) + if !ok { + continue + } + + intValue, err := strconv.Atoi(value) + if err != nil { + global.Info("Got invalid value, number value expected.", key, value) + return defaultValue + } + + return intValue + } + + return defaultValue +} + +// IntEnvOr returns the int value of the environment variable with name key if +// it exists and the value is an int. Otherwise, defaultValue is returned. +func IntEnvOr(key string, defaultValue int) int { + value, ok := os.LookupEnv(key) + if !ok { + return defaultValue + } + + intValue, err := strconv.Atoi(value) + if err != nil { + global.Info("Got invalid value, number value expected.", key, value) + return defaultValue + } + + return intValue +} + +// BatchSpanProcessorScheduleDelay returns the environment variable value for +// the OTEL_BSP_SCHEDULE_DELAY key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorScheduleDelay(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorScheduleDelayKey, defaultValue) +} + +// BatchSpanProcessorExportTimeout returns the environment variable value for +// the OTEL_BSP_EXPORT_TIMEOUT key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorExportTimeout(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorExportTimeoutKey, defaultValue) +} + +// BatchSpanProcessorMaxQueueSize returns the environment variable value for +// the OTEL_BSP_MAX_QUEUE_SIZE key if it exists, otherwise defaultValue is +// returned. +func BatchSpanProcessorMaxQueueSize(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorMaxQueueSizeKey, defaultValue) +} + +// BatchSpanProcessorMaxExportBatchSize returns the environment variable value for +// the OTEL_BSP_MAX_EXPORT_BATCH_SIZE key if it exists, otherwise defaultValue +// is returned. +func BatchSpanProcessorMaxExportBatchSize(defaultValue int) int { + return IntEnvOr(BatchSpanProcessorMaxExportBatchSizeKey, defaultValue) +} + +// SpanAttributeValueLength returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the +// environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT is +// returned or defaultValue if that is not set. +func SpanAttributeValueLength(defaultValue int) int { + return firstInt(defaultValue, SpanAttributeValueLengthKey, AttributeValueLengthKey) +} + +// SpanAttributeCount returns the environment variable value for the +// OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the +// environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT is returned or +// defaultValue if that is not set. +func SpanAttributeCount(defaultValue int) int { + return firstInt(defaultValue, SpanAttributeCountKey, AttributeCountKey) +} + +// SpanEventCount returns the environment variable value for the +// OTEL_SPAN_EVENT_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanEventCount(defaultValue int) int { + return IntEnvOr(SpanEventCountKey, defaultValue) +} + +// SpanEventAttributeCount returns the environment variable value for the +// OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue +// is returned. +func SpanEventAttributeCount(defaultValue int) int { + return IntEnvOr(SpanEventAttributeCountKey, defaultValue) +} + +// SpanLinkCount returns the environment variable value for the +// OTEL_SPAN_LINK_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkCount(defaultValue int) int { + return IntEnvOr(SpanLinkCountKey, defaultValue) +} + +// SpanLinkAttributeCount returns the environment variable value for the +// OTEL_LINK_ATTRIBUTE_COUNT_LIMIT key if it exists, otherwise defaultValue is +// returned. +func SpanLinkAttributeCount(defaultValue int) int { + return IntEnvOr(SpanLinkAttributeCountKey, defaultValue) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/internal.go b/vendor/go.opentelemetry.io/otel/sdk/internal/internal.go new file mode 100644 index 00000000..84a02306 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/internal/internal.go @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// +// 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 internal // import "go.opentelemetry.io/otel/sdk/internal" + +import ( + "fmt" + "time" + + "go.opentelemetry.io/otel" +) + +// UserAgent is the user agent to be added to the outgoing +// requests from the exporters. +var UserAgent = fmt.Sprintf("opentelemetry-go/%s", otel.Version()) + +// MonotonicEndTime returns the end time at present +// but offset from start, monotonically. +// +// The monotonic clock is used in subtractions hence +// the duration since start added back to start gives +// end as a monotonic time. +// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks +func MonotonicEndTime(start time.Time) time.Time { + return start.Add(time.Since(start)) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go b/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go new file mode 100644 index 00000000..a5eaa7e5 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/auto.go @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "errors" + "fmt" +) + +var ( + // ErrPartialResource is returned by a detector when complete source + // information for a Resource is unavailable or the source information + // contains invalid values that are omitted from the returned Resource. + ErrPartialResource = errors.New("partial resource") +) + +// Detector detects OpenTelemetry resource information +type Detector interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Detect returns an initialized Resource based on gathered information. + // If the source information to construct a Resource contains invalid + // values, a Resource is returned with the valid parts of the source + // information used for initialization along with an appropriately + // wrapped ErrPartialResource error. + Detect(ctx context.Context) (*Resource, error) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +// Detect calls all input detectors sequentially and merges each result with the previous one. +// It returns the merged error too. +func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { + var autoDetectedRes *Resource + var errInfo []string + for _, detector := range detectors { + if detector == nil { + continue + } + res, err := detector.Detect(ctx) + if err != nil { + errInfo = append(errInfo, err.Error()) + if !errors.Is(err, ErrPartialResource) { + continue + } + } + autoDetectedRes, err = Merge(autoDetectedRes, res) + if err != nil { + errInfo = append(errInfo, err.Error()) + } + } + + var aggregatedError error + if len(errInfo) > 0 { + aggregatedError = fmt.Errorf("detecting resources: %s", errInfo) + } + return autoDetectedRes, aggregatedError +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go new file mode 100644 index 00000000..701eae40 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +type ( + // telemetrySDK is a Detector that provides information about + // the OpenTelemetry SDK used. This Detector is included as a + // builtin. If these resource attributes are not wanted, use + // the WithTelemetrySDK(nil) or WithoutBuiltin() options to + // explicitly disable them. + telemetrySDK struct{} + + // host is a Detector that provides information about the host + // being run on. This Detector is included as a builtin. If + // these resource attributes are not wanted, use the + // WithHost(nil) or WithoutBuiltin() options to explicitly + // disable them. + host struct{} + + stringDetector struct { + schemaURL string + K attribute.Key + F func() (string, error) + } + + defaultServiceNameDetector struct{} +) + +var ( + _ Detector = telemetrySDK{} + _ Detector = host{} + _ Detector = stringDetector{} + _ Detector = defaultServiceNameDetector{} +) + +// Detect returns a *Resource that describes the OpenTelemetry SDK used. +func (telemetrySDK) Detect(context.Context) (*Resource, error) { + return NewWithAttributes( + semconv.SchemaURL, + semconv.TelemetrySDKNameKey.String("opentelemetry"), + semconv.TelemetrySDKLanguageKey.String("go"), + semconv.TelemetrySDKVersionKey.String(otel.Version()), + ), nil +} + +// Detect returns a *Resource that describes the host being run on. +func (host) Detect(ctx context.Context) (*Resource, error) { + return StringDetector(semconv.SchemaURL, semconv.HostNameKey, os.Hostname).Detect(ctx) +} + +// StringDetector returns a Detector that will produce a *Resource +// containing the string as a value corresponding to k. The resulting Resource +// will have the specified schemaURL. +func StringDetector(schemaURL string, k attribute.Key, f func() (string, error)) Detector { + return stringDetector{schemaURL: schemaURL, K: k, F: f} +} + +// Detect returns a *Resource that describes the string as a value +// corresponding to attribute.Key as well as the specific schemaURL. +func (sd stringDetector) Detect(ctx context.Context) (*Resource, error) { + value, err := sd.F() + if err != nil { + return nil, fmt.Errorf("%s: %w", string(sd.K), err) + } + a := sd.K.String(value) + if !a.Valid() { + return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.Emit()) + } + return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil +} + +// Detect implements Detector +func (defaultServiceNameDetector) Detect(ctx context.Context) (*Resource, error) { + return StringDetector( + semconv.SchemaURL, + semconv.ServiceNameKey, + func() (string, error) { + executable, err := os.Executable() + if err != nil { + return "unknown_service:go", nil + } + return "unknown_service:" + filepath.Base(executable), nil + }, + ).Detect(ctx) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/config.go b/vendor/go.opentelemetry.io/otel/sdk/resource/config.go new file mode 100644 index 00000000..8e212b12 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/config.go @@ -0,0 +1,199 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" +) + +// config contains configuration for Resource creation. +type config struct { + // detectors that will be evaluated. + detectors []Detector + // SchemaURL to associate with the Resource. + schemaURL string +} + +// Option is the interface that applies a configuration option. +type Option interface { + // apply sets the Option value of a config. + apply(config) config +} + +// WithAttributes adds attributes to the configured Resource. +func WithAttributes(attributes ...attribute.KeyValue) Option { + return WithDetectors(detectAttributes{attributes}) +} + +type detectAttributes struct { + attributes []attribute.KeyValue +} + +func (d detectAttributes) Detect(context.Context) (*Resource, error) { + return NewSchemaless(d.attributes...), nil +} + +// WithDetectors adds detectors to be evaluated for the configured resource. +func WithDetectors(detectors ...Detector) Option { + return detectorsOption{detectors: detectors} +} + +type detectorsOption struct { + detectors []Detector +} + +func (o detectorsOption) apply(cfg config) config { + cfg.detectors = append(cfg.detectors, o.detectors...) + return cfg +} + +// WithFromEnv adds attributes from environment variables to the configured resource. +func WithFromEnv() Option { + return WithDetectors(fromEnv{}) +} + +// WithHost adds attributes from the host to the configured resource. +func WithHost() Option { + return WithDetectors(host{}) +} + +// WithTelemetrySDK adds TelemetrySDK version info to the configured resource. +func WithTelemetrySDK() Option { + return WithDetectors(telemetrySDK{}) +} + +// WithSchemaURL sets the schema URL for the configured resource. +func WithSchemaURL(schemaURL string) Option { + return schemaURLOption(schemaURL) +} + +type schemaURLOption string + +func (o schemaURLOption) apply(cfg config) config { + cfg.schemaURL = string(o) + return cfg +} + +// WithOS adds all the OS attributes to the configured Resource. +// See individual WithOS* functions to configure specific attributes. +func WithOS() Option { + return WithDetectors( + osTypeDetector{}, + osDescriptionDetector{}, + ) +} + +// WithOSType adds an attribute with the operating system type to the configured Resource. +func WithOSType() Option { + return WithDetectors(osTypeDetector{}) +} + +// WithOSDescription adds an attribute with the operating system description to the +// configured Resource. The formatted string is equivalent to the output of the +// `uname -snrvm` command. +func WithOSDescription() Option { + return WithDetectors(osDescriptionDetector{}) +} + +// WithProcess adds all the Process attributes to the configured Resource. +// +// Warning! This option will include process command line arguments. If these +// contain sensitive information it will be included in the exported resource. +// +// This option is equivalent to calling WithProcessPID, +// WithProcessExecutableName, WithProcessExecutablePath, +// WithProcessCommandArgs, WithProcessOwner, WithProcessRuntimeName, +// WithProcessRuntimeVersion, and WithProcessRuntimeDescription. See each +// option function for information about what resource attributes each +// includes. +func WithProcess() Option { + return WithDetectors( + processPIDDetector{}, + processExecutableNameDetector{}, + processExecutablePathDetector{}, + processCommandArgsDetector{}, + processOwnerDetector{}, + processRuntimeNameDetector{}, + processRuntimeVersionDetector{}, + processRuntimeDescriptionDetector{}, + ) +} + +// WithProcessPID adds an attribute with the process identifier (PID) to the +// configured Resource. +func WithProcessPID() Option { + return WithDetectors(processPIDDetector{}) +} + +// WithProcessExecutableName adds an attribute with the name of the process +// executable to the configured Resource. +func WithProcessExecutableName() Option { + return WithDetectors(processExecutableNameDetector{}) +} + +// WithProcessExecutablePath adds an attribute with the full path to the process +// executable to the configured Resource. +func WithProcessExecutablePath() Option { + return WithDetectors(processExecutablePathDetector{}) +} + +// WithProcessCommandArgs adds an attribute with all the command arguments (including +// the command/executable itself) as received by the process to the configured +// Resource. +// +// Warning! This option will include process command line arguments. If these +// contain sensitive information it will be included in the exported resource. +func WithProcessCommandArgs() Option { + return WithDetectors(processCommandArgsDetector{}) +} + +// WithProcessOwner adds an attribute with the username of the user that owns the process +// to the configured Resource. +func WithProcessOwner() Option { + return WithDetectors(processOwnerDetector{}) +} + +// WithProcessRuntimeName adds an attribute with the name of the runtime of this +// process to the configured Resource. +func WithProcessRuntimeName() Option { + return WithDetectors(processRuntimeNameDetector{}) +} + +// WithProcessRuntimeVersion adds an attribute with the version of the runtime of +// this process to the configured Resource. +func WithProcessRuntimeVersion() Option { + return WithDetectors(processRuntimeVersionDetector{}) +} + +// WithProcessRuntimeDescription adds an attribute with an additional description +// about the runtime of the process to the configured Resource. +func WithProcessRuntimeDescription() Option { + return WithDetectors(processRuntimeDescriptionDetector{}) +} + +// WithContainer adds all the Container attributes to the configured Resource. +// See individual WithContainer* functions to configure specific attributes. +func WithContainer() Option { + return WithDetectors( + cgroupContainerIDDetector{}, + ) +} + +// WithContainerID adds an attribute with the id of the container to the configured Resource. +func WithContainerID() Option { + return WithDetectors(cgroupContainerIDDetector{}) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go new file mode 100644 index 00000000..e56978ad --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "bufio" + "context" + "errors" + "io" + "os" + "regexp" + + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +type containerIDProvider func() (string, error) + +var ( + containerID containerIDProvider = getContainerIDFromCGroup + cgroupContainerIDRe = regexp.MustCompile(`^.*/(?:.*-)?([0-9a-f]+)(?:\.|\s*$)`) +) + +type cgroupContainerIDDetector struct{} + +const cgroupPath = "/proc/self/cgroup" + +// Detect returns a *Resource that describes the id of the container. +// If no container id found, an empty resource will be returned. +func (cgroupContainerIDDetector) Detect(ctx context.Context) (*Resource, error) { + containerID, err := containerID() + if err != nil { + return nil, err + } + + if containerID == "" { + return Empty(), nil + } + return NewWithAttributes(semconv.SchemaURL, semconv.ContainerIDKey.String(containerID)), nil +} + +var ( + defaultOSStat = os.Stat + osStat = defaultOSStat + + defaultOSOpen = func(name string) (io.ReadCloser, error) { + return os.Open(name) + } + osOpen = defaultOSOpen +) + +// getContainerIDFromCGroup returns the id of the container from the cgroup file. +// If no container id found, an empty string will be returned. +func getContainerIDFromCGroup() (string, error) { + if _, err := osStat(cgroupPath); errors.Is(err, os.ErrNotExist) { + // File does not exist, skip + return "", nil + } + + file, err := osOpen(cgroupPath) + if err != nil { + return "", err + } + defer file.Close() + + return getContainerIDFromReader(file), nil +} + +// getContainerIDFromReader returns the id of the container from reader. +func getContainerIDFromReader(reader io.Reader) string { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := scanner.Text() + + if id := getContainerIDFromLine(line); id != "" { + return id + } + } + return "" +} + +// getContainerIDFromLine returns the id of the container from one string line. +func getContainerIDFromLine(line string) string { + matches := cgroupContainerIDRe.FindStringSubmatch(line) + if len(matches) <= 1 { + return "" + } + return matches[1] +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/doc.go b/vendor/go.opentelemetry.io/otel/sdk/resource/doc.go new file mode 100644 index 00000000..9aab3d83 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/doc.go @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource provides detecting and representing resources. +// +// The fundamental struct is a Resource which holds identifying information +// about the entities for which telemetry is exported. +// +// To automatically construct Resources from an environment a Detector +// interface is defined. Implementations of this interface can be passed to +// the Detect function to generate a Resource from the merged information. +// +// To load a user defined Resource from the environment variable +// OTEL_RESOURCE_ATTRIBUTES the FromEnv Detector can be used. It will interpret +// the value as a list of comma delimited key/value pairs +// (e.g. `=,=,...`). +package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/env.go b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go new file mode 100644 index 00000000..9392296c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go @@ -0,0 +1,99 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "fmt" + "os" + "strings" + + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +const ( + // resourceAttrKey is the environment variable name OpenTelemetry Resource information will be read from. + resourceAttrKey = "OTEL_RESOURCE_ATTRIBUTES" + + // svcNameKey is the environment variable name that Service Name information will be read from. + svcNameKey = "OTEL_SERVICE_NAME" +) + +var ( + // errMissingValue is returned when a resource value is missing. + errMissingValue = fmt.Errorf("%w: missing value", ErrPartialResource) +) + +// fromEnv is a Detector that implements the Detector and collects +// resources from environment. This Detector is included as a +// builtin. +type fromEnv struct{} + +// compile time assertion that FromEnv implements Detector interface +var _ Detector = fromEnv{} + +// Detect collects resources from environment +func (fromEnv) Detect(context.Context) (*Resource, error) { + attrs := strings.TrimSpace(os.Getenv(resourceAttrKey)) + svcName := strings.TrimSpace(os.Getenv(svcNameKey)) + + if attrs == "" && svcName == "" { + return Empty(), nil + } + + var res *Resource + + if svcName != "" { + res = NewSchemaless(semconv.ServiceNameKey.String(svcName)) + } + + r2, err := constructOTResources(attrs) + + // Ensure that the resource with the service name from OTEL_SERVICE_NAME + // takes precedence, if it was defined. + res, err2 := Merge(r2, res) + + if err == nil { + err = err2 + } else if err2 != nil { + err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) + } + + return res, err +} + +func constructOTResources(s string) (*Resource, error) { + if s == "" { + return Empty(), nil + } + pairs := strings.Split(s, ",") + attrs := []attribute.KeyValue{} + var invalid []string + for _, p := range pairs { + field := strings.SplitN(p, "=", 2) + if len(field) != 2 { + invalid = append(invalid, p) + continue + } + k, v := strings.TrimSpace(field[0]), strings.TrimSpace(field[1]) + attrs = append(attrs, attribute.String(k, v)) + } + var err error + if len(invalid) > 0 { + err = fmt.Errorf("%w: %v", errMissingValue, invalid) + } + return NewSchemaless(attrs...), err +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go new file mode 100644 index 00000000..59329770 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "strings" + + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +type osDescriptionProvider func() (string, error) + +var defaultOSDescriptionProvider osDescriptionProvider = platformOSDescription + +var osDescription = defaultOSDescriptionProvider + +func setDefaultOSDescriptionProvider() { + setOSDescriptionProvider(defaultOSDescriptionProvider) +} + +func setOSDescriptionProvider(osDescriptionProvider osDescriptionProvider) { + osDescription = osDescriptionProvider +} + +type osTypeDetector struct{} +type osDescriptionDetector struct{} + +// Detect returns a *Resource that describes the operating system type the +// service is running on. +func (osTypeDetector) Detect(ctx context.Context) (*Resource, error) { + osType := runtimeOS() + + osTypeAttribute := mapRuntimeOSToSemconvOSType(osType) + + return NewWithAttributes( + semconv.SchemaURL, + osTypeAttribute, + ), nil +} + +// Detect returns a *Resource that describes the operating system the +// service is running on. +func (osDescriptionDetector) Detect(ctx context.Context) (*Resource, error) { + description, err := osDescription() + + if err != nil { + return nil, err + } + + return NewWithAttributes( + semconv.SchemaURL, + semconv.OSDescriptionKey.String(description), + ), nil +} + +// mapRuntimeOSToSemconvOSType translates the OS name as provided by the Go runtime +// into an OS type attribute with the corresponding value defined by the semantic +// conventions. In case the provided OS name isn't mapped, it's transformed to lowercase +// and used as the value for the returned OS type attribute. +func mapRuntimeOSToSemconvOSType(osType string) attribute.KeyValue { + // the elements in this map are the intersection between + // available GOOS values and defined semconv OS types + osTypeAttributeMap := map[string]attribute.KeyValue{ + "darwin": semconv.OSTypeDarwin, + "dragonfly": semconv.OSTypeDragonflyBSD, + "freebsd": semconv.OSTypeFreeBSD, + "linux": semconv.OSTypeLinux, + "netbsd": semconv.OSTypeNetBSD, + "openbsd": semconv.OSTypeOpenBSD, + "solaris": semconv.OSTypeSolaris, + "windows": semconv.OSTypeWindows, + } + + var osTypeAttribute attribute.KeyValue + + if attr, ok := osTypeAttributeMap[osType]; ok { + osTypeAttribute = attr + } else { + osTypeAttribute = semconv.OSTypeKey.String(strings.ToLower(osType)) + } + + return osTypeAttribute +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_darwin.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_darwin.go new file mode 100644 index 00000000..24ec8579 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_darwin.go @@ -0,0 +1,102 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "encoding/xml" + "fmt" + "io" + "os" +) + +type plist struct { + XMLName xml.Name `xml:"plist"` + Dict dict `xml:"dict"` +} + +type dict struct { + Key []string `xml:"key"` + String []string `xml:"string"` +} + +// osRelease builds a string describing the operating system release based on the +// contents of the property list (.plist) system files. If no .plist files are found, +// or if the required properties to build the release description string are missing, +// an empty string is returned instead. The generated string resembles the output of +// the `sw_vers` commandline program, but in a single-line string. For more information +// about the `sw_vers` program, see: https://www.unix.com/man-page/osx/1/SW_VERS. +func osRelease() string { + file, err := getPlistFile() + if err != nil { + return "" + } + + defer file.Close() + + values, err := parsePlistFile(file) + if err != nil { + return "" + } + + return buildOSRelease(values) +} + +// getPlistFile returns a *os.File pointing to one of the well-known .plist files +// available on macOS. If no file can be opened, it returns an error. +func getPlistFile() (*os.File, error) { + return getFirstAvailableFile([]string{ + "/System/Library/CoreServices/SystemVersion.plist", + "/System/Library/CoreServices/ServerVersion.plist", + }) +} + +// parsePlistFile process the file pointed by `file` as a .plist file and returns +// a map with the key-values for each pair of correlated and elements +// contained in it. +func parsePlistFile(file io.Reader) (map[string]string, error) { + var v plist + + err := xml.NewDecoder(file).Decode(&v) + if err != nil { + return nil, err + } + + if len(v.Dict.Key) != len(v.Dict.String) { + return nil, fmt.Errorf("the number of and elements doesn't match") + } + + properties := make(map[string]string, len(v.Dict.Key)) + for i, key := range v.Dict.Key { + properties[key] = v.Dict.String[i] + } + + return properties, nil +} + +// buildOSRelease builds a string describing the OS release based on the properties +// available on the provided map. It tries to find the `ProductName`, `ProductVersion` +// and `ProductBuildVersion` properties. If some of these properties are not found, +// it returns an empty string. +func buildOSRelease(properties map[string]string) string { + productName := properties["ProductName"] + productVersion := properties["ProductVersion"] + productBuildVersion := properties["ProductBuildVersion"] + + if productName == "" || productVersion == "" || productBuildVersion == "" { + return "" + } + + return fmt.Sprintf("%s %s (%s)", productName, productVersion, productBuildVersion) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go new file mode 100644 index 00000000..fba6790e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go @@ -0,0 +1,154 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix dragonfly freebsd linux netbsd openbsd solaris zos + +package resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" +) + +// osRelease builds a string describing the operating system release based on the +// properties of the os-release file. If no os-release file is found, or if the +// required properties to build the release description string are missing, an empty +// string is returned instead. For more information about os-release files, see: +// https://www.freedesktop.org/software/systemd/man/os-release.html +func osRelease() string { + file, err := getOSReleaseFile() + if err != nil { + return "" + } + + defer file.Close() + + values := parseOSReleaseFile(file) + + return buildOSRelease(values) +} + +// getOSReleaseFile returns a *os.File pointing to one of the well-known os-release +// files, according to their order of preference. If no file can be opened, it +// returns an error. +func getOSReleaseFile() (*os.File, error) { + return getFirstAvailableFile([]string{"/etc/os-release", "/usr/lib/os-release"}) +} + +// parseOSReleaseFile process the file pointed by `file` as an os-release file and +// returns a map with the key-values contained in it. Empty lines or lines starting +// with a '#' character are ignored, as well as lines with the missing key=value +// separator. Values are unquoted and unescaped. +func parseOSReleaseFile(file io.Reader) map[string]string { + values := make(map[string]string) + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := scanner.Text() + + if skip(line) { + continue + } + + key, value, ok := parse(line) + if ok { + values[key] = value + } + } + + return values +} + +// skip returns true if the line is blank or starts with a '#' character, and +// therefore should be skipped from processing. +func skip(line string) bool { + line = strings.TrimSpace(line) + + return len(line) == 0 || strings.HasPrefix(line, "#") +} + +// parse attempts to split the provided line on the first '=' character, and then +// sanitize each side of the split before returning them as a key-value pair. +func parse(line string) (string, string, bool) { + parts := strings.SplitN(line, "=", 2) + + if len(parts) != 2 || len(parts[0]) == 0 { + return "", "", false + } + + key := strings.TrimSpace(parts[0]) + value := unescape(unquote(strings.TrimSpace(parts[1]))) + + return key, value, true +} + +// unquote checks whether the string `s` is quoted with double or single quotes +// and, if so, returns a version of the string without them. Otherwise it returns +// the provided string unchanged. +func unquote(s string) string { + if len(s) < 2 { + return s + } + + if (s[0] == '"' || s[0] == '\'') && s[0] == s[len(s)-1] { + return s[1 : len(s)-1] + } + + return s +} + +// unescape removes the `\` prefix from some characters that are expected +// to have it added in front of them for escaping purposes. +func unescape(s string) string { + return strings.NewReplacer( + `\$`, `$`, + `\"`, `"`, + `\'`, `'`, + `\\`, `\`, + "\\`", "`", + ).Replace(s) +} + +// buildOSRelease builds a string describing the OS release based on the properties +// available on the provided map. It favors a combination of the `NAME` and `VERSION` +// properties as first option (falling back to `VERSION_ID` if `VERSION` isn't +// found), and using `PRETTY_NAME` alone if some of the previous are not present. If +// none of these properties are found, it returns an empty string. +// +// The rationale behind not using `PRETTY_NAME` as first choice was that, for some +// Linux distributions, it doesn't include the same detail that can be found on the +// individual `NAME` and `VERSION` properties, and combining `PRETTY_NAME` with +// other properties can produce "pretty" redundant strings in some cases. +func buildOSRelease(values map[string]string) string { + var osRelease string + + name := values["NAME"] + version := values["VERSION"] + + if version == "" { + version = values["VERSION_ID"] + } + + if name != "" && version != "" { + osRelease = fmt.Sprintf("%s %s", name, version) + } else { + osRelease = values["PRETTY_NAME"] + } + + return osRelease +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go new file mode 100644 index 00000000..42894a15 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos + +package resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "bytes" + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +type unameProvider func(buf *unix.Utsname) (err error) + +var defaultUnameProvider unameProvider = unix.Uname + +var currentUnameProvider = defaultUnameProvider + +func setDefaultUnameProvider() { + setUnameProvider(defaultUnameProvider) +} + +func setUnameProvider(unameProvider unameProvider) { + currentUnameProvider = unameProvider +} + +// platformOSDescription returns a human readable OS version information string. +// The final string combines OS release information (where available) and the +// result of the `uname` system call. +func platformOSDescription() (string, error) { + uname, err := uname() + if err != nil { + return "", err + } + + osRelease := osRelease() + if osRelease != "" { + return fmt.Sprintf("%s (%s)", osRelease, uname), nil + } + + return uname, nil +} + +// uname issues a uname(2) system call (or equivalent on systems which doesn't +// have one) and formats the output in a single string, similar to the output +// of the `uname` commandline program. The final string resembles the one +// obtained with a call to `uname -snrvm`. +func uname() (string, error) { + var utsName unix.Utsname + + err := currentUnameProvider(&utsName) + if err != nil { + return "", err + } + + return fmt.Sprintf("%s %s %s %s %s", + charsToString(utsName.Sysname[:]), + charsToString(utsName.Nodename[:]), + charsToString(utsName.Release[:]), + charsToString(utsName.Version[:]), + charsToString(utsName.Machine[:]), + ), nil +} + +// charsToString converts a C-like null-terminated char array to a Go string. +func charsToString(charArray []byte) string { + if i := bytes.IndexByte(charArray, 0); i >= 0 { + charArray = charArray[:i] + } + + return string(charArray) +} + +// getFirstAvailableFile returns an *os.File of the first available +// file from a list of candidate file paths. +func getFirstAvailableFile(candidates []string) (*os.File, error) { + for _, c := range candidates { + file, err := os.Open(c) + if err == nil { + return file, nil + } + } + + return nil, fmt.Errorf("no candidate file available: %v", candidates) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go new file mode 100644 index 00000000..3ebcb534 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +// +build !aix +// +build !darwin +// +build !dragonfly +// +build !freebsd +// +build !linux +// +build !netbsd +// +build !openbsd +// +build !solaris +// +build !windows +// +build !zos + +package resource // import "go.opentelemetry.io/otel/sdk/resource" + +// platformOSDescription is a placeholder implementation for OSes +// for which this project currently doesn't support os.description +// attribute detection. See build tags declaration early on this file +// for a list of unsupported OSes. +func platformOSDescription() (string, error) { + return "", nil +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go new file mode 100644 index 00000000..faad64d8 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go @@ -0,0 +1,101 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "fmt" + "strconv" + + "golang.org/x/sys/windows/registry" +) + +// platformOSDescription returns a human readable OS version information string. +// It does so by querying registry values under the +// `SOFTWARE\Microsoft\Windows NT\CurrentVersion` key. The final string +// resembles the one displayed by the Version Reporter Applet (winver.exe). +func platformOSDescription() (string, error) { + k, err := registry.OpenKey( + registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + + if err != nil { + return "", err + } + + defer k.Close() + + var ( + productName = readProductName(k) + displayVersion = readDisplayVersion(k) + releaseID = readReleaseID(k) + currentMajorVersionNumber = readCurrentMajorVersionNumber(k) + currentMinorVersionNumber = readCurrentMinorVersionNumber(k) + currentBuildNumber = readCurrentBuildNumber(k) + ubr = readUBR(k) + ) + + if displayVersion != "" { + displayVersion += " " + } + + return fmt.Sprintf("%s %s(%s) [Version %s.%s.%s.%s]", + productName, + displayVersion, + releaseID, + currentMajorVersionNumber, + currentMinorVersionNumber, + currentBuildNumber, + ubr, + ), nil +} + +func getStringValue(name string, k registry.Key) string { + value, _, _ := k.GetStringValue(name) + + return value +} + +func getIntegerValue(name string, k registry.Key) uint64 { + value, _, _ := k.GetIntegerValue(name) + + return value +} + +func readProductName(k registry.Key) string { + return getStringValue("ProductName", k) +} + +func readDisplayVersion(k registry.Key) string { + return getStringValue("DisplayVersion", k) +} + +func readReleaseID(k registry.Key) string { + return getStringValue("ReleaseID", k) +} + +func readCurrentMajorVersionNumber(k registry.Key) string { + return strconv.FormatUint(getIntegerValue("CurrentMajorVersionNumber", k), 10) +} + +func readCurrentMinorVersionNumber(k registry.Key) string { + return strconv.FormatUint(getIntegerValue("CurrentMinorVersionNumber", k), 10) +} + +func readCurrentBuildNumber(k registry.Key) string { + return getStringValue("CurrentBuildNumber", k) +} + +func readUBR(k registry.Key) string { + return strconv.FormatUint(getIntegerValue("UBR", k), 10) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/process.go b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go new file mode 100644 index 00000000..c1e5ae90 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go @@ -0,0 +1,180 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "fmt" + "os" + "os/user" + "path/filepath" + "runtime" + + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" +) + +type pidProvider func() int +type executablePathProvider func() (string, error) +type commandArgsProvider func() []string +type ownerProvider func() (*user.User, error) +type runtimeNameProvider func() string +type runtimeVersionProvider func() string +type runtimeOSProvider func() string +type runtimeArchProvider func() string + +var ( + defaultPidProvider pidProvider = os.Getpid + defaultExecutablePathProvider executablePathProvider = os.Executable + defaultCommandArgsProvider commandArgsProvider = func() []string { return os.Args } + defaultOwnerProvider ownerProvider = user.Current + defaultRuntimeNameProvider runtimeNameProvider = func() string { + if runtime.Compiler == "gc" { + return "go" + } + return runtime.Compiler + } + defaultRuntimeVersionProvider runtimeVersionProvider = runtime.Version + defaultRuntimeOSProvider runtimeOSProvider = func() string { return runtime.GOOS } + defaultRuntimeArchProvider runtimeArchProvider = func() string { return runtime.GOARCH } +) + +var ( + pid = defaultPidProvider + executablePath = defaultExecutablePathProvider + commandArgs = defaultCommandArgsProvider + owner = defaultOwnerProvider + runtimeName = defaultRuntimeNameProvider + runtimeVersion = defaultRuntimeVersionProvider + runtimeOS = defaultRuntimeOSProvider + runtimeArch = defaultRuntimeArchProvider +) + +func setDefaultOSProviders() { + setOSProviders( + defaultPidProvider, + defaultExecutablePathProvider, + defaultCommandArgsProvider, + ) +} + +func setOSProviders( + pidProvider pidProvider, + executablePathProvider executablePathProvider, + commandArgsProvider commandArgsProvider, +) { + pid = pidProvider + executablePath = executablePathProvider + commandArgs = commandArgsProvider +} + +func setDefaultRuntimeProviders() { + setRuntimeProviders( + defaultRuntimeNameProvider, + defaultRuntimeVersionProvider, + defaultRuntimeOSProvider, + defaultRuntimeArchProvider, + ) +} + +func setRuntimeProviders( + runtimeNameProvider runtimeNameProvider, + runtimeVersionProvider runtimeVersionProvider, + runtimeOSProvider runtimeOSProvider, + runtimeArchProvider runtimeArchProvider, +) { + runtimeName = runtimeNameProvider + runtimeVersion = runtimeVersionProvider + runtimeOS = runtimeOSProvider + runtimeArch = runtimeArchProvider +} + +func setDefaultUserProviders() { + setUserProviders(defaultOwnerProvider) +} + +func setUserProviders(ownerProvider ownerProvider) { + owner = ownerProvider +} + +type processPIDDetector struct{} +type processExecutableNameDetector struct{} +type processExecutablePathDetector struct{} +type processCommandArgsDetector struct{} +type processOwnerDetector struct{} +type processRuntimeNameDetector struct{} +type processRuntimeVersionDetector struct{} +type processRuntimeDescriptionDetector struct{} + +// Detect returns a *Resource that describes the process identifier (PID) of the +// executing process. +func (processPIDDetector) Detect(ctx context.Context) (*Resource, error) { + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessPIDKey.Int(pid())), nil +} + +// Detect returns a *Resource that describes the name of the process executable. +func (processExecutableNameDetector) Detect(ctx context.Context) (*Resource, error) { + executableName := filepath.Base(commandArgs()[0]) + + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessExecutableNameKey.String(executableName)), nil +} + +// Detect returns a *Resource that describes the full path of the process executable. +func (processExecutablePathDetector) Detect(ctx context.Context) (*Resource, error) { + executablePath, err := executablePath() + if err != nil { + return nil, err + } + + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessExecutablePathKey.String(executablePath)), nil +} + +// Detect returns a *Resource that describes all the command arguments as received +// by the process. +func (processCommandArgsDetector) Detect(ctx context.Context) (*Resource, error) { + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessCommandArgsKey.StringSlice(commandArgs())), nil +} + +// Detect returns a *Resource that describes the username of the user that owns the +// process. +func (processOwnerDetector) Detect(ctx context.Context) (*Resource, error) { + owner, err := owner() + if err != nil { + return nil, err + } + + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessOwnerKey.String(owner.Username)), nil +} + +// Detect returns a *Resource that describes the name of the compiler used to compile +// this process image. +func (processRuntimeNameDetector) Detect(ctx context.Context) (*Resource, error) { + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessRuntimeNameKey.String(runtimeName())), nil +} + +// Detect returns a *Resource that describes the version of the runtime of this process. +func (processRuntimeVersionDetector) Detect(ctx context.Context) (*Resource, error) { + return NewWithAttributes(semconv.SchemaURL, semconv.ProcessRuntimeVersionKey.String(runtimeVersion())), nil +} + +// Detect returns a *Resource that describes the runtime of this process. +func (processRuntimeDescriptionDetector) Detect(ctx context.Context) (*Resource, error) { + runtimeDescription := fmt.Sprintf( + "go version %s %s/%s", runtimeVersion(), runtimeOS(), runtimeArch()) + + return NewWithAttributes( + semconv.SchemaURL, + semconv.ProcessRuntimeDescriptionKey.String(runtimeDescription), + ), nil +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go new file mode 100644 index 00000000..e842744a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go @@ -0,0 +1,280 @@ +// Copyright The OpenTelemetry Authors +// +// 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 resource // import "go.opentelemetry.io/otel/sdk/resource" + +import ( + "context" + "errors" + "fmt" + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" +) + +// Resource describes an entity about which identifying information +// and metadata is exposed. Resource is an immutable object, +// equivalent to a map from key to unique value. +// +// Resources should be passed and stored as pointers +// (`*resource.Resource`). The `nil` value is equivalent to an empty +// Resource. +type Resource struct { + attrs attribute.Set + schemaURL string +} + +var ( + emptyResource Resource + defaultResource *Resource + defaultResourceOnce sync.Once +) + +var errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflicting Schema URL") + +// New returns a Resource combined from the user-provided detectors. +func New(ctx context.Context, opts ...Option) (*Resource, error) { + cfg := config{} + for _, opt := range opts { + cfg = opt.apply(cfg) + } + + resource, err := Detect(ctx, cfg.detectors...) + + var err2 error + resource, err2 = Merge(resource, &Resource{schemaURL: cfg.schemaURL}) + if err == nil { + err = err2 + } else if err2 != nil { + err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) + } + + return resource, err +} + +// NewWithAttributes creates a resource from attrs and associates the resource with a +// schema URL. If attrs contains duplicate keys, the last value will be used. If attrs +// contains any invalid items those items will be dropped. The attrs are assumed to be +// in a schema identified by schemaURL. +func NewWithAttributes(schemaURL string, attrs ...attribute.KeyValue) *Resource { + resource := NewSchemaless(attrs...) + resource.schemaURL = schemaURL + return resource +} + +// NewSchemaless creates a resource from attrs. If attrs contains duplicate keys, +// the last value will be used. If attrs contains any invalid items those items will +// be dropped. The resource will not be associated with a schema URL. If the schema +// of the attrs is known use NewWithAttributes instead. +func NewSchemaless(attrs ...attribute.KeyValue) *Resource { + if len(attrs) == 0 { + return &emptyResource + } + + // Ensure attributes comply with the specification: + // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.0.1/specification/common/common.md#attributes + s, _ := attribute.NewSetWithFiltered(attrs, func(kv attribute.KeyValue) bool { + return kv.Valid() + }) + + // If attrs only contains invalid entries do not allocate a new resource. + if s.Len() == 0 { + return &emptyResource + } + + return &Resource{attrs: s} //nolint +} + +// String implements the Stringer interface and provides a +// human-readable form of the resource. +// +// Avoid using this representation as the key in a map of resources, +// use Equivalent() as the key instead. +func (r *Resource) String() string { + if r == nil { + return "" + } + return r.attrs.Encoded(attribute.DefaultEncoder()) +} + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (r *Resource) MarshalLog() interface{} { + return struct { + Attributes attribute.Set + SchemaURL string + }{ + Attributes: r.attrs, + SchemaURL: r.schemaURL, + } +} + +// Attributes returns a copy of attributes from the resource in a sorted order. +// To avoid allocating a new slice, use an iterator. +func (r *Resource) Attributes() []attribute.KeyValue { + if r == nil { + r = Empty() + } + return r.attrs.ToSlice() +} + +func (r *Resource) SchemaURL() string { + if r == nil { + return "" + } + return r.schemaURL +} + +// Iter returns an iterator of the Resource attributes. +// This is ideal to use if you do not want a copy of the attributes. +func (r *Resource) Iter() attribute.Iterator { + if r == nil { + r = Empty() + } + return r.attrs.Iter() +} + +// Equal returns true when a Resource is equivalent to this Resource. +func (r *Resource) Equal(eq *Resource) bool { + if r == nil { + r = Empty() + } + if eq == nil { + eq = Empty() + } + return r.Equivalent() == eq.Equivalent() +} + +// Merge creates a new resource by combining resource a and b. +// +// If there are common keys between resource a and b, then the value +// from resource b will overwrite the value from resource a, even +// if resource b's value is empty. +// +// The SchemaURL of the resources will be merged according to the spec rules: +// https://github.com/open-telemetry/opentelemetry-specification/blob/bad49c714a62da5493f2d1d9bafd7ebe8c8ce7eb/specification/resource/sdk.md#merge +// If the resources have different non-empty schemaURL an empty resource and an error +// will be returned. +func Merge(a, b *Resource) (*Resource, error) { + if a == nil && b == nil { + return Empty(), nil + } + if a == nil { + return b, nil + } + if b == nil { + return a, nil + } + + // Merge the schema URL. + var schemaURL string + if a.schemaURL == "" { + schemaURL = b.schemaURL + } else if b.schemaURL == "" { + schemaURL = a.schemaURL + } else if a.schemaURL == b.schemaURL { + schemaURL = a.schemaURL + } else { + return Empty(), errMergeConflictSchemaURL + } + + // Note: 'b' attributes will overwrite 'a' with last-value-wins in attribute.Key() + // Meaning this is equivalent to: append(a.Attributes(), b.Attributes()...) + mi := attribute.NewMergeIterator(b.Set(), a.Set()) + combine := make([]attribute.KeyValue, 0, a.Len()+b.Len()) + for mi.Next() { + combine = append(combine, mi.Label()) + } + merged := NewWithAttributes(schemaURL, combine...) + return merged, nil +} + +// Empty returns an instance of Resource with no attributes. It is +// equivalent to a `nil` Resource. +func Empty() *Resource { + return &emptyResource +} + +// Default returns an instance of Resource with a default +// "service.name" and OpenTelemetrySDK attributes. +func Default() *Resource { + defaultResourceOnce.Do(func() { + var err error + defaultResource, err = Detect( + context.Background(), + defaultServiceNameDetector{}, + fromEnv{}, + telemetrySDK{}, + ) + if err != nil { + otel.Handle(err) + } + // If Detect did not return a valid resource, fall back to emptyResource. + if defaultResource == nil { + defaultResource = &emptyResource + } + }) + return defaultResource +} + +// Environment returns an instance of Resource with attributes +// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable. +func Environment() *Resource { + detector := &fromEnv{} + resource, err := detector.Detect(context.Background()) + if err != nil { + otel.Handle(err) + } + return resource +} + +// Equivalent returns an object that can be compared for equality +// between two resources. This value is suitable for use as a key in +// a map. +func (r *Resource) Equivalent() attribute.Distinct { + return r.Set().Equivalent() +} + +// Set returns the equivalent *attribute.Set of this resource's attributes. +func (r *Resource) Set() *attribute.Set { + if r == nil { + r = Empty() + } + return &r.attrs +} + +// MarshalJSON encodes the resource attributes as a JSON list of { "Key": +// "...", "Value": ... } pairs in order sorted by key. +func (r *Resource) MarshalJSON() ([]byte, error) { + if r == nil { + r = Empty() + } + return r.attrs.MarshalJSON() +} + +// Len returns the number of unique key-values in this Resource. +func (r *Resource) Len() int { + if r == nil { + return 0 + } + return r.attrs.Len() +} + +// Encoded returns an encoded representation of the resource. +func (r *Resource) Encoded(enc attribute.Encoder) string { + if r == nil { + return "" + } + return r.attrs.Encoded(enc) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go new file mode 100644 index 00000000..56847d9c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -0,0 +1,396 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "runtime" + "sync" + "sync/atomic" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/internal/env" + "go.opentelemetry.io/otel/trace" +) + +// Defaults for BatchSpanProcessorOptions. +const ( + DefaultMaxQueueSize = 2048 + DefaultScheduleDelay = 5000 + DefaultExportTimeout = 30000 + DefaultMaxExportBatchSize = 512 +) + +type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions) + +type BatchSpanProcessorOptions struct { + // MaxQueueSize is the maximum queue size to buffer spans for delayed processing. If the + // queue gets full it drops the spans. Use BlockOnQueueFull to change this behavior. + // The default value of MaxQueueSize is 2048. + MaxQueueSize int + + // BatchTimeout is the maximum duration for constructing a batch. Processor + // forcefully sends available spans when timeout is reached. + // The default value of BatchTimeout is 5000 msec. + BatchTimeout time.Duration + + // ExportTimeout specifies the maximum duration for exporting spans. If the timeout + // is reached, the export will be cancelled. + // The default value of ExportTimeout is 30000 msec. + ExportTimeout time.Duration + + // MaxExportBatchSize is the maximum number of spans to process in a single batch. + // If there are more than one batch worth of spans then it processes multiple batches + // of spans one batch after the other without any delay. + // The default value of MaxExportBatchSize is 512. + MaxExportBatchSize int + + // BlockOnQueueFull blocks onEnd() and onStart() method if the queue is full + // AND if BlockOnQueueFull is set to true. + // Blocking option should be used carefully as it can severely affect the performance of an + // application. + BlockOnQueueFull bool +} + +// batchSpanProcessor is a SpanProcessor that batches asynchronously-received +// spans and sends them to a trace.Exporter when complete. +type batchSpanProcessor struct { + e SpanExporter + o BatchSpanProcessorOptions + + queue chan ReadOnlySpan + dropped uint32 + + batch []ReadOnlySpan + batchMutex sync.Mutex + timer *time.Timer + stopWait sync.WaitGroup + stopOnce sync.Once + stopCh chan struct{} +} + +var _ SpanProcessor = (*batchSpanProcessor)(nil) + +// NewBatchSpanProcessor creates a new SpanProcessor that will send completed +// span batches to the exporter with the supplied options. +// +// If the exporter is nil, the span processor will preform no action. +func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorOption) SpanProcessor { + maxQueueSize := env.BatchSpanProcessorMaxQueueSize(DefaultMaxQueueSize) + maxExportBatchSize := env.BatchSpanProcessorMaxExportBatchSize(DefaultMaxExportBatchSize) + + if maxExportBatchSize > maxQueueSize { + if DefaultMaxExportBatchSize > maxQueueSize { + maxExportBatchSize = maxQueueSize + } else { + maxExportBatchSize = DefaultMaxExportBatchSize + } + } + + o := BatchSpanProcessorOptions{ + BatchTimeout: time.Duration(env.BatchSpanProcessorScheduleDelay(DefaultScheduleDelay)) * time.Millisecond, + ExportTimeout: time.Duration(env.BatchSpanProcessorExportTimeout(DefaultExportTimeout)) * time.Millisecond, + MaxQueueSize: maxQueueSize, + MaxExportBatchSize: maxExportBatchSize, + } + for _, opt := range options { + opt(&o) + } + bsp := &batchSpanProcessor{ + e: exporter, + o: o, + batch: make([]ReadOnlySpan, 0, o.MaxExportBatchSize), + timer: time.NewTimer(o.BatchTimeout), + queue: make(chan ReadOnlySpan, o.MaxQueueSize), + stopCh: make(chan struct{}), + } + + bsp.stopWait.Add(1) + go func() { + defer bsp.stopWait.Done() + bsp.processQueue() + bsp.drainQueue() + }() + + return bsp +} + +// OnStart method does nothing. +func (bsp *batchSpanProcessor) OnStart(parent context.Context, s ReadWriteSpan) {} + +// OnEnd method enqueues a ReadOnlySpan for later processing. +func (bsp *batchSpanProcessor) OnEnd(s ReadOnlySpan) { + // Do not enqueue spans if we are just going to drop them. + if bsp.e == nil { + return + } + bsp.enqueue(s) +} + +// Shutdown flushes the queue and waits until all spans are processed. +// It only executes once. Subsequent call does nothing. +func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error { + var err error + bsp.stopOnce.Do(func() { + wait := make(chan struct{}) + go func() { + close(bsp.stopCh) + bsp.stopWait.Wait() + if bsp.e != nil { + if err := bsp.e.Shutdown(ctx); err != nil { + otel.Handle(err) + } + } + close(wait) + }() + // Wait until the wait group is done or the context is cancelled + select { + case <-wait: + case <-ctx.Done(): + err = ctx.Err() + } + }) + return err +} + +type forceFlushSpan struct { + ReadOnlySpan + flushed chan struct{} +} + +func (f forceFlushSpan) SpanContext() trace.SpanContext { + return trace.NewSpanContext(trace.SpanContextConfig{TraceFlags: trace.FlagsSampled}) +} + +// ForceFlush exports all ended spans that have not yet been exported. +func (bsp *batchSpanProcessor) ForceFlush(ctx context.Context) error { + var err error + if bsp.e != nil { + flushCh := make(chan struct{}) + if bsp.enqueueBlockOnQueueFull(ctx, forceFlushSpan{flushed: flushCh}, true) { + select { + case <-flushCh: + // Processed any items in queue prior to ForceFlush being called + case <-ctx.Done(): + return ctx.Err() + } + } + + wait := make(chan error) + go func() { + wait <- bsp.exportSpans(ctx) + close(wait) + }() + // Wait until the export is finished or the context is cancelled/timed out + select { + case err = <-wait: + case <-ctx.Done(): + err = ctx.Err() + } + } + return err +} + +func WithMaxQueueSize(size int) BatchSpanProcessorOption { + return func(o *BatchSpanProcessorOptions) { + o.MaxQueueSize = size + } +} + +func WithMaxExportBatchSize(size int) BatchSpanProcessorOption { + return func(o *BatchSpanProcessorOptions) { + o.MaxExportBatchSize = size + } +} + +func WithBatchTimeout(delay time.Duration) BatchSpanProcessorOption { + return func(o *BatchSpanProcessorOptions) { + o.BatchTimeout = delay + } +} + +func WithExportTimeout(timeout time.Duration) BatchSpanProcessorOption { + return func(o *BatchSpanProcessorOptions) { + o.ExportTimeout = timeout + } +} + +func WithBlocking() BatchSpanProcessorOption { + return func(o *BatchSpanProcessorOptions) { + o.BlockOnQueueFull = true + } +} + +// exportSpans is a subroutine of processing and draining the queue. +func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { + + bsp.timer.Reset(bsp.o.BatchTimeout) + + bsp.batchMutex.Lock() + defer bsp.batchMutex.Unlock() + + if bsp.o.ExportTimeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, bsp.o.ExportTimeout) + defer cancel() + } + + if l := len(bsp.batch); l > 0 { + global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped)) + err := bsp.e.ExportSpans(ctx, bsp.batch) + + // A new batch is always created after exporting, even if the batch failed to be exported. + // + // It is up to the exporter to implement any type of retry logic if a batch is failing + // to be exported, since it is specific to the protocol and backend being sent to. + bsp.batch = bsp.batch[:0] + + if err != nil { + return err + } + } + return nil +} + +// processQueue removes spans from the `queue` channel until processor +// is shut down. It calls the exporter in batches of up to MaxExportBatchSize +// waiting up to BatchTimeout to form a batch. +func (bsp *batchSpanProcessor) processQueue() { + defer bsp.timer.Stop() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for { + select { + case <-bsp.stopCh: + return + case <-bsp.timer.C: + if err := bsp.exportSpans(ctx); err != nil { + otel.Handle(err) + } + case sd := <-bsp.queue: + if ffs, ok := sd.(forceFlushSpan); ok { + close(ffs.flushed) + continue + } + bsp.batchMutex.Lock() + bsp.batch = append(bsp.batch, sd) + shouldExport := len(bsp.batch) >= bsp.o.MaxExportBatchSize + bsp.batchMutex.Unlock() + if shouldExport { + if !bsp.timer.Stop() { + <-bsp.timer.C + } + if err := bsp.exportSpans(ctx); err != nil { + otel.Handle(err) + } + } + } + } +} + +// drainQueue awaits the any caller that had added to bsp.stopWait +// to finish the enqueue, then exports the final batch. +func (bsp *batchSpanProcessor) drainQueue() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + for { + select { + case sd := <-bsp.queue: + if sd == nil { + if err := bsp.exportSpans(ctx); err != nil { + otel.Handle(err) + } + return + } + + bsp.batchMutex.Lock() + bsp.batch = append(bsp.batch, sd) + shouldExport := len(bsp.batch) == bsp.o.MaxExportBatchSize + bsp.batchMutex.Unlock() + + if shouldExport { + if err := bsp.exportSpans(ctx); err != nil { + otel.Handle(err) + } + } + default: + close(bsp.queue) + } + } +} + +func (bsp *batchSpanProcessor) enqueue(sd ReadOnlySpan) { + bsp.enqueueBlockOnQueueFull(context.TODO(), sd, bsp.o.BlockOnQueueFull) +} + +func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd ReadOnlySpan, block bool) bool { + if !sd.SpanContext().IsSampled() { + return false + } + + // This ensures the bsp.queue<- below does not panic as the + // processor shuts down. + defer func() { + x := recover() + switch err := x.(type) { + case nil: + return + case runtime.Error: + if err.Error() == "send on closed channel" { + return + } + } + panic(x) + }() + + select { + case <-bsp.stopCh: + return false + default: + } + + if block { + select { + case bsp.queue <- sd: + return true + case <-ctx.Done(): + return false + } + } + + select { + case bsp.queue <- sd: + return true + default: + atomic.AddUint32(&bsp.dropped, 1) + } + return false +} + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (bsp *batchSpanProcessor) MarshalLog() interface{} { + return struct { + Type string + SpanExporter SpanExporter + Config BatchSpanProcessorOptions + }{ + Type: "BatchSpanProcessor", + SpanExporter: bsp.e, + Config: bsp.o, + } +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go b/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go new file mode 100644 index 00000000..0285e99b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace contains support for OpenTelemetry distributed tracing. + +The following assumes a basic familiarity with OpenTelemetry concepts. +See https://opentelemetry.io. +*/ +package trace // import "go.opentelemetry.io/otel/sdk/trace" diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/event.go b/vendor/go.opentelemetry.io/otel/sdk/trace/event.go new file mode 100644 index 00000000..1e3b4267 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/event.go @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "time" + + "go.opentelemetry.io/otel/attribute" +) + +// Event is a thing that happened during a Span's lifetime. +type Event struct { + // Name is the name of this event + Name string + + // Attributes describe the aspects of the event. + Attributes []attribute.KeyValue + + // DroppedAttributeCount is the number of attributes that were not + // recorded due to configured limits being reached. + DroppedAttributeCount int + + // Time at which this event was recorded. + Time time.Time +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go b/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go new file mode 100644 index 00000000..d1c86e59 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/evictedqueue.go @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +// evictedQueue is a FIFO queue with a configurable capacity. +type evictedQueue struct { + queue []interface{} + capacity int + droppedCount int +} + +func newEvictedQueue(capacity int) evictedQueue { + // Do not pre-allocate queue, do this lazily. + return evictedQueue{capacity: capacity} +} + +// add adds value to the evictedQueue eq. If eq is at capacity, the oldest +// queued value will be discarded and the drop count incremented. +func (eq *evictedQueue) add(value interface{}) { + if eq.capacity == 0 { + eq.droppedCount++ + return + } + + if eq.capacity > 0 && len(eq.queue) == eq.capacity { + // Drop first-in while avoiding allocating more capacity to eq.queue. + copy(eq.queue[:eq.capacity-1], eq.queue[1:]) + eq.queue = eq.queue[:eq.capacity-1] + eq.droppedCount++ + } + eq.queue = append(eq.queue, value) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go b/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go new file mode 100644 index 00000000..c9e2802a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/id_generator.go @@ -0,0 +1,77 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + crand "crypto/rand" + "encoding/binary" + "math/rand" + "sync" + + "go.opentelemetry.io/otel/trace" +) + +// IDGenerator allows custom generators for TraceID and SpanID. +type IDGenerator interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // NewIDs returns a new trace and span ID. + NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // NewSpanID returns a ID for a new span in the trace with traceID. + NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +type randomIDGenerator struct { + sync.Mutex + randSource *rand.Rand +} + +var _ IDGenerator = &randomIDGenerator{} + +// NewSpanID returns a non-zero span ID from a randomly-chosen sequence. +func (gen *randomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID { + gen.Lock() + defer gen.Unlock() + sid := trace.SpanID{} + gen.randSource.Read(sid[:]) + return sid +} + +// NewIDs returns a non-zero trace ID and a non-zero span ID from a +// randomly-chosen sequence. +func (gen *randomIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) { + gen.Lock() + defer gen.Unlock() + tid := trace.TraceID{} + gen.randSource.Read(tid[:]) + sid := trace.SpanID{} + gen.randSource.Read(sid[:]) + return tid, sid +} + +func defaultIDGenerator() IDGenerator { + gen := &randomIDGenerator{} + var rngSeed int64 + _ = binary.Read(crand.Reader, binary.LittleEndian, &rngSeed) + gen.randSource = rand.New(rand.NewSource(rngSeed)) + return gen +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/link.go b/vendor/go.opentelemetry.io/otel/sdk/trace/link.go new file mode 100644 index 00000000..19cfea4b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/link.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// Link is the relationship between two Spans. The relationship can be within +// the same Trace or across different Traces. +type Link struct { + // SpanContext of the linked Span. + SpanContext trace.SpanContext + + // Attributes describe the aspects of the link. + Attributes []attribute.KeyValue + + // DroppedAttributeCount is the number of attributes that were not + // recorded due to configured limits being reached. + DroppedAttributeCount int +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go new file mode 100644 index 00000000..77b86a84 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -0,0 +1,450 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/trace" +) + +const ( + defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" +) + +// tracerProviderConfig +type tracerProviderConfig struct { + // processors contains collection of SpanProcessors that are processing pipeline + // for spans in the trace signal. + // SpanProcessors registered with a TracerProvider and are called at the start + // and end of a Span's lifecycle, and are called in the order they are + // registered. + processors []SpanProcessor + + // sampler is the default sampler used when creating new spans. + sampler Sampler + + // idGenerator is used to generate all Span and Trace IDs when needed. + idGenerator IDGenerator + + // spanLimits defines the attribute, event, and link limits for spans. + spanLimits SpanLimits + + // resource contains attributes representing an entity that produces telemetry. + resource *resource.Resource +} + +// MarshalLog is the marshaling function used by the logging system to represent this exporter. +func (cfg tracerProviderConfig) MarshalLog() interface{} { + return struct { + SpanProcessors []SpanProcessor + SamplerType string + IDGeneratorType string + SpanLimits SpanLimits + Resource *resource.Resource + }{ + SpanProcessors: cfg.processors, + SamplerType: fmt.Sprintf("%T", cfg.sampler), + IDGeneratorType: fmt.Sprintf("%T", cfg.idGenerator), + SpanLimits: cfg.spanLimits, + Resource: cfg.resource, + } +} + +type TracerProvider struct { + mu sync.Mutex + namedTracer map[instrumentation.Library]*tracer + spanProcessors atomic.Value + + // These fields are not protected by the lock mu. They are assumed to be + // immutable after creation of the TracerProvider. + sampler Sampler + idGenerator IDGenerator + spanLimits SpanLimits + resource *resource.Resource +} + +var _ trace.TracerProvider = &TracerProvider{} + +// NewTracerProvider returns a new and configured TracerProvider. +// +// By default the returned TracerProvider is configured with: +// - a ParentBased(AlwaysSample) Sampler +// - a random number IDGenerator +// - the resource.Default() Resource +// - the default SpanLimits. +// +// The passed opts are used to override these default values and configure the +// returned TracerProvider appropriately. +func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { + o := tracerProviderConfig{ + spanLimits: NewSpanLimits(), + } + o = applyTracerProviderEnvConfigs(o) + + for _, opt := range opts { + o = opt.apply(o) + } + + o = ensureValidTracerProviderConfig(o) + + tp := &TracerProvider{ + namedTracer: make(map[instrumentation.Library]*tracer), + sampler: o.sampler, + idGenerator: o.idGenerator, + spanLimits: o.spanLimits, + resource: o.resource, + } + + global.Info("TracerProvider created", "config", o) + + for _, sp := range o.processors { + tp.RegisterSpanProcessor(sp) + } + + return tp +} + +// Tracer returns a Tracer with the given name and options. If a Tracer for +// the given name and options does not exist it is created, otherwise the +// existing Tracer is returned. +// +// If name is empty, DefaultTracerName is used instead. +// +// This method is safe to be called concurrently. +func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { + c := trace.NewTracerConfig(opts...) + + p.mu.Lock() + defer p.mu.Unlock() + if name == "" { + name = defaultTracerName + } + il := instrumentation.Library{ + Name: name, + Version: c.InstrumentationVersion(), + SchemaURL: c.SchemaURL(), + } + t, ok := p.namedTracer[il] + if !ok { + t = &tracer{ + provider: p, + instrumentationLibrary: il, + } + p.namedTracer[il] = t + global.Info("Tracer created", "name", name, "version", c.InstrumentationVersion(), "schemaURL", c.SchemaURL()) + } + return t +} + +// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors +func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) { + p.mu.Lock() + defer p.mu.Unlock() + new := spanProcessorStates{} + if old, ok := p.spanProcessors.Load().(spanProcessorStates); ok { + new = append(new, old...) + } + newSpanSync := &spanProcessorState{ + sp: s, + state: &sync.Once{}, + } + new = append(new, newSpanSync) + p.spanProcessors.Store(new) +} + +// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors +func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) { + p.mu.Lock() + defer p.mu.Unlock() + spss := spanProcessorStates{} + old, ok := p.spanProcessors.Load().(spanProcessorStates) + if !ok || len(old) == 0 { + return + } + spss = append(spss, old...) + + // stop the span processor if it is started and remove it from the list + var stopOnce *spanProcessorState + var idx int + for i, sps := range spss { + if sps.sp == s { + stopOnce = sps + idx = i + } + } + if stopOnce != nil { + stopOnce.state.Do(func() { + if err := s.Shutdown(context.Background()); err != nil { + otel.Handle(err) + } + }) + } + if len(spss) > 1 { + copy(spss[idx:], spss[idx+1:]) + } + spss[len(spss)-1] = nil + spss = spss[:len(spss)-1] + + p.spanProcessors.Store(spss) +} + +// ForceFlush immediately exports all spans that have not yet been exported for +// all the registered span processors. +func (p *TracerProvider) ForceFlush(ctx context.Context) error { + spss, ok := p.spanProcessors.Load().(spanProcessorStates) + if !ok { + return fmt.Errorf("failed to load span processors") + } + if len(spss) == 0 { + return nil + } + + for _, sps := range spss { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + if err := sps.sp.ForceFlush(ctx); err != nil { + return err + } + } + return nil +} + +// Shutdown shuts down the span processors in the order they were registered. +func (p *TracerProvider) Shutdown(ctx context.Context) error { + spss, ok := p.spanProcessors.Load().(spanProcessorStates) + if !ok { + return fmt.Errorf("failed to load span processors") + } + if len(spss) == 0 { + return nil + } + + for _, sps := range spss { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + var err error + sps.state.Do(func() { + err = sps.sp.Shutdown(ctx) + }) + if err != nil { + return err + } + } + return nil +} + +type TracerProviderOption interface { + apply(tracerProviderConfig) tracerProviderConfig +} + +type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig + +func (fn traceProviderOptionFunc) apply(cfg tracerProviderConfig) tracerProviderConfig { + return fn(cfg) +} + +// WithSyncer registers the exporter with the TracerProvider using a +// SimpleSpanProcessor. +// +// This is not recommended for production use. The synchronous nature of the +// SimpleSpanProcessor that will wrap the exporter make it good for testing, +// debugging, or showing examples of other feature, but it will be slow and +// have a high computation resource usage overhead. The WithBatcher option is +// recommended for production use instead. +func WithSyncer(e SpanExporter) TracerProviderOption { + return WithSpanProcessor(NewSimpleSpanProcessor(e)) +} + +// WithBatcher registers the exporter with the TracerProvider using a +// BatchSpanProcessor configured with the passed opts. +func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption { + return WithSpanProcessor(NewBatchSpanProcessor(e, opts...)) +} + +// WithSpanProcessor registers the SpanProcessor with a TracerProvider. +func WithSpanProcessor(sp SpanProcessor) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + cfg.processors = append(cfg.processors, sp) + return cfg + }) +} + +// WithResource returns a TracerProviderOption that will configure the +// Resource r as a TracerProvider's Resource. The configured Resource is +// referenced by all the Tracers the TracerProvider creates. It represents the +// entity producing telemetry. +// +// If this option is not used, the TracerProvider will use the +// resource.Default() Resource by default. +func WithResource(r *resource.Resource) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + var err error + cfg.resource, err = resource.Merge(resource.Environment(), r) + if err != nil { + otel.Handle(err) + } + return cfg + }) +} + +// WithIDGenerator returns a TracerProviderOption that will configure the +// IDGenerator g as a TracerProvider's IDGenerator. The configured IDGenerator +// is used by the Tracers the TracerProvider creates to generate new Span and +// Trace IDs. +// +// If this option is not used, the TracerProvider will use a random number +// IDGenerator by default. +func WithIDGenerator(g IDGenerator) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + if g != nil { + cfg.idGenerator = g + } + return cfg + }) +} + +// WithSampler returns a TracerProviderOption that will configure the Sampler +// s as a TracerProvider's Sampler. The configured Sampler is used by the +// Tracers the TracerProvider creates to make their sampling decisions for the +// Spans they create. +// +// This option overrides the Sampler configured through the OTEL_TRACES_SAMPLER +// and OTEL_TRACES_SAMPLER_ARG environment variables. If this option is not used +// and the sampler is not configured through environment variables or the environment +// contains invalid/unsupported configuration, the TracerProvider will use a +// ParentBased(AlwaysSample) Sampler by default. +func WithSampler(s Sampler) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + if s != nil { + cfg.sampler = s + } + return cfg + }) +} + +// WithSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use the SpanLimits sl. These SpanLimits bound any Span +// created by a Tracer from the TracerProvider. +// +// If any field of sl is zero or negative it will be replaced with the default +// value for that field. +// +// If this or WithRawSpanLimits are not provided, the TracerProvider will use +// the limits defined by environment variables, or the defaults if unset. +// Refer to the NewSpanLimits documentation for information about this +// relationship. +// +// Deprecated: Use WithRawSpanLimits instead which allows setting unlimited +// and zero limits. This option will be kept until the next major version +// incremented release. +func WithSpanLimits(sl SpanLimits) TracerProviderOption { + if sl.AttributeValueLengthLimit <= 0 { + sl.AttributeValueLengthLimit = DefaultAttributeValueLengthLimit + } + if sl.AttributeCountLimit <= 0 { + sl.AttributeCountLimit = DefaultAttributeCountLimit + } + if sl.EventCountLimit <= 0 { + sl.EventCountLimit = DefaultEventCountLimit + } + if sl.AttributePerEventCountLimit <= 0 { + sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit + } + if sl.LinkCountLimit <= 0 { + sl.LinkCountLimit = DefaultLinkCountLimit + } + if sl.AttributePerLinkCountLimit <= 0 { + sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit + } + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + cfg.spanLimits = sl + return cfg + }) +} + +// WithRawSpanLimits returns a TracerProviderOption that configures a +// TracerProvider to use these limits. These limits bound any Span created by +// a Tracer from the TracerProvider. +// +// The limits will be used as-is. Zero or negative values will not be changed +// to the default value like WithSpanLimits does. Setting a limit to zero will +// effectively disable the related resource it limits and setting to a +// negative value will mean that resource is unlimited. Consequentially, this +// means that the zero-value SpanLimits will disable all span resources. +// Because of this, limits should be constructed using NewSpanLimits and +// updated accordingly. +// +// If this or WithSpanLimits are not provided, the TracerProvider will use the +// limits defined by environment variables, or the defaults if unset. Refer to +// the NewSpanLimits documentation for information about this relationship. +func WithRawSpanLimits(limits SpanLimits) TracerProviderOption { + return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig { + cfg.spanLimits = limits + return cfg + }) +} + +func applyTracerProviderEnvConfigs(cfg tracerProviderConfig) tracerProviderConfig { + for _, opt := range tracerProviderOptionsFromEnv() { + cfg = opt.apply(cfg) + } + + return cfg +} + +func tracerProviderOptionsFromEnv() []TracerProviderOption { + var opts []TracerProviderOption + + sampler, err := samplerFromEnv() + if err != nil { + otel.Handle(err) + } + + if sampler != nil { + opts = append(opts, WithSampler(sampler)) + } + + return opts +} + +// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid. +func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig { + if cfg.sampler == nil { + cfg.sampler = ParentBased(AlwaysSample()) + } + if cfg.idGenerator == nil { + cfg.idGenerator = defaultIDGenerator() + } + if cfg.resource == nil { + cfg.resource = resource.Default() + } + return cfg +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go b/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go new file mode 100644 index 00000000..97f80576 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/sampler_env.go @@ -0,0 +1,107 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" +) + +const ( + tracesSamplerKey = "OTEL_TRACES_SAMPLER" + tracesSamplerArgKey = "OTEL_TRACES_SAMPLER_ARG" + + samplerAlwaysOn = "always_on" + samplerAlwaysOff = "always_off" + samplerTraceIDRatio = "traceidratio" + samplerParentBasedAlwaysOn = "parentbased_always_on" + samplerParsedBasedAlwaysOff = "parentbased_always_off" + samplerParentBasedTraceIDRatio = "parentbased_traceidratio" +) + +type errUnsupportedSampler string + +func (e errUnsupportedSampler) Error() string { + return fmt.Sprintf("unsupported sampler: %s", string(e)) +} + +var ( + errNegativeTraceIDRatio = errors.New("invalid trace ID ratio: less than 0.0") + errGreaterThanOneTraceIDRatio = errors.New("invalid trace ID ratio: greater than 1.0") +) + +type samplerArgParseError struct { + parseErr error +} + +func (e samplerArgParseError) Error() string { + return fmt.Sprintf("parsing sampler argument: %s", e.parseErr.Error()) +} + +func (e samplerArgParseError) Unwrap() error { + return e.parseErr +} + +func samplerFromEnv() (Sampler, error) { + sampler, ok := os.LookupEnv(tracesSamplerKey) + if !ok { + return nil, nil + } + + sampler = strings.ToLower(strings.TrimSpace(sampler)) + samplerArg, hasSamplerArg := os.LookupEnv(tracesSamplerArgKey) + samplerArg = strings.TrimSpace(samplerArg) + + switch sampler { + case samplerAlwaysOn: + return AlwaysSample(), nil + case samplerAlwaysOff: + return NeverSample(), nil + case samplerTraceIDRatio: + ratio, err := parseTraceIDRatio(samplerArg, hasSamplerArg) + return ratio, err + case samplerParentBasedAlwaysOn: + return ParentBased(AlwaysSample()), nil + case samplerParsedBasedAlwaysOff: + return ParentBased(NeverSample()), nil + case samplerParentBasedTraceIDRatio: + ratio, err := parseTraceIDRatio(samplerArg, hasSamplerArg) + return ParentBased(ratio), err + default: + return nil, errUnsupportedSampler(sampler) + } + +} + +func parseTraceIDRatio(arg string, hasSamplerArg bool) (Sampler, error) { + if !hasSamplerArg { + return TraceIDRatioBased(1.0), nil + } + v, err := strconv.ParseFloat(arg, 64) + if err != nil { + return TraceIDRatioBased(1.0), samplerArgParseError{err} + } + if v < 0.0 { + return TraceIDRatioBased(1.0), errNegativeTraceIDRatio + } + if v > 1.0 { + return TraceIDRatioBased(1.0), errGreaterThanOneTraceIDRatio + } + + return TraceIDRatioBased(v), nil +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go new file mode 100644 index 00000000..a4ac588f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go @@ -0,0 +1,292 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "encoding/binary" + "fmt" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// Sampler decides whether a trace should be sampled and exported. +type Sampler interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // ShouldSample returns a SamplingResult based on a decision made from the + // passed parameters. + ShouldSample(parameters SamplingParameters) SamplingResult + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Description returns information describing the Sampler. + Description() string + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +// SamplingParameters contains the values passed to a Sampler. +type SamplingParameters struct { + ParentContext context.Context + TraceID trace.TraceID + Name string + Kind trace.SpanKind + Attributes []attribute.KeyValue + Links []trace.Link +} + +// SamplingDecision indicates whether a span is dropped, recorded and/or sampled. +type SamplingDecision uint8 + +// Valid sampling decisions +const ( + // Drop will not record the span and all attributes/events will be dropped + Drop SamplingDecision = iota + + // Record indicates the span's `IsRecording() == true`, but `Sampled` flag + // *must not* be set + RecordOnly + + // RecordAndSample has span's `IsRecording() == true` and `Sampled` flag + // *must* be set + RecordAndSample +) + +// SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate. +type SamplingResult struct { + Decision SamplingDecision + Attributes []attribute.KeyValue + Tracestate trace.TraceState +} + +type traceIDRatioSampler struct { + traceIDUpperBound uint64 + description string +} + +func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult { + psc := trace.SpanContextFromContext(p.ParentContext) + x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1 + if x < ts.traceIDUpperBound { + return SamplingResult{ + Decision: RecordAndSample, + Tracestate: psc.TraceState(), + } + } + return SamplingResult{ + Decision: Drop, + Tracestate: psc.TraceState(), + } +} + +func (ts traceIDRatioSampler) Description() string { + return ts.description +} + +// TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will +// always sample. Fractions < 0 are treated as zero. To respect the +// parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used +// as a delegate of a `Parent` sampler. +//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased` +func TraceIDRatioBased(fraction float64) Sampler { + if fraction >= 1 { + return AlwaysSample() + } + + if fraction <= 0 { + fraction = 0 + } + + return &traceIDRatioSampler{ + traceIDUpperBound: uint64(fraction * (1 << 63)), + description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction), + } +} + +type alwaysOnSampler struct{} + +func (as alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult { + return SamplingResult{ + Decision: RecordAndSample, + Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), + } +} + +func (as alwaysOnSampler) Description() string { + return "AlwaysOnSampler" +} + +// AlwaysSample returns a Sampler that samples every trace. +// Be careful about using this sampler in a production application with +// significant traffic: a new trace will be started and exported for every +// request. +func AlwaysSample() Sampler { + return alwaysOnSampler{} +} + +type alwaysOffSampler struct{} + +func (as alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult { + return SamplingResult{ + Decision: Drop, + Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), + } +} + +func (as alwaysOffSampler) Description() string { + return "AlwaysOffSampler" +} + +// NeverSample returns a Sampler that samples no traces. +func NeverSample() Sampler { + return alwaysOffSampler{} +} + +// ParentBased returns a composite sampler which behaves differently, +// based on the parent of the span. If the span has no parent, +// the root(Sampler) is used to make sampling decision. If the span has +// a parent, depending on whether the parent is remote and whether it +// is sampled, one of the following samplers will apply: +// - remoteParentSampled(Sampler) (default: AlwaysOn) +// - remoteParentNotSampled(Sampler) (default: AlwaysOff) +// - localParentSampled(Sampler) (default: AlwaysOn) +// - localParentNotSampled(Sampler) (default: AlwaysOff) +func ParentBased(root Sampler, samplers ...ParentBasedSamplerOption) Sampler { + return parentBased{ + root: root, + config: configureSamplersForParentBased(samplers), + } +} + +type parentBased struct { + root Sampler + config samplerConfig +} + +func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) samplerConfig { + c := samplerConfig{ + remoteParentSampled: AlwaysSample(), + remoteParentNotSampled: NeverSample(), + localParentSampled: AlwaysSample(), + localParentNotSampled: NeverSample(), + } + + for _, so := range samplers { + c = so.apply(c) + } + + return c +} + +// samplerConfig is a group of options for parentBased sampler. +type samplerConfig struct { + remoteParentSampled, remoteParentNotSampled Sampler + localParentSampled, localParentNotSampled Sampler +} + +// ParentBasedSamplerOption configures the sampler for a particular sampling case. +type ParentBasedSamplerOption interface { + apply(samplerConfig) samplerConfig +} + +// WithRemoteParentSampled sets the sampler for the case of sampled remote parent. +func WithRemoteParentSampled(s Sampler) ParentBasedSamplerOption { + return remoteParentSampledOption{s} +} + +type remoteParentSampledOption struct { + s Sampler +} + +func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig { + config.remoteParentSampled = o.s + return config +} + +// WithRemoteParentNotSampled sets the sampler for the case of remote parent +// which is not sampled. +func WithRemoteParentNotSampled(s Sampler) ParentBasedSamplerOption { + return remoteParentNotSampledOption{s} +} + +type remoteParentNotSampledOption struct { + s Sampler +} + +func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig { + config.remoteParentNotSampled = o.s + return config +} + +// WithLocalParentSampled sets the sampler for the case of sampled local parent. +func WithLocalParentSampled(s Sampler) ParentBasedSamplerOption { + return localParentSampledOption{s} +} + +type localParentSampledOption struct { + s Sampler +} + +func (o localParentSampledOption) apply(config samplerConfig) samplerConfig { + config.localParentSampled = o.s + return config +} + +// WithLocalParentNotSampled sets the sampler for the case of local parent +// which is not sampled. +func WithLocalParentNotSampled(s Sampler) ParentBasedSamplerOption { + return localParentNotSampledOption{s} +} + +type localParentNotSampledOption struct { + s Sampler +} + +func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig { + config.localParentNotSampled = o.s + return config +} + +func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult { + psc := trace.SpanContextFromContext(p.ParentContext) + if psc.IsValid() { + if psc.IsRemote() { + if psc.IsSampled() { + return pb.config.remoteParentSampled.ShouldSample(p) + } + return pb.config.remoteParentNotSampled.ShouldSample(p) + } + + if psc.IsSampled() { + return pb.config.localParentSampled.ShouldSample(p) + } + return pb.config.localParentNotSampled.ShouldSample(p) + } + return pb.root.ShouldSample(p) +} + +func (pb parentBased) Description() string { + return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+ + "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", + pb.root.Description(), + pb.config.remoteParentSampled.Description(), + pb.config.remoteParentNotSampled.Description(), + pb.config.localParentSampled.Description(), + pb.config.localParentNotSampled.Description(), + ) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go new file mode 100644 index 00000000..e8530a95 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go @@ -0,0 +1,128 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "sync" + + "go.opentelemetry.io/otel" +) + +// simpleSpanProcessor is a SpanProcessor that synchronously sends all +// completed Spans to a trace.Exporter immediately. +type simpleSpanProcessor struct { + exporterMu sync.RWMutex + exporter SpanExporter + stopOnce sync.Once +} + +var _ SpanProcessor = (*simpleSpanProcessor)(nil) + +// NewSimpleSpanProcessor returns a new SpanProcessor that will synchronously +// send completed spans to the exporter immediately. +// +// This SpanProcessor is not recommended for production use. The synchronous +// nature of this SpanProcessor make it good for testing, debugging, or +// showing examples of other feature, but it will be slow and have a high +// computation resource usage overhead. The BatchSpanProcessor is recommended +// for production use instead. +func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { + ssp := &simpleSpanProcessor{ + exporter: exporter, + } + return ssp +} + +// OnStart does nothing. +func (ssp *simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} + +// OnEnd immediately exports a ReadOnlySpan. +func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { + ssp.exporterMu.RLock() + defer ssp.exporterMu.RUnlock() + + if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { + if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { + otel.Handle(err) + } + } +} + +// Shutdown shuts down the exporter this SimpleSpanProcessor exports to. +func (ssp *simpleSpanProcessor) Shutdown(ctx context.Context) error { + var err error + ssp.stopOnce.Do(func() { + stopFunc := func(exp SpanExporter) (<-chan error, func()) { + done := make(chan error) + return done, func() { done <- exp.Shutdown(ctx) } + } + + // The exporter field of the simpleSpanProcessor needs to be zeroed to + // signal it is shut down, meaning all subsequent calls to OnEnd will + // be gracefully ignored. This needs to be done synchronously to avoid + // any race condition. + // + // A closure is used to keep reference to the exporter and then the + // field is zeroed. This ensures the simpleSpanProcessor is shut down + // before the exporter. This order is important as it avoids a + // potential deadlock. If the exporter shut down operation generates a + // span, that span would need to be exported. Meaning, OnEnd would be + // called and try acquiring the lock that is held here. + ssp.exporterMu.Lock() + done, shutdown := stopFunc(ssp.exporter) + ssp.exporter = nil + ssp.exporterMu.Unlock() + + go shutdown() + + // Wait for the exporter to shut down or the deadline to expire. + select { + case err = <-done: + case <-ctx.Done(): + // It is possible for the exporter to have immediately shut down + // and the context to be done simultaneously. In that case this + // outer select statement will randomly choose a case. This will + // result in a different returned error for similar scenarios. + // Instead, double check if the exporter shut down at the same + // time and return that error if so. This will ensure consistency + // as well as ensure the caller knows the exporter shut down + // successfully (they can already determine if the deadline is + // expired given they passed the context). + select { + case err = <-done: + default: + err = ctx.Err() + } + } + }) + return err +} + +// ForceFlush does nothing as there is no data to flush. +func (ssp *simpleSpanProcessor) ForceFlush(context.Context) error { + return nil +} + +// MarshalLog is the marshaling function used by the logging system to represent this Span Processor. +func (ssp *simpleSpanProcessor) MarshalLog() interface{} { + return struct { + Type string + Exporter SpanExporter + }{ + Type: "SimpleSpanProcessor", + Exporter: ssp.exporter, + } +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go b/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go new file mode 100644 index 00000000..53aac61f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/snapshot.go @@ -0,0 +1,138 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "time" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/trace" +) + +// snapshot is an record of a spans state at a particular checkpointed time. +// It is used as a read-only representation of that state. +type snapshot struct { + name string + spanContext trace.SpanContext + parent trace.SpanContext + spanKind trace.SpanKind + startTime time.Time + endTime time.Time + attributes []attribute.KeyValue + events []Event + links []Link + status Status + childSpanCount int + droppedAttributeCount int + droppedEventCount int + droppedLinkCount int + resource *resource.Resource + instrumentationLibrary instrumentation.Library +} + +var _ ReadOnlySpan = snapshot{} + +func (s snapshot) private() {} + +// Name returns the name of the span. +func (s snapshot) Name() string { + return s.name +} + +// SpanContext returns the unique SpanContext that identifies the span. +func (s snapshot) SpanContext() trace.SpanContext { + return s.spanContext +} + +// Parent returns the unique SpanContext that identifies the parent of the +// span if one exists. If the span has no parent the returned SpanContext +// will be invalid. +func (s snapshot) Parent() trace.SpanContext { + return s.parent +} + +// SpanKind returns the role the span plays in a Trace. +func (s snapshot) SpanKind() trace.SpanKind { + return s.spanKind +} + +// StartTime returns the time the span started recording. +func (s snapshot) StartTime() time.Time { + return s.startTime +} + +// EndTime returns the time the span stopped recording. It will be zero if +// the span has not ended. +func (s snapshot) EndTime() time.Time { + return s.endTime +} + +// Attributes returns the defining attributes of the span. +func (s snapshot) Attributes() []attribute.KeyValue { + return s.attributes +} + +// Links returns all the links the span has to other spans. +func (s snapshot) Links() []Link { + return s.links +} + +// Events returns all the events that occurred within in the spans +// lifetime. +func (s snapshot) Events() []Event { + return s.events +} + +// Status returns the spans status. +func (s snapshot) Status() Status { + return s.status +} + +// InstrumentationLibrary returns information about the instrumentation +// library that created the span. +func (s snapshot) InstrumentationLibrary() instrumentation.Library { + return s.instrumentationLibrary +} + +// Resource returns information about the entity that produced the span. +func (s snapshot) Resource() *resource.Resource { + return s.resource +} + +// DroppedAttributes returns the number of attributes dropped by the span +// due to limits being reached. +func (s snapshot) DroppedAttributes() int { + return s.droppedAttributeCount +} + +// DroppedLinks returns the number of links dropped by the span due to limits +// being reached. +func (s snapshot) DroppedLinks() int { + return s.droppedLinkCount +} + +// DroppedEvents returns the number of events dropped by the span due to +// limits being reached. +func (s snapshot) DroppedEvents() int { + return s.droppedEventCount +} + +// ChildSpanCount returns the count of spans that consider the span a +// direct parent. +func (s snapshot) ChildSpanCount() int { + return s.childSpanCount +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go new file mode 100644 index 00000000..f2dda294 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go @@ -0,0 +1,797 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "fmt" + "reflect" + "runtime" + rt "runtime/trace" + "sync" + "time" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/internal" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" +) + +// ReadOnlySpan allows reading information from the data structure underlying a +// trace.Span. It is used in places where reading information from a span is +// necessary but changing the span isn't necessary or allowed. +// +// Warning: methods may be added to this interface in minor releases. +type ReadOnlySpan interface { + // Name returns the name of the span. + Name() string + // SpanContext returns the unique SpanContext that identifies the span. + SpanContext() trace.SpanContext + // Parent returns the unique SpanContext that identifies the parent of the + // span if one exists. If the span has no parent the returned SpanContext + // will be invalid. + Parent() trace.SpanContext + // SpanKind returns the role the span plays in a Trace. + SpanKind() trace.SpanKind + // StartTime returns the time the span started recording. + StartTime() time.Time + // EndTime returns the time the span stopped recording. It will be zero if + // the span has not ended. + EndTime() time.Time + // Attributes returns the defining attributes of the span. + // The order of the returned attributes is not guaranteed to be stable across invocations. + Attributes() []attribute.KeyValue + // Links returns all the links the span has to other spans. + Links() []Link + // Events returns all the events that occurred within in the spans + // lifetime. + Events() []Event + // Status returns the spans status. + Status() Status + // InstrumentationLibrary returns information about the instrumentation + // library that created the span. + InstrumentationLibrary() instrumentation.Library + // Resource returns information about the entity that produced the span. + Resource() *resource.Resource + // DroppedAttributes returns the number of attributes dropped by the span + // due to limits being reached. + DroppedAttributes() int + // DroppedLinks returns the number of links dropped by the span due to + // limits being reached. + DroppedLinks() int + // DroppedEvents returns the number of events dropped by the span due to + // limits being reached. + DroppedEvents() int + // ChildSpanCount returns the count of spans that consider the span a + // direct parent. + ChildSpanCount() int + + // A private method to prevent users implementing the + // interface and so future additions to it will not + // violate compatibility. + private() +} + +// ReadWriteSpan exposes the same methods as trace.Span and in addition allows +// reading information from the underlying data structure. +// This interface exposes the union of the methods of trace.Span (which is a +// "write-only" span) and ReadOnlySpan. New methods for writing or reading span +// information should be added under trace.Span or ReadOnlySpan, respectively. +// +// Warning: methods may be added to this interface in minor releases. +type ReadWriteSpan interface { + trace.Span + ReadOnlySpan +} + +// recordingSpan is an implementation of the OpenTelemetry Span API +// representing the individual component of a trace that is sampled. +type recordingSpan struct { + // mu protects the contents of this span. + mu sync.Mutex + + // parent holds the parent span of this span as a trace.SpanContext. + parent trace.SpanContext + + // spanKind represents the kind of this span as a trace.SpanKind. + spanKind trace.SpanKind + + // name is the name of this span. + name string + + // startTime is the time at which this span was started. + startTime time.Time + + // endTime is the time at which this span was ended. It contains the zero + // value of time.Time until the span is ended. + endTime time.Time + + // status is the status of this span. + status Status + + // childSpanCount holds the number of child spans created for this span. + childSpanCount int + + // spanContext holds the SpanContext of this span. + spanContext trace.SpanContext + + // attributes is a collection of user provided key/values. The collection + // is constrained by a configurable maximum held by the parent + // TracerProvider. When additional attributes are added after this maximum + // is reached these attributes the user is attempting to add are dropped. + // This dropped number of attributes is tracked and reported in the + // ReadOnlySpan exported when the span ends. + attributes []attribute.KeyValue + droppedAttributes int + + // events are stored in FIFO queue capped by configured limit. + events evictedQueue + + // links are stored in FIFO queue capped by configured limit. + links evictedQueue + + // executionTracerTaskEnd ends the execution tracer span. + executionTracerTaskEnd func() + + // tracer is the SDK tracer that created this span. + tracer *tracer +} + +var _ ReadWriteSpan = (*recordingSpan)(nil) +var _ runtimeTracer = (*recordingSpan)(nil) + +// SpanContext returns the SpanContext of this span. +func (s *recordingSpan) SpanContext() trace.SpanContext { + if s == nil { + return trace.SpanContext{} + } + return s.spanContext +} + +// IsRecording returns if this span is being recorded. If this span has ended +// this will return false. +func (s *recordingSpan) IsRecording() bool { + if s == nil { + return false + } + s.mu.Lock() + defer s.mu.Unlock() + + return s.endTime.IsZero() +} + +// SetStatus sets the status of the Span in the form of a code and a +// description, overriding previous values set. The description is only +// included in the set status when the code is for an error. If this span is +// not being recorded than this method does nothing. +func (s *recordingSpan) SetStatus(code codes.Code, description string) { + if !s.IsRecording() { + return + } + + status := Status{Code: code} + if code == codes.Error { + status.Description = description + } + + s.mu.Lock() + s.status = status + s.mu.Unlock() +} + +// SetAttributes sets attributes of this span. +// +// If a key from attributes already exists the value associated with that key +// will be overwritten with the value contained in attributes. +// +// If this span is not being recorded than this method does nothing. +// +// If adding attributes to the span would exceed the maximum amount of +// attributes the span is configured to have, the last added attributes will +// be dropped. +func (s *recordingSpan) SetAttributes(attributes ...attribute.KeyValue) { + if !s.IsRecording() { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + limit := s.tracer.provider.spanLimits.AttributeCountLimit + if limit == 0 { + // No attributes allowed. + s.droppedAttributes += len(attributes) + return + } + + // If adding these attributes could exceed the capacity of s perform a + // de-duplication and truncation while adding to avoid over allocation. + if limit > 0 && len(s.attributes)+len(attributes) > limit { + s.addOverCapAttrs(limit, attributes) + return + } + + // Otherwise, add without deduplication. When attributes are read they + // will be deduplicated, optimizing the operation. + for _, a := range attributes { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) + s.attributes = append(s.attributes, a) + } +} + +// addOverCapAttrs adds the attributes attrs to the span s while +// de-duplicating the attributes of s and attrs and dropping attributes that +// exceed the limit. +// +// This method assumes s.mu.Lock is held by the caller. +// +// This method should only be called when there is a possibility that adding +// attrs to s will exceed the limit. Otherwise, attrs should be added to s +// without checking for duplicates and all retrieval methods of the attributes +// for s will de-duplicate as needed. +// +// This method assumes limit is a value > 0. The argument should be validated +// by the caller. +func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) { + // In order to not allocate more capacity to s.attributes than needed, + // prune and truncate this addition of attributes while adding. + + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) + + // Now that s.attributes is deduplicated, adding unique attributes up to + // the capacity of s will not over allocate s.attributes. + for _, a := range attrs { + if !a.Valid() { + // Drop all invalid attributes. + s.droppedAttributes++ + continue + } + + if idx, ok := exists[a.Key]; ok { + // Perform all updates before dropping, even when at capacity. + s.attributes[idx] = a + continue + } + + if len(s.attributes) >= limit { + // Do not just drop all of the remaining attributes, make sure + // updates are checked and performed. + s.droppedAttributes++ + } else { + a = truncateAttr(s.tracer.provider.spanLimits.AttributeValueLengthLimit, a) + s.attributes = append(s.attributes, a) + exists[a.Key] = len(s.attributes) - 1 + } + } +} + +// truncateAttr returns a truncated version of attr. Only string and string +// slice attribute values are truncated. String values are truncated to at +// most a length of limit. Each string slice value is truncated in this fasion +// (the slice length itself is unaffected). +// +// No truncation is perfromed for a negative limit. +func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue { + if limit < 0 { + return attr + } + switch attr.Value.Type() { + case attribute.STRING: + if v := attr.Value.AsString(); len(v) > limit { + return attr.Key.String(v[:limit]) + } + case attribute.STRINGSLICE: + // Do no mutate the original, make a copy. + trucated := attr.Key.StringSlice(attr.Value.AsStringSlice()) + // Do not do this. + // + // v := trucated.Value.AsStringSlice() + // cp := make([]string, len(v)) + // /* Copy and truncate values to cp ... */ + // trucated.Value = attribute.StringSliceValue(cp) + // + // Copying the []string and then assigning it back as a new value with + // attribute.StringSliceValue will copy the data twice. Instead, we + // already made a copy above that only this function owns, update the + // underlying slice data of our copy. + v := trucated.Value.AsStringSlice() + for i := range v { + if len(v[i]) > limit { + v[i] = v[i][:limit] + } + } + return trucated + } + return attr +} + +// End ends the span. This method does nothing if the span is already ended or +// is not being recorded. +// +// The only SpanOption currently supported is WithTimestamp which will set the +// end time for a Span's life-cycle. +// +// If this method is called while panicking an error event is added to the +// Span before ending it and the panic is continued. +func (s *recordingSpan) End(options ...trace.SpanEndOption) { + // Do not start by checking if the span is being recorded which requires + // acquiring a lock. Make a minimal check that the span is not nil. + if s == nil { + return + } + + // Store the end time as soon as possible to avoid artificially increasing + // the span's duration in case some operation below takes a while. + et := internal.MonotonicEndTime(s.startTime) + + // Do relative expensive check now that we have an end time and see if we + // need to do any more processing. + if !s.IsRecording() { + return + } + + config := trace.NewSpanEndConfig(options...) + if recovered := recover(); recovered != nil { + // Record but don't stop the panic. + defer panic(recovered) + opts := []trace.EventOption{ + trace.WithAttributes( + semconv.ExceptionTypeKey.String(typeStr(recovered)), + semconv.ExceptionMessageKey.String(fmt.Sprint(recovered)), + ), + } + + if config.StackTrace() { + opts = append(opts, trace.WithAttributes( + semconv.ExceptionStacktraceKey.String(recordStackTrace()), + )) + } + + s.addEvent(semconv.ExceptionEventName, opts...) + } + + if s.executionTracerTaskEnd != nil { + s.executionTracerTaskEnd() + } + + s.mu.Lock() + // Setting endTime to non-zero marks the span as ended and not recording. + if config.Timestamp().IsZero() { + s.endTime = et + } else { + s.endTime = config.Timestamp() + } + s.mu.Unlock() + + if sps, ok := s.tracer.provider.spanProcessors.Load().(spanProcessorStates); ok { + if len(sps) == 0 { + return + } + snap := s.snapshot() + for _, sp := range sps { + sp.sp.OnEnd(snap) + } + } +} + +// RecordError will record err as a span event for this span. An additional call to +// SetStatus is required if the Status of the Span should be set to Error, this method +// does not change the Span status. If this span is not being recorded or err is nil +// than this method does nothing. +func (s *recordingSpan) RecordError(err error, opts ...trace.EventOption) { + if s == nil || err == nil || !s.IsRecording() { + return + } + + opts = append(opts, trace.WithAttributes( + semconv.ExceptionTypeKey.String(typeStr(err)), + semconv.ExceptionMessageKey.String(err.Error()), + )) + + c := trace.NewEventConfig(opts...) + if c.StackTrace() { + opts = append(opts, trace.WithAttributes( + semconv.ExceptionStacktraceKey.String(recordStackTrace()), + )) + } + + s.addEvent(semconv.ExceptionEventName, opts...) +} + +func typeStr(i interface{}) string { + t := reflect.TypeOf(i) + if t.PkgPath() == "" && t.Name() == "" { + // Likely a builtin type. + return t.String() + } + return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) +} + +func recordStackTrace() string { + stackTrace := make([]byte, 2048) + n := runtime.Stack(stackTrace, false) + + return string(stackTrace[0:n]) +} + +// AddEvent adds an event with the provided name and options. If this span is +// not being recorded than this method does nothing. +func (s *recordingSpan) AddEvent(name string, o ...trace.EventOption) { + if !s.IsRecording() { + return + } + s.addEvent(name, o...) +} + +func (s *recordingSpan) addEvent(name string, o ...trace.EventOption) { + c := trace.NewEventConfig(o...) + e := Event{Name: name, Attributes: c.Attributes(), Time: c.Timestamp()} + + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerEventCountLimit + if limit == 0 { + // Drop all attributes. + e.DroppedAttributeCount = len(e.Attributes) + e.Attributes = nil + } else if limit > 0 && len(e.Attributes) > limit { + // Drop over capacity. + e.DroppedAttributeCount = len(e.Attributes) - limit + e.Attributes = e.Attributes[:limit] + } + + s.mu.Lock() + s.events.add(e) + s.mu.Unlock() +} + +// SetName sets the name of this span. If this span is not being recorded than +// this method does nothing. +func (s *recordingSpan) SetName(name string) { + if !s.IsRecording() { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + s.name = name +} + +// Name returns the name of this span. +func (s *recordingSpan) Name() string { + s.mu.Lock() + defer s.mu.Unlock() + return s.name +} + +// Name returns the SpanContext of this span's parent span. +func (s *recordingSpan) Parent() trace.SpanContext { + s.mu.Lock() + defer s.mu.Unlock() + return s.parent +} + +// SpanKind returns the SpanKind of this span. +func (s *recordingSpan) SpanKind() trace.SpanKind { + s.mu.Lock() + defer s.mu.Unlock() + return s.spanKind +} + +// StartTime returns the time this span started. +func (s *recordingSpan) StartTime() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.startTime +} + +// EndTime returns the time this span ended. For spans that have not yet +// ended, the returned value will be the zero value of time.Time. +func (s *recordingSpan) EndTime() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.endTime +} + +// Attributes returns the attributes of this span. +// +// The order of the returned attributes is not guaranteed to be stable. +func (s *recordingSpan) Attributes() []attribute.KeyValue { + s.mu.Lock() + defer s.mu.Unlock() + s.dedupeAttrs() + return s.attributes +} + +// dedupeAttrs deduplicates the attributes of s to fit capacity. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrs() { + // Do not set a capacity when creating this map. Benchmark testing has + // showed this to only add unused memory allocations in general use. + exists := make(map[attribute.Key]int) + s.dedupeAttrsFromRecord(&exists) +} + +// dedupeAttrsFromRecord deduplicates the attributes of s to fit capacity +// using record as the record of unique attribute keys to their index. +// +// This method assumes s.mu.Lock is held by the caller. +func (s *recordingSpan) dedupeAttrsFromRecord(record *map[attribute.Key]int) { + // Use the fact that slices share the same backing array. + unique := s.attributes[:0] + for _, a := range s.attributes { + if idx, ok := (*record)[a.Key]; ok { + unique[idx] = a + } else { + unique = append(unique, a) + (*record)[a.Key] = len(unique) - 1 + } + } + // s.attributes have element types of attribute.KeyValue. These types are + // not pointers and they themselves do not contain pointer fields, + // therefore the duplicate values do not need to be zeroed for them to be + // garbage collected. + s.attributes = unique +} + +// Links returns the links of this span. +func (s *recordingSpan) Links() []Link { + s.mu.Lock() + defer s.mu.Unlock() + if len(s.links.queue) == 0 { + return []Link{} + } + return s.interfaceArrayToLinksArray() +} + +// Events returns the events of this span. +func (s *recordingSpan) Events() []Event { + s.mu.Lock() + defer s.mu.Unlock() + if len(s.events.queue) == 0 { + return []Event{} + } + return s.interfaceArrayToEventArray() +} + +// Status returns the status of this span. +func (s *recordingSpan) Status() Status { + s.mu.Lock() + defer s.mu.Unlock() + return s.status +} + +// InstrumentationLibrary returns the instrumentation.Library associated with +// the Tracer that created this span. +func (s *recordingSpan) InstrumentationLibrary() instrumentation.Library { + s.mu.Lock() + defer s.mu.Unlock() + return s.tracer.instrumentationLibrary +} + +// Resource returns the Resource associated with the Tracer that created this +// span. +func (s *recordingSpan) Resource() *resource.Resource { + s.mu.Lock() + defer s.mu.Unlock() + return s.tracer.provider.resource +} + +func (s *recordingSpan) addLink(link trace.Link) { + if !s.IsRecording() || !link.SpanContext.IsValid() { + return + } + + l := Link{SpanContext: link.SpanContext, Attributes: link.Attributes} + + // Discard attributes over limit. + limit := s.tracer.provider.spanLimits.AttributePerLinkCountLimit + if limit == 0 { + // Drop all attributes. + l.DroppedAttributeCount = len(l.Attributes) + l.Attributes = nil + } else if limit > 0 && len(l.Attributes) > limit { + l.DroppedAttributeCount = len(l.Attributes) - limit + l.Attributes = l.Attributes[:limit] + } + + s.mu.Lock() + s.links.add(l) + s.mu.Unlock() +} + +// DroppedAttributes returns the number of attributes dropped by the span +// due to limits being reached. +func (s *recordingSpan) DroppedAttributes() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.droppedAttributes +} + +// DroppedLinks returns the number of links dropped by the span due to limits +// being reached. +func (s *recordingSpan) DroppedLinks() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.links.droppedCount +} + +// DroppedEvents returns the number of events dropped by the span due to +// limits being reached. +func (s *recordingSpan) DroppedEvents() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.events.droppedCount +} + +// ChildSpanCount returns the count of spans that consider the span a +// direct parent. +func (s *recordingSpan) ChildSpanCount() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.childSpanCount +} + +// TracerProvider returns a trace.TracerProvider that can be used to generate +// additional Spans on the same telemetry pipeline as the current Span. +func (s *recordingSpan) TracerProvider() trace.TracerProvider { + return s.tracer.provider +} + +// snapshot creates a read-only copy of the current state of the span. +func (s *recordingSpan) snapshot() ReadOnlySpan { + var sd snapshot + s.mu.Lock() + defer s.mu.Unlock() + + sd.endTime = s.endTime + sd.instrumentationLibrary = s.tracer.instrumentationLibrary + sd.name = s.name + sd.parent = s.parent + sd.resource = s.tracer.provider.resource + sd.spanContext = s.spanContext + sd.spanKind = s.spanKind + sd.startTime = s.startTime + sd.status = s.status + sd.childSpanCount = s.childSpanCount + + if len(s.attributes) > 0 { + s.dedupeAttrs() + sd.attributes = s.attributes + } + sd.droppedAttributeCount = s.droppedAttributes + if len(s.events.queue) > 0 { + sd.events = s.interfaceArrayToEventArray() + sd.droppedEventCount = s.events.droppedCount + } + if len(s.links.queue) > 0 { + sd.links = s.interfaceArrayToLinksArray() + sd.droppedLinkCount = s.links.droppedCount + } + return &sd +} + +func (s *recordingSpan) interfaceArrayToLinksArray() []Link { + linkArr := make([]Link, 0) + for _, value := range s.links.queue { + linkArr = append(linkArr, value.(Link)) + } + return linkArr +} + +func (s *recordingSpan) interfaceArrayToEventArray() []Event { + eventArr := make([]Event, 0) + for _, value := range s.events.queue { + eventArr = append(eventArr, value.(Event)) + } + return eventArr +} + +func (s *recordingSpan) addChild() { + if !s.IsRecording() { + return + } + s.mu.Lock() + s.childSpanCount++ + s.mu.Unlock() +} + +func (*recordingSpan) private() {} + +// runtimeTrace starts a "runtime/trace".Task for the span and returns a +// context containing the task. +func (s *recordingSpan) runtimeTrace(ctx context.Context) context.Context { + if !rt.IsEnabled() { + // Avoid additional overhead if runtime/trace is not enabled. + return ctx + } + nctx, task := rt.NewTask(ctx, s.name) + + s.mu.Lock() + s.executionTracerTaskEnd = task.End + s.mu.Unlock() + + return nctx +} + +// nonRecordingSpan is a minimal implementation of the OpenTelemetry Span API +// that wraps a SpanContext. It performs no operations other than to return +// the wrapped SpanContext or TracerProvider that created it. +type nonRecordingSpan struct { + // tracer is the SDK tracer that created this span. + tracer *tracer + sc trace.SpanContext +} + +var _ trace.Span = nonRecordingSpan{} + +// SpanContext returns the wrapped SpanContext. +func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc } + +// IsRecording always returns false. +func (nonRecordingSpan) IsRecording() bool { return false } + +// SetStatus does nothing. +func (nonRecordingSpan) SetStatus(codes.Code, string) {} + +// SetError does nothing. +func (nonRecordingSpan) SetError(bool) {} + +// SetAttributes does nothing. +func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} + +// End does nothing. +func (nonRecordingSpan) End(...trace.SpanEndOption) {} + +// RecordError does nothing. +func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} + +// AddEvent does nothing. +func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {} + +// SetName does nothing. +func (nonRecordingSpan) SetName(string) {} + +// TracerProvider returns the trace.TracerProvider that provided the Tracer +// that created this span. +func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } + +func isRecording(s SamplingResult) bool { + return s.Decision == RecordOnly || s.Decision == RecordAndSample +} + +func isSampled(s SamplingResult) bool { + return s.Decision == RecordAndSample +} + +// Status is the classified state of a Span. +type Status struct { + // Code is an identifier of a Spans state classification. + Code codes.Code + // Description is a user hint about why that status was set. It is only + // applicable when Code is Error. + Description string +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span_exporter.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span_exporter.go new file mode 100644 index 00000000..9fb3d6ea --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span_exporter.go @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import "context" + +// SpanExporter handles the delivery of spans to external receivers. This is +// the final component in the trace export pipeline. +type SpanExporter interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // ExportSpans exports a batch of spans. + // + // This function is called synchronously, so there is no concurrency + // safety requirement. However, due to the synchronous calling pattern, + // it is critical that all timeouts and cancellations contained in the + // passed context must be honored. + // + // Any retry logic must be contained in this function. The SDK that + // calls this function will not implement any retry logic. All errors + // returned by this function are considered unrecoverable and will be + // reported to a configured error Handler. + ExportSpans(ctx context.Context, spans []ReadOnlySpan) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Shutdown notifies the exporter of a pending halt to operations. The + // exporter is expected to preform any cleanup or synchronization it + // requires while honoring all timeouts and cancellations contained in + // the passed context. + Shutdown(ctx context.Context) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go new file mode 100644 index 00000000..b2c47584 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go @@ -0,0 +1,126 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import "go.opentelemetry.io/otel/sdk/internal/env" + +const ( + // DefaultAttributeValueLengthLimit is the default maximum allowed + // attribute value length, unlimited. + DefaultAttributeValueLengthLimit = -1 + + // DefaultAttributeCountLimit is the default maximum number of attributes + // a span can have. + DefaultAttributeCountLimit = 128 + + // DefaultEventCountLimit is the default maximum number of events a span + // can have. + DefaultEventCountLimit = 128 + + // DefaultLinkCountLimit is the default maximum number of links a span can + // have. + DefaultLinkCountLimit = 128 + + // DefaultAttributePerEventCountLimit is the default maximum number of + // attributes a span event can have. + DefaultAttributePerEventCountLimit = 128 + + // DefaultAttributePerLinkCountLimit is the default maximum number of + // attributes a span link can have. + DefaultAttributePerLinkCountLimit = 128 +) + +// SpanLimits represents the limits of a span. +type SpanLimits struct { + // AttributeValueLengthLimit is the maximum allowed attribute value length. + // + // This limit only applies to string and string slice attribute values. + // Any string longer than this value will be truncated to this length. + // + // Setting this to a negative value means no limit is applied. + AttributeValueLengthLimit int + + // AttributeCountLimit is the maximum allowed span attribute count. Any + // attribute added to a span once this limit is reached will be dropped. + // + // Setting this to zero means no attributes will be recorded. + // + // Setting this to a negative value means no limit is applied. + AttributeCountLimit int + + // EventCountLimit is the maximum allowed span event count. Any event + // added to a span once this limit is reached means it will be added but + // the oldest event will be dropped. + // + // Setting this to zero means no events we be recorded. + // + // Setting this to a negative value means no limit is applied. + EventCountLimit int + + // LinkCountLimit is the maximum allowed span link count. Any link added + // to a span once this limit is reached means it will be added but the + // oldest link will be dropped. + // + // Setting this to zero means no links we be recorded. + // + // Setting this to a negative value means no limit is applied. + LinkCountLimit int + + // AttributePerEventCountLimit is the maximum number of attributes allowed + // per span event. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for events. + // + // Setting this to a negative value means no limit is applied. + AttributePerEventCountLimit int + + // AttributePerLinkCountLimit is the maximum number of attributes allowed + // per span link. Any attribute added after this limit reached will be + // dropped. + // + // Setting this to zero means no attributes will be recorded for links. + // + // Setting this to a negative value means no limit is applied. + AttributePerLinkCountLimit int +} + +// NewSpanLimits returns a SpanLimits with all limits set to the value their +// corresponding environment variable holds, or the default if unset. +// +// • AttributeValueLengthLimit: OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT +// (default: unlimited) +// +// • AttributeCountLimit: OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT (default: 128) +// +// • EventCountLimit: OTEL_SPAN_EVENT_COUNT_LIMIT (default: 128) +// +// • AttributePerEventCountLimit: OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT (default: +// 128) +// +// • LinkCountLimit: OTEL_SPAN_LINK_COUNT_LIMIT (default: 128) +// +// • AttributePerLinkCountLimit: OTEL_LINK_ATTRIBUTE_COUNT_LIMIT (default: +// 128) +func NewSpanLimits() SpanLimits { + return SpanLimits{ + AttributeValueLengthLimit: env.SpanAttributeValueLength(DefaultAttributeValueLengthLimit), + AttributeCountLimit: env.SpanAttributeCount(DefaultAttributeCountLimit), + EventCountLimit: env.SpanEventCount(DefaultEventCountLimit), + LinkCountLimit: env.SpanLinkCount(DefaultLinkCountLimit), + AttributePerEventCountLimit: env.SpanEventAttributeCount(DefaultAttributePerEventCountLimit), + AttributePerLinkCountLimit: env.SpanLinkAttributeCount(DefaultAttributePerLinkCountLimit), + } +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span_processor.go new file mode 100644 index 00000000..b649a2ff --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span_processor.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "sync" +) + +// SpanProcessor is a processing pipeline for spans in the trace signal. +// SpanProcessors registered with a TracerProvider and are called at the start +// and end of a Span's lifecycle, and are called in the order they are +// registered. +type SpanProcessor interface { + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // OnStart is called when a span is started. It is called synchronously + // and should not block. + OnStart(parent context.Context, s ReadWriteSpan) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // OnEnd is called when span is finished. It is called synchronously and + // hence not block. + OnEnd(s ReadOnlySpan) + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // Shutdown is called when the SDK shuts down. Any cleanup or release of + // resources held by the processor should be done in this call. + // + // Calls to OnStart, OnEnd, or ForceFlush after this has been called + // should be ignored. + // + // All timeouts and cancellations contained in ctx must be honored, this + // should not block indefinitely. + Shutdown(ctx context.Context) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. + + // ForceFlush exports all ended spans to the configured Exporter that have not yet + // been exported. It should only be called when absolutely necessary, such as when + // using a FaaS provider that may suspend the process after an invocation, but before + // the Processor can export the completed spans. + ForceFlush(ctx context.Context) error + // DO NOT CHANGE: any modification will not be backwards compatible and + // must never be done outside of a new major release. +} + +type spanProcessorState struct { + sp SpanProcessor + state *sync.Once +} +type spanProcessorStates []*spanProcessorState diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go b/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go new file mode 100644 index 00000000..5b8ab43b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/sdk/trace" + +import ( + "context" + "time" + + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/trace" +) + +type tracer struct { + provider *TracerProvider + instrumentationLibrary instrumentation.Library +} + +var _ trace.Tracer = &tracer{} + +// Start starts a Span and returns it along with a context containing it. +// +// The Span is created with the provided name and as a child of any existing +// span context found in the passed context. The created Span will be +// configured appropriately by any SpanOption passed. +func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) { + config := trace.NewSpanStartConfig(options...) + + // For local spans created by this SDK, track child span count. + if p := trace.SpanFromContext(ctx); p != nil { + if sdkSpan, ok := p.(*recordingSpan); ok { + sdkSpan.addChild() + } + } + + s := tr.newSpan(ctx, name, &config) + if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() { + sps, _ := tr.provider.spanProcessors.Load().(spanProcessorStates) + for _, sp := range sps { + sp.sp.OnStart(ctx, rw) + } + } + if rtt, ok := s.(runtimeTracer); ok { + ctx = rtt.runtimeTrace(ctx) + } + + return trace.ContextWithSpan(ctx, s), s +} + +type runtimeTracer interface { + // runtimeTrace starts a "runtime/trace".Task for the span and + // returns a context containing the task. + runtimeTrace(ctx context.Context) context.Context +} + +// newSpan returns a new configured span. +func (tr *tracer) newSpan(ctx context.Context, name string, config *trace.SpanConfig) trace.Span { + // If told explicitly to make this a new root use a zero value SpanContext + // as a parent which contains an invalid trace ID and is not remote. + var psc trace.SpanContext + if config.NewRoot() { + ctx = trace.ContextWithSpanContext(ctx, psc) + } else { + psc = trace.SpanContextFromContext(ctx) + } + + // If there is a valid parent trace ID, use it to ensure the continuity of + // the trace. Always generate a new span ID so other components can rely + // on a unique span ID, even if the Span is non-recording. + var tid trace.TraceID + var sid trace.SpanID + if !psc.TraceID().IsValid() { + tid, sid = tr.provider.idGenerator.NewIDs(ctx) + } else { + tid = psc.TraceID() + sid = tr.provider.idGenerator.NewSpanID(ctx, tid) + } + + samplingResult := tr.provider.sampler.ShouldSample(SamplingParameters{ + ParentContext: ctx, + TraceID: tid, + Name: name, + Kind: config.SpanKind(), + Attributes: config.Attributes(), + Links: config.Links(), + }) + + scc := trace.SpanContextConfig{ + TraceID: tid, + SpanID: sid, + TraceState: samplingResult.Tracestate, + } + if isSampled(samplingResult) { + scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled + } else { + scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled + } + sc := trace.NewSpanContext(scc) + + if !isRecording(samplingResult) { + return tr.newNonRecordingSpan(sc) + } + return tr.newRecordingSpan(psc, sc, name, samplingResult, config) +} + +// newRecordingSpan returns a new configured recordingSpan. +func (tr *tracer) newRecordingSpan(psc, sc trace.SpanContext, name string, sr SamplingResult, config *trace.SpanConfig) *recordingSpan { + startTime := config.Timestamp() + if startTime.IsZero() { + startTime = time.Now() + } + + s := &recordingSpan{ + // Do not pre-allocate the attributes slice here! Doing so will + // allocate memory that is likely never going to be used, or if used, + // will be over-sized. The default Go compiler has been tested to + // dynamically allocate needed space very well. Benchmarking has shown + // it to be more performant than what we can predetermine here, + // especially for the common use case of few to no added + // attributes. + + parent: psc, + spanContext: sc, + spanKind: trace.ValidateSpanKind(config.SpanKind()), + name: name, + startTime: startTime, + events: newEvictedQueue(tr.provider.spanLimits.EventCountLimit), + links: newEvictedQueue(tr.provider.spanLimits.LinkCountLimit), + tracer: tr, + } + + for _, l := range config.Links() { + s.addLink(l) + } + + s.SetAttributes(sr.Attributes...) + s.SetAttributes(config.Attributes()...) + + return s +} + +// newNonRecordingSpan returns a new configured nonRecordingSpan. +func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan { + return nonRecordingSpan{tracer: tr, sc: sc} +} diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go new file mode 100644 index 00000000..ba878d1c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/doc.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// +// 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 semconv implements OpenTelemetry semantic conventions. +// +// OpenTelemetry semantic conventions are agreed standardized naming +// patterns for OpenTelemetry things. This package represents the conventions +// as of the v1.7.0 version of the OpenTelemetry specification. +package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go new file mode 100644 index 00000000..ea370686 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/exception.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// +// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" + +const ( + // ExceptionEventName is the name of the Span event representing an exception. + ExceptionEventName = "exception" +) diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go new file mode 100644 index 00000000..fafee88a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/http.go @@ -0,0 +1,314 @@ +// Copyright The OpenTelemetry Authors +// +// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" + +import ( + "fmt" + "net" + "net/http" + "strconv" + "strings" + + "go.opentelemetry.io/otel/trace" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" +) + +// HTTP scheme attributes. +var ( + HTTPSchemeHTTP = HTTPSchemeKey.String("http") + HTTPSchemeHTTPS = HTTPSchemeKey.String("https") +) + +// NetAttributesFromHTTPRequest generates attributes of the net +// namespace as specified by the OpenTelemetry specification for a +// span. The network parameter is a string that net.Dial function +// from standard library can understand. +func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue { + attrs := []attribute.KeyValue{} + + switch network { + case "tcp", "tcp4", "tcp6": + attrs = append(attrs, NetTransportTCP) + case "udp", "udp4", "udp6": + attrs = append(attrs, NetTransportUDP) + case "ip", "ip4", "ip6": + attrs = append(attrs, NetTransportIP) + case "unix", "unixgram", "unixpacket": + attrs = append(attrs, NetTransportUnix) + default: + attrs = append(attrs, NetTransportOther) + } + + peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr) + if peerIP != "" { + attrs = append(attrs, NetPeerIPKey.String(peerIP)) + } + if peerName != "" { + attrs = append(attrs, NetPeerNameKey.String(peerName)) + } + if peerPort != 0 { + attrs = append(attrs, NetPeerPortKey.Int(peerPort)) + } + + hostIP, hostName, hostPort := "", "", 0 + for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} { + hostIP, hostName, hostPort = hostIPNamePort(someHost) + if hostIP != "" || hostName != "" || hostPort != 0 { + break + } + } + if hostIP != "" { + attrs = append(attrs, NetHostIPKey.String(hostIP)) + } + if hostName != "" { + attrs = append(attrs, NetHostNameKey.String(hostName)) + } + if hostPort != 0 { + attrs = append(attrs, NetHostPortKey.Int(hostPort)) + } + + return attrs +} + +// hostIPNamePort extracts the IP address, name and (optional) port from hostWithPort. +// It handles both IPv4 and IPv6 addresses. If the host portion is not recognized +// as a valid IPv4 or IPv6 address, the `ip` result will be empty and the +// host portion will instead be returned in `name`. +func hostIPNamePort(hostWithPort string) (ip string, name string, port int) { + var ( + hostPart, portPart string + parsedPort uint64 + err error + ) + if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil { + hostPart, portPart = hostWithPort, "" + } + if parsedIP := net.ParseIP(hostPart); parsedIP != nil { + ip = parsedIP.String() + } else { + name = hostPart + } + if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil { + port = int(parsedPort) + } + return +} + +// EndUserAttributesFromHTTPRequest generates attributes of the +// enduser namespace as specified by the OpenTelemetry specification +// for a span. +func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + if username, _, ok := request.BasicAuth(); ok { + return []attribute.KeyValue{EnduserIDKey.String(username)} + } + return nil +} + +// HTTPClientAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the client side. +func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + attrs := []attribute.KeyValue{} + + if request.Method != "" { + attrs = append(attrs, HTTPMethodKey.String(request.Method)) + } else { + attrs = append(attrs, HTTPMethodKey.String(http.MethodGet)) + } + + // remove any username/password info that may be in the URL + // before adding it to the attributes + userinfo := request.URL.User + request.URL.User = nil + + attrs = append(attrs, HTTPURLKey.String(request.URL.String())) + + // restore any username/password info that was removed + request.URL.User = userinfo + + return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) +} + +func httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + attrs := []attribute.KeyValue{} + if ua := request.UserAgent(); ua != "" { + attrs = append(attrs, HTTPUserAgentKey.String(ua)) + } + if request.ContentLength > 0 { + attrs = append(attrs, HTTPRequestContentLengthKey.Int64(request.ContentLength)) + } + + return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) +} + +func httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue { + // as these attributes are used by HTTPServerMetricAttributesFromHTTPRequest, they should be low-cardinality + attrs := []attribute.KeyValue{} + + if request.TLS != nil { + attrs = append(attrs, HTTPSchemeHTTPS) + } else { + attrs = append(attrs, HTTPSchemeHTTP) + } + + if request.Host != "" { + attrs = append(attrs, HTTPHostKey.String(request.Host)) + } else if request.URL != nil && request.URL.Host != "" { + attrs = append(attrs, HTTPHostKey.String(request.URL.Host)) + } + + flavor := "" + if request.ProtoMajor == 1 { + flavor = fmt.Sprintf("1.%d", request.ProtoMinor) + } else if request.ProtoMajor == 2 { + flavor = "2" + } + if flavor != "" { + attrs = append(attrs, HTTPFlavorKey.String(flavor)) + } + + return attrs +} + +// HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes +// to be used with server-side HTTP metrics. +func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue { + attrs := []attribute.KeyValue{} + if serverName != "" { + attrs = append(attrs, HTTPServerNameKey.String(serverName)) + } + return append(attrs, httpBasicAttributesFromHTTPRequest(request)...) +} + +// HTTPServerAttributesFromHTTPRequest generates attributes of the +// http namespace as specified by the OpenTelemetry specification for +// a span on the server side. Currently, only basic authentication is +// supported. +func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue { + attrs := []attribute.KeyValue{ + HTTPMethodKey.String(request.Method), + HTTPTargetKey.String(request.RequestURI), + } + + if serverName != "" { + attrs = append(attrs, HTTPServerNameKey.String(serverName)) + } + if route != "" { + attrs = append(attrs, HTTPRouteKey.String(route)) + } + if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 { + if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 { + attrs = append(attrs, HTTPClientIPKey.String(addresses[0])) + } + } + + return append(attrs, httpCommonAttributesFromHTTPRequest(request)...) +} + +// HTTPAttributesFromHTTPStatusCode generates attributes of the http +// namespace as specified by the OpenTelemetry specification for a +// span. +func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue { + attrs := []attribute.KeyValue{ + HTTPStatusCodeKey.Int(code), + } + return attrs +} + +type codeRange struct { + fromInclusive int + toInclusive int +} + +func (r codeRange) contains(code int) bool { + return r.fromInclusive <= code && code <= r.toInclusive +} + +var validRangesPerCategory = map[int][]codeRange{ + 1: { + {http.StatusContinue, http.StatusEarlyHints}, + }, + 2: { + {http.StatusOK, http.StatusAlreadyReported}, + {http.StatusIMUsed, http.StatusIMUsed}, + }, + 3: { + {http.StatusMultipleChoices, http.StatusUseProxy}, + {http.StatusTemporaryRedirect, http.StatusPermanentRedirect}, + }, + 4: { + {http.StatusBadRequest, http.StatusTeapot}, // yes, teapot is so useful… + {http.StatusMisdirectedRequest, http.StatusUpgradeRequired}, + {http.StatusPreconditionRequired, http.StatusTooManyRequests}, + {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge}, + {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons}, + }, + 5: { + {http.StatusInternalServerError, http.StatusLoopDetected}, + {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired}, + }, +} + +// SpanStatusFromHTTPStatusCode generates a status code and a message +// as specified by the OpenTelemetry specification for a span. +func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) { + spanCode, valid := validateHTTPStatusCode(code) + if !valid { + return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) + } + return spanCode, "" +} + +// SpanStatusFromHTTPStatusCodeAndSpanKind generates a status code and a message +// as specified by the OpenTelemetry specification for a span. +// Exclude 4xx for SERVER to set the appropriate status. +func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) { + spanCode, valid := validateHTTPStatusCode(code) + if !valid { + return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code) + } + category := code / 100 + if spanKind == trace.SpanKindServer && category == 4 { + return codes.Unset, "" + } + return spanCode, "" +} + +// Validates the HTTP status code and returns corresponding span status code. +// If the `code` is not a valid HTTP status code, returns span status Error +// and false. +func validateHTTPStatusCode(code int) (codes.Code, bool) { + category := code / 100 + ranges, ok := validRangesPerCategory[category] + if !ok { + return codes.Error, false + } + ok = false + for _, crange := range ranges { + ok = crange.contains(code) + if ok { + break + } + } + if !ok { + return codes.Error, false + } + if category > 0 && category < 4 { + return codes.Unset, true + } + return codes.Error, true +} diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go new file mode 100644 index 00000000..aab6daf3 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/resource.go @@ -0,0 +1,946 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +// Code generated from semantic convention specification. DO NOT EDIT. + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" + +import "go.opentelemetry.io/otel/attribute" + +// A cloud environment (e.g. GCP, Azure, AWS) +const ( + // Name of the cloud provider. + // + // Type: Enum + // Required: No + // Stability: stable + CloudProviderKey = attribute.Key("cloud.provider") + // The cloud account ID the resource is assigned to. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '111111111111', 'opentelemetry' + CloudAccountIDKey = attribute.Key("cloud.account.id") + // The geographical region the resource is running. Refer to your provider's docs + // to see the available regions, for example [Alibaba Cloud + // regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS + // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), + // [Azure regions](https://azure.microsoft.com/en-us/global- + // infrastructure/geographies/), or [Google Cloud + // regions](https://cloud.google.com/about/locations). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'us-central1', 'us-east-1' + CloudRegionKey = attribute.Key("cloud.region") + // Cloud regions often have multiple, isolated locations known as zones to + // increase availability. Availability zone represents the zone where the resource + // is running. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'us-east-1c' + // Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud. + CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") + // The cloud platform in use. + // + // Type: Enum + // Required: No + // Stability: stable + // Note: The prefix of the service SHOULD match the one specified in + // `cloud.provider`. + CloudPlatformKey = attribute.Key("cloud.platform") +) + +var ( + // Alibaba Cloud + CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud") + // Amazon Web Services + CloudProviderAWS = CloudProviderKey.String("aws") + // Microsoft Azure + CloudProviderAzure = CloudProviderKey.String("azure") + // Google Cloud Platform + CloudProviderGCP = CloudProviderKey.String("gcp") +) + +var ( + // Alibaba Cloud Elastic Compute Service + CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs") + // Alibaba Cloud Function Compute + CloudPlatformAlibabaCloudFc = CloudPlatformKey.String("alibaba_cloud_fc") + // AWS Elastic Compute Cloud + CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") + // AWS Elastic Container Service + CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") + // AWS Elastic Kubernetes Service + CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") + // AWS Lambda + CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") + // AWS Elastic Beanstalk + CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") + // Azure Virtual Machines + CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") + // Azure Container Instances + CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") + // Azure Kubernetes Service + CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") + // Azure Functions + CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") + // Azure App Service + CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") + // Google Cloud Compute Engine (GCE) + CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") + // Google Cloud Run + CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") + // Google Cloud Kubernetes Engine (GKE) + CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") + // Google Cloud Functions (GCF) + CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") + // Google Cloud App Engine (GAE) + CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") +) + +// Resources used by AWS Elastic Container Service (ECS). +const ( + // The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws. + // amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:ecs:us- + // west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' + AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") + // The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo + // perguide/clusters.html). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' + AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") + // The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l + // aunch_types.html) for an ECS task. + // + // Type: Enum + // Required: No + // Stability: stable + AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") + // The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates + // t/developerguide/task_definitions.html). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:ecs:us- + // west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' + AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") + // The task definition family this task definition is a member of. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-family' + AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") + // The revision for this task definition. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '8', '26' + AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") +) + +var ( + // ec2 + AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") + // fargate + AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") +) + +// Resources used by AWS Elastic Kubernetes Service (EKS). +const ( + // The ARN of an EKS cluster. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' + AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") +) + +// Resources specific to Amazon Web Services. +const ( + // The name(s) of the AWS log group(s) an application is writing to. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '/aws/lambda/my-function', 'opentelemetry-service' + // Note: Multiple log groups must be supported for cases like multi-container + // applications, where a single application has sidecar containers, and each write + // to their own log group. + AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") + // The Amazon Resource Name(s) (ARN) of the AWS log group(s). + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' + // Note: See the [log group ARN format + // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- + // access-control-overview-cwl.html#CWL_ARN_Format). + AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") + // The name(s) of the AWS log stream(s) an application is writing to. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' + AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") + // The ARN(s) of the AWS log stream(s). + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log- + // stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' + // Note: See the [log stream ARN format + // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam- + // access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain + // several log streams, so these ARNs necessarily identify both a log group and a + // log stream. + AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") +) + +// A container instance. +const ( + // Container name. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-autoconf' + ContainerNameKey = attribute.Key("container.name") + // Container ID. Usually a UUID, as for example used to [identify Docker + // containers](https://docs.docker.com/engine/reference/run/#container- + // identification). The UUID might be abbreviated. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'a3bf90e006b2' + ContainerIDKey = attribute.Key("container.id") + // The container runtime managing this container. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'docker', 'containerd', 'rkt' + ContainerRuntimeKey = attribute.Key("container.runtime") + // Name of the image the container was built on. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'gcr.io/opentelemetry/operator' + ContainerImageNameKey = attribute.Key("container.image.name") + // Container image tag. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '0.1' + ContainerImageTagKey = attribute.Key("container.image.tag") +) + +// The software deployment. +const ( + // Name of the [deployment + // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka + // deployment tier). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'staging', 'production' + DeploymentEnvironmentKey = attribute.Key("deployment.environment") +) + +// The device on which the process represented by this resource is running. +const ( + // A unique identifier representing the device + // + // Type: string + // Required: No + // Stability: stable + // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' + // Note: The device identifier MUST only be defined using the values outlined + // below. This value is not an advertising identifier and MUST NOT be used as + // such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id + // entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden + // tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the + // Firebase Installation ID or a globally unique UUID which is persisted across + // sessions in your application. More information can be found + // [here](https://developer.android.com/training/articles/user-data-ids) on best + // practices and exact implementation details. Caution should be taken when + // storing personal data or anything which can identify a user. GDPR and data + // protection laws may apply, ensure you do your own due diligence. + DeviceIDKey = attribute.Key("device.id") + // The model identifier for the device + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'iPhone3,4', 'SM-G920F' + // Note: It's recommended this value represents a machine readable version of the + // model identifier rather than the market or consumer-friendly name of the + // device. + DeviceModelIdentifierKey = attribute.Key("device.model.identifier") + // The marketing name for the device model + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' + // Note: It's recommended this value represents a human readable version of the + // device model rather than a machine readable alternative. + DeviceModelNameKey = attribute.Key("device.model.name") +) + +// A serverless instance. +const ( + // The name of the single function that this runtime instance executes. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'my-function' + // Note: This is the name of the function as configured/deployed on the FaaS + // platform and is usually different from the name of the callback function (which + // may be stored in the + // [`code.namespace`/`code.function`](../../trace/semantic_conventions/span- + // general.md#source-code-attributes) span attributes). + FaaSNameKey = attribute.Key("faas.name") + // The unique ID of the single function that this runtime instance executes. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' + // Note: Depending on the cloud provider, use: + + // * **AWS Lambda:** The function + // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and- + // namespaces.html). + // Take care not to use the "invoked ARN" directly but replace any + // [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration- + // aliases.html) with the resolved function version, as the same runtime instance + // may be invokable with multiple + // different aliases. + // * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full- + // resource-names) + // * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en- + // us/rest/api/resources/resources/get-by-id). + + // On some providers, it may not be possible to determine the full ID at startup, + // which is why this field cannot be made required. For example, on AWS the + // account ID + // part of the ARN is not available without calling another AWS API + // which may be deemed too slow for a short-running lambda function. + // As an alternative, consider setting `faas.id` as a span attribute instead. + FaaSIDKey = attribute.Key("faas.id") + // The immutable version of the function being executed. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '26', 'pinkfroid-00002' + // Note: Depending on the cloud provider and platform, use: + + // * **AWS Lambda:** The [function + // version](https://docs.aws.amazon.com/lambda/latest/dg/configuration- + // versions.html) + // (an integer represented as a decimal string). + // * **Google Cloud Run:** The + // [revision](https://cloud.google.com/run/docs/managing/revisions) + // (i.e., the function name plus the revision suffix). + // * **Google Cloud Functions:** The value of the + // [`K_REVISION` environment + // variable](https://cloud.google.com/functions/docs/env- + // var#runtime_environment_variables_set_automatically). + // * **Azure Functions:** Not applicable. Do not set this attribute. + FaaSVersionKey = attribute.Key("faas.version") + // The execution environment ID as a string, that will be potentially reused for + // other invocations to the same function/function version. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de' + // Note: * **AWS Lambda:** Use the (full) log stream name. + FaaSInstanceKey = attribute.Key("faas.instance") + // The amount of memory available to the serverless function in MiB. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 128 + // Note: It's recommended to set this attribute since e.g. too little memory can + // easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, + // the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this + // information. + FaaSMaxMemoryKey = attribute.Key("faas.max_memory") +) + +// A host is defined as a general computing instance. +const ( + // Unique host ID. For Cloud, this must be the instance_id assigned by the cloud + // provider. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-test' + HostIDKey = attribute.Key("host.id") + // Name of the host. On Unix systems, it may contain what the hostname command + // returns, or the fully qualified hostname, or another name specified by the + // user. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-test' + HostNameKey = attribute.Key("host.name") + // Type of host. For Cloud, this must be the machine type. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'n1-standard-1' + HostTypeKey = attribute.Key("host.type") + // The CPU architecture the host system is running on. + // + // Type: Enum + // Required: No + // Stability: stable + HostArchKey = attribute.Key("host.arch") + // Name of the VM image or OS install the host was instantiated from. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' + HostImageNameKey = attribute.Key("host.image.name") + // VM image ID. For Cloud, this value is from the provider. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'ami-07b06b442921831e5' + HostImageIDKey = attribute.Key("host.image.id") + // The version string of the VM image as defined in [Version + // Attributes](README.md#version-attributes). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '0.1' + HostImageVersionKey = attribute.Key("host.image.version") +) + +var ( + // AMD64 + HostArchAMD64 = HostArchKey.String("amd64") + // ARM32 + HostArchARM32 = HostArchKey.String("arm32") + // ARM64 + HostArchARM64 = HostArchKey.String("arm64") + // Itanium + HostArchIA64 = HostArchKey.String("ia64") + // 32-bit PowerPC + HostArchPPC32 = HostArchKey.String("ppc32") + // 64-bit PowerPC + HostArchPPC64 = HostArchKey.String("ppc64") + // 32-bit x86 + HostArchX86 = HostArchKey.String("x86") +) + +// A Kubernetes Cluster. +const ( + // The name of the cluster. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-cluster' + K8SClusterNameKey = attribute.Key("k8s.cluster.name") +) + +// A Kubernetes Node object. +const ( + // The name of the Node. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'node-1' + K8SNodeNameKey = attribute.Key("k8s.node.name") + // The UID of the Node. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' + K8SNodeUIDKey = attribute.Key("k8s.node.uid") +) + +// A Kubernetes Namespace. +const ( + // The name of the namespace that the pod is running in. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'default' + K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") +) + +// A Kubernetes Pod object. +const ( + // The UID of the Pod. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SPodUIDKey = attribute.Key("k8s.pod.uid") + // The name of the Pod. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry-pod-autoconf' + K8SPodNameKey = attribute.Key("k8s.pod.name") +) + +// A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). +const ( + // The name of the Container in a Pod template. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'redis' + K8SContainerNameKey = attribute.Key("k8s.container.name") +) + +// A Kubernetes ReplicaSet object. +const ( + // The UID of the ReplicaSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") + // The name of the ReplicaSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") +) + +// A Kubernetes Deployment object. +const ( + // The UID of the Deployment. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") + // The name of the Deployment. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") +) + +// A Kubernetes StatefulSet object. +const ( + // The UID of the StatefulSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") + // The name of the StatefulSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") +) + +// A Kubernetes DaemonSet object. +const ( + // The UID of the DaemonSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") + // The name of the DaemonSet. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") +) + +// A Kubernetes Job object. +const ( + // The UID of the Job. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SJobUIDKey = attribute.Key("k8s.job.uid") + // The name of the Job. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SJobNameKey = attribute.Key("k8s.job.name") +) + +// A Kubernetes CronJob object. +const ( + // The UID of the CronJob. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' + K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") + // The name of the CronJob. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") +) + +// The operating system (OS) on which the process represented by this resource is running. +const ( + // The operating system type. + // + // Type: Enum + // Required: Always + // Stability: stable + OSTypeKey = attribute.Key("os.type") + // Human readable (not intended to be parsed) OS version information, like e.g. + // reported by `ver` or `lsb_release -a` commands. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS' + OSDescriptionKey = attribute.Key("os.description") + // Human readable operating system name. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'iOS', 'Android', 'Ubuntu' + OSNameKey = attribute.Key("os.name") + // The version string of the operating system as defined in [Version + // Attributes](../../resource/semantic_conventions/README.md#version-attributes). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '14.2.1', '18.04.1' + OSVersionKey = attribute.Key("os.version") +) + +var ( + // Microsoft Windows + OSTypeWindows = OSTypeKey.String("windows") + // Linux + OSTypeLinux = OSTypeKey.String("linux") + // Apple Darwin + OSTypeDarwin = OSTypeKey.String("darwin") + // FreeBSD + OSTypeFreeBSD = OSTypeKey.String("freebsd") + // NetBSD + OSTypeNetBSD = OSTypeKey.String("netbsd") + // OpenBSD + OSTypeOpenBSD = OSTypeKey.String("openbsd") + // DragonFly BSD + OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") + // HP-UX (Hewlett Packard Unix) + OSTypeHPUX = OSTypeKey.String("hpux") + // AIX (Advanced Interactive eXecutive) + OSTypeAIX = OSTypeKey.String("aix") + // Oracle Solaris + OSTypeSolaris = OSTypeKey.String("solaris") + // IBM z/OS + OSTypeZOS = OSTypeKey.String("z_os") +) + +// An operating system process. +const ( + // Process identifier (PID). + // + // Type: int + // Required: No + // Stability: stable + // Examples: 1234 + ProcessPIDKey = attribute.Key("process.pid") + // The name of the process executable. On Linux based systems, can be set to the + // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of + // `GetProcessImageFileNameW`. + // + // Type: string + // Required: See below + // Stability: stable + // Examples: 'otelcol' + ProcessExecutableNameKey = attribute.Key("process.executable.name") + // The full path to the process executable. On Linux based systems, can be set to + // the target of `proc/[pid]/exe`. On Windows, can be set to the result of + // `GetProcessImageFileNameW`. + // + // Type: string + // Required: See below + // Stability: stable + // Examples: '/usr/bin/cmd/otelcol' + ProcessExecutablePathKey = attribute.Key("process.executable.path") + // The command used to launch the process (i.e. the command name). On Linux based + // systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, + // can be set to the first parameter extracted from `GetCommandLineW`. + // + // Type: string + // Required: See below + // Stability: stable + // Examples: 'cmd/otelcol' + ProcessCommandKey = attribute.Key("process.command") + // The full command used to launch the process as a single string representing the + // full command. On Windows, can be set to the result of `GetCommandLineW`. Do not + // set this if you have to assemble it just for monitoring; use + // `process.command_args` instead. + // + // Type: string + // Required: See below + // Stability: stable + // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' + ProcessCommandLineKey = attribute.Key("process.command_line") + // All the command arguments (including the command/executable itself) as received + // by the process. On Linux-based systems (and some other Unixoid systems + // supporting procfs), can be set according to the list of null-delimited strings + // extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be + // the full argv vector passed to `main`. + // + // Type: string[] + // Required: See below + // Stability: stable + // Examples: 'cmd/otecol', '--config=config.yaml' + ProcessCommandArgsKey = attribute.Key("process.command_args") + // The username of the user that owns the process. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'root' + ProcessOwnerKey = attribute.Key("process.owner") +) + +// The single (language) runtime instance which is monitored. +const ( + // The name of the runtime of this process. For compiled native binaries, this + // SHOULD be the name of the compiler. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'OpenJDK Runtime Environment' + ProcessRuntimeNameKey = attribute.Key("process.runtime.name") + // The version of the runtime of this process, as returned by the runtime without + // modification. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '14.0.2' + ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") + // An additional description about the runtime of the process, for example a + // specific vendor customization of the runtime environment. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' + ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") +) + +// A service instance. +const ( + // Logical name of the service. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'shoppingcart' + // Note: MUST be the same for all instances of horizontally scaled services. If + // the value was not specified, SDKs MUST fallback to `unknown_service:` + // concatenated with [`process.executable.name`](process.md#process), e.g. + // `unknown_service:bash`. If `process.executable.name` is not available, the + // value MUST be set to `unknown_service`. + ServiceNameKey = attribute.Key("service.name") + // A namespace for `service.name`. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Shop' + // Note: A string value having a meaning that helps to distinguish a group of + // services, for example the team name that owns a group of services. + // `service.name` is expected to be unique within the same namespace. If + // `service.namespace` is not specified in the Resource then `service.name` is + // expected to be unique for all services that have no explicit namespace defined + // (so the empty/unspecified namespace is simply one more valid namespace). Zero- + // length namespace string is assumed equal to unspecified namespace. + ServiceNamespaceKey = attribute.Key("service.namespace") + // The string ID of the service instance. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '627cc493-f310-47de-96bd-71410b7dec09' + // Note: MUST be unique for each instance of the same + // `service.namespace,service.name` pair (in other words + // `service.namespace,service.name,service.instance.id` triplet MUST be globally + // unique). The ID helps to distinguish instances of the same service that exist + // at the same time (e.g. instances of a horizontally scaled service). It is + // preferable for the ID to be persistent and stay the same for the lifetime of + // the service instance, however it is acceptable that the ID is ephemeral and + // changes during important lifetime events for the service (e.g. service + // restarts). If the service has no inherent unique ID that can be used as the + // value of this attribute it is recommended to generate a random Version 1 or + // Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use + // Version 5, see RFC 4122 for more recommendations). + ServiceInstanceIDKey = attribute.Key("service.instance.id") + // The version string of the service API or implementation. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '2.0.0' + ServiceVersionKey = attribute.Key("service.version") +) + +// The telemetry SDK used to capture data recorded by the instrumentation libraries. +const ( + // The name of the telemetry SDK as defined above. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'opentelemetry' + TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") + // The language of the telemetry SDK. + // + // Type: Enum + // Required: No + // Stability: stable + TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") + // The version string of the telemetry SDK. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '1.2.3' + TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") + // The version string of the auto instrumentation agent, if used. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '1.2.3' + TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") +) + +var ( + // cpp + TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") + // dotnet + TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") + // erlang + TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") + // go + TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") + // java + TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") + // nodejs + TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") + // php + TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") + // python + TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") + // ruby + TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") + // webjs + TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") +) + +// Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime. +const ( + // The name of the web engine. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'WildFly' + WebEngineNameKey = attribute.Key("webengine.name") + // The version of the web engine. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '21.0.0' + WebEngineVersionKey = attribute.Key("webengine.version") + // Additional description of the web engine (e.g. detailed version and edition + // information). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final' + WebEngineDescriptionKey = attribute.Key("webengine.description") +) diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go new file mode 100644 index 00000000..64b6c46a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/schema.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// +// 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 semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" + +// SchemaURL is the schema URL that matches the version of the semantic conventions +// that this package defines. Semconv packages starting from v1.4.0 must declare +// non-empty schema URL in the form https://opentelemetry.io/schemas/ +const SchemaURL = "https://opentelemetry.io/schemas/1.7.0" diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go new file mode 100644 index 00000000..9b75bd77 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.7.0/trace.go @@ -0,0 +1,1558 @@ +// Copyright The OpenTelemetry Authors +// +// 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. + +// Code generated from semantic convention specification. DO NOT EDIT. + +package semconv // import "go.opentelemetry.io/otel/semconv/v1.7.0" + +import "go.opentelemetry.io/otel/attribute" + +// Span attributes used by AWS Lambda (in addition to general `faas` attributes). +const ( + // The full invoked ARN as provided on the `Context` passed to the function + // (`Lambda-Runtime-Invoked-Function-ARN` header on the `/runtime/invocation/next` + // applicable). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'arn:aws:lambda:us-east-1:123456:function:myfunction:myalias' + // Note: This may be different from `faas.id` if an alias is involved. + AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") +) + +// This document defines the attributes used to perform database client calls. +const ( + // An identifier for the database management system (DBMS) product being used. See + // below for a list of well-known identifiers. + // + // Type: Enum + // Required: Always + // Stability: stable + DBSystemKey = attribute.Key("db.system") + // The connection string used to connect to the database. It is recommended to + // remove embedded credentials. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' + DBConnectionStringKey = attribute.Key("db.connection_string") + // Username for accessing the database. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'readonly_user', 'reporting_user' + DBUserKey = attribute.Key("db.user") + // The fully-qualified class name of the [Java Database Connectivity + // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver + // used to connect. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'org.postgresql.Driver', + // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' + DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") + // If no [tech-specific attribute](#call-level-attributes-for-specific- + // technologies) is defined, this attribute is used to report the name of the + // database being accessed. For commands that switch the database, this should be + // set to the target database (even if the command fails). + // + // Type: string + // Required: Required, if applicable and no more-specific attribute is defined. + // Stability: stable + // Examples: 'customers', 'main' + // Note: In some SQL databases, the database name to be used is called "schema + // name". + DBNameKey = attribute.Key("db.name") + // The database statement being executed. + // + // Type: string + // Required: Required if applicable and not explicitly disabled via + // instrumentation configuration. + // Stability: stable + // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' + // Note: The value may be sanitized to exclude sensitive information. + DBStatementKey = attribute.Key("db.statement") + // The name of the operation being executed, e.g. the [MongoDB command + // name](https://docs.mongodb.com/manual/reference/command/#database-operations) + // such as `findAndModify`, or the SQL keyword. + // + // Type: string + // Required: Required, if `db.statement` is not applicable. + // Stability: stable + // Examples: 'findAndModify', 'HMSET', 'SELECT' + // Note: When setting this to an SQL keyword, it is not recommended to attempt any + // client-side parsing of `db.statement` just to get this property, but it should + // be set if the operation name is provided by the library being instrumented. If + // the SQL statement has an ambiguous operation, or performs more than one + // operation, this value may be omitted. + DBOperationKey = attribute.Key("db.operation") +) + +var ( + // Some other SQL database. Fallback only. See notes + DBSystemOtherSQL = DBSystemKey.String("other_sql") + // Microsoft SQL Server + DBSystemMSSQL = DBSystemKey.String("mssql") + // MySQL + DBSystemMySQL = DBSystemKey.String("mysql") + // Oracle Database + DBSystemOracle = DBSystemKey.String("oracle") + // IBM DB2 + DBSystemDB2 = DBSystemKey.String("db2") + // PostgreSQL + DBSystemPostgreSQL = DBSystemKey.String("postgresql") + // Amazon Redshift + DBSystemRedshift = DBSystemKey.String("redshift") + // Apache Hive + DBSystemHive = DBSystemKey.String("hive") + // Cloudscape + DBSystemCloudscape = DBSystemKey.String("cloudscape") + // HyperSQL DataBase + DBSystemHSQLDB = DBSystemKey.String("hsqldb") + // Progress Database + DBSystemProgress = DBSystemKey.String("progress") + // SAP MaxDB + DBSystemMaxDB = DBSystemKey.String("maxdb") + // SAP HANA + DBSystemHanaDB = DBSystemKey.String("hanadb") + // Ingres + DBSystemIngres = DBSystemKey.String("ingres") + // FirstSQL + DBSystemFirstSQL = DBSystemKey.String("firstsql") + // EnterpriseDB + DBSystemEDB = DBSystemKey.String("edb") + // InterSystems Caché + DBSystemCache = DBSystemKey.String("cache") + // Adabas (Adaptable Database System) + DBSystemAdabas = DBSystemKey.String("adabas") + // Firebird + DBSystemFirebird = DBSystemKey.String("firebird") + // Apache Derby + DBSystemDerby = DBSystemKey.String("derby") + // FileMaker + DBSystemFilemaker = DBSystemKey.String("filemaker") + // Informix + DBSystemInformix = DBSystemKey.String("informix") + // InstantDB + DBSystemInstantDB = DBSystemKey.String("instantdb") + // InterBase + DBSystemInterbase = DBSystemKey.String("interbase") + // MariaDB + DBSystemMariaDB = DBSystemKey.String("mariadb") + // Netezza + DBSystemNetezza = DBSystemKey.String("netezza") + // Pervasive PSQL + DBSystemPervasive = DBSystemKey.String("pervasive") + // PointBase + DBSystemPointbase = DBSystemKey.String("pointbase") + // SQLite + DBSystemSqlite = DBSystemKey.String("sqlite") + // Sybase + DBSystemSybase = DBSystemKey.String("sybase") + // Teradata + DBSystemTeradata = DBSystemKey.String("teradata") + // Vertica + DBSystemVertica = DBSystemKey.String("vertica") + // H2 + DBSystemH2 = DBSystemKey.String("h2") + // ColdFusion IMQ + DBSystemColdfusion = DBSystemKey.String("coldfusion") + // Apache Cassandra + DBSystemCassandra = DBSystemKey.String("cassandra") + // Apache HBase + DBSystemHBase = DBSystemKey.String("hbase") + // MongoDB + DBSystemMongoDB = DBSystemKey.String("mongodb") + // Redis + DBSystemRedis = DBSystemKey.String("redis") + // Couchbase + DBSystemCouchbase = DBSystemKey.String("couchbase") + // CouchDB + DBSystemCouchDB = DBSystemKey.String("couchdb") + // Microsoft Azure Cosmos DB + DBSystemCosmosDB = DBSystemKey.String("cosmosdb") + // Amazon DynamoDB + DBSystemDynamoDB = DBSystemKey.String("dynamodb") + // Neo4j + DBSystemNeo4j = DBSystemKey.String("neo4j") + // Apache Geode + DBSystemGeode = DBSystemKey.String("geode") + // Elasticsearch + DBSystemElasticsearch = DBSystemKey.String("elasticsearch") + // Memcached + DBSystemMemcached = DBSystemKey.String("memcached") + // CockroachDB + DBSystemCockroachdb = DBSystemKey.String("cockroachdb") +) + +// Connection-level attributes for Microsoft SQL Server +const ( + // The Microsoft SQL Server [instance name](https://docs.microsoft.com/en- + // us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) + // connecting to. This name is used to determine the port of a named instance. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'MSSQLSERVER' + // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer + // required (but still recommended if non-standard). + DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") +) + +// Call-level attributes for Cassandra +const ( + // The name of the keyspace being accessed. To be used instead of the generic + // `db.name` attribute. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'mykeyspace' + DBCassandraKeyspaceKey = attribute.Key("db.cassandra.keyspace") + // The fetch size used for paging, i.e. how many rows will be returned at once. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 5000 + DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") + // The consistency level of the query. Based on consistency values from + // [CQL](https://docs.datastax.com/en/cassandra- + // oss/3.0/cassandra/dml/dmlConfigConsistency.html). + // + // Type: Enum + // Required: No + // Stability: stable + DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") + // The name of the primary table that the operation is acting upon, including the + // schema name (if applicable). + // + // Type: string + // Required: Recommended if available. + // Stability: stable + // Examples: 'mytable' + // Note: This mirrors the db.sql.table attribute but references cassandra rather + // than sql. It is not recommended to attempt any client-side parsing of + // `db.statement` just to get this property, but it should be set if it is + // provided by the library being instrumented. If the operation is acting upon an + // anonymous table, or more than one table, this value MUST NOT be set. + DBCassandraTableKey = attribute.Key("db.cassandra.table") + // Whether or not the query is idempotent. + // + // Type: boolean + // Required: No + // Stability: stable + DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") + // The number of times a query was speculatively executed. Not set or `0` if the + // query was not executed speculatively. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 0, 2 + DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") + // The ID of the coordinating node for a query. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' + DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") + // The data center of the coordinating node for a query. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'us-west-2' + DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") +) + +var ( + // all + DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") + // each_quorum + DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") + // quorum + DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") + // local_quorum + DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") + // one + DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") + // two + DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") + // three + DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") + // local_one + DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") + // any + DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") + // serial + DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") + // local_serial + DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") +) + +// Call-level attributes for Apache HBase +const ( + // The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being + // accessed. To be used instead of the generic `db.name` attribute. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'default' + DBHBaseNamespaceKey = attribute.Key("db.hbase.namespace") +) + +// Call-level attributes for Redis +const ( + // The index of the database being accessed as used in the [`SELECT` + // command](https://redis.io/commands/select), provided as an integer. To be used + // instead of the generic `db.name` attribute. + // + // Type: int + // Required: Required, if other than the default database (`0`). + // Stability: stable + // Examples: 0, 1, 15 + DBRedisDBIndexKey = attribute.Key("db.redis.database_index") +) + +// Call-level attributes for MongoDB +const ( + // The collection being accessed within the database stated in `db.name`. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'customers', 'products' + DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") +) + +// Call-level attrbiutes for SQL databases +const ( + // The name of the primary table that the operation is acting upon, including the + // schema name (if applicable). + // + // Type: string + // Required: Recommended if available. + // Stability: stable + // Examples: 'public.users', 'customers' + // Note: It is not recommended to attempt any client-side parsing of + // `db.statement` just to get this property, but it should be set if it is + // provided by the library being instrumented. If the operation is acting upon an + // anonymous table, or more than one table, this value MUST NOT be set. + DBSQLTableKey = attribute.Key("db.sql.table") +) + +// This document defines the attributes used to report a single exception associated with a span. +const ( + // The type of the exception (its fully-qualified class name, if applicable). The + // dynamic type of the exception should be preferred over the static type in + // languages that support it. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'java.net.ConnectException', 'OSError' + ExceptionTypeKey = attribute.Key("exception.type") + // The exception message. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Division by zero', "Can't convert 'int' object to str implicitly" + ExceptionMessageKey = attribute.Key("exception.message") + // A stacktrace as a string in the natural representation for the language + // runtime. The representation is to be determined and documented by each language + // SIG. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test + // exception\\n at ' + // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' + // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' + // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' + ExceptionStacktraceKey = attribute.Key("exception.stacktrace") + // SHOULD be set to true if the exception event is recorded at a point where it is + // known that the exception is escaping the scope of the span. + // + // Type: boolean + // Required: No + // Stability: stable + // Note: An exception is considered to have escaped (or left) the scope of a span, + // if that span is ended while the exception is still logically "in flight". + // This may be actually "in flight" in some languages (e.g. if the exception + // is passed to a Context manager's `__exit__` method in Python) but will + // usually be caught at the point of recording the exception in most languages. + + // It is usually not possible to determine at the point where an exception is + // thrown + // whether it will escape the scope of a span. + // However, it is trivial to know that an exception + // will escape, if one checks for an active exception just before ending the span, + // as done in the [example above](#exception-end-example). + + // It follows that an exception may still escape the scope of the span + // even if the `exception.escaped` attribute was not set or set to false, + // since the event might have been recorded at a time where it was not + // clear whether the exception will escape. + ExceptionEscapedKey = attribute.Key("exception.escaped") +) + +// This semantic convention describes an instance of a function that runs without provisioning or managing of servers (also known as serverless functions or Function as a Service (FaaS)) with spans. +const ( + // Type of the trigger on which the function is executed. + // + // Type: Enum + // Required: On FaaS instances, faas.trigger MUST be set on incoming invocations. + // Clients invoking FaaS instances MUST set `faas.trigger` on outgoing + // invocations, if it is known to the client. This is, for example, not the case, + // when the transport layer is abstracted in a FaaS client framework without + // access to its configuration. + // Stability: stable + FaaSTriggerKey = attribute.Key("faas.trigger") + // The execution ID of the current function execution. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' + FaaSExecutionKey = attribute.Key("faas.execution") +) + +var ( + // A response to some data source operation such as a database or filesystem read/write + FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") + // To provide an answer to an inbound HTTP request + FaaSTriggerHTTP = FaaSTriggerKey.String("http") + // A function is set to be executed when messages are sent to a messaging system + FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") + // A function is scheduled to be executed regularly + FaaSTriggerTimer = FaaSTriggerKey.String("timer") + // If none of the others apply + FaaSTriggerOther = FaaSTriggerKey.String("other") +) + +// Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. +const ( + // The name of the source on which the triggering operation was performed. For + // example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos + // DB to the database name. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'myBucketName', 'myDBName' + FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") + // Describes the type of the operation that was performed on the data. + // + // Type: Enum + // Required: Always + // Stability: stable + FaaSDocumentOperationKey = attribute.Key("faas.document.operation") + // A string containing the time when the data was accessed in the [ISO + // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed + // in [UTC](https://www.w3.org/TR/NOTE-datetime). + // + // Type: string + // Required: Always + // Stability: stable + // Examples: '2020-01-23T13:47:06Z' + FaaSDocumentTimeKey = attribute.Key("faas.document.time") + // The document name/table subjected to the operation. For example, in Cloud + // Storage or S3 is the name of the file, and in Cosmos DB the table name. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'myFile.txt', 'myTableName' + FaaSDocumentNameKey = attribute.Key("faas.document.name") +) + +var ( + // When a new object is created + FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") + // When an object is modified + FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") + // When an object is deleted + FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") +) + +// Semantic Convention for FaaS scheduled to be executed regularly. +const ( + // A string containing the function invocation time in the [ISO + // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed + // in [UTC](https://www.w3.org/TR/NOTE-datetime). + // + // Type: string + // Required: Always + // Stability: stable + // Examples: '2020-01-23T13:47:06Z' + FaaSTimeKey = attribute.Key("faas.time") + // A string containing the schedule period as [Cron Expression](https://docs.oracl + // e.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '0/5 * * * ? *' + FaaSCronKey = attribute.Key("faas.cron") +) + +// Contains additional attributes for incoming FaaS spans. +const ( + // A boolean that is true if the serverless function is executed for the first + // time (aka cold-start). + // + // Type: boolean + // Required: No + // Stability: stable + FaaSColdstartKey = attribute.Key("faas.coldstart") +) + +// Contains additional attributes for outgoing FaaS spans. +const ( + // The name of the invoked function. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'my-function' + // Note: SHOULD be equal to the `faas.name` resource attribute of the invoked + // function. + FaaSInvokedNameKey = attribute.Key("faas.invoked_name") + // The cloud provider of the invoked function. + // + // Type: Enum + // Required: Always + // Stability: stable + // Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked + // function. + FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") + // The cloud region of the invoked function. + // + // Type: string + // Required: For some cloud providers, like AWS or GCP, the region in which a + // function is hosted is essential to uniquely identify the function and also part + // of its endpoint. Since it's part of the endpoint being called, the region is + // always known to clients. In these cases, `faas.invoked_region` MUST be set + // accordingly. If the region is unknown to the client or not required for + // identifying the invoked function, setting `faas.invoked_region` is optional. + // Stability: stable + // Examples: 'eu-central-1' + // Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked + // function. + FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") +) + +var ( + // Alibaba Cloud + FaaSInvokedProviderAlibabaCloud = FaaSInvokedProviderKey.String("alibaba_cloud") + // Amazon Web Services + FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") + // Microsoft Azure + FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") + // Google Cloud Platform + FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") +) + +// These attributes may be used for any network related operation. +const ( + // Transport protocol used. See note below. + // + // Type: Enum + // Required: No + // Stability: stable + NetTransportKey = attribute.Key("net.transport") + // Remote address of the peer (dotted decimal for IPv4 or + // [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6) + // + // Type: string + // Required: No + // Stability: stable + // Examples: '127.0.0.1' + NetPeerIPKey = attribute.Key("net.peer.ip") + // Remote port number. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 80, 8080, 443 + NetPeerPortKey = attribute.Key("net.peer.port") + // Remote hostname or similar, see note below. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'example.com' + NetPeerNameKey = attribute.Key("net.peer.name") + // Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '192.168.0.1' + NetHostIPKey = attribute.Key("net.host.ip") + // Like `net.peer.port` but for the host port. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 35555 + NetHostPortKey = attribute.Key("net.host.port") + // Local hostname or similar, see note below. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'localhost' + NetHostNameKey = attribute.Key("net.host.name") + // The internet connection type currently being used by the host. + // + // Type: Enum + // Required: No + // Stability: stable + // Examples: 'wifi' + NetHostConnectionTypeKey = attribute.Key("net.host.connection.type") + // This describes more details regarding the connection.type. It may be the type + // of cell technology connection, but it could be used for describing details + // about a wifi connection. + // + // Type: Enum + // Required: No + // Stability: stable + // Examples: 'LTE' + NetHostConnectionSubtypeKey = attribute.Key("net.host.connection.subtype") + // The name of the mobile carrier. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'sprint' + NetHostCarrierNameKey = attribute.Key("net.host.carrier.name") + // The mobile carrier country code. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '310' + NetHostCarrierMccKey = attribute.Key("net.host.carrier.mcc") + // The mobile carrier network code. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '001' + NetHostCarrierMncKey = attribute.Key("net.host.carrier.mnc") + // The ISO 3166-1 alpha-2 2-character country code associated with the mobile + // carrier network. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'DE' + NetHostCarrierIccKey = attribute.Key("net.host.carrier.icc") +) + +var ( + // ip_tcp + NetTransportTCP = NetTransportKey.String("ip_tcp") + // ip_udp + NetTransportUDP = NetTransportKey.String("ip_udp") + // Another IP-based protocol + NetTransportIP = NetTransportKey.String("ip") + // Unix Domain socket. See below + NetTransportUnix = NetTransportKey.String("unix") + // Named or anonymous pipe. See note below + NetTransportPipe = NetTransportKey.String("pipe") + // In-process communication + NetTransportInProc = NetTransportKey.String("inproc") + // Something else (non IP-based) + NetTransportOther = NetTransportKey.String("other") +) + +var ( + // wifi + NetHostConnectionTypeWifi = NetHostConnectionTypeKey.String("wifi") + // wired + NetHostConnectionTypeWired = NetHostConnectionTypeKey.String("wired") + // cell + NetHostConnectionTypeCell = NetHostConnectionTypeKey.String("cell") + // unavailable + NetHostConnectionTypeUnavailable = NetHostConnectionTypeKey.String("unavailable") + // unknown + NetHostConnectionTypeUnknown = NetHostConnectionTypeKey.String("unknown") +) + +var ( + // GPRS + NetHostConnectionSubtypeGprs = NetHostConnectionSubtypeKey.String("gprs") + // EDGE + NetHostConnectionSubtypeEdge = NetHostConnectionSubtypeKey.String("edge") + // UMTS + NetHostConnectionSubtypeUmts = NetHostConnectionSubtypeKey.String("umts") + // CDMA + NetHostConnectionSubtypeCdma = NetHostConnectionSubtypeKey.String("cdma") + // EVDO Rel. 0 + NetHostConnectionSubtypeEvdo0 = NetHostConnectionSubtypeKey.String("evdo_0") + // EVDO Rev. A + NetHostConnectionSubtypeEvdoA = NetHostConnectionSubtypeKey.String("evdo_a") + // CDMA2000 1XRTT + NetHostConnectionSubtypeCdma20001xrtt = NetHostConnectionSubtypeKey.String("cdma2000_1xrtt") + // HSDPA + NetHostConnectionSubtypeHsdpa = NetHostConnectionSubtypeKey.String("hsdpa") + // HSUPA + NetHostConnectionSubtypeHsupa = NetHostConnectionSubtypeKey.String("hsupa") + // HSPA + NetHostConnectionSubtypeHspa = NetHostConnectionSubtypeKey.String("hspa") + // IDEN + NetHostConnectionSubtypeIden = NetHostConnectionSubtypeKey.String("iden") + // EVDO Rev. B + NetHostConnectionSubtypeEvdoB = NetHostConnectionSubtypeKey.String("evdo_b") + // LTE + NetHostConnectionSubtypeLte = NetHostConnectionSubtypeKey.String("lte") + // EHRPD + NetHostConnectionSubtypeEhrpd = NetHostConnectionSubtypeKey.String("ehrpd") + // HSPAP + NetHostConnectionSubtypeHspap = NetHostConnectionSubtypeKey.String("hspap") + // GSM + NetHostConnectionSubtypeGsm = NetHostConnectionSubtypeKey.String("gsm") + // TD-SCDMA + NetHostConnectionSubtypeTdScdma = NetHostConnectionSubtypeKey.String("td_scdma") + // IWLAN + NetHostConnectionSubtypeIwlan = NetHostConnectionSubtypeKey.String("iwlan") + // 5G NR (New Radio) + NetHostConnectionSubtypeNr = NetHostConnectionSubtypeKey.String("nr") + // 5G NRNSA (New Radio Non-Standalone) + NetHostConnectionSubtypeNrnsa = NetHostConnectionSubtypeKey.String("nrnsa") + // LTE CA + NetHostConnectionSubtypeLteCa = NetHostConnectionSubtypeKey.String("lte_ca") +) + +// Operations that access some remote service. +const ( + // The [`service.name`](../../resource/semantic_conventions/README.md#service) of + // the remote service. SHOULD be equal to the actual `service.name` resource + // attribute of the remote service if any. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'AuthTokenCache' + PeerServiceKey = attribute.Key("peer.service") +) + +// These attributes may be used for any operation with an authenticated and/or authorized enduser. +const ( + // Username or client_id extracted from the access token or + // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the + // inbound request from outside the system. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'username' + EnduserIDKey = attribute.Key("enduser.id") + // Actual/assumed role the client is making the request under extracted from token + // or application security context. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'admin' + EnduserRoleKey = attribute.Key("enduser.role") + // Scopes or granted authorities the client currently possesses extracted from + // token or application security context. The value would come from the scope + // associated with an [OAuth 2.0 Access + // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value + // in a [SAML 2.0 Assertion](http://docs.oasis- + // open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'read:message, write:files' + EnduserScopeKey = attribute.Key("enduser.scope") +) + +// These attributes may be used for any operation to store information about a thread that started a span. +const ( + // Current "managed" thread ID (as opposed to OS thread ID). + // + // Type: int + // Required: No + // Stability: stable + // Examples: 42 + ThreadIDKey = attribute.Key("thread.id") + // Current thread name. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'main' + ThreadNameKey = attribute.Key("thread.name") +) + +// These attributes allow to report this unit of code and therefore to provide more context about the span. +const ( + // The method or function name, or equivalent (usually rightmost part of the code + // unit's name). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'serveRequest' + CodeFunctionKey = attribute.Key("code.function") + // The "namespace" within which `code.function` is defined. Usually the qualified + // class or module name, such that `code.namespace` + some separator + + // `code.function` form a unique identifier for the code unit. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'com.example.MyHTTPService' + CodeNamespaceKey = attribute.Key("code.namespace") + // The source code file name that identifies the code unit as uniquely as possible + // (preferably an absolute file path). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '/usr/local/MyApplication/content_root/app/index.php' + CodeFilepathKey = attribute.Key("code.filepath") + // The line number in `code.filepath` best representing the operation. It SHOULD + // point within the code unit named in `code.function`. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 42 + CodeLineNumberKey = attribute.Key("code.lineno") +) + +// This document defines semantic conventions for HTTP client and server Spans. +const ( + // HTTP request method. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'GET', 'POST', 'HEAD' + HTTPMethodKey = attribute.Key("http.method") + // Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. + // Usually the fragment is not transmitted over HTTP, but if it is known, it + // should be included nevertheless. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' + // Note: `http.url` MUST NOT contain credentials passed via URL in form of + // `https://username:password@www.example.com/`. In such case the attribute's + // value should be `https://www.example.com/`. + HTTPURLKey = attribute.Key("http.url") + // The full request target as passed in a HTTP request line or equivalent. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '/path/12314/?q=ddds#123' + HTTPTargetKey = attribute.Key("http.target") + // The value of the [HTTP host + // header](https://tools.ietf.org/html/rfc7230#section-5.4). An empty Host header + // should also be reported, see note. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'www.example.org' + // Note: When the header is present but empty the attribute SHOULD be set to the + // empty string. Note that this is a valid situation that is expected in certain + // cases, according the aforementioned [section of RFC + // 7230](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is not + // set the attribute MUST NOT be set. + HTTPHostKey = attribute.Key("http.host") + // The URI scheme identifying the used protocol. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'http', 'https' + HTTPSchemeKey = attribute.Key("http.scheme") + // [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + // + // Type: int + // Required: If and only if one was received/sent. + // Stability: stable + // Examples: 200 + HTTPStatusCodeKey = attribute.Key("http.status_code") + // Kind of HTTP protocol used. + // + // Type: Enum + // Required: No + // Stability: stable + // Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` + // except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. + HTTPFlavorKey = attribute.Key("http.flavor") + // Value of the [HTTP User- + // Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the + // client. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' + HTTPUserAgentKey = attribute.Key("http.user_agent") + // The size of the request payload body in bytes. This is the number of bytes + // transferred excluding headers and is often, but not always, present as the + // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For + // requests using transport encoding, this should be the compressed size. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 3495 + HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") + // The size of the uncompressed request payload body after transport decoding. Not + // set if transport encoding not used. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 5493 + HTTPRequestContentLengthUncompressedKey = attribute.Key("http.request_content_length_uncompressed") + // The size of the response payload body in bytes. This is the number of bytes + // transferred excluding headers and is often, but not always, present as the + // [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For + // requests using transport encoding, this should be the compressed size. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 3495 + HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") + // The size of the uncompressed response payload body after transport decoding. + // Not set if transport encoding not used. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 5493 + HTTPResponseContentLengthUncompressedKey = attribute.Key("http.response_content_length_uncompressed") +) + +var ( + // HTTP 1.0 + HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") + // HTTP 1.1 + HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") + // HTTP 2 + HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") + // SPDY protocol + HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") + // QUIC protocol + HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") +) + +// Semantic Convention for HTTP Server +const ( + // The primary server name of the matched virtual host. This should be obtained + // via configuration. If no such configuration can be obtained, this attribute + // MUST NOT be set ( `net.host.name` should be used instead). + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'example.com' + // Note: `http.url` is usually not readily available on the server side but would + // have to be assembled in a cumbersome and sometimes lossy process from other + // information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus + // preferred to supply the raw data that is available. + HTTPServerNameKey = attribute.Key("http.server_name") + // The matched route (path template). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '/users/:userID?' + HTTPRouteKey = attribute.Key("http.route") + // The IP address of the original client behind all proxies, if known (e.g. from + // [X-Forwarded-For](https://developer.mozilla.org/en- + // US/docs/Web/HTTP/Headers/X-Forwarded-For)). + // + // Type: string + // Required: No + // Stability: stable + // Examples: '83.164.160.102' + // Note: This is not necessarily the same as `net.peer.ip`, which would + // identify the network-level peer, which may be a proxy. + + // This attribute should be set when a source of information different + // from the one used for `net.peer.ip`, is available even if that other + // source just confirms the same value as `net.peer.ip`. + // Rationale: For `net.peer.ip`, one typically does not know if it + // comes from a proxy, reverse proxy, or the actual client. Setting + // `http.client_ip` when it's the same as `net.peer.ip` means that + // one is at least somewhat confident that the address is not that of + // the closest proxy. + HTTPClientIPKey = attribute.Key("http.client_ip") +) + +// Attributes that exist for multiple DynamoDB request types. +const ( + // The keys in the `RequestItems` object field. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'Users', 'Cats' + AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") + // The JSON-serialized value of each item in the `ConsumedCapacity` response + // field. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { "string" : { + // "CapacityUnits": number, "ReadCapacityUnits": number, "WriteCapacityUnits": + // number } }, "LocalSecondaryIndexes": { "string" : { "CapacityUnits": number, + // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }, + // "ReadCapacityUnits": number, "Table": { "CapacityUnits": number, + // "ReadCapacityUnits": number, "WriteCapacityUnits": number }, "TableName": + // "string", "WriteCapacityUnits": number }' + AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") + // The JSON-serialized value of the `ItemCollectionMetrics` response field. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": blob, + // "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { "string" : + // "AttributeValue" }, "N": "string", "NS": [ "string" ], "NULL": boolean, "S": + // "string", "SS": [ "string" ] } }, "SizeEstimateRangeGB": [ number ] } ] }' + AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") + // The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. + // + // Type: double + // Required: No + // Stability: stable + // Examples: 1.0, 2.0 + AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") + // The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. + // + // Type: double + // Required: No + // Stability: stable + // Examples: 1.0, 2.0 + AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") + // The value of the `ConsistentRead` request parameter. + // + // Type: boolean + // Required: No + // Stability: stable + AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") + // The value of the `ProjectionExpression` request parameter. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Title', 'Title, Price, Color', 'Title, Description, RelatedItems, + // ProductReviews' + AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") + // The value of the `Limit` request parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 10 + AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") + // The value of the `AttributesToGet` request parameter. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: 'lives', 'id' + AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") + // The value of the `IndexName` request parameter. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'name_to_group' + AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") + // The value of the `Select` request parameter. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'ALL_ATTRIBUTES', 'COUNT' + AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") +) + +// DynamoDB.CreateTable +const ( + // The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request + // field + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": "string", + // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], + // "ProjectionType": "string" }, "ProvisionedThroughput": { "ReadCapacityUnits": + // number, "WriteCapacityUnits": number } }' + AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") + // The JSON-serialized value of each item of the `LocalSecondaryIndexes` request + // field. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '{ "IndexARN": "string", "IndexName": "string", "IndexSizeBytes": + // number, "ItemCount": number, "KeySchema": [ { "AttributeName": "string", + // "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ "string" ], + // "ProjectionType": "string" } }' + AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") +) + +// DynamoDB.ListTables +const ( + // The value of the `ExclusiveStartTableName` request parameter. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Users', 'CatsTable' + AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") + // The the number of items in the `TableNames` response parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 20 + AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") +) + +// DynamoDB.Query +const ( + // The value of the `ScanIndexForward` request parameter. + // + // Type: boolean + // Required: No + // Stability: stable + AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") +) + +// DynamoDB.Scan +const ( + // The value of the `Segment` request parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 10 + AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") + // The value of the `TotalSegments` request parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 100 + AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") + // The value of the `Count` response parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 10 + AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") + // The value of the `ScannedCount` response parameter. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 50 + AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") +) + +// DynamoDB.UpdateTable +const ( + // The JSON-serialized value of each item in the `AttributeDefinitions` request + // field. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' + AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") + // The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` + // request field. + // + // Type: string[] + // Required: No + // Stability: stable + // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { + // "AttributeName": "string", "KeyType": "string" } ], "Projection": { + // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, + // "ProvisionedThroughput": { "ReadCapacityUnits": number, "WriteCapacityUnits": + // number } }' + AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") +) + +// This document defines the attributes used in messaging systems. +const ( + // A string identifying the messaging system. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'kafka', 'rabbitmq', 'activemq', 'AmazonSQS' + MessagingSystemKey = attribute.Key("messaging.system") + // The message destination name. This might be equal to the span name but is + // required nevertheless. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'MyQueue', 'MyTopic' + MessagingDestinationKey = attribute.Key("messaging.destination") + // The kind of message destination + // + // Type: Enum + // Required: Required only if the message destination is either a `queue` or + // `topic`. + // Stability: stable + MessagingDestinationKindKey = attribute.Key("messaging.destination_kind") + // A boolean that is true if the message destination is temporary. + // + // Type: boolean + // Required: If missing, it is assumed to be false. + // Stability: stable + MessagingTempDestinationKey = attribute.Key("messaging.temp_destination") + // The name of the transport protocol. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'AMQP', 'MQTT' + MessagingProtocolKey = attribute.Key("messaging.protocol") + // The version of the transport protocol. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '0.9.1' + MessagingProtocolVersionKey = attribute.Key("messaging.protocol_version") + // Connection string. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'tibjmsnaming://localhost:7222', + // 'https://queue.amazonaws.com/80398EXAMPLE/MyQueue' + MessagingURLKey = attribute.Key("messaging.url") + // A value used by the messaging system as an identifier for the message, + // represented as a string. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '452a7c7c7c7048c2f887f61572b18fc2' + MessagingMessageIDKey = attribute.Key("messaging.message_id") + // The [conversation ID](#conversations) identifying the conversation to which the + // message belongs, represented as a string. Sometimes called "Correlation ID". + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'MyConversationID' + MessagingConversationIDKey = attribute.Key("messaging.conversation_id") + // The (uncompressed) size of the message payload in bytes. Also use this + // attribute if it is unknown whether the compressed or uncompressed payload size + // is reported. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 2738 + MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message_payload_size_bytes") + // The compressed size of the message payload in bytes. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 2048 + MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message_payload_compressed_size_bytes") +) + +var ( + // A message sent to a queue + MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") + // A message sent to a topic + MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") +) + +// Semantic convention for a consumer of messages received from a messaging system +const ( + // A string identifying the kind of message consumption as defined in the + // [Operation names](#operation-names) section above. If the operation is "send", + // this attribute MUST NOT be set, since the operation can be inferred from the + // span kind in that case. + // + // Type: Enum + // Required: No + // Stability: stable + MessagingOperationKey = attribute.Key("messaging.operation") + // The identifier for the consumer receiving a message. For Kafka, set it to + // `{messaging.kafka.consumer_group} - {messaging.kafka.client_id}`, if both are + // present, or only `messaging.kafka.consumer_group`. For brokers, such as + // RabbitMQ and Artemis, set it to the `client_id` of the client consuming the + // message. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'mygroup - client-6' + MessagingConsumerIDKey = attribute.Key("messaging.consumer_id") +) + +var ( + // receive + MessagingOperationReceive = MessagingOperationKey.String("receive") + // process + MessagingOperationProcess = MessagingOperationKey.String("process") +) + +// Attributes for RabbitMQ +const ( + // RabbitMQ message routing key. + // + // Type: string + // Required: Unless it is empty. + // Stability: stable + // Examples: 'myKey' + MessagingRabbitmqRoutingKeyKey = attribute.Key("messaging.rabbitmq.routing_key") +) + +// Attributes for Apache Kafka +const ( + // Message keys in Kafka are used for grouping alike messages to ensure they're + // processed on the same partition. They differ from `messaging.message_id` in + // that they're not unique. If the key is `null`, the attribute MUST NOT be set. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'myKey' + // Note: If the key type is not string, it's string representation has to be + // supplied for the attribute. If the key has no unambiguous, canonical string + // form, don't include its value. + MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message_key") + // Name of the Kafka Consumer Group that is handling the message. Only applies to + // consumers, not producers. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'my-group' + MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer_group") + // Client ID for the Consumer or Producer that is handling the message. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'client-5' + MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") + // Partition the message is sent to. + // + // Type: int + // Required: No + // Stability: stable + // Examples: 2 + MessagingKafkaPartitionKey = attribute.Key("messaging.kafka.partition") + // A boolean that is true if the message is a tombstone. + // + // Type: boolean + // Required: If missing, it is assumed to be false. + // Stability: stable + MessagingKafkaTombstoneKey = attribute.Key("messaging.kafka.tombstone") +) + +// This document defines semantic conventions for remote procedure calls. +const ( + // A string identifying the remoting system. + // + // Type: string + // Required: Always + // Stability: stable + // Examples: 'grpc', 'java_rmi', 'wcf' + RPCSystemKey = attribute.Key("rpc.system") + // The full (logical) name of the service being called, including its package + // name, if applicable. + // + // Type: string + // Required: No, but recommended + // Stability: stable + // Examples: 'myservice.EchoService' + // Note: This is the logical name of the service from the RPC interface + // perspective, which can be different from the name of any implementing class. + // The `code.namespace` attribute may be used to store the latter (despite the + // attribute name, it may include a class name; e.g., class with method actually + // executing the call on the server side, RPC client stub class on the client + // side). + RPCServiceKey = attribute.Key("rpc.service") + // The name of the (logical) method being called, must be equal to the $method + // part in the span name. + // + // Type: string + // Required: No, but recommended + // Stability: stable + // Examples: 'exampleMethod' + // Note: This is the logical name of the method from the RPC interface + // perspective, which can be different from the name of any implementing + // method/function. The `code.function` attribute may be used to store the latter + // (e.g., method actually executing the call on the server side, RPC client stub + // method on the client side). + RPCMethodKey = attribute.Key("rpc.method") +) + +// Tech-specific attributes for gRPC. +const ( + // The [numeric status + // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC + // request. + // + // Type: Enum + // Required: Always + // Stability: stable + RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") +) + +var ( + // OK + RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) + // CANCELLED + RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) + // UNKNOWN + RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) + // INVALID_ARGUMENT + RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) + // DEADLINE_EXCEEDED + RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) + // NOT_FOUND + RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) + // ALREADY_EXISTS + RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) + // PERMISSION_DENIED + RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) + // RESOURCE_EXHAUSTED + RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) + // FAILED_PRECONDITION + RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) + // ABORTED + RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) + // OUT_OF_RANGE + RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) + // UNIMPLEMENTED + RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) + // INTERNAL + RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) + // UNAVAILABLE + RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) + // DATA_LOSS + RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) + // UNAUTHENTICATED + RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) +) + +// Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). +const ( + // Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC + // 1.0 does not specify this, the value can be omitted. + // + // Type: string + // Required: If missing, it is assumed to be "1.0". + // Stability: stable + // Examples: '2.0', '1.0' + RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") + // `id` property of request or response. Since protocol allows id to be int, + // string, `null` or missing (for notifications), value is expected to be cast to + // string for simplicity. Use empty string in case of `null` value. Omit entirely + // if this is a notification. + // + // Type: string + // Required: No + // Stability: stable + // Examples: '10', 'request-7', '' + RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") + // `error.code` property of response if it is an error response. + // + // Type: int + // Required: If missing, response is assumed to be successful. + // Stability: stable + // Examples: -32700, 100 + RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") + // `error.message` property of response if it is an error response. + // + // Type: string + // Required: No + // Stability: stable + // Examples: 'Parse error', 'User already exists' + RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") +) + +// RPC received/sent message. +const ( + // Whether this is a received or sent message. + // + // Type: Enum + // Required: No + // Stability: stable + MessageTypeKey = attribute.Key("message.type") + // MUST be calculated as two different counters starting from `1` one for sent + // messages and one for received message. + // + // Type: int + // Required: No + // Stability: stable + // Note: This way we guarantee that the values will be consistent between + // different implementations. + MessageIDKey = attribute.Key("message.id") + // Compressed size of the message in bytes. + // + // Type: int + // Required: No + // Stability: stable + MessageCompressedSizeKey = attribute.Key("message.compressed_size") + // Uncompressed size of the message in bytes. + // + // Type: int + // Required: No + // Stability: stable + MessageUncompressedSizeKey = attribute.Key("message.uncompressed_size") +) + +var ( + // sent + MessageTypeSent = MessageTypeKey.String("SENT") + // received + MessageTypeReceived = MessageTypeKey.String("RECEIVED") +) diff --git a/vendor/go.opentelemetry.io/otel/trace.go b/vendor/go.opentelemetry.io/otel/trace.go new file mode 100644 index 00000000..28b4f5e4 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace.go @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +import ( + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/trace" +) + +// Tracer creates a named tracer that implements Tracer interface. +// If the name is an empty string then provider uses default name. +// +// This is short for GetTracerProvider().Tracer(name, opts...) +func Tracer(name string, opts ...trace.TracerOption) trace.Tracer { + return GetTracerProvider().Tracer(name, opts...) +} + +// GetTracerProvider returns the registered global trace provider. +// If none is registered then an instance of NoopTracerProvider is returned. +// +// Use the trace provider to create a named tracer. E.g. +// tracer := otel.GetTracerProvider().Tracer("example.com/foo") +// or +// tracer := otel.Tracer("example.com/foo") +func GetTracerProvider() trace.TracerProvider { + return global.TracerProvider() +} + +// SetTracerProvider registers `tp` as the global trace provider. +func SetTracerProvider(tp trace.TracerProvider) { + global.SetTracerProvider(tp) +} diff --git a/vendor/go.opentelemetry.io/otel/trace/LICENSE b/vendor/go.opentelemetry.io/otel/trace/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/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/go.opentelemetry.io/otel/trace/config.go b/vendor/go.opentelemetry.io/otel/trace/config.go new file mode 100644 index 00000000..bcc333e0 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/config.go @@ -0,0 +1,316 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +import ( + "time" + + "go.opentelemetry.io/otel/attribute" +) + +// TracerConfig is a group of options for a Tracer. +type TracerConfig struct { + instrumentationVersion string + // Schema URL of the telemetry emitted by the Tracer. + schemaURL string +} + +// InstrumentationVersion returns the version of the library providing instrumentation. +func (t *TracerConfig) InstrumentationVersion() string { + return t.instrumentationVersion +} + +// SchemaURL returns the Schema URL of the telemetry emitted by the Tracer. +func (t *TracerConfig) SchemaURL() string { + return t.schemaURL +} + +// NewTracerConfig applies all the options to a returned TracerConfig. +func NewTracerConfig(options ...TracerOption) TracerConfig { + var config TracerConfig + for _, option := range options { + config = option.apply(config) + } + return config +} + +// TracerOption applies an option to a TracerConfig. +type TracerOption interface { + apply(TracerConfig) TracerConfig +} + +type tracerOptionFunc func(TracerConfig) TracerConfig + +func (fn tracerOptionFunc) apply(cfg TracerConfig) TracerConfig { + return fn(cfg) +} + +// SpanConfig is a group of options for a Span. +type SpanConfig struct { + attributes []attribute.KeyValue + timestamp time.Time + links []Link + newRoot bool + spanKind SpanKind + stackTrace bool +} + +// Attributes describe the associated qualities of a Span. +func (cfg *SpanConfig) Attributes() []attribute.KeyValue { + return cfg.attributes +} + +// Timestamp is a time in a Span life-cycle. +func (cfg *SpanConfig) Timestamp() time.Time { + return cfg.timestamp +} + +// StackTrace checks whether stack trace capturing is enabled. +func (cfg *SpanConfig) StackTrace() bool { + return cfg.stackTrace +} + +// Links are the associations a Span has with other Spans. +func (cfg *SpanConfig) Links() []Link { + return cfg.links +} + +// NewRoot identifies a Span as the root Span for a new trace. This is +// commonly used when an existing trace crosses trust boundaries and the +// remote parent span context should be ignored for security. +func (cfg *SpanConfig) NewRoot() bool { + return cfg.newRoot +} + +// SpanKind is the role a Span has in a trace. +func (cfg *SpanConfig) SpanKind() SpanKind { + return cfg.spanKind +} + +// NewSpanStartConfig applies all the options to a returned SpanConfig. +// No validation is performed on the returned SpanConfig (e.g. no uniqueness +// checking or bounding of data), it is left to the SDK to perform this +// action. +func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { + var c SpanConfig + for _, option := range options { + c = option.applySpanStart(c) + } + return c +} + +// NewSpanEndConfig applies all the options to a returned SpanConfig. +// No validation is performed on the returned SpanConfig (e.g. no uniqueness +// checking or bounding of data), it is left to the SDK to perform this +// action. +func NewSpanEndConfig(options ...SpanEndOption) SpanConfig { + var c SpanConfig + for _, option := range options { + c = option.applySpanEnd(c) + } + return c +} + +// SpanStartOption applies an option to a SpanConfig. These options are applicable +// only when the span is created +type SpanStartOption interface { + applySpanStart(SpanConfig) SpanConfig +} + +type spanOptionFunc func(SpanConfig) SpanConfig + +func (fn spanOptionFunc) applySpanStart(cfg SpanConfig) SpanConfig { + return fn(cfg) +} + +// SpanEndOption applies an option to a SpanConfig. These options are +// applicable only when the span is ended. +type SpanEndOption interface { + applySpanEnd(SpanConfig) SpanConfig +} + +// EventConfig is a group of options for an Event. +type EventConfig struct { + attributes []attribute.KeyValue + timestamp time.Time + stackTrace bool +} + +// Attributes describe the associated qualities of an Event. +func (cfg *EventConfig) Attributes() []attribute.KeyValue { + return cfg.attributes +} + +// Timestamp is a time in an Event life-cycle. +func (cfg *EventConfig) Timestamp() time.Time { + return cfg.timestamp +} + +// StackTrace checks whether stack trace capturing is enabled. +func (cfg *EventConfig) StackTrace() bool { + return cfg.stackTrace +} + +// NewEventConfig applies all the EventOptions to a returned EventConfig. If no +// timestamp option is passed, the returned EventConfig will have a Timestamp +// set to the call time, otherwise no validation is performed on the returned +// EventConfig. +func NewEventConfig(options ...EventOption) EventConfig { + var c EventConfig + for _, option := range options { + c = option.applyEvent(c) + } + if c.timestamp.IsZero() { + c.timestamp = time.Now() + } + return c +} + +// EventOption applies span event options to an EventConfig. +type EventOption interface { + applyEvent(EventConfig) EventConfig +} + +// SpanOption are options that can be used at both the beginning and end of a span. +type SpanOption interface { + SpanStartOption + SpanEndOption +} + +// SpanStartEventOption are options that can be used at the start of a span, or with an event. +type SpanStartEventOption interface { + SpanStartOption + EventOption +} + +// SpanEndEventOption are options that can be used at the end of a span, or with an event. +type SpanEndEventOption interface { + SpanEndOption + EventOption +} + +type attributeOption []attribute.KeyValue + +func (o attributeOption) applySpan(c SpanConfig) SpanConfig { + c.attributes = append(c.attributes, []attribute.KeyValue(o)...) + return c +} +func (o attributeOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o attributeOption) applyEvent(c EventConfig) EventConfig { + c.attributes = append(c.attributes, []attribute.KeyValue(o)...) + return c +} + +var _ SpanStartEventOption = attributeOption{} + +// WithAttributes adds the attributes related to a span life-cycle event. +// These attributes are used to describe the work a Span represents when this +// option is provided to a Span's start or end events. Otherwise, these +// attributes provide additional information about the event being recorded +// (e.g. error, state change, processing progress, system event). +// +// If multiple of these options are passed the attributes of each successive +// option will extend the attributes instead of overwriting. There is no +// guarantee of uniqueness in the resulting attributes. +func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption { + return attributeOption(attributes) +} + +// SpanEventOption are options that can be used with an event or a span. +type SpanEventOption interface { + SpanOption + EventOption +} + +type timestampOption time.Time + +func (o timestampOption) applySpan(c SpanConfig) SpanConfig { + c.timestamp = time.Time(o) + return c +} +func (o timestampOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o timestampOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } +func (o timestampOption) applyEvent(c EventConfig) EventConfig { + c.timestamp = time.Time(o) + return c +} + +var _ SpanEventOption = timestampOption{} + +// WithTimestamp sets the time of a Span or Event life-cycle moment (e.g. +// started, stopped, errored). +func WithTimestamp(t time.Time) SpanEventOption { + return timestampOption(t) +} + +type stackTraceOption bool + +func (o stackTraceOption) applyEvent(c EventConfig) EventConfig { + c.stackTrace = bool(o) + return c +} +func (o stackTraceOption) applySpan(c SpanConfig) SpanConfig { + c.stackTrace = bool(o) + return c +} +func (o stackTraceOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } + +// WithStackTrace sets the flag to capture the error with stack trace (e.g. true, false). +func WithStackTrace(b bool) SpanEndEventOption { + return stackTraceOption(b) +} + +// WithLinks adds links to a Span. The links are added to the existing Span +// links, i.e. this does not overwrite. Links with invalid span context are ignored. +func WithLinks(links ...Link) SpanStartOption { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { + cfg.links = append(cfg.links, links...) + return cfg + }) +} + +// WithNewRoot specifies that the Span should be treated as a root Span. Any +// existing parent span context will be ignored when defining the Span's trace +// identifiers. +func WithNewRoot() SpanStartOption { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { + cfg.newRoot = true + return cfg + }) +} + +// WithSpanKind sets the SpanKind of a Span. +func WithSpanKind(kind SpanKind) SpanStartOption { + return spanOptionFunc(func(cfg SpanConfig) SpanConfig { + cfg.spanKind = kind + return cfg + }) +} + +// WithInstrumentationVersion sets the instrumentation version. +func WithInstrumentationVersion(version string) TracerOption { + return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { + cfg.instrumentationVersion = version + return cfg + }) +} + +// WithSchemaURL sets the schema URL for the Tracer. +func WithSchemaURL(schemaURL string) TracerOption { + return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { + cfg.schemaURL = schemaURL + return cfg + }) +} diff --git a/vendor/go.opentelemetry.io/otel/trace/context.go b/vendor/go.opentelemetry.io/otel/trace/context.go new file mode 100644 index 00000000..76f9a083 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/context.go @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +import "context" + +type traceContextKeyType int + +const currentSpanKey traceContextKeyType = iota + +// ContextWithSpan returns a copy of parent with span set as the current Span. +func ContextWithSpan(parent context.Context, span Span) context.Context { + return context.WithValue(parent, currentSpanKey, span) +} + +// ContextWithSpanContext returns a copy of parent with sc as the current +// Span. The Span implementation that wraps sc is non-recording and performs +// no operations other than to return sc as the SpanContext from the +// SpanContext method. +func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Context { + return ContextWithSpan(parent, nonRecordingSpan{sc: sc}) +} + +// ContextWithRemoteSpanContext returns a copy of parent with rsc set explicly +// as a remote SpanContext and as the current Span. The Span implementation +// that wraps rsc is non-recording and performs no operations other than to +// return rsc as the SpanContext from the SpanContext method. +func ContextWithRemoteSpanContext(parent context.Context, rsc SpanContext) context.Context { + return ContextWithSpanContext(parent, rsc.WithRemote(true)) +} + +// SpanFromContext returns the current Span from ctx. +// +// If no Span is currently set in ctx an implementation of a Span that +// performs no operations is returned. +func SpanFromContext(ctx context.Context) Span { + if ctx == nil { + return noopSpan{} + } + if span, ok := ctx.Value(currentSpanKey).(Span); ok { + return span + } + return noopSpan{} +} + +// SpanContextFromContext returns the current Span's SpanContext. +func SpanContextFromContext(ctx context.Context) SpanContext { + return SpanFromContext(ctx).SpanContext() +} diff --git a/vendor/go.opentelemetry.io/otel/trace/doc.go b/vendor/go.opentelemetry.io/otel/trace/doc.go new file mode 100644 index 00000000..39141771 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/doc.go @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace provides an implementation of the tracing part of the +OpenTelemetry API. + +To participate in distributed traces a Span needs to be created for the +operation being performed as part of a traced workflow. It its simplest form: + + var tracer trace.Tracer + + func init() { + tracer = otel.Tracer("instrumentation/package/name") + } + + func operation(ctx context.Context) { + var span trace.Span + ctx, span = tracer.Start(ctx, "operation") + defer span.End() + // ... + } + +A Tracer is unique to the instrumentation and is used to create Spans. +Instrumentation should be designed to accept a TracerProvider from which it +can create its own unique Tracer. Alternatively, the registered global +TracerProvider from the go.opentelemetry.io/otel package can be used as +a default. + + const ( + name = "instrumentation/package/name" + version = "0.1.0" + ) + + type Instrumentation struct { + tracer trace.Tracer + } + + func NewInstrumentation(tp trace.TracerProvider) *Instrumentation { + if tp == nil { + tp = otel.TracerProvider() + } + return &Instrumentation{ + tracer: tp.Tracer(name, trace.WithInstrumentationVersion(version)), + } + } + + func operation(ctx context.Context, inst *Instrumentation) { + var span trace.Span + ctx, span = inst.tracer.Start(ctx, "operation") + defer span.End() + // ... + } +*/ +package trace // import "go.opentelemetry.io/otel/trace" diff --git a/vendor/go.opentelemetry.io/otel/trace/nonrecording.go b/vendor/go.opentelemetry.io/otel/trace/nonrecording.go new file mode 100644 index 00000000..88fcb816 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/nonrecording.go @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +// nonRecordingSpan is a minimal implementation of a Span that wraps a +// SpanContext. It performs no operations other than to return the wrapped +// SpanContext. +type nonRecordingSpan struct { + noopSpan + + sc SpanContext +} + +// SpanContext returns the wrapped SpanContext. +func (s nonRecordingSpan) SpanContext() SpanContext { return s.sc } diff --git a/vendor/go.opentelemetry.io/otel/trace/noop.go b/vendor/go.opentelemetry.io/otel/trace/noop.go new file mode 100644 index 00000000..ad9a9fc5 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/noop.go @@ -0,0 +1,89 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" +) + +// NewNoopTracerProvider returns an implementation of TracerProvider that +// performs no operations. The Tracer and Spans created from the returned +// TracerProvider also perform no operations. +func NewNoopTracerProvider() TracerProvider { + return noopTracerProvider{} +} + +type noopTracerProvider struct{} + +var _ TracerProvider = noopTracerProvider{} + +// Tracer returns noop implementation of Tracer. +func (p noopTracerProvider) Tracer(string, ...TracerOption) Tracer { + return noopTracer{} +} + +// noopTracer is an implementation of Tracer that preforms no operations. +type noopTracer struct{} + +var _ Tracer = noopTracer{} + +// Start carries forward a non-recording Span, if one is present in the context, otherwise it +// creates a no-op Span. +func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanStartOption) (context.Context, Span) { + span := SpanFromContext(ctx) + if _, ok := span.(nonRecordingSpan); !ok { + // span is likely already a noopSpan, but let's be sure + span = noopSpan{} + } + return ContextWithSpan(ctx, span), span +} + +// noopSpan is an implementation of Span that preforms no operations. +type noopSpan struct{} + +var _ Span = noopSpan{} + +// SpanContext returns an empty span context. +func (noopSpan) SpanContext() SpanContext { return SpanContext{} } + +// IsRecording always returns false. +func (noopSpan) IsRecording() bool { return false } + +// SetStatus does nothing. +func (noopSpan) SetStatus(codes.Code, string) {} + +// SetError does nothing. +func (noopSpan) SetError(bool) {} + +// SetAttributes does nothing. +func (noopSpan) SetAttributes(...attribute.KeyValue) {} + +// End does nothing. +func (noopSpan) End(...SpanEndOption) {} + +// RecordError does nothing. +func (noopSpan) RecordError(error, ...EventOption) {} + +// AddEvent does nothing. +func (noopSpan) AddEvent(string, ...EventOption) {} + +// SetName does nothing. +func (noopSpan) SetName(string) {} + +// TracerProvider returns a no-op TracerProvider +func (noopSpan) TracerProvider() TracerProvider { return noopTracerProvider{} } diff --git a/vendor/go.opentelemetry.io/otel/trace/trace.go b/vendor/go.opentelemetry.io/otel/trace/trace.go new file mode 100644 index 00000000..0923ceb9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/trace.go @@ -0,0 +1,519 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" +) + +const ( + // FlagsSampled is a bitmask with the sampled bit set. A SpanContext + // with the sampling bit set means the span is sampled. + FlagsSampled = TraceFlags(0x01) + + errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase" + + errInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32" + errNilTraceID errorConst = "trace-id can't be all zero" + + errInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16" + errNilSpanID errorConst = "span-id can't be all zero" +) + +type errorConst string + +func (e errorConst) Error() string { + return string(e) +} + +// TraceID is a unique identity of a trace. +// nolint:revive // revive complains about stutter of `trace.TraceID`. +type TraceID [16]byte + +var nilTraceID TraceID +var _ json.Marshaler = nilTraceID + +// IsValid checks whether the trace TraceID is valid. A valid trace ID does +// not consist of zeros only. +func (t TraceID) IsValid() bool { + return !bytes.Equal(t[:], nilTraceID[:]) +} + +// MarshalJSON implements a custom marshal function to encode TraceID +// as a hex string. +func (t TraceID) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} + +// String returns the hex string representation form of a TraceID +func (t TraceID) String() string { + return hex.EncodeToString(t[:]) +} + +// SpanID is a unique identity of a span in a trace. +type SpanID [8]byte + +var nilSpanID SpanID +var _ json.Marshaler = nilSpanID + +// IsValid checks whether the SpanID is valid. A valid SpanID does not consist +// of zeros only. +func (s SpanID) IsValid() bool { + return !bytes.Equal(s[:], nilSpanID[:]) +} + +// MarshalJSON implements a custom marshal function to encode SpanID +// as a hex string. +func (s SpanID) MarshalJSON() ([]byte, error) { + return json.Marshal(s.String()) +} + +// String returns the hex string representation form of a SpanID +func (s SpanID) String() string { + return hex.EncodeToString(s[:]) +} + +// TraceIDFromHex returns a TraceID from a hex string if it is compliant with +// the W3C trace-context specification. See more at +// https://www.w3.org/TR/trace-context/#trace-id +// nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`. +func TraceIDFromHex(h string) (TraceID, error) { + t := TraceID{} + if len(h) != 32 { + return t, errInvalidTraceIDLength + } + + if err := decodeHex(h, t[:]); err != nil { + return t, err + } + + if !t.IsValid() { + return t, errNilTraceID + } + return t, nil +} + +// SpanIDFromHex returns a SpanID from a hex string if it is compliant +// with the w3c trace-context specification. +// See more at https://www.w3.org/TR/trace-context/#parent-id +func SpanIDFromHex(h string) (SpanID, error) { + s := SpanID{} + if len(h) != 16 { + return s, errInvalidSpanIDLength + } + + if err := decodeHex(h, s[:]); err != nil { + return s, err + } + + if !s.IsValid() { + return s, errNilSpanID + } + return s, nil +} + +func decodeHex(h string, b []byte) error { + for _, r := range h { + switch { + case 'a' <= r && r <= 'f': + continue + case '0' <= r && r <= '9': + continue + default: + return errInvalidHexID + } + } + + decoded, err := hex.DecodeString(h) + if err != nil { + return err + } + + copy(b, decoded) + return nil +} + +// TraceFlags contains flags that can be set on a SpanContext +type TraceFlags byte //nolint:revive // revive complains about stutter of `trace.TraceFlags`. + +// IsSampled returns if the sampling bit is set in the TraceFlags. +func (tf TraceFlags) IsSampled() bool { + return tf&FlagsSampled == FlagsSampled +} + +// WithSampled sets the sampling bit in a new copy of the TraceFlags. +func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { + if sampled { + return tf | FlagsSampled + } + + return tf &^ FlagsSampled +} + +// MarshalJSON implements a custom marshal function to encode TraceFlags +// as a hex string. +func (tf TraceFlags) MarshalJSON() ([]byte, error) { + return json.Marshal(tf.String()) +} + +// String returns the hex string representation form of TraceFlags +func (tf TraceFlags) String() string { + return hex.EncodeToString([]byte{byte(tf)}[:]) +} + +// SpanContextConfig contains mutable fields usable for constructing +// an immutable SpanContext. +type SpanContextConfig struct { + TraceID TraceID + SpanID SpanID + TraceFlags TraceFlags + TraceState TraceState + Remote bool +} + +// NewSpanContext constructs a SpanContext using values from the provided +// SpanContextConfig. +func NewSpanContext(config SpanContextConfig) SpanContext { + return SpanContext{ + traceID: config.TraceID, + spanID: config.SpanID, + traceFlags: config.TraceFlags, + traceState: config.TraceState, + remote: config.Remote, + } +} + +// SpanContext contains identifying trace information about a Span. +type SpanContext struct { + traceID TraceID + spanID SpanID + traceFlags TraceFlags + traceState TraceState + remote bool +} + +var _ json.Marshaler = SpanContext{} + +// IsValid returns if the SpanContext is valid. A valid span context has a +// valid TraceID and SpanID. +func (sc SpanContext) IsValid() bool { + return sc.HasTraceID() && sc.HasSpanID() +} + +// IsRemote indicates whether the SpanContext represents a remotely-created Span. +func (sc SpanContext) IsRemote() bool { + return sc.remote +} + +// WithRemote returns a copy of sc with the Remote property set to remote. +func (sc SpanContext) WithRemote(remote bool) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: sc.spanID, + traceFlags: sc.traceFlags, + traceState: sc.traceState, + remote: remote, + } +} + +// TraceID returns the TraceID from the SpanContext. +func (sc SpanContext) TraceID() TraceID { + return sc.traceID +} + +// HasTraceID checks if the SpanContext has a valid TraceID. +func (sc SpanContext) HasTraceID() bool { + return sc.traceID.IsValid() +} + +// WithTraceID returns a new SpanContext with the TraceID replaced. +func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext { + return SpanContext{ + traceID: traceID, + spanID: sc.spanID, + traceFlags: sc.traceFlags, + traceState: sc.traceState, + remote: sc.remote, + } +} + +// SpanID returns the SpanID from the SpanContext. +func (sc SpanContext) SpanID() SpanID { + return sc.spanID +} + +// HasSpanID checks if the SpanContext has a valid SpanID. +func (sc SpanContext) HasSpanID() bool { + return sc.spanID.IsValid() +} + +// WithSpanID returns a new SpanContext with the SpanID replaced. +func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: spanID, + traceFlags: sc.traceFlags, + traceState: sc.traceState, + remote: sc.remote, + } +} + +// TraceFlags returns the flags from the SpanContext. +func (sc SpanContext) TraceFlags() TraceFlags { + return sc.traceFlags +} + +// IsSampled returns if the sampling bit is set in the SpanContext's TraceFlags. +func (sc SpanContext) IsSampled() bool { + return sc.traceFlags.IsSampled() +} + +// WithTraceFlags returns a new SpanContext with the TraceFlags replaced. +func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: sc.spanID, + traceFlags: flags, + traceState: sc.traceState, + remote: sc.remote, + } +} + +// TraceState returns the TraceState from the SpanContext. +func (sc SpanContext) TraceState() TraceState { + return sc.traceState +} + +// WithTraceState returns a new SpanContext with the TraceState replaced. +func (sc SpanContext) WithTraceState(state TraceState) SpanContext { + return SpanContext{ + traceID: sc.traceID, + spanID: sc.spanID, + traceFlags: sc.traceFlags, + traceState: state, + remote: sc.remote, + } +} + +// Equal is a predicate that determines whether two SpanContext values are equal. +func (sc SpanContext) Equal(other SpanContext) bool { + return sc.traceID == other.traceID && + sc.spanID == other.spanID && + sc.traceFlags == other.traceFlags && + sc.traceState.String() == other.traceState.String() && + sc.remote == other.remote +} + +// MarshalJSON implements a custom marshal function to encode a SpanContext. +func (sc SpanContext) MarshalJSON() ([]byte, error) { + return json.Marshal(SpanContextConfig{ + TraceID: sc.traceID, + SpanID: sc.spanID, + TraceFlags: sc.traceFlags, + TraceState: sc.traceState, + Remote: sc.remote, + }) +} + +// Span is the individual component of a trace. It represents a single named +// and timed operation of a workflow that is traced. A Tracer is used to +// create a Span and it is then up to the operation the Span represents to +// properly end the Span when the operation itself ends. +// +// Warning: methods may be added to this interface in minor releases. +type Span interface { + // End completes the Span. The Span is considered complete and ready to be + // delivered through the rest of the telemetry pipeline after this method + // is called. Therefore, updates to the Span are not allowed after this + // method has been called. + End(options ...SpanEndOption) + + // AddEvent adds an event with the provided name and options. + AddEvent(name string, options ...EventOption) + + // IsRecording returns the recording state of the Span. It will return + // true if the Span is active and events can be recorded. + IsRecording() bool + + // RecordError will record err as an exception span event for this span. An + // additional call to SetStatus is required if the Status of the Span should + // be set to Error, as this method does not change the Span status. If this + // span is not being recorded or err is nil then this method does nothing. + RecordError(err error, options ...EventOption) + + // SpanContext returns the SpanContext of the Span. The returned SpanContext + // is usable even after the End method has been called for the Span. + SpanContext() SpanContext + + // SetStatus sets the status of the Span in the form of a code and a + // description, overriding previous values set. The description is only + // included in a status when the code is for an error. + SetStatus(code codes.Code, description string) + + // SetName sets the Span name. + SetName(name string) + + // SetAttributes sets kv as attributes of the Span. If a key from kv + // already exists for an attribute of the Span it will be overwritten with + // the value contained in kv. + SetAttributes(kv ...attribute.KeyValue) + + // TracerProvider returns a TracerProvider that can be used to generate + // additional Spans on the same telemetry pipeline as the current Span. + TracerProvider() TracerProvider +} + +// Link is the relationship between two Spans. The relationship can be within +// the same Trace or across different Traces. +// +// For example, a Link is used in the following situations: +// +// 1. Batch Processing: A batch of operations may contain operations +// associated with one or more traces/spans. Since there can only be one +// parent SpanContext, a Link is used to keep reference to the +// SpanContext of all operations in the batch. +// 2. Public Endpoint: A SpanContext for an in incoming client request on a +// public endpoint should be considered untrusted. In such a case, a new +// trace with its own identity and sampling decision needs to be created, +// but this new trace needs to be related to the original trace in some +// form. A Link is used to keep reference to the original SpanContext and +// track the relationship. +type Link struct { + // SpanContext of the linked Span. + SpanContext SpanContext + + // Attributes describe the aspects of the link. + Attributes []attribute.KeyValue +} + +// LinkFromContext returns a link encapsulating the SpanContext in the provided ctx. +func LinkFromContext(ctx context.Context, attrs ...attribute.KeyValue) Link { + return Link{ + SpanContext: SpanContextFromContext(ctx), + Attributes: attrs, + } +} + +// SpanKind is the role a Span plays in a Trace. +type SpanKind int + +// As a convenience, these match the proto definition, see +// https://github.com/open-telemetry/opentelemetry-proto/blob/30d237e1ff3ab7aa50e0922b5bebdd93505090af/opentelemetry/proto/trace/v1/trace.proto#L101-L129 +// +// The unspecified value is not a valid `SpanKind`. Use `ValidateSpanKind()` +// to coerce a span kind to a valid value. +const ( + // SpanKindUnspecified is an unspecified SpanKind and is not a valid + // SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal + // if it is received. + SpanKindUnspecified SpanKind = 0 + // SpanKindInternal is a SpanKind for a Span that represents an internal + // operation within an application. + SpanKindInternal SpanKind = 1 + // SpanKindServer is a SpanKind for a Span that represents the operation + // of handling a request from a client. + SpanKindServer SpanKind = 2 + // SpanKindClient is a SpanKind for a Span that represents the operation + // of client making a request to a server. + SpanKindClient SpanKind = 3 + // SpanKindProducer is a SpanKind for a Span that represents the operation + // of a producer sending a message to a message broker. Unlike + // SpanKindClient and SpanKindServer, there is often no direct + // relationship between this kind of Span and a SpanKindConsumer kind. A + // SpanKindProducer Span will end once the message is accepted by the + // message broker which might not overlap with the processing of that + // message. + SpanKindProducer SpanKind = 4 + // SpanKindConsumer is a SpanKind for a Span that represents the operation + // of a consumer receiving a message from a message broker. Like + // SpanKindProducer Spans, there is often no direct relationship between + // this Span and the Span that produced the message. + SpanKindConsumer SpanKind = 5 +) + +// ValidateSpanKind returns a valid span kind value. This will coerce +// invalid values into the default value, SpanKindInternal. +func ValidateSpanKind(spanKind SpanKind) SpanKind { + switch spanKind { + case SpanKindInternal, + SpanKindServer, + SpanKindClient, + SpanKindProducer, + SpanKindConsumer: + // valid + return spanKind + default: + return SpanKindInternal + } +} + +// String returns the specified name of the SpanKind in lower-case. +func (sk SpanKind) String() string { + switch sk { + case SpanKindInternal: + return "internal" + case SpanKindServer: + return "server" + case SpanKindClient: + return "client" + case SpanKindProducer: + return "producer" + case SpanKindConsumer: + return "consumer" + default: + return "unspecified" + } +} + +// Tracer is the creator of Spans. +// +// Warning: methods may be added to this interface in minor releases. +type Tracer interface { + // Start creates a span and a context.Context containing the newly-created span. + // + // If the context.Context provided in `ctx` contains a Span then the newly-created + // Span will be a child of that span, otherwise it will be a root span. This behavior + // can be overridden by providing `WithNewRoot()` as a SpanOption, causing the + // newly-created Span to be a root span even if `ctx` contains a Span. + // + // When creating a Span it is recommended to provide all known span attributes using + // the `WithAttributes()` SpanOption as samplers will only have access to the + // attributes provided when a Span is created. + // + // Any Span that is created MUST also be ended. This is the responsibility of the user. + // Implementations of this API may leak memory or other resources if Spans are not ended. + Start(ctx context.Context, spanName string, opts ...SpanStartOption) (context.Context, Span) +} + +// TracerProvider provides access to instrumentation Tracers. +// +// Warning: methods may be added to this interface in minor releases. +type TracerProvider interface { + // Tracer creates an implementation of the Tracer interface. + // The instrumentationName must be the name of the library providing + // instrumentation. This name may be the same as the instrumented code + // only if that code provides built-in instrumentation. If the + // instrumentationName is empty, then a implementation defined default + // name will be used instead. + // + // This method must be concurrency safe. + Tracer(instrumentationName string, opts ...TracerOption) Tracer +} diff --git a/vendor/go.opentelemetry.io/otel/trace/tracestate.go b/vendor/go.opentelemetry.io/otel/trace/tracestate.go new file mode 100644 index 00000000..7b7af695 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/tracestate.go @@ -0,0 +1,211 @@ +// Copyright The OpenTelemetry Authors +// +// 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 trace // import "go.opentelemetry.io/otel/trace" + +import ( + "encoding/json" + "fmt" + "regexp" + "strings" +) + +var ( + maxListMembers = 32 + + listDelimiter = "," + + // based on the W3C Trace Context specification, see + // https://www.w3.org/TR/trace-context-1/#tracestate-header + noTenantKeyFormat = `[a-z][_0-9a-z\-\*\/]{0,255}` + withTenantKeyFormat = `[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}` + valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]` + + keyRe = regexp.MustCompile(`^((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))$`) + valueRe = regexp.MustCompile(`^(` + valueFormat + `)$`) + memberRe = regexp.MustCompile(`^\s*((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))=(` + valueFormat + `)\s*$`) + + errInvalidKey errorConst = "invalid tracestate key" + errInvalidValue errorConst = "invalid tracestate value" + errInvalidMember errorConst = "invalid tracestate list-member" + errMemberNumber errorConst = "too many list-members in tracestate" + errDuplicate errorConst = "duplicate list-member in tracestate" +) + +type member struct { + Key string + Value string +} + +func newMember(key, value string) (member, error) { + if !keyRe.MatchString(key) { + return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) + } + if !valueRe.MatchString(value) { + return member{}, fmt.Errorf("%w: %s", errInvalidValue, value) + } + return member{Key: key, Value: value}, nil +} + +func parseMember(m string) (member, error) { + matches := memberRe.FindStringSubmatch(m) + if len(matches) != 5 { + return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) + } + + return member{ + Key: matches[1], + Value: matches[4], + }, nil + +} + +// String encodes member into a string compliant with the W3C Trace Context +// specification. +func (m member) String() string { + return fmt.Sprintf("%s=%s", m.Key, m.Value) +} + +// TraceState provides additional vendor-specific trace identification +// information across different distributed tracing systems. It represents an +// immutable list consisting of key/value pairs, each pair is referred to as a +// list-member. +// +// TraceState conforms to the W3C Trace Context specification +// (https://www.w3.org/TR/trace-context-1). All operations that create or copy +// a TraceState do so by validating all input and will only produce TraceState +// that conform to the specification. Specifically, this means that all +// list-member's key/value pairs are valid, no duplicate list-members exist, +// and the maximum number of list-members (32) is not exceeded. +type TraceState struct { //nolint:revive // revive complains about stutter of `trace.TraceState` + // list is the members in order. + list []member +} + +var _ json.Marshaler = TraceState{} + +// ParseTraceState attempts to decode a TraceState from the passed +// string. It returns an error if the input is invalid according to the W3C +// Trace Context specification. +func ParseTraceState(tracestate string) (TraceState, error) { + if tracestate == "" { + return TraceState{}, nil + } + + wrapErr := func(err error) error { + return fmt.Errorf("failed to parse tracestate: %w", err) + } + + var members []member + found := make(map[string]struct{}) + for _, memberStr := range strings.Split(tracestate, listDelimiter) { + if len(memberStr) == 0 { + continue + } + + m, err := parseMember(memberStr) + if err != nil { + return TraceState{}, wrapErr(err) + } + + if _, ok := found[m.Key]; ok { + return TraceState{}, wrapErr(errDuplicate) + } + found[m.Key] = struct{}{} + + members = append(members, m) + if n := len(members); n > maxListMembers { + return TraceState{}, wrapErr(errMemberNumber) + } + } + + return TraceState{list: members}, nil +} + +// MarshalJSON marshals the TraceState into JSON. +func (ts TraceState) MarshalJSON() ([]byte, error) { + return json.Marshal(ts.String()) +} + +// String encodes the TraceState into a string compliant with the W3C +// Trace Context specification. The returned string will be invalid if the +// TraceState contains any invalid members. +func (ts TraceState) String() string { + members := make([]string, len(ts.list)) + for i, m := range ts.list { + members[i] = m.String() + } + return strings.Join(members, listDelimiter) +} + +// Get returns the value paired with key from the corresponding TraceState +// list-member if it exists, otherwise an empty string is returned. +func (ts TraceState) Get(key string) string { + for _, member := range ts.list { + if member.Key == key { + return member.Value + } + } + + return "" +} + +// Insert adds a new list-member defined by the key/value pair to the +// TraceState. If a list-member already exists for the given key, that +// list-member's value is updated. The new or updated list-member is always +// moved to the beginning of the TraceState as specified by the W3C Trace +// Context specification. +// +// If key or value are invalid according to the W3C Trace Context +// specification an error is returned with the original TraceState. +// +// If adding a new list-member means the TraceState would have more members +// then is allowed, the new list-member will be inserted and the right-most +// list-member will be dropped in the returned TraceState. +func (ts TraceState) Insert(key, value string) (TraceState, error) { + m, err := newMember(key, value) + if err != nil { + return ts, err + } + + cTS := ts.Delete(key) + if cTS.Len()+1 <= maxListMembers { + cTS.list = append(cTS.list, member{}) + } + // When the number of members exceeds capacity, drop the "right-most". + copy(cTS.list[1:], cTS.list) + cTS.list[0] = m + + return cTS, nil +} + +// Delete returns a copy of the TraceState with the list-member identified by +// key removed. +func (ts TraceState) Delete(key string) TraceState { + members := make([]member, ts.Len()) + copy(members, ts.list) + for i, member := range ts.list { + if member.Key == key { + members = append(members[:i], members[i+1:]...) + // TraceState should contain no duplicate members. + break + } + } + return TraceState{list: members} +} + +// Len returns the number of list-members in the TraceState. +func (ts TraceState) Len() int { + return len(ts.list) +} diff --git a/vendor/go.opentelemetry.io/otel/verify_examples.sh b/vendor/go.opentelemetry.io/otel/verify_examples.sh new file mode 100644 index 00000000..dbb61a42 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/verify_examples.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Copyright The OpenTelemetry Authors +# +# 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. + +set -euo pipefail + +cd $(dirname $0) +TOOLS_DIR=$(pwd)/.tools + +if [ -z "${GOPATH}" ] ; then + printf "GOPATH is not defined.\n" + exit -1 +fi + +if [ ! -d "${GOPATH}" ] ; then + printf "GOPATH ${GOPATH} is invalid \n" + exit -1 +fi + +# Pre-requisites +if ! git diff --quiet; then \ + git status + printf "\n\nError: working tree is not clean\n" + exit -1 +fi + +if [ "$(git tag --contains $(git log -1 --pretty=format:"%H"))" = "" ] ; then + printf "$(git log -1)" + printf "\n\nError: HEAD is not pointing to a tagged version" +fi + +make ${TOOLS_DIR}/gojq + +DIR_TMP="${GOPATH}/src/oteltmp/" +rm -rf $DIR_TMP +mkdir -p $DIR_TMP + +printf "Copy examples to ${DIR_TMP}\n" +cp -a ./example ${DIR_TMP} + +# Update go.mod files +printf "Update go.mod: rename module and remove replace\n" + +PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep 'example' | sed 's/^\.\///' | sort) + +for dir in $PACKAGE_DIRS; do + printf " Update go.mod for $dir\n" + (cd "${DIR_TMP}/${dir}" && \ + # replaces is ("mod1" "mod2" …) + replaces=($(go mod edit -json | ${TOOLS_DIR}/gojq '.Replace[].Old.Path')) && \ + # strip double quotes + replaces=("${replaces[@]%\"}") && \ + replaces=("${replaces[@]#\"}") && \ + # make an array (-dropreplace=mod1 -dropreplace=mod2 …) + dropreplaces=("${replaces[@]/#/-dropreplace=}") && \ + go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \ + go mod tidy) +done +printf "Update done:\n\n" + +# Build directories that contain main package. These directories are different than +# directories that contain go.mod files. +printf "Build examples:\n" +EXAMPLES=$(./get_main_pkgs.sh ./example) +for ex in $EXAMPLES; do + printf " Build $ex in ${DIR_TMP}/${ex}\n" + (cd "${DIR_TMP}/${ex}" && \ + go build .) +done + +# Cleanup +printf "Remove copied files.\n" +rm -rf $DIR_TMP diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go new file mode 100644 index 00000000..e36a2263 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/version.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// +// 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 otel // import "go.opentelemetry.io/otel" + +// Version is the current release version of OpenTelemetry in use. +func Version() string { + return "1.6.3" +} diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml new file mode 100644 index 00000000..f97bd571 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/versions.yaml @@ -0,0 +1,58 @@ +# Copyright The OpenTelemetry Authors +# +# 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. + +module-sets: + stable-v1: + version: v1.6.3 + modules: + - go.opentelemetry.io/otel + - go.opentelemetry.io/otel/bridge/opentracing + - go.opentelemetry.io/otel/example/fib + - go.opentelemetry.io/otel/example/jaeger + - go.opentelemetry.io/otel/example/namedtracer + - go.opentelemetry.io/otel/example/otel-collector + - go.opentelemetry.io/otel/example/passthrough + - go.opentelemetry.io/otel/example/zipkin + - go.opentelemetry.io/otel/exporters/jaeger + - go.opentelemetry.io/otel/exporters/zipkin + - go.opentelemetry.io/otel/exporters/otlp/otlptrace + - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc + - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp + - go.opentelemetry.io/otel/exporters/otlp/internal/retry + - go.opentelemetry.io/otel/exporters/stdout/stdouttrace + - go.opentelemetry.io/otel/trace + - go.opentelemetry.io/otel/sdk + experimental-metrics: + version: v0.28.0 + modules: + - go.opentelemetry.io/otel/example/prometheus + - go.opentelemetry.io/otel/exporters/otlp/otlpmetric + - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc + - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp + - go.opentelemetry.io/otel/exporters/prometheus + - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric + - go.opentelemetry.io/otel/metric + - go.opentelemetry.io/otel/sdk/metric + experimental-schema: + version: v0.0.2 + modules: + - go.opentelemetry.io/otel/schema + bridge: + version: v0.28.0 + modules: + - go.opentelemetry.io/otel/bridge/opencensus + - go.opentelemetry.io/otel/bridge/opencensus/test + - go.opentelemetry.io/otel/example/opencensus +excluded-modules: + - go.opentelemetry.io/otel/internal/tools diff --git a/vendor/go.opentelemetry.io/proto/otlp/LICENSE b/vendor/go.opentelemetry.io/proto/otlp/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/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/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_config.pb.go b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_config.pb.go new file mode 100644 index 00000000..71ae5b57 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_config.pb.go @@ -0,0 +1,568 @@ +// Copyright 2019, OpenTelemetry Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.3 +// source: opentelemetry/proto/trace/v1/trace_config.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// How spans should be sampled: +// - Always off +// - Always on +// - Always follow the parent Span's decision (off if no parent). +type ConstantSampler_ConstantDecision int32 + +const ( + ConstantSampler_ALWAYS_OFF ConstantSampler_ConstantDecision = 0 + ConstantSampler_ALWAYS_ON ConstantSampler_ConstantDecision = 1 + ConstantSampler_ALWAYS_PARENT ConstantSampler_ConstantDecision = 2 +) + +// Enum value maps for ConstantSampler_ConstantDecision. +var ( + ConstantSampler_ConstantDecision_name = map[int32]string{ + 0: "ALWAYS_OFF", + 1: "ALWAYS_ON", + 2: "ALWAYS_PARENT", + } + ConstantSampler_ConstantDecision_value = map[string]int32{ + "ALWAYS_OFF": 0, + "ALWAYS_ON": 1, + "ALWAYS_PARENT": 2, + } +) + +func (x ConstantSampler_ConstantDecision) Enum() *ConstantSampler_ConstantDecision { + p := new(ConstantSampler_ConstantDecision) + *p = x + return p +} + +func (x ConstantSampler_ConstantDecision) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ConstantSampler_ConstantDecision) Descriptor() protoreflect.EnumDescriptor { + return file_opentelemetry_proto_trace_v1_trace_config_proto_enumTypes[0].Descriptor() +} + +func (ConstantSampler_ConstantDecision) Type() protoreflect.EnumType { + return &file_opentelemetry_proto_trace_v1_trace_config_proto_enumTypes[0] +} + +func (x ConstantSampler_ConstantDecision) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ConstantSampler_ConstantDecision.Descriptor instead. +func (ConstantSampler_ConstantDecision) EnumDescriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP(), []int{1, 0} +} + +// Global configuration of the trace service. All fields must be specified, or +// the default (zero) values will be used for each type. +type TraceConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The global default sampler used to make decisions on span sampling. + // + // Types that are assignable to Sampler: + // *TraceConfig_ConstantSampler + // *TraceConfig_TraceIdRatioBased + // *TraceConfig_RateLimitingSampler + Sampler isTraceConfig_Sampler `protobuf_oneof:"sampler"` + // The global default max number of attributes per span. + MaxNumberOfAttributes int64 `protobuf:"varint,4,opt,name=max_number_of_attributes,json=maxNumberOfAttributes,proto3" json:"max_number_of_attributes,omitempty"` + // The global default max number of annotation events per span. + MaxNumberOfTimedEvents int64 `protobuf:"varint,5,opt,name=max_number_of_timed_events,json=maxNumberOfTimedEvents,proto3" json:"max_number_of_timed_events,omitempty"` + // The global default max number of attributes per timed event. + MaxNumberOfAttributesPerTimedEvent int64 `protobuf:"varint,6,opt,name=max_number_of_attributes_per_timed_event,json=maxNumberOfAttributesPerTimedEvent,proto3" json:"max_number_of_attributes_per_timed_event,omitempty"` + // The global default max number of link entries per span. + MaxNumberOfLinks int64 `protobuf:"varint,7,opt,name=max_number_of_links,json=maxNumberOfLinks,proto3" json:"max_number_of_links,omitempty"` + // The global default max number of attributes per span. + MaxNumberOfAttributesPerLink int64 `protobuf:"varint,8,opt,name=max_number_of_attributes_per_link,json=maxNumberOfAttributesPerLink,proto3" json:"max_number_of_attributes_per_link,omitempty"` +} + +func (x *TraceConfig) Reset() { + *x = TraceConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TraceConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TraceConfig) ProtoMessage() {} + +func (x *TraceConfig) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TraceConfig.ProtoReflect.Descriptor instead. +func (*TraceConfig) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP(), []int{0} +} + +func (m *TraceConfig) GetSampler() isTraceConfig_Sampler { + if m != nil { + return m.Sampler + } + return nil +} + +func (x *TraceConfig) GetConstantSampler() *ConstantSampler { + if x, ok := x.GetSampler().(*TraceConfig_ConstantSampler); ok { + return x.ConstantSampler + } + return nil +} + +func (x *TraceConfig) GetTraceIdRatioBased() *TraceIdRatioBased { + if x, ok := x.GetSampler().(*TraceConfig_TraceIdRatioBased); ok { + return x.TraceIdRatioBased + } + return nil +} + +func (x *TraceConfig) GetRateLimitingSampler() *RateLimitingSampler { + if x, ok := x.GetSampler().(*TraceConfig_RateLimitingSampler); ok { + return x.RateLimitingSampler + } + return nil +} + +func (x *TraceConfig) GetMaxNumberOfAttributes() int64 { + if x != nil { + return x.MaxNumberOfAttributes + } + return 0 +} + +func (x *TraceConfig) GetMaxNumberOfTimedEvents() int64 { + if x != nil { + return x.MaxNumberOfTimedEvents + } + return 0 +} + +func (x *TraceConfig) GetMaxNumberOfAttributesPerTimedEvent() int64 { + if x != nil { + return x.MaxNumberOfAttributesPerTimedEvent + } + return 0 +} + +func (x *TraceConfig) GetMaxNumberOfLinks() int64 { + if x != nil { + return x.MaxNumberOfLinks + } + return 0 +} + +func (x *TraceConfig) GetMaxNumberOfAttributesPerLink() int64 { + if x != nil { + return x.MaxNumberOfAttributesPerLink + } + return 0 +} + +type isTraceConfig_Sampler interface { + isTraceConfig_Sampler() +} + +type TraceConfig_ConstantSampler struct { + ConstantSampler *ConstantSampler `protobuf:"bytes,1,opt,name=constant_sampler,json=constantSampler,proto3,oneof"` +} + +type TraceConfig_TraceIdRatioBased struct { + TraceIdRatioBased *TraceIdRatioBased `protobuf:"bytes,2,opt,name=trace_id_ratio_based,json=traceIdRatioBased,proto3,oneof"` +} + +type TraceConfig_RateLimitingSampler struct { + RateLimitingSampler *RateLimitingSampler `protobuf:"bytes,3,opt,name=rate_limiting_sampler,json=rateLimitingSampler,proto3,oneof"` +} + +func (*TraceConfig_ConstantSampler) isTraceConfig_Sampler() {} + +func (*TraceConfig_TraceIdRatioBased) isTraceConfig_Sampler() {} + +func (*TraceConfig_RateLimitingSampler) isTraceConfig_Sampler() {} + +// Sampler that always makes a constant decision on span sampling. +type ConstantSampler struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Decision ConstantSampler_ConstantDecision `protobuf:"varint,1,opt,name=decision,proto3,enum=opentelemetry.proto.trace.v1.ConstantSampler_ConstantDecision" json:"decision,omitempty"` +} + +func (x *ConstantSampler) Reset() { + *x = ConstantSampler{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConstantSampler) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConstantSampler) ProtoMessage() {} + +func (x *ConstantSampler) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConstantSampler.ProtoReflect.Descriptor instead. +func (*ConstantSampler) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP(), []int{1} +} + +func (x *ConstantSampler) GetDecision() ConstantSampler_ConstantDecision { + if x != nil { + return x.Decision + } + return ConstantSampler_ALWAYS_OFF +} + +// Sampler that tries to uniformly sample traces with a given ratio. +// The ratio of sampling a trace is equal to that of the specified ratio. +type TraceIdRatioBased struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The desired ratio of sampling. Must be within [0.0, 1.0]. + SamplingRatio float64 `protobuf:"fixed64,1,opt,name=samplingRatio,proto3" json:"samplingRatio,omitempty"` +} + +func (x *TraceIdRatioBased) Reset() { + *x = TraceIdRatioBased{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TraceIdRatioBased) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TraceIdRatioBased) ProtoMessage() {} + +func (x *TraceIdRatioBased) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TraceIdRatioBased.ProtoReflect.Descriptor instead. +func (*TraceIdRatioBased) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP(), []int{2} +} + +func (x *TraceIdRatioBased) GetSamplingRatio() float64 { + if x != nil { + return x.SamplingRatio + } + return 0 +} + +// Sampler that tries to sample with a rate per time window. +type RateLimitingSampler struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Rate per second. + Qps int64 `protobuf:"varint,1,opt,name=qps,proto3" json:"qps,omitempty"` +} + +func (x *RateLimitingSampler) Reset() { + *x = RateLimitingSampler{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RateLimitingSampler) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RateLimitingSampler) ProtoMessage() {} + +func (x *RateLimitingSampler) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RateLimitingSampler.ProtoReflect.Descriptor instead. +func (*RateLimitingSampler) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP(), []int{3} +} + +func (x *RateLimitingSampler) GetQps() int64 { + if x != nil { + return x.Qps + } + return 0 +} + +var File_opentelemetry_proto_trace_v1_trace_config_proto protoreflect.FileDescriptor + +var file_opentelemetry_proto_trace_v1_trace_config_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x1c, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x22, + 0x84, 0x05, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x5a, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x62, 0x0a, 0x14, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x5f, 0x62, 0x61, + 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x52, 0x61, 0x74, 0x69, 0x6f, 0x42, 0x61, 0x73, 0x65, 0x64, 0x48, 0x00, 0x52, 0x11, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x49, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x42, 0x61, 0x73, 0x65, 0x64, 0x12, + 0x67, 0x0a, 0x15, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, + 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, + 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x72, 0x48, 0x00, 0x52, 0x13, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, + 0x67, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, + 0x6f, 0x66, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x4f, 0x66, 0x54, 0x69, 0x6d, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, + 0x28, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x22, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x64, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x10, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x4c, 0x69, 0x6e, + 0x6b, 0x73, 0x12, 0x47, 0x0a, 0x21, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x5f, 0x6f, 0x66, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1c, 0x6d, + 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x5a, 0x0a, 0x08, 0x64, 0x65, + 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3e, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, + 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x4c, + 0x57, 0x41, 0x59, 0x53, 0x5f, 0x4f, 0x46, 0x46, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x4c, + 0x57, 0x41, 0x59, 0x53, 0x5f, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4c, 0x57, + 0x41, 0x59, 0x53, 0x5f, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x22, 0x39, 0x0a, 0x11, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x42, 0x61, 0x73, 0x65, + 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x52, 0x61, 0x74, + 0x69, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69, + 0x6e, 0x67, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x22, 0x27, 0x0a, 0x13, 0x52, 0x61, 0x74, 0x65, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x12, 0x10, + 0x0a, 0x03, 0x71, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x71, 0x70, 0x73, + 0x42, 0x68, 0x0a, 0x1f, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2e, 0x76, 0x31, 0x42, 0x10, 0x54, 0x72, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescOnce sync.Once + file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescData = file_opentelemetry_proto_trace_v1_trace_config_proto_rawDesc +) + +func file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescGZIP() []byte { + file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescOnce.Do(func() { + file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescData) + }) + return file_opentelemetry_proto_trace_v1_trace_config_proto_rawDescData +} + +var file_opentelemetry_proto_trace_v1_trace_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_opentelemetry_proto_trace_v1_trace_config_proto_goTypes = []interface{}{ + (ConstantSampler_ConstantDecision)(0), // 0: opentelemetry.proto.trace.v1.ConstantSampler.ConstantDecision + (*TraceConfig)(nil), // 1: opentelemetry.proto.trace.v1.TraceConfig + (*ConstantSampler)(nil), // 2: opentelemetry.proto.trace.v1.ConstantSampler + (*TraceIdRatioBased)(nil), // 3: opentelemetry.proto.trace.v1.TraceIdRatioBased + (*RateLimitingSampler)(nil), // 4: opentelemetry.proto.trace.v1.RateLimitingSampler +} +var file_opentelemetry_proto_trace_v1_trace_config_proto_depIdxs = []int32{ + 2, // 0: opentelemetry.proto.trace.v1.TraceConfig.constant_sampler:type_name -> opentelemetry.proto.trace.v1.ConstantSampler + 3, // 1: opentelemetry.proto.trace.v1.TraceConfig.trace_id_ratio_based:type_name -> opentelemetry.proto.trace.v1.TraceIdRatioBased + 4, // 2: opentelemetry.proto.trace.v1.TraceConfig.rate_limiting_sampler:type_name -> opentelemetry.proto.trace.v1.RateLimitingSampler + 0, // 3: opentelemetry.proto.trace.v1.ConstantSampler.decision:type_name -> opentelemetry.proto.trace.v1.ConstantSampler.ConstantDecision + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_opentelemetry_proto_trace_v1_trace_config_proto_init() } +func file_opentelemetry_proto_trace_v1_trace_config_proto_init() { + if File_opentelemetry_proto_trace_v1_trace_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TraceConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConstantSampler); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TraceIdRatioBased); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RateLimitingSampler); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*TraceConfig_ConstantSampler)(nil), + (*TraceConfig_TraceIdRatioBased)(nil), + (*TraceConfig_RateLimitingSampler)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opentelemetry_proto_trace_v1_trace_config_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_opentelemetry_proto_trace_v1_trace_config_proto_goTypes, + DependencyIndexes: file_opentelemetry_proto_trace_v1_trace_config_proto_depIdxs, + EnumInfos: file_opentelemetry_proto_trace_v1_trace_config_proto_enumTypes, + MessageInfos: file_opentelemetry_proto_trace_v1_trace_config_proto_msgTypes, + }.Build() + File_opentelemetry_proto_trace_v1_trace_config_proto = out.File + file_opentelemetry_proto_trace_v1_trace_config_proto_rawDesc = nil + file_opentelemetry_proto_trace_v1_trace_config_proto_goTypes = nil + file_opentelemetry_proto_trace_v1_trace_config_proto_depIdxs = nil +} diff --git a/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.go b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.go new file mode 100644 index 00000000..a5e166b1 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.go @@ -0,0 +1,247 @@ +// Copyright 2019, OpenTelemetry Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.3 +// source: opentelemetry/proto/collector/trace/v1/trace_service.proto + +package v1 + +import ( + v1 "go.opentelemetry.io/proto/otlp/trace/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ExportTraceServiceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain one + // element. Intermediary nodes (such as OpenTelemetry Collector) that receive + // data from multiple origins typically batch the data before forwarding further and + // in that case this array will contain multiple elements. + ResourceSpans []*v1.ResourceSpans `protobuf:"bytes,1,rep,name=resource_spans,json=resourceSpans,proto3" json:"resource_spans,omitempty"` +} + +func (x *ExportTraceServiceRequest) Reset() { + *x = ExportTraceServiceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExportTraceServiceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExportTraceServiceRequest) ProtoMessage() {} + +func (x *ExportTraceServiceRequest) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExportTraceServiceRequest.ProtoReflect.Descriptor instead. +func (*ExportTraceServiceRequest) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescGZIP(), []int{0} +} + +func (x *ExportTraceServiceRequest) GetResourceSpans() []*v1.ResourceSpans { + if x != nil { + return x.ResourceSpans + } + return nil +} + +type ExportTraceServiceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ExportTraceServiceResponse) Reset() { + *x = ExportTraceServiceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExportTraceServiceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExportTraceServiceResponse) ProtoMessage() {} + +func (x *ExportTraceServiceResponse) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExportTraceServiceResponse.ProtoReflect.Descriptor instead. +func (*ExportTraceServiceResponse) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescGZIP(), []int{1} +} + +var File_opentelemetry_proto_collector_trace_v1_trace_service_proto protoreflect.FileDescriptor + +var file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDesc = []byte{ + 0x0a, 0x3a, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x26, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x28, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6f, + 0x0a, 0x19, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, + 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x22, + 0x1c, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xa2, 0x01, + 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x91, + 0x01, 0x0a, 0x06, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x41, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x42, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x63, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x73, 0x0a, 0x29, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x42, + 0x11, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescOnce sync.Once + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescData = file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDesc +) + +func file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescGZIP() []byte { + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescOnce.Do(func() { + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescData) + }) + return file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDescData +} + +var file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_opentelemetry_proto_collector_trace_v1_trace_service_proto_goTypes = []interface{}{ + (*ExportTraceServiceRequest)(nil), // 0: opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest + (*ExportTraceServiceResponse)(nil), // 1: opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse + (*v1.ResourceSpans)(nil), // 2: opentelemetry.proto.trace.v1.ResourceSpans +} +var file_opentelemetry_proto_collector_trace_v1_trace_service_proto_depIdxs = []int32{ + 2, // 0: opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest.resource_spans:type_name -> opentelemetry.proto.trace.v1.ResourceSpans + 0, // 1: opentelemetry.proto.collector.trace.v1.TraceService.Export:input_type -> opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest + 1, // 2: opentelemetry.proto.collector.trace.v1.TraceService.Export:output_type -> opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_opentelemetry_proto_collector_trace_v1_trace_service_proto_init() } +func file_opentelemetry_proto_collector_trace_v1_trace_service_proto_init() { + if File_opentelemetry_proto_collector_trace_v1_trace_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExportTraceServiceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExportTraceServiceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_opentelemetry_proto_collector_trace_v1_trace_service_proto_goTypes, + DependencyIndexes: file_opentelemetry_proto_collector_trace_v1_trace_service_proto_depIdxs, + MessageInfos: file_opentelemetry_proto_collector_trace_v1_trace_service_proto_msgTypes, + }.Build() + File_opentelemetry_proto_collector_trace_v1_trace_service_proto = out.File + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_rawDesc = nil + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_goTypes = nil + file_opentelemetry_proto_collector_trace_v1_trace_service_proto_depIdxs = nil +} diff --git a/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.gw.go b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.gw.go new file mode 100644 index 00000000..d142c2a4 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service.pb.gw.go @@ -0,0 +1,167 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: opentelemetry/proto/collector/trace/v1/trace_service.proto + +/* +Package v1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package v1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_TraceService_Export_0(ctx context.Context, marshaler runtime.Marshaler, client TraceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExportTraceServiceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Export(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TraceService_Export_0(ctx context.Context, marshaler runtime.Marshaler, server TraceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ExportTraceServiceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Export(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterTraceServiceHandlerServer registers the http handlers for service TraceService to "mux". +// UnaryRPC :call TraceServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterTraceServiceHandlerFromEndpoint instead. +func RegisterTraceServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server TraceServiceServer) error { + + mux.Handle("POST", pattern_TraceService_Export_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/opentelemetry.proto.collector.trace.v1.TraceService/Export", runtime.WithHTTPPathPattern("/v1/trace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TraceService_Export_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TraceService_Export_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterTraceServiceHandlerFromEndpoint is same as RegisterTraceServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterTraceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterTraceServiceHandler(ctx, mux, conn) +} + +// RegisterTraceServiceHandler registers the http handlers for service TraceService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterTraceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterTraceServiceHandlerClient(ctx, mux, NewTraceServiceClient(conn)) +} + +// RegisterTraceServiceHandlerClient registers the http handlers for service TraceService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "TraceServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "TraceServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "TraceServiceClient" to call the correct interceptors. +func RegisterTraceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client TraceServiceClient) error { + + mux.Handle("POST", pattern_TraceService_Export_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/opentelemetry.proto.collector.trace.v1.TraceService/Export", runtime.WithHTTPPathPattern("/v1/trace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TraceService_Export_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_TraceService_Export_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_TraceService_Export_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "trace"}, "")) +) + +var ( + forward_TraceService_Export_0 = runtime.ForwardResponseMessage +) diff --git a/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service_grpc.pb.go b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service_grpc.pb.go new file mode 100644 index 00000000..c21f2cb4 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/collector/trace/v1/trace_service_grpc.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.1.0 +// - protoc v3.17.3 +// source: opentelemetry/proto/collector/trace/v1/trace_service.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// TraceServiceClient is the client API for TraceService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TraceServiceClient interface { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + Export(ctx context.Context, in *ExportTraceServiceRequest, opts ...grpc.CallOption) (*ExportTraceServiceResponse, error) +} + +type traceServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTraceServiceClient(cc grpc.ClientConnInterface) TraceServiceClient { + return &traceServiceClient{cc} +} + +func (c *traceServiceClient) Export(ctx context.Context, in *ExportTraceServiceRequest, opts ...grpc.CallOption) (*ExportTraceServiceResponse, error) { + out := new(ExportTraceServiceResponse) + err := c.cc.Invoke(ctx, "/opentelemetry.proto.collector.trace.v1.TraceService/Export", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TraceServiceServer is the server API for TraceService service. +// All implementations must embed UnimplementedTraceServiceServer +// for forward compatibility +type TraceServiceServer interface { + // For performance reasons, it is recommended to keep this RPC + // alive for the entire life of the application. + Export(context.Context, *ExportTraceServiceRequest) (*ExportTraceServiceResponse, error) + mustEmbedUnimplementedTraceServiceServer() +} + +// UnimplementedTraceServiceServer must be embedded to have forward compatible implementations. +type UnimplementedTraceServiceServer struct { +} + +func (UnimplementedTraceServiceServer) Export(context.Context, *ExportTraceServiceRequest) (*ExportTraceServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Export not implemented") +} +func (UnimplementedTraceServiceServer) mustEmbedUnimplementedTraceServiceServer() {} + +// UnsafeTraceServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TraceServiceServer will +// result in compilation errors. +type UnsafeTraceServiceServer interface { + mustEmbedUnimplementedTraceServiceServer() +} + +func RegisterTraceServiceServer(s grpc.ServiceRegistrar, srv TraceServiceServer) { + s.RegisterService(&TraceService_ServiceDesc, srv) +} + +func _TraceService_Export_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExportTraceServiceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TraceServiceServer).Export(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/opentelemetry.proto.collector.trace.v1.TraceService/Export", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TraceServiceServer).Export(ctx, req.(*ExportTraceServiceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TraceService_ServiceDesc is the grpc.ServiceDesc for TraceService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TraceService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "opentelemetry.proto.collector.trace.v1.TraceService", + HandlerType: (*TraceServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Export", + Handler: _TraceService_Export_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "opentelemetry/proto/collector/trace/v1/trace_service.proto", +} diff --git a/vendor/go.opentelemetry.io/proto/otlp/common/v1/common.pb.go b/vendor/go.opentelemetry.io/proto/otlp/common/v1/common.pb.go new file mode 100644 index 00000000..e7ccdc0e --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/common/v1/common.pb.go @@ -0,0 +1,681 @@ +// Copyright 2019, OpenTelemetry Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.3 +// source: opentelemetry/proto/common/v1/common.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// AnyValue is used to represent any type of attribute value. AnyValue may contain a +// primitive value such as a string or integer or it may contain an arbitrary nested +// object containing arrays, key-value lists and primitives. +type AnyValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The value is one of the listed fields. It is valid for all values to be unspecified + // in which case this AnyValue is considered to be "empty". + // + // Types that are assignable to Value: + // *AnyValue_StringValue + // *AnyValue_BoolValue + // *AnyValue_IntValue + // *AnyValue_DoubleValue + // *AnyValue_ArrayValue + // *AnyValue_KvlistValue + // *AnyValue_BytesValue + Value isAnyValue_Value `protobuf_oneof:"value"` +} + +func (x *AnyValue) Reset() { + *x = AnyValue{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AnyValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AnyValue) ProtoMessage() {} + +func (x *AnyValue) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AnyValue.ProtoReflect.Descriptor instead. +func (*AnyValue) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{0} +} + +func (m *AnyValue) GetValue() isAnyValue_Value { + if m != nil { + return m.Value + } + return nil +} + +func (x *AnyValue) GetStringValue() string { + if x, ok := x.GetValue().(*AnyValue_StringValue); ok { + return x.StringValue + } + return "" +} + +func (x *AnyValue) GetBoolValue() bool { + if x, ok := x.GetValue().(*AnyValue_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (x *AnyValue) GetIntValue() int64 { + if x, ok := x.GetValue().(*AnyValue_IntValue); ok { + return x.IntValue + } + return 0 +} + +func (x *AnyValue) GetDoubleValue() float64 { + if x, ok := x.GetValue().(*AnyValue_DoubleValue); ok { + return x.DoubleValue + } + return 0 +} + +func (x *AnyValue) GetArrayValue() *ArrayValue { + if x, ok := x.GetValue().(*AnyValue_ArrayValue); ok { + return x.ArrayValue + } + return nil +} + +func (x *AnyValue) GetKvlistValue() *KeyValueList { + if x, ok := x.GetValue().(*AnyValue_KvlistValue); ok { + return x.KvlistValue + } + return nil +} + +func (x *AnyValue) GetBytesValue() []byte { + if x, ok := x.GetValue().(*AnyValue_BytesValue); ok { + return x.BytesValue + } + return nil +} + +type isAnyValue_Value interface { + isAnyValue_Value() +} + +type AnyValue_StringValue struct { + StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type AnyValue_BoolValue struct { + BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type AnyValue_IntValue struct { + IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"` +} + +type AnyValue_DoubleValue struct { + DoubleValue float64 `protobuf:"fixed64,4,opt,name=double_value,json=doubleValue,proto3,oneof"` +} + +type AnyValue_ArrayValue struct { + ArrayValue *ArrayValue `protobuf:"bytes,5,opt,name=array_value,json=arrayValue,proto3,oneof"` +} + +type AnyValue_KvlistValue struct { + KvlistValue *KeyValueList `protobuf:"bytes,6,opt,name=kvlist_value,json=kvlistValue,proto3,oneof"` +} + +type AnyValue_BytesValue struct { + BytesValue []byte `protobuf:"bytes,7,opt,name=bytes_value,json=bytesValue,proto3,oneof"` +} + +func (*AnyValue_StringValue) isAnyValue_Value() {} + +func (*AnyValue_BoolValue) isAnyValue_Value() {} + +func (*AnyValue_IntValue) isAnyValue_Value() {} + +func (*AnyValue_DoubleValue) isAnyValue_Value() {} + +func (*AnyValue_ArrayValue) isAnyValue_Value() {} + +func (*AnyValue_KvlistValue) isAnyValue_Value() {} + +func (*AnyValue_BytesValue) isAnyValue_Value() {} + +// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message +// since oneof in AnyValue does not allow repeated fields. +type ArrayValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Array of values. The array may be empty (contain 0 elements). + Values []*AnyValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *ArrayValue) Reset() { + *x = ArrayValue{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ArrayValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArrayValue) ProtoMessage() {} + +func (x *ArrayValue) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArrayValue.ProtoReflect.Descriptor instead. +func (*ArrayValue) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{1} +} + +func (x *ArrayValue) GetValues() []*AnyValue { + if x != nil { + return x.Values + } + return nil +} + +// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message +// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need +// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to +// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches +// are semantically equivalent. +type KeyValueList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A collection of key/value pairs of key-value pairs. The list may be empty (may + // contain 0 elements). + // The keys MUST be unique (it is not allowed to have more than one + // value with the same key). + Values []*KeyValue `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *KeyValueList) Reset() { + *x = KeyValueList{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeyValueList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyValueList) ProtoMessage() {} + +func (x *KeyValueList) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyValueList.ProtoReflect.Descriptor instead. +func (*KeyValueList) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{2} +} + +func (x *KeyValueList) GetValues() []*KeyValue { + if x != nil { + return x.Values + } + return nil +} + +// KeyValue is a key-value pair that is used to store Span attributes, Link +// attributes, etc. +type KeyValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value *AnyValue `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *KeyValue) Reset() { + *x = KeyValue{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeyValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyValue) ProtoMessage() {} + +func (x *KeyValue) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyValue.ProtoReflect.Descriptor instead. +func (*KeyValue) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{3} +} + +func (x *KeyValue) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *KeyValue) GetValue() *AnyValue { + if x != nil { + return x.Value + } + return nil +} + +// InstrumentationLibrary is a message representing the instrumentation library information +// such as the fully qualified name and version. +// InstrumentationLibrary is wire-compatible with InstrumentationScope for binary +// Protobuf format. +// This message is deprecated and will be removed on June 15, 2022. +// +// Deprecated: Do not use. +type InstrumentationLibrary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // An empty instrumentation library name means the name is unknown. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` +} + +func (x *InstrumentationLibrary) Reset() { + *x = InstrumentationLibrary{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstrumentationLibrary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstrumentationLibrary) ProtoMessage() {} + +func (x *InstrumentationLibrary) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstrumentationLibrary.ProtoReflect.Descriptor instead. +func (*InstrumentationLibrary) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{4} +} + +func (x *InstrumentationLibrary) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InstrumentationLibrary) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +// InstrumentationScope is a message representing the instrumentation scope information +// such as the fully qualified name and version. +type InstrumentationScope struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // An empty instrumentation scope name means the name is unknown. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` +} + +func (x *InstrumentationScope) Reset() { + *x = InstrumentationScope{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstrumentationScope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstrumentationScope) ProtoMessage() {} + +func (x *InstrumentationScope) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_common_v1_common_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstrumentationScope.ProtoReflect.Descriptor instead. +func (*InstrumentationScope) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP(), []int{5} +} + +func (x *InstrumentationScope) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InstrumentationScope) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +var File_opentelemetry_proto_common_v1_common_proto protoreflect.FileDescriptor + +var file_opentelemetry_proto_common_v1_common_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1d, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x22, 0xe0, 0x02, 0x0a, 0x08, + 0x41, 0x6e, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, + 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, + 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, + 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x72, 0x72, 0x61, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x50, 0x0a, 0x0c, 0x6b, 0x76, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x6b, 0x76, 0x6c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, + 0x0a, 0x0a, 0x41, 0x72, 0x72, 0x61, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4f, 0x0a, + 0x0c, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3f, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, + 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x5b, + 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4a, 0x0a, 0x16, 0x49, + 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x44, 0x0a, 0x14, 0x49, 0x6e, 0x73, 0x74, 0x72, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x5b, 0x0a, + 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, + 0x31, 0x42, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x28, 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x74, 0x6c, 0x70, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_opentelemetry_proto_common_v1_common_proto_rawDescOnce sync.Once + file_opentelemetry_proto_common_v1_common_proto_rawDescData = file_opentelemetry_proto_common_v1_common_proto_rawDesc +) + +func file_opentelemetry_proto_common_v1_common_proto_rawDescGZIP() []byte { + file_opentelemetry_proto_common_v1_common_proto_rawDescOnce.Do(func() { + file_opentelemetry_proto_common_v1_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_opentelemetry_proto_common_v1_common_proto_rawDescData) + }) + return file_opentelemetry_proto_common_v1_common_proto_rawDescData +} + +var file_opentelemetry_proto_common_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_opentelemetry_proto_common_v1_common_proto_goTypes = []interface{}{ + (*AnyValue)(nil), // 0: opentelemetry.proto.common.v1.AnyValue + (*ArrayValue)(nil), // 1: opentelemetry.proto.common.v1.ArrayValue + (*KeyValueList)(nil), // 2: opentelemetry.proto.common.v1.KeyValueList + (*KeyValue)(nil), // 3: opentelemetry.proto.common.v1.KeyValue + (*InstrumentationLibrary)(nil), // 4: opentelemetry.proto.common.v1.InstrumentationLibrary + (*InstrumentationScope)(nil), // 5: opentelemetry.proto.common.v1.InstrumentationScope +} +var file_opentelemetry_proto_common_v1_common_proto_depIdxs = []int32{ + 1, // 0: opentelemetry.proto.common.v1.AnyValue.array_value:type_name -> opentelemetry.proto.common.v1.ArrayValue + 2, // 1: opentelemetry.proto.common.v1.AnyValue.kvlist_value:type_name -> opentelemetry.proto.common.v1.KeyValueList + 0, // 2: opentelemetry.proto.common.v1.ArrayValue.values:type_name -> opentelemetry.proto.common.v1.AnyValue + 3, // 3: opentelemetry.proto.common.v1.KeyValueList.values:type_name -> opentelemetry.proto.common.v1.KeyValue + 0, // 4: opentelemetry.proto.common.v1.KeyValue.value:type_name -> opentelemetry.proto.common.v1.AnyValue + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_opentelemetry_proto_common_v1_common_proto_init() } +func file_opentelemetry_proto_common_v1_common_proto_init() { + if File_opentelemetry_proto_common_v1_common_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opentelemetry_proto_common_v1_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AnyValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ArrayValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KeyValueList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KeyValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstrumentationLibrary); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstrumentationScope); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_opentelemetry_proto_common_v1_common_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*AnyValue_StringValue)(nil), + (*AnyValue_BoolValue)(nil), + (*AnyValue_IntValue)(nil), + (*AnyValue_DoubleValue)(nil), + (*AnyValue_ArrayValue)(nil), + (*AnyValue_KvlistValue)(nil), + (*AnyValue_BytesValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opentelemetry_proto_common_v1_common_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_opentelemetry_proto_common_v1_common_proto_goTypes, + DependencyIndexes: file_opentelemetry_proto_common_v1_common_proto_depIdxs, + MessageInfos: file_opentelemetry_proto_common_v1_common_proto_msgTypes, + }.Build() + File_opentelemetry_proto_common_v1_common_proto = out.File + file_opentelemetry_proto_common_v1_common_proto_rawDesc = nil + file_opentelemetry_proto_common_v1_common_proto_goTypes = nil + file_opentelemetry_proto_common_v1_common_proto_depIdxs = nil +} diff --git a/vendor/go.opentelemetry.io/proto/otlp/resource/v1/resource.pb.go b/vendor/go.opentelemetry.io/proto/otlp/resource/v1/resource.pb.go new file mode 100644 index 00000000..43810938 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/resource/v1/resource.pb.go @@ -0,0 +1,191 @@ +// Copyright 2019, OpenTelemetry Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.3 +// source: opentelemetry/proto/resource/v1/resource.proto + +package v1 + +import ( + v1 "go.opentelemetry.io/proto/otlp/common/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Resource information. +type Resource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Set of attributes that describe the resource. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attributes []*v1.KeyValue `protobuf:"bytes,1,rep,name=attributes,proto3" json:"attributes,omitempty"` + // dropped_attributes_count is the number of dropped attributes. If the value is 0, then + // no attributes were dropped. + DroppedAttributesCount uint32 `protobuf:"varint,2,opt,name=dropped_attributes_count,json=droppedAttributesCount,proto3" json:"dropped_attributes_count,omitempty"` +} + +func (x *Resource) Reset() { + *x = Resource{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_resource_v1_resource_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Resource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Resource) ProtoMessage() {} + +func (x *Resource) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_resource_v1_resource_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Resource.ProtoReflect.Descriptor instead. +func (*Resource) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_resource_v1_resource_proto_rawDescGZIP(), []int{0} +} + +func (x *Resource) GetAttributes() []*v1.KeyValue { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Resource) GetDroppedAttributesCount() uint32 { + if x != nil { + return x.DroppedAttributesCount + } + return 0 +} + +var File_opentelemetry_proto_resource_v1_resource_proto protoreflect.FileDescriptor + +var file_opentelemetry_proto_resource_v1_resource_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x1f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x76, + 0x31, 0x1a, 0x2a, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, + 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x0a, + 0x22, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2a, 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_opentelemetry_proto_resource_v1_resource_proto_rawDescOnce sync.Once + file_opentelemetry_proto_resource_v1_resource_proto_rawDescData = file_opentelemetry_proto_resource_v1_resource_proto_rawDesc +) + +func file_opentelemetry_proto_resource_v1_resource_proto_rawDescGZIP() []byte { + file_opentelemetry_proto_resource_v1_resource_proto_rawDescOnce.Do(func() { + file_opentelemetry_proto_resource_v1_resource_proto_rawDescData = protoimpl.X.CompressGZIP(file_opentelemetry_proto_resource_v1_resource_proto_rawDescData) + }) + return file_opentelemetry_proto_resource_v1_resource_proto_rawDescData +} + +var file_opentelemetry_proto_resource_v1_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_opentelemetry_proto_resource_v1_resource_proto_goTypes = []interface{}{ + (*Resource)(nil), // 0: opentelemetry.proto.resource.v1.Resource + (*v1.KeyValue)(nil), // 1: opentelemetry.proto.common.v1.KeyValue +} +var file_opentelemetry_proto_resource_v1_resource_proto_depIdxs = []int32{ + 1, // 0: opentelemetry.proto.resource.v1.Resource.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_opentelemetry_proto_resource_v1_resource_proto_init() } +func file_opentelemetry_proto_resource_v1_resource_proto_init() { + if File_opentelemetry_proto_resource_v1_resource_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opentelemetry_proto_resource_v1_resource_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Resource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opentelemetry_proto_resource_v1_resource_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_opentelemetry_proto_resource_v1_resource_proto_goTypes, + DependencyIndexes: file_opentelemetry_proto_resource_v1_resource_proto_depIdxs, + MessageInfos: file_opentelemetry_proto_resource_v1_resource_proto_msgTypes, + }.Build() + File_opentelemetry_proto_resource_v1_resource_proto = out.File + file_opentelemetry_proto_resource_v1_resource_proto_rawDesc = nil + file_opentelemetry_proto_resource_v1_resource_proto_goTypes = nil + file_opentelemetry_proto_resource_v1_resource_proto_depIdxs = nil +} diff --git a/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go b/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go new file mode 100644 index 00000000..fcd6beb5 --- /dev/null +++ b/vendor/go.opentelemetry.io/proto/otlp/trace/v1/trace.pb.go @@ -0,0 +1,1295 @@ +// Copyright 2019, OpenTelemetry Authors +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.3 +// source: opentelemetry/proto/trace/v1/trace.proto + +package v1 + +import ( + v11 "go.opentelemetry.io/proto/otlp/common/v1" + v1 "go.opentelemetry.io/proto/otlp/resource/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// SpanKind is the type of span. Can be used to specify additional relationships between spans +// in addition to a parent/child relationship. +type Span_SpanKind int32 + +const ( + // Unspecified. Do NOT use as default. + // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + Span_SPAN_KIND_UNSPECIFIED Span_SpanKind = 0 + // Indicates that the span represents an internal operation within an application, + // as opposed to an operation happening at the boundaries. Default value. + Span_SPAN_KIND_INTERNAL Span_SpanKind = 1 + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + Span_SPAN_KIND_SERVER Span_SpanKind = 2 + // Indicates that the span describes a request to some remote service. + Span_SPAN_KIND_CLIENT Span_SpanKind = 3 + // Indicates that the span describes a producer sending a message to a broker. + // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + // between producer and consumer spans. A PRODUCER span ends when the message was accepted + // by the broker while the logical processing of the message might span a much longer time. + Span_SPAN_KIND_PRODUCER Span_SpanKind = 4 + // Indicates that the span describes consumer receiving a message from a broker. + // Like the PRODUCER kind, there is often no direct critical path latency relationship + // between producer and consumer spans. + Span_SPAN_KIND_CONSUMER Span_SpanKind = 5 +) + +// Enum value maps for Span_SpanKind. +var ( + Span_SpanKind_name = map[int32]string{ + 0: "SPAN_KIND_UNSPECIFIED", + 1: "SPAN_KIND_INTERNAL", + 2: "SPAN_KIND_SERVER", + 3: "SPAN_KIND_CLIENT", + 4: "SPAN_KIND_PRODUCER", + 5: "SPAN_KIND_CONSUMER", + } + Span_SpanKind_value = map[string]int32{ + "SPAN_KIND_UNSPECIFIED": 0, + "SPAN_KIND_INTERNAL": 1, + "SPAN_KIND_SERVER": 2, + "SPAN_KIND_CLIENT": 3, + "SPAN_KIND_PRODUCER": 4, + "SPAN_KIND_CONSUMER": 5, + } +) + +func (x Span_SpanKind) Enum() *Span_SpanKind { + p := new(Span_SpanKind) + *p = x + return p +} + +func (x Span_SpanKind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Span_SpanKind) Descriptor() protoreflect.EnumDescriptor { + return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0].Descriptor() +} + +func (Span_SpanKind) Type() protoreflect.EnumType { + return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[0] +} + +func (x Span_SpanKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Span_SpanKind.Descriptor instead. +func (Span_SpanKind) EnumDescriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{4, 0} +} + +// For the semantics of status codes see +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status +type Status_StatusCode int32 + +const ( + // The default status. + Status_STATUS_CODE_UNSET Status_StatusCode = 0 + // The Span has been validated by an Application developers or Operator to have + // completed successfully. + Status_STATUS_CODE_OK Status_StatusCode = 1 + // The Span contains an error. + Status_STATUS_CODE_ERROR Status_StatusCode = 2 +) + +// Enum value maps for Status_StatusCode. +var ( + Status_StatusCode_name = map[int32]string{ + 0: "STATUS_CODE_UNSET", + 1: "STATUS_CODE_OK", + 2: "STATUS_CODE_ERROR", + } + Status_StatusCode_value = map[string]int32{ + "STATUS_CODE_UNSET": 0, + "STATUS_CODE_OK": 1, + "STATUS_CODE_ERROR": 2, + } +) + +func (x Status_StatusCode) Enum() *Status_StatusCode { + p := new(Status_StatusCode) + *p = x + return p +} + +func (x Status_StatusCode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Status_StatusCode) Descriptor() protoreflect.EnumDescriptor { + return file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1].Descriptor() +} + +func (Status_StatusCode) Type() protoreflect.EnumType { + return &file_opentelemetry_proto_trace_v1_trace_proto_enumTypes[1] +} + +func (x Status_StatusCode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Status_StatusCode.Descriptor instead. +func (Status_StatusCode) EnumDescriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{5, 0} +} + +// TracesData represents the traces data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP traces data but do +// not implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +type TracesData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + ResourceSpans []*ResourceSpans `protobuf:"bytes,1,rep,name=resource_spans,json=resourceSpans,proto3" json:"resource_spans,omitempty"` +} + +func (x *TracesData) Reset() { + *x = TracesData{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TracesData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TracesData) ProtoMessage() {} + +func (x *TracesData) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TracesData.ProtoReflect.Descriptor instead. +func (*TracesData) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{0} +} + +func (x *TracesData) GetResourceSpans() []*ResourceSpans { + if x != nil { + return x.ResourceSpans + } + return nil +} + +// A collection of ScopeSpans from a Resource. +type ResourceSpans struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The resource for the spans in this message. + // If this field is not set then no resource info is known. + Resource *v1.Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` + // A list of ScopeSpans that originate from a resource. + ScopeSpans []*ScopeSpans `protobuf:"bytes,2,rep,name=scope_spans,json=scopeSpans,proto3" json:"scope_spans,omitempty"` + // A list of InstrumentationLibrarySpans that originate from a resource. + // This field is deprecated and will be removed after grace period expires on June 15, 2022. + // + // During the grace period the following rules SHOULD be followed: + // + // For Binary Protobufs + // ==================== + // Binary Protobuf senders SHOULD NOT set instrumentation_library_spans. Instead + // scope_spans SHOULD be set. + // + // Binary Protobuf receivers SHOULD check if instrumentation_library_spans is set + // and scope_spans is not set then the value in instrumentation_library_spans + // SHOULD be used instead by converting InstrumentationLibrarySpans into ScopeSpans. + // If scope_spans is set then instrumentation_library_spans SHOULD be ignored. + // + // For JSON + // ======== + // JSON senders that set instrumentation_library_spans field MAY also set + // scope_spans to carry the same spans, essentially double-publishing the same data. + // Such double-publishing MAY be controlled by a user-settable option. + // If double-publishing is not used then the senders SHOULD set scope_spans and + // SHOULD NOT set instrumentation_library_spans. + // + // JSON receivers SHOULD check if instrumentation_library_spans is set and + // scope_spans is not set then the value in instrumentation_library_spans + // SHOULD be used instead by converting InstrumentationLibrarySpans into ScopeSpans. + // If scope_spans is set then instrumentation_library_spans field SHOULD be ignored. + // + // Deprecated: Do not use. + InstrumentationLibrarySpans []*InstrumentationLibrarySpans `protobuf:"bytes,1000,rep,name=instrumentation_library_spans,json=instrumentationLibrarySpans,proto3" json:"instrumentation_library_spans,omitempty"` + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_spans" field which have their own schema_url field. + SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"` +} + +func (x *ResourceSpans) Reset() { + *x = ResourceSpans{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResourceSpans) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResourceSpans) ProtoMessage() {} + +func (x *ResourceSpans) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResourceSpans.ProtoReflect.Descriptor instead. +func (*ResourceSpans) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{1} +} + +func (x *ResourceSpans) GetResource() *v1.Resource { + if x != nil { + return x.Resource + } + return nil +} + +func (x *ResourceSpans) GetScopeSpans() []*ScopeSpans { + if x != nil { + return x.ScopeSpans + } + return nil +} + +// Deprecated: Do not use. +func (x *ResourceSpans) GetInstrumentationLibrarySpans() []*InstrumentationLibrarySpans { + if x != nil { + return x.InstrumentationLibrarySpans + } + return nil +} + +func (x *ResourceSpans) GetSchemaUrl() string { + if x != nil { + return x.SchemaUrl + } + return "" +} + +// A collection of Spans produced by an InstrumentationScope. +type ScopeSpans struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The instrumentation scope information for the spans in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + Scope *v11.InstrumentationScope `protobuf:"bytes,1,opt,name=scope,proto3" json:"scope,omitempty"` + // A list of Spans that originate from an instrumentation scope. + Spans []*Span `protobuf:"bytes,2,rep,name=spans,proto3" json:"spans,omitempty"` + // This schema_url applies to all spans and span events in the "spans" field. + SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"` +} + +func (x *ScopeSpans) Reset() { + *x = ScopeSpans{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ScopeSpans) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScopeSpans) ProtoMessage() {} + +func (x *ScopeSpans) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScopeSpans.ProtoReflect.Descriptor instead. +func (*ScopeSpans) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{2} +} + +func (x *ScopeSpans) GetScope() *v11.InstrumentationScope { + if x != nil { + return x.Scope + } + return nil +} + +func (x *ScopeSpans) GetSpans() []*Span { + if x != nil { + return x.Spans + } + return nil +} + +func (x *ScopeSpans) GetSchemaUrl() string { + if x != nil { + return x.SchemaUrl + } + return "" +} + +// A collection of Spans produced by an InstrumentationLibrary. +// InstrumentationLibrarySpans is wire-compatible with ScopeSpans for binary +// Protobuf format. +// This message is deprecated and will be removed on June 15, 2022. +// +// Deprecated: Do not use. +type InstrumentationLibrarySpans struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The instrumentation library information for the spans in this message. + // Semantically when InstrumentationLibrary isn't set, it is equivalent with + // an empty instrumentation library name (unknown). + InstrumentationLibrary *v11.InstrumentationLibrary `protobuf:"bytes,1,opt,name=instrumentation_library,json=instrumentationLibrary,proto3" json:"instrumentation_library,omitempty"` + // A list of Spans that originate from an instrumentation library. + Spans []*Span `protobuf:"bytes,2,rep,name=spans,proto3" json:"spans,omitempty"` + // This schema_url applies to all spans and span events in the "spans" field. + SchemaUrl string `protobuf:"bytes,3,opt,name=schema_url,json=schemaUrl,proto3" json:"schema_url,omitempty"` +} + +func (x *InstrumentationLibrarySpans) Reset() { + *x = InstrumentationLibrarySpans{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InstrumentationLibrarySpans) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InstrumentationLibrarySpans) ProtoMessage() {} + +func (x *InstrumentationLibrarySpans) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InstrumentationLibrarySpans.ProtoReflect.Descriptor instead. +func (*InstrumentationLibrarySpans) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{3} +} + +func (x *InstrumentationLibrarySpans) GetInstrumentationLibrary() *v11.InstrumentationLibrary { + if x != nil { + return x.InstrumentationLibrary + } + return nil +} + +func (x *InstrumentationLibrarySpans) GetSpans() []*Span { + if x != nil { + return x.Spans + } + return nil +} + +func (x *InstrumentationLibrarySpans) GetSchemaUrl() string { + if x != nil { + return x.SchemaUrl + } + return "" +} + +// Span represents a single operation within a trace. Spans can be +// nested to form a trace tree. Spans may also be linked to other spans +// from the same or different trace and form graphs. Often, a trace +// contains a root span that describes the end-to-end latency, and one +// or more subspans for its sub-operations. A trace can also contain +// multiple root spans, or none at all. Spans do not need to be +// contiguous - there may be gaps or overlaps between spans in a trace. +// +// The next available field id is 17. +type Span struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A unique identifier for a trace. All spans from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes + // is considered invalid. + // + // This field is semantically required. Receiver should generate new + // random trace_id if empty or invalid trace_id was received. + // + // This field is required. + TraceId []byte `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes is considered + // invalid. + // + // This field is semantically required. Receiver should generate new + // random span_id if empty or invalid span_id was received. + // + // This field is required. + SpanId []byte `protobuf:"bytes,2,opt,name=span_id,json=spanId,proto3" json:"span_id,omitempty"` + // trace_state conveys information about request position in multiple distributed tracing graphs. + // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + // See also https://github.com/w3c/distributed-tracing for more details about this field. + TraceState string `protobuf:"bytes,3,opt,name=trace_state,json=traceState,proto3" json:"trace_state,omitempty"` + // The `span_id` of this span's parent span. If this is a root span, then this + // field must be empty. The ID is an 8-byte array. + ParentSpanId []byte `protobuf:"bytes,4,opt,name=parent_span_id,json=parentSpanId,proto3" json:"parent_span_id,omitempty"` + // A description of the span's operation. + // + // For example, the name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name at the same call point in an application. + // This makes it easier to correlate spans in different traces. + // + // This field is semantically required to be set to non-empty string. + // Empty value is equivalent to an unknown span name. + // + // This field is required. + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify queueing latency associated with the span. + Kind Span_SpanKind `protobuf:"varint,6,opt,name=kind,proto3,enum=opentelemetry.proto.trace.v1.Span_SpanKind" json:"kind,omitempty"` + // start_time_unix_nano is the start time of the span. On the client side, this is the time + // kept by the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + StartTimeUnixNano uint64 `protobuf:"fixed64,7,opt,name=start_time_unix_nano,json=startTimeUnixNano,proto3" json:"start_time_unix_nano,omitempty"` + // end_time_unix_nano is the end time of the span. On the client side, this is the time + // kept by the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + EndTimeUnixNano uint64 `protobuf:"fixed64,8,opt,name=end_time_unix_nano,json=endTimeUnixNano,proto3" json:"end_time_unix_nano,omitempty"` + // attributes is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "abc.com/myattribute": true + // "abc.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/common.md#attributes + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attributes []*v11.KeyValue `protobuf:"bytes,9,rep,name=attributes,proto3" json:"attributes,omitempty"` + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + DroppedAttributesCount uint32 `protobuf:"varint,10,opt,name=dropped_attributes_count,json=droppedAttributesCount,proto3" json:"dropped_attributes_count,omitempty"` + // events is a collection of Event items. + Events []*Span_Event `protobuf:"bytes,11,rep,name=events,proto3" json:"events,omitempty"` + // dropped_events_count is the number of dropped events. If the value is 0, then no + // events were dropped. + DroppedEventsCount uint32 `protobuf:"varint,12,opt,name=dropped_events_count,json=droppedEventsCount,proto3" json:"dropped_events_count,omitempty"` + // links is a collection of Links, which are references from this span to a span + // in the same or different trace. + Links []*Span_Link `protobuf:"bytes,13,rep,name=links,proto3" json:"links,omitempty"` + // dropped_links_count is the number of dropped links after the maximum size was + // enforced. If this value is 0, then no links were dropped. + DroppedLinksCount uint32 `protobuf:"varint,14,opt,name=dropped_links_count,json=droppedLinksCount,proto3" json:"dropped_links_count,omitempty"` + // An optional final status for this span. Semantically when Status isn't set, it means + // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + Status *Status `protobuf:"bytes,15,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *Span) Reset() { + *x = Span{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Span) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Span) ProtoMessage() {} + +func (x *Span) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Span.ProtoReflect.Descriptor instead. +func (*Span) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{4} +} + +func (x *Span) GetTraceId() []byte { + if x != nil { + return x.TraceId + } + return nil +} + +func (x *Span) GetSpanId() []byte { + if x != nil { + return x.SpanId + } + return nil +} + +func (x *Span) GetTraceState() string { + if x != nil { + return x.TraceState + } + return "" +} + +func (x *Span) GetParentSpanId() []byte { + if x != nil { + return x.ParentSpanId + } + return nil +} + +func (x *Span) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Span) GetKind() Span_SpanKind { + if x != nil { + return x.Kind + } + return Span_SPAN_KIND_UNSPECIFIED +} + +func (x *Span) GetStartTimeUnixNano() uint64 { + if x != nil { + return x.StartTimeUnixNano + } + return 0 +} + +func (x *Span) GetEndTimeUnixNano() uint64 { + if x != nil { + return x.EndTimeUnixNano + } + return 0 +} + +func (x *Span) GetAttributes() []*v11.KeyValue { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Span) GetDroppedAttributesCount() uint32 { + if x != nil { + return x.DroppedAttributesCount + } + return 0 +} + +func (x *Span) GetEvents() []*Span_Event { + if x != nil { + return x.Events + } + return nil +} + +func (x *Span) GetDroppedEventsCount() uint32 { + if x != nil { + return x.DroppedEventsCount + } + return 0 +} + +func (x *Span) GetLinks() []*Span_Link { + if x != nil { + return x.Links + } + return nil +} + +func (x *Span) GetDroppedLinksCount() uint32 { + if x != nil { + return x.DroppedLinksCount + } + return 0 +} + +func (x *Span) GetStatus() *Status { + if x != nil { + return x.Status + } + return nil +} + +// The Status type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A developer-facing human readable error message. + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + // The status code. + Code Status_StatusCode `protobuf:"varint,3,opt,name=code,proto3,enum=opentelemetry.proto.trace.v1.Status_StatusCode" json:"code,omitempty"` +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{5} +} + +func (x *Status) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Status) GetCode() Status_StatusCode { + if x != nil { + return x.Code + } + return Status_STATUS_CODE_UNSET +} + +// Event is a time-stamped annotation of the span, consisting of user-supplied +// text description and key-value pairs. +type Span_Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // time_unix_nano is the time the event occurred. + TimeUnixNano uint64 `protobuf:"fixed64,1,opt,name=time_unix_nano,json=timeUnixNano,proto3" json:"time_unix_nano,omitempty"` + // name of the event. + // This field is semantically required to be set to non-empty string. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // attributes is a collection of attribute key/value pairs on the event. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attributes []*v11.KeyValue `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty"` + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + DroppedAttributesCount uint32 `protobuf:"varint,4,opt,name=dropped_attributes_count,json=droppedAttributesCount,proto3" json:"dropped_attributes_count,omitempty"` +} + +func (x *Span_Event) Reset() { + *x = Span_Event{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Span_Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Span_Event) ProtoMessage() {} + +func (x *Span_Event) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Span_Event.ProtoReflect.Descriptor instead. +func (*Span_Event) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *Span_Event) GetTimeUnixNano() uint64 { + if x != nil { + return x.TimeUnixNano + } + return 0 +} + +func (x *Span_Event) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Span_Event) GetAttributes() []*v11.KeyValue { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Span_Event) GetDroppedAttributesCount() uint32 { + if x != nil { + return x.DroppedAttributesCount + } + return 0 +} + +// A pointer from the current span to another span in the same trace or in a +// different trace. For example, this can be used in batching operations, +// where a single batch handler processes multiple requests from different +// traces or when the handler receives a request from a different project. +type Span_Link struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + TraceId []byte `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + // A unique identifier for the linked span. The ID is an 8-byte array. + SpanId []byte `protobuf:"bytes,2,opt,name=span_id,json=spanId,proto3" json:"span_id,omitempty"` + // The trace_state associated with the link. + TraceState string `protobuf:"bytes,3,opt,name=trace_state,json=traceState,proto3" json:"trace_state,omitempty"` + // attributes is a collection of attribute key/value pairs on the link. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attributes []*v11.KeyValue `protobuf:"bytes,4,rep,name=attributes,proto3" json:"attributes,omitempty"` + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + DroppedAttributesCount uint32 `protobuf:"varint,5,opt,name=dropped_attributes_count,json=droppedAttributesCount,proto3" json:"dropped_attributes_count,omitempty"` +} + +func (x *Span_Link) Reset() { + *x = Span_Link{} + if protoimpl.UnsafeEnabled { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Span_Link) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Span_Link) ProtoMessage() {} + +func (x *Span_Link) ProtoReflect() protoreflect.Message { + mi := &file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Span_Link.ProtoReflect.Descriptor instead. +func (*Span_Link) Descriptor() ([]byte, []int) { + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP(), []int{4, 1} +} + +func (x *Span_Link) GetTraceId() []byte { + if x != nil { + return x.TraceId + } + return nil +} + +func (x *Span_Link) GetSpanId() []byte { + if x != nil { + return x.SpanId + } + return nil +} + +func (x *Span_Link) GetTraceState() string { + if x != nil { + return x.TraceState + } + return "" +} + +func (x *Span_Link) GetAttributes() []*v11.KeyValue { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Span_Link) GetDroppedAttributesCount() uint32 { + if x != nil { + return x.DroppedAttributesCount + } + return 0 +} + +var File_opentelemetry_proto_trace_v1_trace_proto protoreflect.FileDescriptor + +var file_opentelemetry_proto_trace_v1_trace_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2a, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x60, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, + 0x70, 0x61, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, + 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x22, 0xc5, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x45, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x70, 0x65, + 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x49, 0x0a, 0x0b, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x52, 0x0a, + 0x73, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x82, 0x01, 0x0a, 0x1d, 0x69, + 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18, 0xe8, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x1b, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x55, 0x72, 0x6c, 0x22, 0xb0, + 0x01, 0x0a, 0x0a, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x49, 0x0a, + 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, + 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x6f, 0x70, + 0x65, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, + 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x61, + 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x55, 0x72, + 0x6c, 0x22, 0xea, 0x01, 0x0a, 0x1b, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x53, 0x70, 0x61, 0x6e, + 0x73, 0x12, 0x6e, 0x0a, 0x17, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x52, 0x16, 0x69, 0x6e, 0x73, 0x74, 0x72, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x79, 0x12, 0x38, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x70, 0x61, 0x6e, 0x52, 0x05, 0x73, 0x70, 0x61, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x55, 0x72, 0x6c, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9c, + 0x0a, 0x0a, 0x04, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, + 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, + 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x06, 0x52, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x2b, 0x0a, 0x12, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x06, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, + 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x47, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, + 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x72, + 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x05, + 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x6e, 0x2e, + 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, + 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0xc4, 0x01, 0x0a, 0x05, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x78, + 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0c, 0x74, 0x69, 0x6d, + 0x65, 0x55, 0x6e, 0x69, 0x78, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x1a, 0xde, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, + 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x47, + 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x72, 0x6f, 0x70, 0x70, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x64, 0x72, 0x6f, 0x70, 0x70, + 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x99, 0x01, 0x0a, 0x08, 0x53, 0x70, 0x61, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x19, + 0x0a, 0x15, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, + 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, + 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, + 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x50, 0x41, 0x4e, 0x5f, + 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x16, 0x0a, + 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, + 0x43, 0x45, 0x52, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x50, 0x41, 0x4e, 0x5f, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x55, 0x4d, 0x45, 0x52, 0x10, 0x05, 0x22, 0xbd, 0x01, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x4e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x54, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4f, 0x4b, 0x10, 0x01, + 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x42, 0x58, 0x0a, + 0x1f, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x76, 0x31, + 0x42, 0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x27, + 0x67, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x2e, 0x69, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x74, 0x6c, 0x70, 0x2f, 0x74, + 0x72, 0x61, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_opentelemetry_proto_trace_v1_trace_proto_rawDescOnce sync.Once + file_opentelemetry_proto_trace_v1_trace_proto_rawDescData = file_opentelemetry_proto_trace_v1_trace_proto_rawDesc +) + +func file_opentelemetry_proto_trace_v1_trace_proto_rawDescGZIP() []byte { + file_opentelemetry_proto_trace_v1_trace_proto_rawDescOnce.Do(func() { + file_opentelemetry_proto_trace_v1_trace_proto_rawDescData = protoimpl.X.CompressGZIP(file_opentelemetry_proto_trace_v1_trace_proto_rawDescData) + }) + return file_opentelemetry_proto_trace_v1_trace_proto_rawDescData +} + +var file_opentelemetry_proto_trace_v1_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_opentelemetry_proto_trace_v1_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_opentelemetry_proto_trace_v1_trace_proto_goTypes = []interface{}{ + (Span_SpanKind)(0), // 0: opentelemetry.proto.trace.v1.Span.SpanKind + (Status_StatusCode)(0), // 1: opentelemetry.proto.trace.v1.Status.StatusCode + (*TracesData)(nil), // 2: opentelemetry.proto.trace.v1.TracesData + (*ResourceSpans)(nil), // 3: opentelemetry.proto.trace.v1.ResourceSpans + (*ScopeSpans)(nil), // 4: opentelemetry.proto.trace.v1.ScopeSpans + (*InstrumentationLibrarySpans)(nil), // 5: opentelemetry.proto.trace.v1.InstrumentationLibrarySpans + (*Span)(nil), // 6: opentelemetry.proto.trace.v1.Span + (*Status)(nil), // 7: opentelemetry.proto.trace.v1.Status + (*Span_Event)(nil), // 8: opentelemetry.proto.trace.v1.Span.Event + (*Span_Link)(nil), // 9: opentelemetry.proto.trace.v1.Span.Link + (*v1.Resource)(nil), // 10: opentelemetry.proto.resource.v1.Resource + (*v11.InstrumentationScope)(nil), // 11: opentelemetry.proto.common.v1.InstrumentationScope + (*v11.InstrumentationLibrary)(nil), // 12: opentelemetry.proto.common.v1.InstrumentationLibrary + (*v11.KeyValue)(nil), // 13: opentelemetry.proto.common.v1.KeyValue +} +var file_opentelemetry_proto_trace_v1_trace_proto_depIdxs = []int32{ + 3, // 0: opentelemetry.proto.trace.v1.TracesData.resource_spans:type_name -> opentelemetry.proto.trace.v1.ResourceSpans + 10, // 1: opentelemetry.proto.trace.v1.ResourceSpans.resource:type_name -> opentelemetry.proto.resource.v1.Resource + 4, // 2: opentelemetry.proto.trace.v1.ResourceSpans.scope_spans:type_name -> opentelemetry.proto.trace.v1.ScopeSpans + 5, // 3: opentelemetry.proto.trace.v1.ResourceSpans.instrumentation_library_spans:type_name -> opentelemetry.proto.trace.v1.InstrumentationLibrarySpans + 11, // 4: opentelemetry.proto.trace.v1.ScopeSpans.scope:type_name -> opentelemetry.proto.common.v1.InstrumentationScope + 6, // 5: opentelemetry.proto.trace.v1.ScopeSpans.spans:type_name -> opentelemetry.proto.trace.v1.Span + 12, // 6: opentelemetry.proto.trace.v1.InstrumentationLibrarySpans.instrumentation_library:type_name -> opentelemetry.proto.common.v1.InstrumentationLibrary + 6, // 7: opentelemetry.proto.trace.v1.InstrumentationLibrarySpans.spans:type_name -> opentelemetry.proto.trace.v1.Span + 0, // 8: opentelemetry.proto.trace.v1.Span.kind:type_name -> opentelemetry.proto.trace.v1.Span.SpanKind + 13, // 9: opentelemetry.proto.trace.v1.Span.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 8, // 10: opentelemetry.proto.trace.v1.Span.events:type_name -> opentelemetry.proto.trace.v1.Span.Event + 9, // 11: opentelemetry.proto.trace.v1.Span.links:type_name -> opentelemetry.proto.trace.v1.Span.Link + 7, // 12: opentelemetry.proto.trace.v1.Span.status:type_name -> opentelemetry.proto.trace.v1.Status + 1, // 13: opentelemetry.proto.trace.v1.Status.code:type_name -> opentelemetry.proto.trace.v1.Status.StatusCode + 13, // 14: opentelemetry.proto.trace.v1.Span.Event.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 13, // 15: opentelemetry.proto.trace.v1.Span.Link.attributes:type_name -> opentelemetry.proto.common.v1.KeyValue + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name +} + +func init() { file_opentelemetry_proto_trace_v1_trace_proto_init() } +func file_opentelemetry_proto_trace_v1_trace_proto_init() { + if File_opentelemetry_proto_trace_v1_trace_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TracesData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceSpans); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ScopeSpans); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InstrumentationLibrarySpans); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Span); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Span_Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opentelemetry_proto_trace_v1_trace_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Span_Link); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opentelemetry_proto_trace_v1_trace_proto_rawDesc, + NumEnums: 2, + NumMessages: 8, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_opentelemetry_proto_trace_v1_trace_proto_goTypes, + DependencyIndexes: file_opentelemetry_proto_trace_v1_trace_proto_depIdxs, + EnumInfos: file_opentelemetry_proto_trace_v1_trace_proto_enumTypes, + MessageInfos: file_opentelemetry_proto_trace_v1_trace_proto_msgTypes, + }.Build() + File_opentelemetry_proto_trace_v1_trace_proto = out.File + file_opentelemetry_proto_trace_v1_trace_proto_rawDesc = nil + file_opentelemetry_proto_trace_v1_trace_proto_goTypes = nil + file_opentelemetry_proto_trace_v1_trace_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go b/vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go new file mode 100644 index 00000000..7ea5ced8 --- /dev/null +++ b/vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go @@ -0,0 +1,236 @@ +// Copyright 2015 Google LLC +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.2 +// source: google/api/httpbody.proto + +package httpbody + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) +// returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) +// returns (google.protobuf.Empty); +// +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +type HttpBody struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The HTTP Content-Type header value specifying the content type of the body. + ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + // The HTTP request/response body as raw binary. + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + Extensions []*anypb.Any `protobuf:"bytes,3,rep,name=extensions,proto3" json:"extensions,omitempty"` +} + +func (x *HttpBody) Reset() { + *x = HttpBody{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_httpbody_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HttpBody) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HttpBody) ProtoMessage() {} + +func (x *HttpBody) ProtoReflect() protoreflect.Message { + mi := &file_google_api_httpbody_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HttpBody.ProtoReflect.Descriptor instead. +func (*HttpBody) Descriptor() ([]byte, []int) { + return file_google_api_httpbody_proto_rawDescGZIP(), []int{0} +} + +func (x *HttpBody) GetContentType() string { + if x != nil { + return x.ContentType + } + return "" +} + +func (x *HttpBody) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *HttpBody) GetExtensions() []*anypb.Any { + if x != nil { + return x.Extensions + } + return nil +} + +var File_google_api_httpbody_proto protoreflect.FileDescriptor + +var file_google_api_httpbody_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, + 0x70, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x77, 0x0a, 0x08, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21, + 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, + 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x68, 0x0a, 0x0e, 0x63, + 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0d, 0x48, + 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, + 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f, + 0x64, 0x79, 0x3b, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f, 0x64, 0x79, 0xf8, 0x01, 0x01, 0xa2, 0x02, + 0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_api_httpbody_proto_rawDescOnce sync.Once + file_google_api_httpbody_proto_rawDescData = file_google_api_httpbody_proto_rawDesc +) + +func file_google_api_httpbody_proto_rawDescGZIP() []byte { + file_google_api_httpbody_proto_rawDescOnce.Do(func() { + file_google_api_httpbody_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_httpbody_proto_rawDescData) + }) + return file_google_api_httpbody_proto_rawDescData +} + +var file_google_api_httpbody_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_google_api_httpbody_proto_goTypes = []interface{}{ + (*HttpBody)(nil), // 0: google.api.HttpBody + (*anypb.Any)(nil), // 1: google.protobuf.Any +} +var file_google_api_httpbody_proto_depIdxs = []int32{ + 1, // 0: google.api.HttpBody.extensions:type_name -> google.protobuf.Any + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_google_api_httpbody_proto_init() } +func file_google_api_httpbody_proto_init() { + if File_google_api_httpbody_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_api_httpbody_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HttpBody); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_httpbody_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_httpbody_proto_goTypes, + DependencyIndexes: file_google_api_httpbody_proto_depIdxs, + MessageInfos: file_google_api_httpbody_proto_msgTypes, + }.Build() + File_google_api_httpbody_proto = out.File + file_google_api_httpbody_proto_rawDesc = nil + file_google_api_httpbody_proto_goTypes = nil + file_google_api_httpbody_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go b/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go new file mode 100644 index 00000000..d10ad665 --- /dev/null +++ b/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go @@ -0,0 +1,23 @@ +// Copyright 2020 Google LLC +// +// 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 field_mask aliases all exported identifiers in +// package "google.golang.org/protobuf/types/known/fieldmaskpb". +package field_mask + +import "google.golang.org/protobuf/types/known/fieldmaskpb" + +type FieldMask = fieldmaskpb.FieldMask + +var File_google_protobuf_field_mask_proto = fieldmaskpb.File_google_protobuf_field_mask_proto diff --git a/vendor/google.golang.org/grpc/CONTRIBUTING.md b/vendor/google.golang.org/grpc/CONTRIBUTING.md index cd03f8c7..52338d00 100644 --- a/vendor/google.golang.org/grpc/CONTRIBUTING.md +++ b/vendor/google.golang.org/grpc/CONTRIBUTING.md @@ -53,9 +53,8 @@ How to get your contributions merged smoothly and quickly. - **All tests need to be passing** before your change can be merged. We recommend you **run tests locally** before creating your PR to catch breakages early on. - - `make all` to test everything, OR - - `make vet` to catch vet errors - - `make test` to run the tests - - `make testrace` to run tests in race mode + - `VET_SKIP_PROTO=1 ./vet.sh` to catch vet errors + - `go test -cpu 1,4 -timeout 7m ./...` to run the tests + - `go test -race -cpu 1,4 -timeout 7m ./...` to run tests in race mode - Exceptions to the rules can be made if there's a compelling reason for doing so. diff --git a/vendor/google.golang.org/grpc/attributes/attributes.go b/vendor/google.golang.org/grpc/attributes/attributes.go index 6ff2792e..ae13ddac 100644 --- a/vendor/google.golang.org/grpc/attributes/attributes.go +++ b/vendor/google.golang.org/grpc/attributes/attributes.go @@ -69,7 +69,9 @@ func (a *Attributes) Value(key interface{}) interface{} { // bool' is implemented for a value in the attributes, it is called to // determine if the value matches the one stored in the other attributes. If // Equal is not implemented, standard equality is used to determine if the two -// values are equal. +// values are equal. Note that some types (e.g. maps) aren't comparable by +// default, so they must be wrapped in a struct, or in an alias type, with Equal +// defined. func (a *Attributes) Equal(o *Attributes) bool { if a == nil && o == nil { return true diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index 28f09dc8..f9af7891 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -79,7 +79,7 @@ var ( // errNoTransportSecurity indicates that there is no transport security // being set for ClientConn. Users should either set one or explicitly // call WithInsecure DialOption to disable security. - errNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)") + errNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithTransportCredentials(insecure.NewCredentials()) explicitly or set credentials)") // errTransportCredsAndBundle indicates that creds bundle is used together // with other individual Transport Credentials. errTransportCredsAndBundle = errors.New("grpc: credentials.Bundle may not be used with individual TransportCredentials") diff --git a/vendor/google.golang.org/grpc/credentials/insecure/insecure.go b/vendor/google.golang.org/grpc/credentials/insecure/insecure.go index 22a8f996..82bee144 100644 --- a/vendor/google.golang.org/grpc/credentials/insecure/insecure.go +++ b/vendor/google.golang.org/grpc/credentials/insecure/insecure.go @@ -18,11 +18,6 @@ // Package insecure provides an implementation of the // credentials.TransportCredentials interface which disables transport security. -// -// Experimental -// -// Notice: This package is EXPERIMENTAL and may be changed or removed in a -// later release. package insecure import ( @@ -75,3 +70,29 @@ type info struct { func (info) AuthType() string { return "insecure" } + +// insecureBundle implements an insecure bundle. +// An insecure bundle provides a thin wrapper around insecureTC to support +// the credentials.Bundle interface. +type insecureBundle struct{} + +// NewBundle returns a bundle with disabled transport security and no per rpc credential. +func NewBundle() credentials.Bundle { + return insecureBundle{} +} + +// NewWithMode returns a new insecure Bundle. The mode is ignored. +func (insecureBundle) NewWithMode(string) (credentials.Bundle, error) { + return insecureBundle{}, nil +} + +// PerRPCCredentials returns an nil implementation as insecure +// bundle does not support a per rpc credential. +func (insecureBundle) PerRPCCredentials() credentials.PerRPCCredentials { + return nil +} + +// TransportCredentials returns the underlying insecure transport credential. +func (insecureBundle) TransportCredentials() credentials.TransportCredentials { + return NewCredentials() +} diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index 063f1e90..c4bf09f9 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -272,7 +272,7 @@ func withBackoff(bs internalbackoff.Strategy) DialOption { }) } -// WithBlock returns a DialOption which makes caller of Dial blocks until the +// WithBlock returns a DialOption which makes callers of Dial block until the // underlying connection is up. Without this, Dial returns immediately and // connecting the server happens in background. func WithBlock() DialOption { @@ -304,7 +304,7 @@ func WithReturnConnectionError() DialOption { // WithCredentialsBundle or WithPerRPCCredentials) which require transport // security is incompatible and will cause grpc.Dial() to fail. // -// Deprecated: use insecure.NewCredentials() instead. +// Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead. // Will be supported throughout 1.x. func WithInsecure() DialOption { return newFuncDialOption(func(o *dialOptions) { diff --git a/vendor/google.golang.org/grpc/grpclog/loggerv2.go b/vendor/google.golang.org/grpc/grpclog/loggerv2.go index 34098bb8..7c1f6640 100644 --- a/vendor/google.golang.org/grpc/grpclog/loggerv2.go +++ b/vendor/google.golang.org/grpc/grpclog/loggerv2.go @@ -248,12 +248,12 @@ func (g *loggerT) V(l int) bool { // later release. type DepthLoggerV2 interface { LoggerV2 - // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print. + // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println. InfoDepth(depth int, args ...interface{}) - // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print. + // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println. WarningDepth(depth int, args ...interface{}) - // ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print. + // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println. ErrorDepth(depth int, args ...interface{}) - // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print. + // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println. FatalDepth(depth int, args ...interface{}) } diff --git a/vendor/google.golang.org/grpc/interceptor.go b/vendor/google.golang.org/grpc/interceptor.go index 668e0adc..bb96ef57 100644 --- a/vendor/google.golang.org/grpc/interceptor.go +++ b/vendor/google.golang.org/grpc/interceptor.go @@ -72,9 +72,12 @@ type UnaryServerInfo struct { } // UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal -// execution of a unary RPC. If a UnaryHandler returns an error, it should be produced by the -// status package, or else gRPC will use codes.Unknown as the status code and err.Error() as -// the status message of the RPC. +// execution of a unary RPC. +// +// If a UnaryHandler returns an error, it should either be produced by the +// status package, or be one of the context errors. Otherwise, gRPC will use +// codes.Unknown as the status code and err.Error() as the status message of the +// RPC. type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error) // UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info diff --git a/vendor/google.golang.org/grpc/internal/channelz/funcs.go b/vendor/google.golang.org/grpc/internal/channelz/funcs.go index cd180754..ea660a14 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/funcs.go +++ b/vendor/google.golang.org/grpc/internal/channelz/funcs.go @@ -24,6 +24,7 @@ package channelz import ( + "context" "fmt" "sort" "sync" @@ -49,7 +50,8 @@ var ( // TurnOn turns on channelz data collection. func TurnOn() { if !IsOn() { - NewChannelzStorage() + db.set(newChannelMap()) + idGen.reset() atomic.StoreInt32(&curState, 1) } } @@ -94,46 +96,40 @@ func (d *dbWrapper) get() *channelMap { return d.DB } -// NewChannelzStorage initializes channelz data storage and id generator. +// NewChannelzStorageForTesting initializes channelz data storage and id +// generator for testing purposes. // -// This function returns a cleanup function to wait for all channelz state to be reset by the -// grpc goroutines when those entities get closed. By using this cleanup function, we make sure tests -// don't mess up each other, i.e. lingering goroutine from previous test doing entity removal happen -// to remove some entity just register by the new test, since the id space is the same. -// -// Note: This function is exported for testing purpose only. User should not call -// it in most cases. -func NewChannelzStorage() (cleanup func() error) { - db.set(&channelMap{ - topLevelChannels: make(map[int64]struct{}), - channels: make(map[int64]*channel), - listenSockets: make(map[int64]*listenSocket), - normalSockets: make(map[int64]*normalSocket), - servers: make(map[int64]*server), - subChannels: make(map[int64]*subChannel), - }) +// Returns a cleanup function to be invoked by the test, which waits for up to +// 10s for all channelz state to be reset by the grpc goroutines when those +// entities get closed. This cleanup function helps with ensuring that tests +// don't mess up each other. +func NewChannelzStorageForTesting() (cleanup func() error) { + db.set(newChannelMap()) idGen.reset() + return func() error { - var err error cm := db.get() if cm == nil { return nil } - for i := 0; i < 1000; i++ { - cm.mu.Lock() - if len(cm.topLevelChannels) == 0 && len(cm.servers) == 0 && len(cm.channels) == 0 && len(cm.subChannels) == 0 && len(cm.listenSockets) == 0 && len(cm.normalSockets) == 0 { - cm.mu.Unlock() - // all things stored in the channelz map have been cleared. + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + for { + cm.mu.RLock() + topLevelChannels, servers, channels, subChannels, listenSockets, normalSockets := len(cm.topLevelChannels), len(cm.servers), len(cm.channels), len(cm.subChannels), len(cm.listenSockets), len(cm.normalSockets) + cm.mu.RUnlock() + + if err := ctx.Err(); err != nil { + return fmt.Errorf("after 10s the channelz map has not been cleaned up yet, topchannels: %d, servers: %d, channels: %d, subchannels: %d, listen sockets: %d, normal sockets: %d", topLevelChannels, servers, channels, subChannels, listenSockets, normalSockets) + } + if topLevelChannels == 0 && servers == 0 && channels == 0 && subChannels == 0 && listenSockets == 0 && normalSockets == 0 { return nil } - cm.mu.Unlock() - time.Sleep(10 * time.Millisecond) + <-ticker.C } - - cm.mu.Lock() - err = fmt.Errorf("after 10s the channelz map has not been cleaned up yet, topchannels: %d, servers: %d, channels: %d, subchannels: %d, listen sockets: %d, normal sockets: %d", len(cm.topLevelChannels), len(cm.servers), len(cm.channels), len(cm.subChannels), len(cm.listenSockets), len(cm.normalSockets)) - cm.mu.Unlock() - return err } } @@ -326,6 +322,17 @@ type channelMap struct { normalSockets map[int64]*normalSocket } +func newChannelMap() *channelMap { + return &channelMap{ + topLevelChannels: make(map[int64]struct{}), + channels: make(map[int64]*channel), + listenSockets: make(map[int64]*listenSocket), + normalSockets: make(map[int64]*normalSocket), + servers: make(map[int64]*server), + subChannels: make(map[int64]*subChannel), + } +} + func (c *channelMap) addServer(id int64, s *server) { c.mu.Lock() s.cm = c diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index 93522d71..7d996e51 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -26,13 +26,13 @@ import ( const ( // XDSBootstrapFileNameEnv is the env variable to set bootstrap file name. // Do not use this and read from env directly. Its value is read and kept in - // variable BootstrapFileName. + // variable XDSBootstrapFileName. // // When both bootstrap FileName and FileContent are set, FileName is used. XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP" - // XDSBootstrapFileContentEnv is the env variable to set bootstrapp file + // XDSBootstrapFileContentEnv is the env variable to set bootstrap file // content. Do not use this and read from env directly. Its value is read - // and kept in variable BootstrapFileName. + // and kept in variable XDSBootstrapFileContent. // // When both bootstrap FileName and FileContent are set, FileName is used. XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG" @@ -41,7 +41,9 @@ const ( clientSideSecuritySupportEnv = "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT" aggregateAndDNSSupportEnv = "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" rbacSupportEnv = "GRPC_XDS_EXPERIMENTAL_RBAC" + outlierDetectionSupportEnv = "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" federationEnv = "GRPC_EXPERIMENTAL_XDS_FEDERATION" + rlsInXDSEnv = "GRPC_EXPERIMENTAL_XDS_RLS_LB" c2pResolverTestOnlyTrafficDirectorURIEnv = "GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI" ) @@ -81,10 +83,19 @@ var ( // which can be disabled by setting the environment variable // "GRPC_XDS_EXPERIMENTAL_RBAC" to "false". XDSRBAC = !strings.EqualFold(os.Getenv(rbacSupportEnv), "false") - + // XDSOutlierDetection indicates whether outlier detection support is + // enabled, which can be enabled by setting the environment variable + // "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" to "true". + XDSOutlierDetection = strings.EqualFold(os.Getenv(outlierDetectionSupportEnv), "true") // XDSFederation indicates whether federation support is enabled. XDSFederation = strings.EqualFold(os.Getenv(federationEnv), "true") + // XDSRLS indicates whether processing of Cluster Specifier plugins and + // support for the RLS CLuster Specifier is enabled, which can be enabled by + // setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to + // "true". + XDSRLS = strings.EqualFold(os.Getenv(rlsInXDSEnv), "true") + // C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing. C2PResolverTestOnlyTrafficDirectorURI = os.Getenv(c2pResolverTestOnlyTrafficDirectorURIEnv) ) diff --git a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go index e6f975cb..30a3b425 100644 --- a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go +++ b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go @@ -115,12 +115,12 @@ type LoggerV2 interface { // Notice: This type is EXPERIMENTAL and may be changed or removed in a // later release. type DepthLoggerV2 interface { - // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print. + // InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Println. InfoDepth(depth int, args ...interface{}) - // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print. + // WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Println. WarningDepth(depth int, args ...interface{}) - // ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print. + // ErrorDepth logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Println. ErrorDepth(depth int, args ...interface{}) - // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print. + // FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Println. FatalDepth(depth int, args ...interface{}) } diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go b/vendor/google.golang.org/grpc/internal/grpcutil/regex.go index 2810a8ba..7a092b2b 100644 --- a/vendor/google.golang.org/grpc/internal/grpcutil/regex.go +++ b/vendor/google.golang.org/grpc/internal/grpcutil/regex.go @@ -20,9 +20,12 @@ package grpcutil import "regexp" -// FullMatchWithRegex returns whether the full string matches the regex provided. -func FullMatchWithRegex(re *regexp.Regexp, string string) bool { +// FullMatchWithRegex returns whether the full text matches the regex provided. +func FullMatchWithRegex(re *regexp.Regexp, text string) bool { + if len(text) == 0 { + return re.MatchString(text) + } re.Longest() - rem := re.FindString(string) - return len(rem) == len(string) + rem := re.FindString(text) + return len(rem) == len(text) } diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 1b596bf3..20fb880f 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -38,11 +38,10 @@ var ( // KeepaliveMinPingTime is the minimum ping interval. This must be 10s by // default, but tests may wish to set it lower for convenience. KeepaliveMinPingTime = 10 * time.Second - // ParseServiceConfigForTesting is for creating a fake - // ClientConn for resolver testing only - ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult + // ParseServiceConfig parses a JSON representation of the service config. + ParseServiceConfig interface{} // func(string) *serviceconfig.ParseResult // EqualServiceConfigForTesting is for testing service config generation and - // parsing. Both a and b should be returned by ParseServiceConfigForTesting. + // parsing. Both a and b should be returned by ParseServiceConfig. // This function compares the config without rawJSON stripped, in case the // there's difference in white space. EqualServiceConfigForTesting func(a, b serviceconfig.Config) bool diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index d3bf65b2..0c43efaa 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -741,6 +741,12 @@ func (e ConnectionError) Origin() error { return e.err } +// Unwrap returns the original error of this connection error or nil when the +// origin is nil. +func (e ConnectionError) Unwrap() error { + return e.err +} + var ( // ErrConnClosing indicates that the transport is closing. ErrConnClosing = connectionErrorf(true, nil, "transport is closing") diff --git a/vendor/google.golang.org/grpc/regenerate.sh b/vendor/google.golang.org/grpc/regenerate.sh index a0a71aae..978b89f3 100644 --- a/vendor/google.golang.org/grpc/regenerate.sh +++ b/vendor/google.golang.org/grpc/regenerate.sh @@ -27,9 +27,9 @@ export PATH=${GOBIN}:${PATH} mkdir -p ${GOBIN} echo "remove existing generated files" -# grpc_testingv3/testv3.pb.go is not re-generated because it was -# intentionally generated by an older version of protoc-gen-go. -rm -f $(find . -name '*.pb.go' | grep -v 'grpc_testingv3/testv3.pb.go') +# grpc_testing_not_regenerate/*.pb.go is not re-generated, +# see grpc_testing_not_regenerate/README.md for details. +rm -f $(find . -name '*.pb.go' | grep -v 'grpc_testing_not_regenerate') echo "go install google.golang.org/protobuf/cmd/protoc-gen-go" (cd test/tools && go install google.golang.org/protobuf/cmd/protoc-gen-go) @@ -76,7 +76,21 @@ SOURCES=( # These options of the form 'Mfoo.proto=bar' instruct the codegen to use an # import path of 'bar' in the generated code when 'foo.proto' is imported in # one of the sources. -OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,Mgrpc/core/stats.proto=google.golang.org/grpc/interop/grpc_testing/core +# +# Note that the protos listed here are all for testing purposes. All protos to +# be used externally should have a go_package option (and they don't need to be +# listed here). +OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,\ +Mgrpc/core/stats.proto=google.golang.org/grpc/interop/grpc_testing/core,\ +Mgrpc/testing/benchmark_service.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/stats.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/report_qps_scenario_service.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/messages.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/worker_service.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/control.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/test.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/payloads.proto=google.golang.org/grpc/interop/grpc_testing,\ +Mgrpc/testing/empty.proto=google.golang.org/grpc/interop/grpc_testing for src in ${SOURCES[@]}; do echo "protoc ${src}" @@ -85,7 +99,6 @@ for src in ${SOURCES[@]}; do -I${WORKDIR}/grpc-proto \ -I${WORKDIR}/googleapis \ -I${WORKDIR}/protobuf/src \ - -I${WORKDIR}/istio \ ${src} done @@ -96,7 +109,6 @@ for src in ${LEGACY_SOURCES[@]}; do -I${WORKDIR}/grpc-proto \ -I${WORKDIR}/googleapis \ -I${WORKDIR}/protobuf/src \ - -I${WORKDIR}/istio \ ${src} done @@ -105,9 +117,9 @@ done mkdir -p ${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1 mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/google.golang.org/grpc/internal/proto/grpc_lookup_v1 -# grpc_testingv3/testv3.pb.go is not re-generated because it was -# intentionally generated by an older version of protoc-gen-go. -rm ${WORKDIR}/out/google.golang.org/grpc/reflection/grpc_testingv3/*.pb.go +# grpc_testing_not_regenerate/*.pb.go are not re-generated, +# see grpc_testing_not_regenerate/README.md for details. +rm ${WORKDIR}/out/google.golang.org/grpc/reflection/grpc_testing_not_regenerate/*.pb.go # grpc/service_config/service_config.proto does not have a go_package option. mv ${WORKDIR}/out/grpc/service_config/service_config.pb.go internal/proto/grpc_service_config diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index eadf9e05..b24b6d53 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -1283,9 +1283,10 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport. if appErr != nil { appStatus, ok := status.FromError(appErr) if !ok { - // Convert appErr if it is not a grpc status error. - appErr = status.Error(codes.Unknown, appErr.Error()) - appStatus, _ = status.FromError(appErr) + // Convert non-status application error to a status error with code + // Unknown, but handle context errors specifically. + appStatus = status.FromContextError(appErr) + appErr = appStatus.Err() } if trInfo != nil { trInfo.tr.LazyLog(stringer(appStatus.Message()), true) @@ -1549,7 +1550,9 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp if appErr != nil { appStatus, ok := status.FromError(appErr) if !ok { - appStatus = status.New(codes.Unknown, appErr.Error()) + // Convert non-status application error to a status error with code + // Unknown, but handle context errors specifically. + appStatus = status.FromContextError(appErr) appErr = appStatus.Err() } if trInfo != nil { diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go index 22c4240c..6926a06d 100644 --- a/vendor/google.golang.org/grpc/service_config.go +++ b/vendor/google.golang.org/grpc/service_config.go @@ -218,7 +218,7 @@ type jsonSC struct { } func init() { - internal.ParseServiceConfigForTesting = parseServiceConfig + internal.ParseServiceConfig = parseServiceConfig } func parseServiceConfig(js string) *serviceconfig.ParseResult { if len(js) == 0 { diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index 625d47b3..8cdd652e 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -46,10 +46,12 @@ import ( ) // StreamHandler defines the handler called by gRPC server to complete the -// execution of a streaming RPC. If a StreamHandler returns an error, it -// should be produced by the status package, or else gRPC will use -// codes.Unknown as the status code and err.Error() as the status message -// of the RPC. +// execution of a streaming RPC. +// +// If a StreamHandler returns an error, it should either be produced by the +// status package, or be one of the context errors. Otherwise, gRPC will use +// codes.Unknown as the status code and err.Error() as the status message of the +// RPC. type StreamHandler func(srv interface{}, stream ServerStream) error // StreamDesc represents a streaming RPC service's method specification. Used diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 8ef09587..5bd4f534 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.43.0" +const Version = "1.45.0" diff --git a/vendor/google.golang.org/grpc/vet.sh b/vendor/google.golang.org/grpc/vet.sh index d923187a..ceb436c6 100644 --- a/vendor/google.golang.org/grpc/vet.sh +++ b/vendor/google.golang.org/grpc/vet.sh @@ -107,7 +107,7 @@ for MOD_FILE in $(find . -name 'go.mod'); do go vet -all ./... | fail_on_output gofmt -s -d -l . 2>&1 | fail_on_output goimports -l . 2>&1 | not grep -vE "\.pb\.go" - golint ./... 2>&1 | not grep -vE "/testv3\.pb\.go:" + golint ./... 2>&1 | not grep -vE "/grpc_testing_not_regenerate/.*\.pb\.go:" go mod tidy git status --porcelain 2>&1 | fail_on_output || \ diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/decode.go b/vendor/google.golang.org/protobuf/encoding/protojson/decode.go new file mode 100644 index 00000000..07da5db3 --- /dev/null +++ b/vendor/google.golang.org/protobuf/encoding/protojson/decode.go @@ -0,0 +1,665 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protojson + +import ( + "encoding/base64" + "fmt" + "math" + "strconv" + "strings" + + "google.golang.org/protobuf/internal/encoding/json" + "google.golang.org/protobuf/internal/encoding/messageset" + "google.golang.org/protobuf/internal/errors" + "google.golang.org/protobuf/internal/flags" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/pragma" + "google.golang.org/protobuf/internal/set" + "google.golang.org/protobuf/proto" + pref "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +// Unmarshal reads the given []byte into the given proto.Message. +// The provided message must be mutable (e.g., a non-nil pointer to a message). +func Unmarshal(b []byte, m proto.Message) error { + return UnmarshalOptions{}.Unmarshal(b, m) +} + +// UnmarshalOptions is a configurable JSON format parser. +type UnmarshalOptions struct { + pragma.NoUnkeyedLiterals + + // If AllowPartial is set, input for messages that will result in missing + // required fields will not return an error. + AllowPartial bool + + // If DiscardUnknown is set, unknown fields are ignored. + DiscardUnknown bool + + // Resolver is used for looking up types when unmarshaling + // google.protobuf.Any messages or extension fields. + // If nil, this defaults to using protoregistry.GlobalTypes. + Resolver interface { + protoregistry.MessageTypeResolver + protoregistry.ExtensionTypeResolver + } +} + +// Unmarshal reads the given []byte and populates the given proto.Message +// using options in the UnmarshalOptions object. +// It will clear the message first before setting the fields. +// If it returns an error, the given message may be partially set. +// The provided message must be mutable (e.g., a non-nil pointer to a message). +func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error { + return o.unmarshal(b, m) +} + +// unmarshal is a centralized function that all unmarshal operations go through. +// For profiling purposes, avoid changing the name of this function or +// introducing other code paths for unmarshal that do not go through this. +func (o UnmarshalOptions) unmarshal(b []byte, m proto.Message) error { + proto.Reset(m) + + if o.Resolver == nil { + o.Resolver = protoregistry.GlobalTypes + } + + dec := decoder{json.NewDecoder(b), o} + if err := dec.unmarshalMessage(m.ProtoReflect(), false); err != nil { + return err + } + + // Check for EOF. + tok, err := dec.Read() + if err != nil { + return err + } + if tok.Kind() != json.EOF { + return dec.unexpectedTokenError(tok) + } + + if o.AllowPartial { + return nil + } + return proto.CheckInitialized(m) +} + +type decoder struct { + *json.Decoder + opts UnmarshalOptions +} + +// newError returns an error object with position info. +func (d decoder) newError(pos int, f string, x ...interface{}) error { + line, column := d.Position(pos) + head := fmt.Sprintf("(line %d:%d): ", line, column) + return errors.New(head+f, x...) +} + +// unexpectedTokenError returns a syntax error for the given unexpected token. +func (d decoder) unexpectedTokenError(tok json.Token) error { + return d.syntaxError(tok.Pos(), "unexpected token %s", tok.RawString()) +} + +// syntaxError returns a syntax error for given position. +func (d decoder) syntaxError(pos int, f string, x ...interface{}) error { + line, column := d.Position(pos) + head := fmt.Sprintf("syntax error (line %d:%d): ", line, column) + return errors.New(head+f, x...) +} + +// unmarshalMessage unmarshals a message into the given protoreflect.Message. +func (d decoder) unmarshalMessage(m pref.Message, skipTypeURL bool) error { + if unmarshal := wellKnownTypeUnmarshaler(m.Descriptor().FullName()); unmarshal != nil { + return unmarshal(d, m) + } + + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.ObjectOpen { + return d.unexpectedTokenError(tok) + } + + messageDesc := m.Descriptor() + if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) { + return errors.New("no support for proto1 MessageSets") + } + + var seenNums set.Ints + var seenOneofs set.Ints + fieldDescs := messageDesc.Fields() + for { + // Read field name. + tok, err := d.Read() + if err != nil { + return err + } + switch tok.Kind() { + default: + return d.unexpectedTokenError(tok) + case json.ObjectClose: + return nil + case json.Name: + // Continue below. + } + + name := tok.Name() + // Unmarshaling a non-custom embedded message in Any will contain the + // JSON field "@type" which should be skipped because it is not a field + // of the embedded message, but simply an artifact of the Any format. + if skipTypeURL && name == "@type" { + d.Read() + continue + } + + // Get the FieldDescriptor. + var fd pref.FieldDescriptor + if strings.HasPrefix(name, "[") && strings.HasSuffix(name, "]") { + // Only extension names are in [name] format. + extName := pref.FullName(name[1 : len(name)-1]) + extType, err := d.opts.Resolver.FindExtensionByName(extName) + if err != nil && err != protoregistry.NotFound { + return d.newError(tok.Pos(), "unable to resolve %s: %v", tok.RawString(), err) + } + if extType != nil { + fd = extType.TypeDescriptor() + if !messageDesc.ExtensionRanges().Has(fd.Number()) || fd.ContainingMessage().FullName() != messageDesc.FullName() { + return d.newError(tok.Pos(), "message %v cannot be extended by %v", messageDesc.FullName(), fd.FullName()) + } + } + } else { + // The name can either be the JSON name or the proto field name. + fd = fieldDescs.ByJSONName(name) + if fd == nil { + fd = fieldDescs.ByTextName(name) + } + } + if flags.ProtoLegacy { + if fd != nil && fd.IsWeak() && fd.Message().IsPlaceholder() { + fd = nil // reset since the weak reference is not linked in + } + } + + if fd == nil { + // Field is unknown. + if d.opts.DiscardUnknown { + if err := d.skipJSONValue(); err != nil { + return err + } + continue + } + return d.newError(tok.Pos(), "unknown field %v", tok.RawString()) + } + + // Do not allow duplicate fields. + num := uint64(fd.Number()) + if seenNums.Has(num) { + return d.newError(tok.Pos(), "duplicate field %v", tok.RawString()) + } + seenNums.Set(num) + + // No need to set values for JSON null unless the field type is + // google.protobuf.Value or google.protobuf.NullValue. + if tok, _ := d.Peek(); tok.Kind() == json.Null && !isKnownValue(fd) && !isNullValue(fd) { + d.Read() + continue + } + + switch { + case fd.IsList(): + list := m.Mutable(fd).List() + if err := d.unmarshalList(list, fd); err != nil { + return err + } + case fd.IsMap(): + mmap := m.Mutable(fd).Map() + if err := d.unmarshalMap(mmap, fd); err != nil { + return err + } + default: + // If field is a oneof, check if it has already been set. + if od := fd.ContainingOneof(); od != nil { + idx := uint64(od.Index()) + if seenOneofs.Has(idx) { + return d.newError(tok.Pos(), "error parsing %s, oneof %v is already set", tok.RawString(), od.FullName()) + } + seenOneofs.Set(idx) + } + + // Required or optional fields. + if err := d.unmarshalSingular(m, fd); err != nil { + return err + } + } + } +} + +func isKnownValue(fd pref.FieldDescriptor) bool { + md := fd.Message() + return md != nil && md.FullName() == genid.Value_message_fullname +} + +func isNullValue(fd pref.FieldDescriptor) bool { + ed := fd.Enum() + return ed != nil && ed.FullName() == genid.NullValue_enum_fullname +} + +// unmarshalSingular unmarshals to the non-repeated field specified +// by the given FieldDescriptor. +func (d decoder) unmarshalSingular(m pref.Message, fd pref.FieldDescriptor) error { + var val pref.Value + var err error + switch fd.Kind() { + case pref.MessageKind, pref.GroupKind: + val = m.NewField(fd) + err = d.unmarshalMessage(val.Message(), false) + default: + val, err = d.unmarshalScalar(fd) + } + + if err != nil { + return err + } + m.Set(fd, val) + return nil +} + +// unmarshalScalar unmarshals to a scalar/enum protoreflect.Value specified by +// the given FieldDescriptor. +func (d decoder) unmarshalScalar(fd pref.FieldDescriptor) (pref.Value, error) { + const b32 int = 32 + const b64 int = 64 + + tok, err := d.Read() + if err != nil { + return pref.Value{}, err + } + + kind := fd.Kind() + switch kind { + case pref.BoolKind: + if tok.Kind() == json.Bool { + return pref.ValueOfBool(tok.Bool()), nil + } + + case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind: + if v, ok := unmarshalInt(tok, b32); ok { + return v, nil + } + + case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind: + if v, ok := unmarshalInt(tok, b64); ok { + return v, nil + } + + case pref.Uint32Kind, pref.Fixed32Kind: + if v, ok := unmarshalUint(tok, b32); ok { + return v, nil + } + + case pref.Uint64Kind, pref.Fixed64Kind: + if v, ok := unmarshalUint(tok, b64); ok { + return v, nil + } + + case pref.FloatKind: + if v, ok := unmarshalFloat(tok, b32); ok { + return v, nil + } + + case pref.DoubleKind: + if v, ok := unmarshalFloat(tok, b64); ok { + return v, nil + } + + case pref.StringKind: + if tok.Kind() == json.String { + return pref.ValueOfString(tok.ParsedString()), nil + } + + case pref.BytesKind: + if v, ok := unmarshalBytes(tok); ok { + return v, nil + } + + case pref.EnumKind: + if v, ok := unmarshalEnum(tok, fd); ok { + return v, nil + } + + default: + panic(fmt.Sprintf("unmarshalScalar: invalid scalar kind %v", kind)) + } + + return pref.Value{}, d.newError(tok.Pos(), "invalid value for %v type: %v", kind, tok.RawString()) +} + +func unmarshalInt(tok json.Token, bitSize int) (pref.Value, bool) { + switch tok.Kind() { + case json.Number: + return getInt(tok, bitSize) + + case json.String: + // Decode number from string. + s := strings.TrimSpace(tok.ParsedString()) + if len(s) != len(tok.ParsedString()) { + return pref.Value{}, false + } + dec := json.NewDecoder([]byte(s)) + tok, err := dec.Read() + if err != nil { + return pref.Value{}, false + } + return getInt(tok, bitSize) + } + return pref.Value{}, false +} + +func getInt(tok json.Token, bitSize int) (pref.Value, bool) { + n, ok := tok.Int(bitSize) + if !ok { + return pref.Value{}, false + } + if bitSize == 32 { + return pref.ValueOfInt32(int32(n)), true + } + return pref.ValueOfInt64(n), true +} + +func unmarshalUint(tok json.Token, bitSize int) (pref.Value, bool) { + switch tok.Kind() { + case json.Number: + return getUint(tok, bitSize) + + case json.String: + // Decode number from string. + s := strings.TrimSpace(tok.ParsedString()) + if len(s) != len(tok.ParsedString()) { + return pref.Value{}, false + } + dec := json.NewDecoder([]byte(s)) + tok, err := dec.Read() + if err != nil { + return pref.Value{}, false + } + return getUint(tok, bitSize) + } + return pref.Value{}, false +} + +func getUint(tok json.Token, bitSize int) (pref.Value, bool) { + n, ok := tok.Uint(bitSize) + if !ok { + return pref.Value{}, false + } + if bitSize == 32 { + return pref.ValueOfUint32(uint32(n)), true + } + return pref.ValueOfUint64(n), true +} + +func unmarshalFloat(tok json.Token, bitSize int) (pref.Value, bool) { + switch tok.Kind() { + case json.Number: + return getFloat(tok, bitSize) + + case json.String: + s := tok.ParsedString() + switch s { + case "NaN": + if bitSize == 32 { + return pref.ValueOfFloat32(float32(math.NaN())), true + } + return pref.ValueOfFloat64(math.NaN()), true + case "Infinity": + if bitSize == 32 { + return pref.ValueOfFloat32(float32(math.Inf(+1))), true + } + return pref.ValueOfFloat64(math.Inf(+1)), true + case "-Infinity": + if bitSize == 32 { + return pref.ValueOfFloat32(float32(math.Inf(-1))), true + } + return pref.ValueOfFloat64(math.Inf(-1)), true + } + + // Decode number from string. + if len(s) != len(strings.TrimSpace(s)) { + return pref.Value{}, false + } + dec := json.NewDecoder([]byte(s)) + tok, err := dec.Read() + if err != nil { + return pref.Value{}, false + } + return getFloat(tok, bitSize) + } + return pref.Value{}, false +} + +func getFloat(tok json.Token, bitSize int) (pref.Value, bool) { + n, ok := tok.Float(bitSize) + if !ok { + return pref.Value{}, false + } + if bitSize == 32 { + return pref.ValueOfFloat32(float32(n)), true + } + return pref.ValueOfFloat64(n), true +} + +func unmarshalBytes(tok json.Token) (pref.Value, bool) { + if tok.Kind() != json.String { + return pref.Value{}, false + } + + s := tok.ParsedString() + enc := base64.StdEncoding + if strings.ContainsAny(s, "-_") { + enc = base64.URLEncoding + } + if len(s)%4 != 0 { + enc = enc.WithPadding(base64.NoPadding) + } + b, err := enc.DecodeString(s) + if err != nil { + return pref.Value{}, false + } + return pref.ValueOfBytes(b), true +} + +func unmarshalEnum(tok json.Token, fd pref.FieldDescriptor) (pref.Value, bool) { + switch tok.Kind() { + case json.String: + // Lookup EnumNumber based on name. + s := tok.ParsedString() + if enumVal := fd.Enum().Values().ByName(pref.Name(s)); enumVal != nil { + return pref.ValueOfEnum(enumVal.Number()), true + } + + case json.Number: + if n, ok := tok.Int(32); ok { + return pref.ValueOfEnum(pref.EnumNumber(n)), true + } + + case json.Null: + // This is only valid for google.protobuf.NullValue. + if isNullValue(fd) { + return pref.ValueOfEnum(0), true + } + } + + return pref.Value{}, false +} + +func (d decoder) unmarshalList(list pref.List, fd pref.FieldDescriptor) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.ArrayOpen { + return d.unexpectedTokenError(tok) + } + + switch fd.Kind() { + case pref.MessageKind, pref.GroupKind: + for { + tok, err := d.Peek() + if err != nil { + return err + } + + if tok.Kind() == json.ArrayClose { + d.Read() + return nil + } + + val := list.NewElement() + if err := d.unmarshalMessage(val.Message(), false); err != nil { + return err + } + list.Append(val) + } + default: + for { + tok, err := d.Peek() + if err != nil { + return err + } + + if tok.Kind() == json.ArrayClose { + d.Read() + return nil + } + + val, err := d.unmarshalScalar(fd) + if err != nil { + return err + } + list.Append(val) + } + } + + return nil +} + +func (d decoder) unmarshalMap(mmap pref.Map, fd pref.FieldDescriptor) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.ObjectOpen { + return d.unexpectedTokenError(tok) + } + + // Determine ahead whether map entry is a scalar type or a message type in + // order to call the appropriate unmarshalMapValue func inside the for loop + // below. + var unmarshalMapValue func() (pref.Value, error) + switch fd.MapValue().Kind() { + case pref.MessageKind, pref.GroupKind: + unmarshalMapValue = func() (pref.Value, error) { + val := mmap.NewValue() + if err := d.unmarshalMessage(val.Message(), false); err != nil { + return pref.Value{}, err + } + return val, nil + } + default: + unmarshalMapValue = func() (pref.Value, error) { + return d.unmarshalScalar(fd.MapValue()) + } + } + +Loop: + for { + // Read field name. + tok, err := d.Read() + if err != nil { + return err + } + switch tok.Kind() { + default: + return d.unexpectedTokenError(tok) + case json.ObjectClose: + break Loop + case json.Name: + // Continue. + } + + // Unmarshal field name. + pkey, err := d.unmarshalMapKey(tok, fd.MapKey()) + if err != nil { + return err + } + + // Check for duplicate field name. + if mmap.Has(pkey) { + return d.newError(tok.Pos(), "duplicate map key %v", tok.RawString()) + } + + // Read and unmarshal field value. + pval, err := unmarshalMapValue() + if err != nil { + return err + } + + mmap.Set(pkey, pval) + } + + return nil +} + +// unmarshalMapKey converts given token of Name kind into a protoreflect.MapKey. +// A map key type is any integral or string type. +func (d decoder) unmarshalMapKey(tok json.Token, fd pref.FieldDescriptor) (pref.MapKey, error) { + const b32 = 32 + const b64 = 64 + const base10 = 10 + + name := tok.Name() + kind := fd.Kind() + switch kind { + case pref.StringKind: + return pref.ValueOfString(name).MapKey(), nil + + case pref.BoolKind: + switch name { + case "true": + return pref.ValueOfBool(true).MapKey(), nil + case "false": + return pref.ValueOfBool(false).MapKey(), nil + } + + case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind: + if n, err := strconv.ParseInt(name, base10, b32); err == nil { + return pref.ValueOfInt32(int32(n)).MapKey(), nil + } + + case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind: + if n, err := strconv.ParseInt(name, base10, b64); err == nil { + return pref.ValueOfInt64(int64(n)).MapKey(), nil + } + + case pref.Uint32Kind, pref.Fixed32Kind: + if n, err := strconv.ParseUint(name, base10, b32); err == nil { + return pref.ValueOfUint32(uint32(n)).MapKey(), nil + } + + case pref.Uint64Kind, pref.Fixed64Kind: + if n, err := strconv.ParseUint(name, base10, b64); err == nil { + return pref.ValueOfUint64(uint64(n)).MapKey(), nil + } + + default: + panic(fmt.Sprintf("invalid kind for map key: %v", kind)) + } + + return pref.MapKey{}, d.newError(tok.Pos(), "invalid value for %v key: %s", kind, tok.RawString()) +} diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/doc.go b/vendor/google.golang.org/protobuf/encoding/protojson/doc.go new file mode 100644 index 00000000..00ea2fec --- /dev/null +++ b/vendor/google.golang.org/protobuf/encoding/protojson/doc.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package protojson marshals and unmarshals protocol buffer messages as JSON +// format. It follows the guide at +// https://developers.google.com/protocol-buffers/docs/proto3#json. +// +// This package produces a different output than the standard "encoding/json" +// package, which does not operate correctly on protocol buffer messages. +package protojson diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/encode.go b/vendor/google.golang.org/protobuf/encoding/protojson/encode.go new file mode 100644 index 00000000..ba971f07 --- /dev/null +++ b/vendor/google.golang.org/protobuf/encoding/protojson/encode.go @@ -0,0 +1,344 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protojson + +import ( + "encoding/base64" + "fmt" + + "google.golang.org/protobuf/internal/encoding/json" + "google.golang.org/protobuf/internal/encoding/messageset" + "google.golang.org/protobuf/internal/errors" + "google.golang.org/protobuf/internal/filedesc" + "google.golang.org/protobuf/internal/flags" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/order" + "google.golang.org/protobuf/internal/pragma" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + pref "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +const defaultIndent = " " + +// Format formats the message as a multiline string. +// This function is only intended for human consumption and ignores errors. +// Do not depend on the output being stable. It may change over time across +// different versions of the program. +func Format(m proto.Message) string { + return MarshalOptions{Multiline: true}.Format(m) +} + +// Marshal writes the given proto.Message in JSON format using default options. +// Do not depend on the output being stable. It may change over time across +// different versions of the program. +func Marshal(m proto.Message) ([]byte, error) { + return MarshalOptions{}.Marshal(m) +} + +// MarshalOptions is a configurable JSON format marshaler. +type MarshalOptions struct { + pragma.NoUnkeyedLiterals + + // Multiline specifies whether the marshaler should format the output in + // indented-form with every textual element on a new line. + // If Indent is an empty string, then an arbitrary indent is chosen. + Multiline bool + + // Indent specifies the set of indentation characters to use in a multiline + // formatted output such that every entry is preceded by Indent and + // terminated by a newline. If non-empty, then Multiline is treated as true. + // Indent can only be composed of space or tab characters. + Indent string + + // AllowPartial allows messages that have missing required fields to marshal + // without returning an error. If AllowPartial is false (the default), + // Marshal will return error if there are any missing required fields. + AllowPartial bool + + // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON + // field names. + UseProtoNames bool + + // UseEnumNumbers emits enum values as numbers. + UseEnumNumbers bool + + // EmitUnpopulated specifies whether to emit unpopulated fields. It does not + // emit unpopulated oneof fields or unpopulated extension fields. + // The JSON value emitted for unpopulated fields are as follows: + // ╔═══════╤════════════════════════════╗ + // ║ JSON │ Protobuf field ║ + // ╠═══════╪════════════════════════════╣ + // ║ false │ proto3 boolean fields ║ + // ║ 0 │ proto3 numeric fields ║ + // ║ "" │ proto3 string/bytes fields ║ + // ║ null │ proto2 scalar fields ║ + // ║ null │ message fields ║ + // ║ [] │ list fields ║ + // ║ {} │ map fields ║ + // ╚═══════╧════════════════════════════╝ + EmitUnpopulated bool + + // Resolver is used for looking up types when expanding google.protobuf.Any + // messages. If nil, this defaults to using protoregistry.GlobalTypes. + Resolver interface { + protoregistry.ExtensionTypeResolver + protoregistry.MessageTypeResolver + } +} + +// Format formats the message as a string. +// This method is only intended for human consumption and ignores errors. +// Do not depend on the output being stable. It may change over time across +// different versions of the program. +func (o MarshalOptions) Format(m proto.Message) string { + if m == nil || !m.ProtoReflect().IsValid() { + return "" // invalid syntax, but okay since this is for debugging + } + o.AllowPartial = true + b, _ := o.Marshal(m) + return string(b) +} + +// Marshal marshals the given proto.Message in the JSON format using options in +// MarshalOptions. Do not depend on the output being stable. It may change over +// time across different versions of the program. +func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { + return o.marshal(m) +} + +// marshal is a centralized function that all marshal operations go through. +// For profiling purposes, avoid changing the name of this function or +// introducing other code paths for marshal that do not go through this. +func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { + if o.Multiline && o.Indent == "" { + o.Indent = defaultIndent + } + if o.Resolver == nil { + o.Resolver = protoregistry.GlobalTypes + } + + internalEnc, err := json.NewEncoder(o.Indent) + if err != nil { + return nil, err + } + + // Treat nil message interface as an empty message, + // in which case the output in an empty JSON object. + if m == nil { + return []byte("{}"), nil + } + + enc := encoder{internalEnc, o} + if err := enc.marshalMessage(m.ProtoReflect(), ""); err != nil { + return nil, err + } + if o.AllowPartial { + return enc.Bytes(), nil + } + return enc.Bytes(), proto.CheckInitialized(m) +} + +type encoder struct { + *json.Encoder + opts MarshalOptions +} + +// typeFieldDesc is a synthetic field descriptor used for the "@type" field. +var typeFieldDesc = func() protoreflect.FieldDescriptor { + var fd filedesc.Field + fd.L0.FullName = "@type" + fd.L0.Index = -1 + fd.L1.Cardinality = protoreflect.Optional + fd.L1.Kind = protoreflect.StringKind + return &fd +}() + +// typeURLFieldRanger wraps a protoreflect.Message and modifies its Range method +// to additionally iterate over a synthetic field for the type URL. +type typeURLFieldRanger struct { + order.FieldRanger + typeURL string +} + +func (m typeURLFieldRanger) Range(f func(pref.FieldDescriptor, pref.Value) bool) { + if !f(typeFieldDesc, pref.ValueOfString(m.typeURL)) { + return + } + m.FieldRanger.Range(f) +} + +// unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range +// method to additionally iterate over unpopulated fields. +type unpopulatedFieldRanger struct{ pref.Message } + +func (m unpopulatedFieldRanger) Range(f func(pref.FieldDescriptor, pref.Value) bool) { + fds := m.Descriptor().Fields() + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if m.Has(fd) || fd.ContainingOneof() != nil { + continue // ignore populated fields and fields within a oneofs + } + + v := m.Get(fd) + isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid() + isSingularMessage := fd.Cardinality() != pref.Repeated && fd.Message() != nil + if isProto2Scalar || isSingularMessage { + v = pref.Value{} // use invalid value to emit null + } + if !f(fd, v) { + return + } + } + m.Message.Range(f) +} + +// marshalMessage marshals the fields in the given protoreflect.Message. +// If the typeURL is non-empty, then a synthetic "@type" field is injected +// containing the URL as the value. +func (e encoder) marshalMessage(m pref.Message, typeURL string) error { + if !flags.ProtoLegacy && messageset.IsMessageSet(m.Descriptor()) { + return errors.New("no support for proto1 MessageSets") + } + + if marshal := wellKnownTypeMarshaler(m.Descriptor().FullName()); marshal != nil { + return marshal(e, m) + } + + e.StartObject() + defer e.EndObject() + + var fields order.FieldRanger = m + if e.opts.EmitUnpopulated { + fields = unpopulatedFieldRanger{m} + } + if typeURL != "" { + fields = typeURLFieldRanger{fields, typeURL} + } + + var err error + order.RangeFields(fields, order.IndexNameFieldOrder, func(fd pref.FieldDescriptor, v pref.Value) bool { + name := fd.JSONName() + if e.opts.UseProtoNames { + name = fd.TextName() + } + + if err = e.WriteName(name); err != nil { + return false + } + if err = e.marshalValue(v, fd); err != nil { + return false + } + return true + }) + return err +} + +// marshalValue marshals the given protoreflect.Value. +func (e encoder) marshalValue(val pref.Value, fd pref.FieldDescriptor) error { + switch { + case fd.IsList(): + return e.marshalList(val.List(), fd) + case fd.IsMap(): + return e.marshalMap(val.Map(), fd) + default: + return e.marshalSingular(val, fd) + } +} + +// marshalSingular marshals the given non-repeated field value. This includes +// all scalar types, enums, messages, and groups. +func (e encoder) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error { + if !val.IsValid() { + e.WriteNull() + return nil + } + + switch kind := fd.Kind(); kind { + case pref.BoolKind: + e.WriteBool(val.Bool()) + + case pref.StringKind: + if e.WriteString(val.String()) != nil { + return errors.InvalidUTF8(string(fd.FullName())) + } + + case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind: + e.WriteInt(val.Int()) + + case pref.Uint32Kind, pref.Fixed32Kind: + e.WriteUint(val.Uint()) + + case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind, + pref.Sfixed64Kind, pref.Fixed64Kind: + // 64-bit integers are written out as JSON string. + e.WriteString(val.String()) + + case pref.FloatKind: + // Encoder.WriteFloat handles the special numbers NaN and infinites. + e.WriteFloat(val.Float(), 32) + + case pref.DoubleKind: + // Encoder.WriteFloat handles the special numbers NaN and infinites. + e.WriteFloat(val.Float(), 64) + + case pref.BytesKind: + e.WriteString(base64.StdEncoding.EncodeToString(val.Bytes())) + + case pref.EnumKind: + if fd.Enum().FullName() == genid.NullValue_enum_fullname { + e.WriteNull() + } else { + desc := fd.Enum().Values().ByNumber(val.Enum()) + if e.opts.UseEnumNumbers || desc == nil { + e.WriteInt(int64(val.Enum())) + } else { + e.WriteString(string(desc.Name())) + } + } + + case pref.MessageKind, pref.GroupKind: + if err := e.marshalMessage(val.Message(), ""); err != nil { + return err + } + + default: + panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind)) + } + return nil +} + +// marshalList marshals the given protoreflect.List. +func (e encoder) marshalList(list pref.List, fd pref.FieldDescriptor) error { + e.StartArray() + defer e.EndArray() + + for i := 0; i < list.Len(); i++ { + item := list.Get(i) + if err := e.marshalSingular(item, fd); err != nil { + return err + } + } + return nil +} + +// marshalMap marshals given protoreflect.Map. +func (e encoder) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) error { + e.StartObject() + defer e.EndObject() + + var err error + order.RangeEntries(mmap, order.GenericKeyOrder, func(k pref.MapKey, v pref.Value) bool { + if err = e.WriteName(k.String()); err != nil { + return false + } + if err = e.marshalSingular(v, fd.MapValue()); err != nil { + return false + } + return true + }) + return err +} diff --git a/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go b/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go new file mode 100644 index 00000000..72924a90 --- /dev/null +++ b/vendor/google.golang.org/protobuf/encoding/protojson/well_known_types.go @@ -0,0 +1,889 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package protojson + +import ( + "bytes" + "fmt" + "math" + "strconv" + "strings" + "time" + + "google.golang.org/protobuf/internal/encoding/json" + "google.golang.org/protobuf/internal/errors" + "google.golang.org/protobuf/internal/genid" + "google.golang.org/protobuf/internal/strs" + "google.golang.org/protobuf/proto" + pref "google.golang.org/protobuf/reflect/protoreflect" +) + +type marshalFunc func(encoder, pref.Message) error + +// wellKnownTypeMarshaler returns a marshal function if the message type +// has specialized serialization behavior. It returns nil otherwise. +func wellKnownTypeMarshaler(name pref.FullName) marshalFunc { + if name.Parent() == genid.GoogleProtobuf_package { + switch name.Name() { + case genid.Any_message_name: + return encoder.marshalAny + case genid.Timestamp_message_name: + return encoder.marshalTimestamp + case genid.Duration_message_name: + return encoder.marshalDuration + case genid.BoolValue_message_name, + genid.Int32Value_message_name, + genid.Int64Value_message_name, + genid.UInt32Value_message_name, + genid.UInt64Value_message_name, + genid.FloatValue_message_name, + genid.DoubleValue_message_name, + genid.StringValue_message_name, + genid.BytesValue_message_name: + return encoder.marshalWrapperType + case genid.Struct_message_name: + return encoder.marshalStruct + case genid.ListValue_message_name: + return encoder.marshalListValue + case genid.Value_message_name: + return encoder.marshalKnownValue + case genid.FieldMask_message_name: + return encoder.marshalFieldMask + case genid.Empty_message_name: + return encoder.marshalEmpty + } + } + return nil +} + +type unmarshalFunc func(decoder, pref.Message) error + +// wellKnownTypeUnmarshaler returns a unmarshal function if the message type +// has specialized serialization behavior. It returns nil otherwise. +func wellKnownTypeUnmarshaler(name pref.FullName) unmarshalFunc { + if name.Parent() == genid.GoogleProtobuf_package { + switch name.Name() { + case genid.Any_message_name: + return decoder.unmarshalAny + case genid.Timestamp_message_name: + return decoder.unmarshalTimestamp + case genid.Duration_message_name: + return decoder.unmarshalDuration + case genid.BoolValue_message_name, + genid.Int32Value_message_name, + genid.Int64Value_message_name, + genid.UInt32Value_message_name, + genid.UInt64Value_message_name, + genid.FloatValue_message_name, + genid.DoubleValue_message_name, + genid.StringValue_message_name, + genid.BytesValue_message_name: + return decoder.unmarshalWrapperType + case genid.Struct_message_name: + return decoder.unmarshalStruct + case genid.ListValue_message_name: + return decoder.unmarshalListValue + case genid.Value_message_name: + return decoder.unmarshalKnownValue + case genid.FieldMask_message_name: + return decoder.unmarshalFieldMask + case genid.Empty_message_name: + return decoder.unmarshalEmpty + } + } + return nil +} + +// The JSON representation of an Any message uses the regular representation of +// the deserialized, embedded message, with an additional field `@type` which +// contains the type URL. If the embedded message type is well-known and has a +// custom JSON representation, that representation will be embedded adding a +// field `value` which holds the custom JSON in addition to the `@type` field. + +func (e encoder) marshalAny(m pref.Message) error { + fds := m.Descriptor().Fields() + fdType := fds.ByNumber(genid.Any_TypeUrl_field_number) + fdValue := fds.ByNumber(genid.Any_Value_field_number) + + if !m.Has(fdType) { + if !m.Has(fdValue) { + // If message is empty, marshal out empty JSON object. + e.StartObject() + e.EndObject() + return nil + } else { + // Return error if type_url field is not set, but value is set. + return errors.New("%s: %v is not set", genid.Any_message_fullname, genid.Any_TypeUrl_field_name) + } + } + + typeVal := m.Get(fdType) + valueVal := m.Get(fdValue) + + // Resolve the type in order to unmarshal value field. + typeURL := typeVal.String() + emt, err := e.opts.Resolver.FindMessageByURL(typeURL) + if err != nil { + return errors.New("%s: unable to resolve %q: %v", genid.Any_message_fullname, typeURL, err) + } + + em := emt.New() + err = proto.UnmarshalOptions{ + AllowPartial: true, // never check required fields inside an Any + Resolver: e.opts.Resolver, + }.Unmarshal(valueVal.Bytes(), em.Interface()) + if err != nil { + return errors.New("%s: unable to unmarshal %q: %v", genid.Any_message_fullname, typeURL, err) + } + + // If type of value has custom JSON encoding, marshal out a field "value" + // with corresponding custom JSON encoding of the embedded message as a + // field. + if marshal := wellKnownTypeMarshaler(emt.Descriptor().FullName()); marshal != nil { + e.StartObject() + defer e.EndObject() + + // Marshal out @type field. + e.WriteName("@type") + if err := e.WriteString(typeURL); err != nil { + return err + } + + e.WriteName("value") + return marshal(e, em) + } + + // Else, marshal out the embedded message's fields in this Any object. + if err := e.marshalMessage(em, typeURL); err != nil { + return err + } + + return nil +} + +func (d decoder) unmarshalAny(m pref.Message) error { + // Peek to check for json.ObjectOpen to avoid advancing a read. + start, err := d.Peek() + if err != nil { + return err + } + if start.Kind() != json.ObjectOpen { + return d.unexpectedTokenError(start) + } + + // Use another decoder to parse the unread bytes for @type field. This + // avoids advancing a read from current decoder because the current JSON + // object may contain the fields of the embedded type. + dec := decoder{d.Clone(), UnmarshalOptions{}} + tok, err := findTypeURL(dec) + switch err { + case errEmptyObject: + // An empty JSON object translates to an empty Any message. + d.Read() // Read json.ObjectOpen. + d.Read() // Read json.ObjectClose. + return nil + + case errMissingType: + if d.opts.DiscardUnknown { + // Treat all fields as unknowns, similar to an empty object. + return d.skipJSONValue() + } + // Use start.Pos() for line position. + return d.newError(start.Pos(), err.Error()) + + default: + if err != nil { + return err + } + } + + typeURL := tok.ParsedString() + emt, err := d.opts.Resolver.FindMessageByURL(typeURL) + if err != nil { + return d.newError(tok.Pos(), "unable to resolve %v: %q", tok.RawString(), err) + } + + // Create new message for the embedded message type and unmarshal into it. + em := emt.New() + if unmarshal := wellKnownTypeUnmarshaler(emt.Descriptor().FullName()); unmarshal != nil { + // If embedded message is a custom type, + // unmarshal the JSON "value" field into it. + if err := d.unmarshalAnyValue(unmarshal, em); err != nil { + return err + } + } else { + // Else unmarshal the current JSON object into it. + if err := d.unmarshalMessage(em, true); err != nil { + return err + } + } + // Serialize the embedded message and assign the resulting bytes to the + // proto value field. + b, err := proto.MarshalOptions{ + AllowPartial: true, // No need to check required fields inside an Any. + Deterministic: true, + }.Marshal(em.Interface()) + if err != nil { + return d.newError(start.Pos(), "error in marshaling Any.value field: %v", err) + } + + fds := m.Descriptor().Fields() + fdType := fds.ByNumber(genid.Any_TypeUrl_field_number) + fdValue := fds.ByNumber(genid.Any_Value_field_number) + + m.Set(fdType, pref.ValueOfString(typeURL)) + m.Set(fdValue, pref.ValueOfBytes(b)) + return nil +} + +var errEmptyObject = fmt.Errorf(`empty object`) +var errMissingType = fmt.Errorf(`missing "@type" field`) + +// findTypeURL returns the token for the "@type" field value from the given +// JSON bytes. It is expected that the given bytes start with json.ObjectOpen. +// It returns errEmptyObject if the JSON object is empty or errMissingType if +// @type field does not exist. It returns other error if the @type field is not +// valid or other decoding issues. +func findTypeURL(d decoder) (json.Token, error) { + var typeURL string + var typeTok json.Token + numFields := 0 + // Skip start object. + d.Read() + +Loop: + for { + tok, err := d.Read() + if err != nil { + return json.Token{}, err + } + + switch tok.Kind() { + case json.ObjectClose: + if typeURL == "" { + // Did not find @type field. + if numFields > 0 { + return json.Token{}, errMissingType + } + return json.Token{}, errEmptyObject + } + break Loop + + case json.Name: + numFields++ + if tok.Name() != "@type" { + // Skip value. + if err := d.skipJSONValue(); err != nil { + return json.Token{}, err + } + continue + } + + // Return error if this was previously set already. + if typeURL != "" { + return json.Token{}, d.newError(tok.Pos(), `duplicate "@type" field`) + } + // Read field value. + tok, err := d.Read() + if err != nil { + return json.Token{}, err + } + if tok.Kind() != json.String { + return json.Token{}, d.newError(tok.Pos(), `@type field value is not a string: %v`, tok.RawString()) + } + typeURL = tok.ParsedString() + if typeURL == "" { + return json.Token{}, d.newError(tok.Pos(), `@type field contains empty value`) + } + typeTok = tok + } + } + + return typeTok, nil +} + +// skipJSONValue parses a JSON value (null, boolean, string, number, object and +// array) in order to advance the read to the next JSON value. It relies on +// the decoder returning an error if the types are not in valid sequence. +func (d decoder) skipJSONValue() error { + tok, err := d.Read() + if err != nil { + return err + } + // Only need to continue reading for objects and arrays. + switch tok.Kind() { + case json.ObjectOpen: + for { + tok, err := d.Read() + if err != nil { + return err + } + switch tok.Kind() { + case json.ObjectClose: + return nil + case json.Name: + // Skip object field value. + if err := d.skipJSONValue(); err != nil { + return err + } + } + } + + case json.ArrayOpen: + for { + tok, err := d.Peek() + if err != nil { + return err + } + switch tok.Kind() { + case json.ArrayClose: + d.Read() + return nil + default: + // Skip array item. + if err := d.skipJSONValue(); err != nil { + return err + } + } + } + } + return nil +} + +// unmarshalAnyValue unmarshals the given custom-type message from the JSON +// object's "value" field. +func (d decoder) unmarshalAnyValue(unmarshal unmarshalFunc, m pref.Message) error { + // Skip ObjectOpen, and start reading the fields. + d.Read() + + var found bool // Used for detecting duplicate "value". + for { + tok, err := d.Read() + if err != nil { + return err + } + switch tok.Kind() { + case json.ObjectClose: + if !found { + return d.newError(tok.Pos(), `missing "value" field`) + } + return nil + + case json.Name: + switch tok.Name() { + case "@type": + // Skip the value as this was previously parsed already. + d.Read() + + case "value": + if found { + return d.newError(tok.Pos(), `duplicate "value" field`) + } + // Unmarshal the field value into the given message. + if err := unmarshal(d, m); err != nil { + return err + } + found = true + + default: + if d.opts.DiscardUnknown { + if err := d.skipJSONValue(); err != nil { + return err + } + continue + } + return d.newError(tok.Pos(), "unknown field %v", tok.RawString()) + } + } + } +} + +// Wrapper types are encoded as JSON primitives like string, number or boolean. + +func (e encoder) marshalWrapperType(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.WrapperValue_Value_field_number) + val := m.Get(fd) + return e.marshalSingular(val, fd) +} + +func (d decoder) unmarshalWrapperType(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.WrapperValue_Value_field_number) + val, err := d.unmarshalScalar(fd) + if err != nil { + return err + } + m.Set(fd, val) + return nil +} + +// The JSON representation for Empty is an empty JSON object. + +func (e encoder) marshalEmpty(pref.Message) error { + e.StartObject() + e.EndObject() + return nil +} + +func (d decoder) unmarshalEmpty(pref.Message) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.ObjectOpen { + return d.unexpectedTokenError(tok) + } + + for { + tok, err := d.Read() + if err != nil { + return err + } + switch tok.Kind() { + case json.ObjectClose: + return nil + + case json.Name: + if d.opts.DiscardUnknown { + if err := d.skipJSONValue(); err != nil { + return err + } + continue + } + return d.newError(tok.Pos(), "unknown field %v", tok.RawString()) + + default: + return d.unexpectedTokenError(tok) + } + } +} + +// The JSON representation for Struct is a JSON object that contains the encoded +// Struct.fields map and follows the serialization rules for a map. + +func (e encoder) marshalStruct(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.Struct_Fields_field_number) + return e.marshalMap(m.Get(fd).Map(), fd) +} + +func (d decoder) unmarshalStruct(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.Struct_Fields_field_number) + return d.unmarshalMap(m.Mutable(fd).Map(), fd) +} + +// The JSON representation for ListValue is JSON array that contains the encoded +// ListValue.values repeated field and follows the serialization rules for a +// repeated field. + +func (e encoder) marshalListValue(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.ListValue_Values_field_number) + return e.marshalList(m.Get(fd).List(), fd) +} + +func (d decoder) unmarshalListValue(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.ListValue_Values_field_number) + return d.unmarshalList(m.Mutable(fd).List(), fd) +} + +// The JSON representation for a Value is dependent on the oneof field that is +// set. Each of the field in the oneof has its own custom serialization rule. A +// Value message needs to be a oneof field set, else it is an error. + +func (e encoder) marshalKnownValue(m pref.Message) error { + od := m.Descriptor().Oneofs().ByName(genid.Value_Kind_oneof_name) + fd := m.WhichOneof(od) + if fd == nil { + return errors.New("%s: none of the oneof fields is set", genid.Value_message_fullname) + } + if fd.Number() == genid.Value_NumberValue_field_number { + if v := m.Get(fd).Float(); math.IsNaN(v) || math.IsInf(v, 0) { + return errors.New("%s: invalid %v value", genid.Value_NumberValue_field_fullname, v) + } + } + return e.marshalSingular(m.Get(fd), fd) +} + +func (d decoder) unmarshalKnownValue(m pref.Message) error { + tok, err := d.Peek() + if err != nil { + return err + } + + var fd pref.FieldDescriptor + var val pref.Value + switch tok.Kind() { + case json.Null: + d.Read() + fd = m.Descriptor().Fields().ByNumber(genid.Value_NullValue_field_number) + val = pref.ValueOfEnum(0) + + case json.Bool: + tok, err := d.Read() + if err != nil { + return err + } + fd = m.Descriptor().Fields().ByNumber(genid.Value_BoolValue_field_number) + val = pref.ValueOfBool(tok.Bool()) + + case json.Number: + tok, err := d.Read() + if err != nil { + return err + } + fd = m.Descriptor().Fields().ByNumber(genid.Value_NumberValue_field_number) + var ok bool + val, ok = unmarshalFloat(tok, 64) + if !ok { + return d.newError(tok.Pos(), "invalid %v: %v", genid.Value_message_fullname, tok.RawString()) + } + + case json.String: + // A JSON string may have been encoded from the number_value field, + // e.g. "NaN", "Infinity", etc. Parsing a proto double type also allows + // for it to be in JSON string form. Given this custom encoding spec, + // however, there is no way to identify that and hence a JSON string is + // always assigned to the string_value field, which means that certain + // encoding cannot be parsed back to the same field. + tok, err := d.Read() + if err != nil { + return err + } + fd = m.Descriptor().Fields().ByNumber(genid.Value_StringValue_field_number) + val = pref.ValueOfString(tok.ParsedString()) + + case json.ObjectOpen: + fd = m.Descriptor().Fields().ByNumber(genid.Value_StructValue_field_number) + val = m.NewField(fd) + if err := d.unmarshalStruct(val.Message()); err != nil { + return err + } + + case json.ArrayOpen: + fd = m.Descriptor().Fields().ByNumber(genid.Value_ListValue_field_number) + val = m.NewField(fd) + if err := d.unmarshalListValue(val.Message()); err != nil { + return err + } + + default: + return d.newError(tok.Pos(), "invalid %v: %v", genid.Value_message_fullname, tok.RawString()) + } + + m.Set(fd, val) + return nil +} + +// The JSON representation for a Duration is a JSON string that ends in the +// suffix "s" (indicating seconds) and is preceded by the number of seconds, +// with nanoseconds expressed as fractional seconds. +// +// Durations less than one second are represented with a 0 seconds field and a +// positive or negative nanos field. For durations of one second or more, a +// non-zero value for the nanos field must be of the same sign as the seconds +// field. +// +// Duration.seconds must be from -315,576,000,000 to +315,576,000,000 inclusive. +// Duration.nanos must be from -999,999,999 to +999,999,999 inclusive. + +const ( + secondsInNanos = 999999999 + maxSecondsInDuration = 315576000000 +) + +func (e encoder) marshalDuration(m pref.Message) error { + fds := m.Descriptor().Fields() + fdSeconds := fds.ByNumber(genid.Duration_Seconds_field_number) + fdNanos := fds.ByNumber(genid.Duration_Nanos_field_number) + + secsVal := m.Get(fdSeconds) + nanosVal := m.Get(fdNanos) + secs := secsVal.Int() + nanos := nanosVal.Int() + if secs < -maxSecondsInDuration || secs > maxSecondsInDuration { + return errors.New("%s: seconds out of range %v", genid.Duration_message_fullname, secs) + } + if nanos < -secondsInNanos || nanos > secondsInNanos { + return errors.New("%s: nanos out of range %v", genid.Duration_message_fullname, nanos) + } + if (secs > 0 && nanos < 0) || (secs < 0 && nanos > 0) { + return errors.New("%s: signs of seconds and nanos do not match", genid.Duration_message_fullname) + } + // Generated output always contains 0, 3, 6, or 9 fractional digits, + // depending on required precision, followed by the suffix "s". + var sign string + if secs < 0 || nanos < 0 { + sign, secs, nanos = "-", -1*secs, -1*nanos + } + x := fmt.Sprintf("%s%d.%09d", sign, secs, nanos) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + e.WriteString(x + "s") + return nil +} + +func (d decoder) unmarshalDuration(m pref.Message) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.String { + return d.unexpectedTokenError(tok) + } + + secs, nanos, ok := parseDuration(tok.ParsedString()) + if !ok { + return d.newError(tok.Pos(), "invalid %v value %v", genid.Duration_message_fullname, tok.RawString()) + } + // Validate seconds. No need to validate nanos because parseDuration would + // have covered that already. + if secs < -maxSecondsInDuration || secs > maxSecondsInDuration { + return d.newError(tok.Pos(), "%v value out of range: %v", genid.Duration_message_fullname, tok.RawString()) + } + + fds := m.Descriptor().Fields() + fdSeconds := fds.ByNumber(genid.Duration_Seconds_field_number) + fdNanos := fds.ByNumber(genid.Duration_Nanos_field_number) + + m.Set(fdSeconds, pref.ValueOfInt64(secs)) + m.Set(fdNanos, pref.ValueOfInt32(nanos)) + return nil +} + +// parseDuration parses the given input string for seconds and nanoseconds value +// for the Duration JSON format. The format is a decimal number with a suffix +// 's'. It can have optional plus/minus sign. There needs to be at least an +// integer or fractional part. Fractional part is limited to 9 digits only for +// nanoseconds precision, regardless of whether there are trailing zero digits. +// Example values are 1s, 0.1s, 1.s, .1s, +1s, -1s, -.1s. +func parseDuration(input string) (int64, int32, bool) { + b := []byte(input) + size := len(b) + if size < 2 { + return 0, 0, false + } + if b[size-1] != 's' { + return 0, 0, false + } + b = b[:size-1] + + // Read optional plus/minus symbol. + var neg bool + switch b[0] { + case '-': + neg = true + b = b[1:] + case '+': + b = b[1:] + } + if len(b) == 0 { + return 0, 0, false + } + + // Read the integer part. + var intp []byte + switch { + case b[0] == '0': + b = b[1:] + + case '1' <= b[0] && b[0] <= '9': + intp = b[0:] + b = b[1:] + n := 1 + for len(b) > 0 && '0' <= b[0] && b[0] <= '9' { + n++ + b = b[1:] + } + intp = intp[:n] + + case b[0] == '.': + // Continue below. + + default: + return 0, 0, false + } + + hasFrac := false + var frac [9]byte + if len(b) > 0 { + if b[0] != '.' { + return 0, 0, false + } + // Read the fractional part. + b = b[1:] + n := 0 + for len(b) > 0 && n < 9 && '0' <= b[0] && b[0] <= '9' { + frac[n] = b[0] + n++ + b = b[1:] + } + // It is not valid if there are more bytes left. + if len(b) > 0 { + return 0, 0, false + } + // Pad fractional part with 0s. + for i := n; i < 9; i++ { + frac[i] = '0' + } + hasFrac = true + } + + var secs int64 + if len(intp) > 0 { + var err error + secs, err = strconv.ParseInt(string(intp), 10, 64) + if err != nil { + return 0, 0, false + } + } + + var nanos int64 + if hasFrac { + nanob := bytes.TrimLeft(frac[:], "0") + if len(nanob) > 0 { + var err error + nanos, err = strconv.ParseInt(string(nanob), 10, 32) + if err != nil { + return 0, 0, false + } + } + } + + if neg { + if secs > 0 { + secs = -secs + } + if nanos > 0 { + nanos = -nanos + } + } + return secs, int32(nanos), true +} + +// The JSON representation for a Timestamp is a JSON string in the RFC 3339 +// format, i.e. "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where +// {year} is always expressed using four digits while {month}, {day}, {hour}, +// {min}, and {sec} are zero-padded to two digits each. The fractional seconds, +// which can go up to 9 digits, up to 1 nanosecond resolution, is optional. The +// "Z" suffix indicates the timezone ("UTC"); the timezone is required. Encoding +// should always use UTC (as indicated by "Z") and a decoder should be able to +// accept both UTC and other timezones (as indicated by an offset). +// +// Timestamp.seconds must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z +// inclusive. +// Timestamp.nanos must be from 0 to 999,999,999 inclusive. + +const ( + maxTimestampSeconds = 253402300799 + minTimestampSeconds = -62135596800 +) + +func (e encoder) marshalTimestamp(m pref.Message) error { + fds := m.Descriptor().Fields() + fdSeconds := fds.ByNumber(genid.Timestamp_Seconds_field_number) + fdNanos := fds.ByNumber(genid.Timestamp_Nanos_field_number) + + secsVal := m.Get(fdSeconds) + nanosVal := m.Get(fdNanos) + secs := secsVal.Int() + nanos := nanosVal.Int() + if secs < minTimestampSeconds || secs > maxTimestampSeconds { + return errors.New("%s: seconds out of range %v", genid.Timestamp_message_fullname, secs) + } + if nanos < 0 || nanos > secondsInNanos { + return errors.New("%s: nanos out of range %v", genid.Timestamp_message_fullname, nanos) + } + // Uses RFC 3339, where generated output will be Z-normalized and uses 0, 3, + // 6 or 9 fractional digits. + t := time.Unix(secs, nanos).UTC() + x := t.Format("2006-01-02T15:04:05.000000000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + e.WriteString(x + "Z") + return nil +} + +func (d decoder) unmarshalTimestamp(m pref.Message) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.String { + return d.unexpectedTokenError(tok) + } + + t, err := time.Parse(time.RFC3339Nano, tok.ParsedString()) + if err != nil { + return d.newError(tok.Pos(), "invalid %v value %v", genid.Timestamp_message_fullname, tok.RawString()) + } + // Validate seconds. No need to validate nanos because time.Parse would have + // covered that already. + secs := t.Unix() + if secs < minTimestampSeconds || secs > maxTimestampSeconds { + return d.newError(tok.Pos(), "%v value out of range: %v", genid.Timestamp_message_fullname, tok.RawString()) + } + + fds := m.Descriptor().Fields() + fdSeconds := fds.ByNumber(genid.Timestamp_Seconds_field_number) + fdNanos := fds.ByNumber(genid.Timestamp_Nanos_field_number) + + m.Set(fdSeconds, pref.ValueOfInt64(secs)) + m.Set(fdNanos, pref.ValueOfInt32(int32(t.Nanosecond()))) + return nil +} + +// The JSON representation for a FieldMask is a JSON string where paths are +// separated by a comma. Fields name in each path are converted to/from +// lower-camel naming conventions. Encoding should fail if the path name would +// end up differently after a round-trip. + +func (e encoder) marshalFieldMask(m pref.Message) error { + fd := m.Descriptor().Fields().ByNumber(genid.FieldMask_Paths_field_number) + list := m.Get(fd).List() + paths := make([]string, 0, list.Len()) + + for i := 0; i < list.Len(); i++ { + s := list.Get(i).String() + if !pref.FullName(s).IsValid() { + return errors.New("%s contains invalid path: %q", genid.FieldMask_Paths_field_fullname, s) + } + // Return error if conversion to camelCase is not reversible. + cc := strs.JSONCamelCase(s) + if s != strs.JSONSnakeCase(cc) { + return errors.New("%s contains irreversible value %q", genid.FieldMask_Paths_field_fullname, s) + } + paths = append(paths, cc) + } + + e.WriteString(strings.Join(paths, ",")) + return nil +} + +func (d decoder) unmarshalFieldMask(m pref.Message) error { + tok, err := d.Read() + if err != nil { + return err + } + if tok.Kind() != json.String { + return d.unexpectedTokenError(tok) + } + str := strings.TrimSpace(tok.ParsedString()) + if str == "" { + return nil + } + paths := strings.Split(str, ",") + + fd := m.Descriptor().Fields().ByNumber(genid.FieldMask_Paths_field_number) + list := m.Mutable(fd).List() + + for _, s0 := range paths { + s := strs.JSONSnakeCase(s0) + if strings.Contains(s0, "_") || !pref.FullName(s).IsValid() { + return d.newError(tok.Pos(), "%v contains invalid path: %q", genid.FieldMask_Paths_field_fullname, s0) + } + list.Append(pref.ValueOfString(s)) + } + return nil +} diff --git a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go index a427f8b7..9c61112f 100644 --- a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go +++ b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go @@ -21,10 +21,11 @@ import ( type Number int32 const ( - MinValidNumber Number = 1 - FirstReservedNumber Number = 19000 - LastReservedNumber Number = 19999 - MaxValidNumber Number = 1<<29 - 1 + MinValidNumber Number = 1 + FirstReservedNumber Number = 19000 + LastReservedNumber Number = 19999 + MaxValidNumber Number = 1<<29 - 1 + DefaultRecursionLimit = 10000 ) // IsValid reports whether the field number is semantically valid. @@ -55,6 +56,7 @@ const ( errCodeOverflow errCodeReserved errCodeEndGroup + errCodeRecursionDepth ) var ( @@ -112,6 +114,10 @@ func ConsumeField(b []byte) (Number, Type, int) { // When parsing a group, the length includes the end group marker and // the end group is verified to match the starting field number. func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { + return consumeFieldValueD(num, typ, b, DefaultRecursionLimit) +} + +func consumeFieldValueD(num Number, typ Type, b []byte, depth int) (n int) { switch typ { case VarintType: _, n = ConsumeVarint(b) @@ -126,6 +132,9 @@ func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { _, n = ConsumeBytes(b) return n case StartGroupType: + if depth < 0 { + return errCodeRecursionDepth + } n0 := len(b) for { num2, typ2, n := ConsumeTag(b) @@ -140,7 +149,7 @@ func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { return n0 - len(b) } - n = ConsumeFieldValue(num2, typ2, b) + n = consumeFieldValueD(num2, typ2, b, depth-1) if n < 0 { return n // forward error code } diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go new file mode 100644 index 00000000..b13fd29e --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/decode.go @@ -0,0 +1,340 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "bytes" + "fmt" + "io" + "regexp" + "unicode/utf8" + + "google.golang.org/protobuf/internal/errors" +) + +// call specifies which Decoder method was invoked. +type call uint8 + +const ( + readCall call = iota + peekCall +) + +const unexpectedFmt = "unexpected token %s" + +// ErrUnexpectedEOF means that EOF was encountered in the middle of the input. +var ErrUnexpectedEOF = errors.New("%v", io.ErrUnexpectedEOF) + +// Decoder is a token-based JSON decoder. +type Decoder struct { + // lastCall is last method called, either readCall or peekCall. + // Initial value is readCall. + lastCall call + + // lastToken contains the last read token. + lastToken Token + + // lastErr contains the last read error. + lastErr error + + // openStack is a stack containing ObjectOpen and ArrayOpen values. The + // top of stack represents the object or the array the current value is + // directly located in. + openStack []Kind + + // orig is used in reporting line and column. + orig []byte + // in contains the unconsumed input. + in []byte +} + +// NewDecoder returns a Decoder to read the given []byte. +func NewDecoder(b []byte) *Decoder { + return &Decoder{orig: b, in: b} +} + +// Peek looks ahead and returns the next token kind without advancing a read. +func (d *Decoder) Peek() (Token, error) { + defer func() { d.lastCall = peekCall }() + if d.lastCall == readCall { + d.lastToken, d.lastErr = d.Read() + } + return d.lastToken, d.lastErr +} + +// Read returns the next JSON token. +// It will return an error if there is no valid token. +func (d *Decoder) Read() (Token, error) { + const scalar = Null | Bool | Number | String + + defer func() { d.lastCall = readCall }() + if d.lastCall == peekCall { + return d.lastToken, d.lastErr + } + + tok, err := d.parseNext() + if err != nil { + return Token{}, err + } + + switch tok.kind { + case EOF: + if len(d.openStack) != 0 || + d.lastToken.kind&scalar|ObjectClose|ArrayClose == 0 { + return Token{}, ErrUnexpectedEOF + } + + case Null: + if !d.isValueNext() { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + + case Bool, Number: + if !d.isValueNext() { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + + case String: + if d.isValueNext() { + break + } + // This string token should only be for a field name. + if d.lastToken.kind&(ObjectOpen|comma) == 0 { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + if len(d.in) == 0 { + return Token{}, ErrUnexpectedEOF + } + if c := d.in[0]; c != ':' { + return Token{}, d.newSyntaxError(d.currPos(), `unexpected character %s, missing ":" after field name`, string(c)) + } + tok.kind = Name + d.consume(1) + + case ObjectOpen, ArrayOpen: + if !d.isValueNext() { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + d.openStack = append(d.openStack, tok.kind) + + case ObjectClose: + if len(d.openStack) == 0 || + d.lastToken.kind == comma || + d.openStack[len(d.openStack)-1] != ObjectOpen { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + d.openStack = d.openStack[:len(d.openStack)-1] + + case ArrayClose: + if len(d.openStack) == 0 || + d.lastToken.kind == comma || + d.openStack[len(d.openStack)-1] != ArrayOpen { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + d.openStack = d.openStack[:len(d.openStack)-1] + + case comma: + if len(d.openStack) == 0 || + d.lastToken.kind&(scalar|ObjectClose|ArrayClose) == 0 { + return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString()) + } + } + + // Update d.lastToken only after validating token to be in the right sequence. + d.lastToken = tok + + if d.lastToken.kind == comma { + return d.Read() + } + return tok, nil +} + +// Any sequence that looks like a non-delimiter (for error reporting). +var errRegexp = regexp.MustCompile(`^([-+._a-zA-Z0-9]{1,32}|.)`) + +// parseNext parses for the next JSON token. It returns a Token object for +// different types, except for Name. It does not handle whether the next token +// is in a valid sequence or not. +func (d *Decoder) parseNext() (Token, error) { + // Trim leading spaces. + d.consume(0) + + in := d.in + if len(in) == 0 { + return d.consumeToken(EOF, 0), nil + } + + switch in[0] { + case 'n': + if n := matchWithDelim("null", in); n != 0 { + return d.consumeToken(Null, n), nil + } + + case 't': + if n := matchWithDelim("true", in); n != 0 { + return d.consumeBoolToken(true, n), nil + } + + case 'f': + if n := matchWithDelim("false", in); n != 0 { + return d.consumeBoolToken(false, n), nil + } + + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if n, ok := parseNumber(in); ok { + return d.consumeToken(Number, n), nil + } + + case '"': + s, n, err := d.parseString(in) + if err != nil { + return Token{}, err + } + return d.consumeStringToken(s, n), nil + + case '{': + return d.consumeToken(ObjectOpen, 1), nil + + case '}': + return d.consumeToken(ObjectClose, 1), nil + + case '[': + return d.consumeToken(ArrayOpen, 1), nil + + case ']': + return d.consumeToken(ArrayClose, 1), nil + + case ',': + return d.consumeToken(comma, 1), nil + } + return Token{}, d.newSyntaxError(d.currPos(), "invalid value %s", errRegexp.Find(in)) +} + +// newSyntaxError returns an error with line and column information useful for +// syntax errors. +func (d *Decoder) newSyntaxError(pos int, f string, x ...interface{}) error { + e := errors.New(f, x...) + line, column := d.Position(pos) + return errors.New("syntax error (line %d:%d): %v", line, column, e) +} + +// Position returns line and column number of given index of the original input. +// It will panic if index is out of range. +func (d *Decoder) Position(idx int) (line int, column int) { + b := d.orig[:idx] + line = bytes.Count(b, []byte("\n")) + 1 + if i := bytes.LastIndexByte(b, '\n'); i >= 0 { + b = b[i+1:] + } + column = utf8.RuneCount(b) + 1 // ignore multi-rune characters + return line, column +} + +// currPos returns the current index position of d.in from d.orig. +func (d *Decoder) currPos() int { + return len(d.orig) - len(d.in) +} + +// matchWithDelim matches s with the input b and verifies that the match +// terminates with a delimiter of some form (e.g., r"[^-+_.a-zA-Z0-9]"). +// As a special case, EOF is considered a delimiter. It returns the length of s +// if there is a match, else 0. +func matchWithDelim(s string, b []byte) int { + if !bytes.HasPrefix(b, []byte(s)) { + return 0 + } + + n := len(s) + if n < len(b) && isNotDelim(b[n]) { + return 0 + } + return n +} + +// isNotDelim returns true if given byte is a not delimiter character. +func isNotDelim(c byte) bool { + return (c == '-' || c == '+' || c == '.' || c == '_' || + ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9')) +} + +// consume consumes n bytes of input and any subsequent whitespace. +func (d *Decoder) consume(n int) { + d.in = d.in[n:] + for len(d.in) > 0 { + switch d.in[0] { + case ' ', '\n', '\r', '\t': + d.in = d.in[1:] + default: + return + } + } +} + +// isValueNext returns true if next type should be a JSON value: Null, +// Number, String or Bool. +func (d *Decoder) isValueNext() bool { + if len(d.openStack) == 0 { + return d.lastToken.kind == 0 + } + + start := d.openStack[len(d.openStack)-1] + switch start { + case ObjectOpen: + return d.lastToken.kind&Name != 0 + case ArrayOpen: + return d.lastToken.kind&(ArrayOpen|comma) != 0 + } + panic(fmt.Sprintf( + "unreachable logic in Decoder.isValueNext, lastToken.kind: %v, openStack: %v", + d.lastToken.kind, start)) +} + +// consumeToken constructs a Token for given Kind with raw value derived from +// current d.in and given size, and consumes the given size-lenght of it. +func (d *Decoder) consumeToken(kind Kind, size int) Token { + tok := Token{ + kind: kind, + raw: d.in[:size], + pos: len(d.orig) - len(d.in), + } + d.consume(size) + return tok +} + +// consumeBoolToken constructs a Token for a Bool kind with raw value derived from +// current d.in and given size. +func (d *Decoder) consumeBoolToken(b bool, size int) Token { + tok := Token{ + kind: Bool, + raw: d.in[:size], + pos: len(d.orig) - len(d.in), + boo: b, + } + d.consume(size) + return tok +} + +// consumeStringToken constructs a Token for a String kind with raw value derived +// from current d.in and given size. +func (d *Decoder) consumeStringToken(s string, size int) Token { + tok := Token{ + kind: String, + raw: d.in[:size], + pos: len(d.orig) - len(d.in), + str: s, + } + d.consume(size) + return tok +} + +// Clone returns a copy of the Decoder for use in reading ahead the next JSON +// object, array or other values without affecting current Decoder. +func (d *Decoder) Clone() *Decoder { + ret := *d + ret.openStack = append([]Kind(nil), ret.openStack...) + return &ret +} diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/decode_number.go b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_number.go new file mode 100644 index 00000000..2999d713 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_number.go @@ -0,0 +1,254 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "bytes" + "strconv" +) + +// parseNumber reads the given []byte for a valid JSON number. If it is valid, +// it returns the number of bytes. Parsing logic follows the definition in +// https://tools.ietf.org/html/rfc7159#section-6, and is based off +// encoding/json.isValidNumber function. +func parseNumber(input []byte) (int, bool) { + var n int + + s := input + if len(s) == 0 { + return 0, false + } + + // Optional - + if s[0] == '-' { + s = s[1:] + n++ + if len(s) == 0 { + return 0, false + } + } + + // Digits + switch { + case s[0] == '0': + s = s[1:] + n++ + + case '1' <= s[0] && s[0] <= '9': + s = s[1:] + n++ + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + + default: + return 0, false + } + + // . followed by 1 or more digits. + if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { + s = s[2:] + n += 2 + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { + s = s[1:] + n++ + if s[0] == '+' || s[0] == '-' { + s = s[1:] + n++ + if len(s) == 0 { + return 0, false + } + } + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + } + + // Check that next byte is a delimiter or it is at the end. + if n < len(input) && isNotDelim(input[n]) { + return 0, false + } + + return n, true +} + +// numberParts is the result of parsing out a valid JSON number. It contains +// the parts of a number. The parts are used for integer conversion. +type numberParts struct { + neg bool + intp []byte + frac []byte + exp []byte +} + +// parseNumber constructs numberParts from given []byte. The logic here is +// similar to consumeNumber above with the difference of having to construct +// numberParts. The slice fields in numberParts are subslices of the input. +func parseNumberParts(input []byte) (numberParts, bool) { + var neg bool + var intp []byte + var frac []byte + var exp []byte + + s := input + if len(s) == 0 { + return numberParts{}, false + } + + // Optional - + if s[0] == '-' { + neg = true + s = s[1:] + if len(s) == 0 { + return numberParts{}, false + } + } + + // Digits + switch { + case s[0] == '0': + // Skip first 0 and no need to store. + s = s[1:] + + case '1' <= s[0] && s[0] <= '9': + intp = s + n := 1 + s = s[1:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + intp = intp[:n] + + default: + return numberParts{}, false + } + + // . followed by 1 or more digits. + if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { + frac = s[1:] + n := 1 + s = s[2:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + frac = frac[:n] + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { + s = s[1:] + exp = s + n := 0 + if s[0] == '+' || s[0] == '-' { + s = s[1:] + n++ + if len(s) == 0 { + return numberParts{}, false + } + } + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + n++ + } + exp = exp[:n] + } + + return numberParts{ + neg: neg, + intp: intp, + frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right. + exp: exp, + }, true +} + +// normalizeToIntString returns an integer string in normal form without the +// E-notation for given numberParts. It will return false if it is not an +// integer or if the exponent exceeds than max/min int value. +func normalizeToIntString(n numberParts) (string, bool) { + intpSize := len(n.intp) + fracSize := len(n.frac) + + if intpSize == 0 && fracSize == 0 { + return "0", true + } + + var exp int + if len(n.exp) > 0 { + i, err := strconv.ParseInt(string(n.exp), 10, 32) + if err != nil { + return "", false + } + exp = int(i) + } + + var num []byte + if exp >= 0 { + // For positive E, shift fraction digits into integer part and also pad + // with zeroes as needed. + + // If there are more digits in fraction than the E value, then the + // number is not an integer. + if fracSize > exp { + return "", false + } + + // Make sure resulting digits are within max value limit to avoid + // unnecessarily constructing a large byte slice that may simply fail + // later on. + const maxDigits = 20 // Max uint64 value has 20 decimal digits. + if intpSize+exp > maxDigits { + return "", false + } + + // Set cap to make a copy of integer part when appended. + num = n.intp[:len(n.intp):len(n.intp)] + num = append(num, n.frac...) + for i := 0; i < exp-fracSize; i++ { + num = append(num, '0') + } + } else { + // For negative E, shift digits in integer part out. + + // If there are fractions, then the number is not an integer. + if fracSize > 0 { + return "", false + } + + // index is where the decimal point will be after adjusting for negative + // exponent. + index := intpSize + exp + if index < 0 { + return "", false + } + + num = n.intp + // If any of the digits being shifted to the right of the decimal point + // is non-zero, then the number is not an integer. + for i := index; i < intpSize; i++ { + if num[i] != '0' { + return "", false + } + } + num = num[:index] + } + + if n.neg { + return "-" + string(num), true + } + return string(num), true +} diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/decode_string.go b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_string.go new file mode 100644 index 00000000..f7fea7d8 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_string.go @@ -0,0 +1,91 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" + + "google.golang.org/protobuf/internal/strs" +) + +func (d *Decoder) parseString(in []byte) (string, int, error) { + in0 := in + if len(in) == 0 { + return "", 0, ErrUnexpectedEOF + } + if in[0] != '"' { + return "", 0, d.newSyntaxError(d.currPos(), "invalid character %q at start of string", in[0]) + } + in = in[1:] + i := indexNeedEscapeInBytes(in) + in, out := in[i:], in[:i:i] // set cap to prevent mutations + for len(in) > 0 { + switch r, n := utf8.DecodeRune(in); { + case r == utf8.RuneError && n == 1: + return "", 0, d.newSyntaxError(d.currPos(), "invalid UTF-8 in string") + case r < ' ': + return "", 0, d.newSyntaxError(d.currPos(), "invalid character %q in string", r) + case r == '"': + in = in[1:] + n := len(in0) - len(in) + return string(out), n, nil + case r == '\\': + if len(in) < 2 { + return "", 0, ErrUnexpectedEOF + } + switch r := in[1]; r { + case '"', '\\', '/': + in, out = in[2:], append(out, r) + case 'b': + in, out = in[2:], append(out, '\b') + case 'f': + in, out = in[2:], append(out, '\f') + case 'n': + in, out = in[2:], append(out, '\n') + case 'r': + in, out = in[2:], append(out, '\r') + case 't': + in, out = in[2:], append(out, '\t') + case 'u': + if len(in) < 6 { + return "", 0, ErrUnexpectedEOF + } + v, err := strconv.ParseUint(string(in[2:6]), 16, 16) + if err != nil { + return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:6]) + } + in = in[6:] + + r := rune(v) + if utf16.IsSurrogate(r) { + if len(in) < 6 { + return "", 0, ErrUnexpectedEOF + } + v, err := strconv.ParseUint(string(in[2:6]), 16, 16) + r = utf16.DecodeRune(r, rune(v)) + if in[0] != '\\' || in[1] != 'u' || + r == unicode.ReplacementChar || err != nil { + return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:6]) + } + in = in[6:] + } + out = append(out, string(r)...) + default: + return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:2]) + } + default: + i := indexNeedEscapeInBytes(in[n:]) + in, out = in[n+i:], append(out, in[:n+i]...) + } + } + return "", 0, ErrUnexpectedEOF +} + +// indexNeedEscapeInBytes returns the index of the character that needs +// escaping. If no characters need escaping, this returns the input length. +func indexNeedEscapeInBytes(b []byte) int { return indexNeedEscapeInString(strs.UnsafeString(b)) } diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/decode_token.go b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_token.go new file mode 100644 index 00000000..50578d65 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/decode_token.go @@ -0,0 +1,192 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "bytes" + "fmt" + "strconv" +) + +// Kind represents a token kind expressible in the JSON format. +type Kind uint16 + +const ( + Invalid Kind = (1 << iota) / 2 + EOF + Null + Bool + Number + String + Name + ObjectOpen + ObjectClose + ArrayOpen + ArrayClose + + // comma is only for parsing in between tokens and + // does not need to be exported. + comma +) + +func (k Kind) String() string { + switch k { + case EOF: + return "eof" + case Null: + return "null" + case Bool: + return "bool" + case Number: + return "number" + case String: + return "string" + case ObjectOpen: + return "{" + case ObjectClose: + return "}" + case Name: + return "name" + case ArrayOpen: + return "[" + case ArrayClose: + return "]" + case comma: + return "," + } + return "" +} + +// Token provides a parsed token kind and value. +// +// Values are provided by the difference accessor methods. The accessor methods +// Name, Bool, and ParsedString will panic if called on the wrong kind. There +// are different accessor methods for the Number kind for converting to the +// appropriate Go numeric type and those methods have the ok return value. +type Token struct { + // Token kind. + kind Kind + // pos provides the position of the token in the original input. + pos int + // raw bytes of the serialized token. + // This is a subslice into the original input. + raw []byte + // boo is parsed boolean value. + boo bool + // str is parsed string value. + str string +} + +// Kind returns the token kind. +func (t Token) Kind() Kind { + return t.kind +} + +// RawString returns the read value in string. +func (t Token) RawString() string { + return string(t.raw) +} + +// Pos returns the token position from the input. +func (t Token) Pos() int { + return t.pos +} + +// Name returns the object name if token is Name, else it panics. +func (t Token) Name() string { + if t.kind == Name { + return t.str + } + panic(fmt.Sprintf("Token is not a Name: %v", t.RawString())) +} + +// Bool returns the bool value if token kind is Bool, else it panics. +func (t Token) Bool() bool { + if t.kind == Bool { + return t.boo + } + panic(fmt.Sprintf("Token is not a Bool: %v", t.RawString())) +} + +// ParsedString returns the string value for a JSON string token or the read +// value in string if token is not a string. +func (t Token) ParsedString() string { + if t.kind == String { + return t.str + } + panic(fmt.Sprintf("Token is not a String: %v", t.RawString())) +} + +// Float returns the floating-point number if token kind is Number. +// +// The floating-point precision is specified by the bitSize parameter: 32 for +// float32 or 64 for float64. If bitSize=32, the result still has type float64, +// but it will be convertible to float32 without changing its value. It will +// return false if the number exceeds the floating point limits for given +// bitSize. +func (t Token) Float(bitSize int) (float64, bool) { + if t.kind != Number { + return 0, false + } + f, err := strconv.ParseFloat(t.RawString(), bitSize) + if err != nil { + return 0, false + } + return f, true +} + +// Int returns the signed integer number if token is Number. +// +// The given bitSize specifies the integer type that the result must fit into. +// It returns false if the number is not an integer value or if the result +// exceeds the limits for given bitSize. +func (t Token) Int(bitSize int) (int64, bool) { + s, ok := t.getIntStr() + if !ok { + return 0, false + } + n, err := strconv.ParseInt(s, 10, bitSize) + if err != nil { + return 0, false + } + return n, true +} + +// Uint returns the signed integer number if token is Number. +// +// The given bitSize specifies the unsigned integer type that the result must +// fit into. It returns false if the number is not an unsigned integer value +// or if the result exceeds the limits for given bitSize. +func (t Token) Uint(bitSize int) (uint64, bool) { + s, ok := t.getIntStr() + if !ok { + return 0, false + } + n, err := strconv.ParseUint(s, 10, bitSize) + if err != nil { + return 0, false + } + return n, true +} + +func (t Token) getIntStr() (string, bool) { + if t.kind != Number { + return "", false + } + parts, ok := parseNumberParts(t.raw) + if !ok { + return "", false + } + return normalizeToIntString(parts) +} + +// TokenEquals returns true if given Tokens are equal, else false. +func TokenEquals(x, y Token) bool { + return x.kind == y.kind && + x.pos == y.pos && + bytes.Equal(x.raw, y.raw) && + x.boo == y.boo && + x.str == y.str +} diff --git a/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go new file mode 100644 index 00000000..fbdf3487 --- /dev/null +++ b/vendor/google.golang.org/protobuf/internal/encoding/json/encode.go @@ -0,0 +1,276 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "math" + "math/bits" + "strconv" + "strings" + "unicode/utf8" + + "google.golang.org/protobuf/internal/detrand" + "google.golang.org/protobuf/internal/errors" +) + +// kind represents an encoding type. +type kind uint8 + +const ( + _ kind = (1 << iota) / 2 + name + scalar + objectOpen + objectClose + arrayOpen + arrayClose +) + +// Encoder provides methods to write out JSON constructs and values. The user is +// responsible for producing valid sequences of JSON constructs and values. +type Encoder struct { + indent string + lastKind kind + indents []byte + out []byte +} + +// NewEncoder returns an Encoder. +// +// If indent is a non-empty string, it causes every entry for an Array or Object +// to be preceded by the indent and trailed by a newline. +func NewEncoder(indent string) (*Encoder, error) { + e := &Encoder{} + if len(indent) > 0 { + if strings.Trim(indent, " \t") != "" { + return nil, errors.New("indent may only be composed of space or tab characters") + } + e.indent = indent + } + return e, nil +} + +// Bytes returns the content of the written bytes. +func (e *Encoder) Bytes() []byte { + return e.out +} + +// WriteNull writes out the null value. +func (e *Encoder) WriteNull() { + e.prepareNext(scalar) + e.out = append(e.out, "null"...) +} + +// WriteBool writes out the given boolean value. +func (e *Encoder) WriteBool(b bool) { + e.prepareNext(scalar) + if b { + e.out = append(e.out, "true"...) + } else { + e.out = append(e.out, "false"...) + } +} + +// WriteString writes out the given string in JSON string value. Returns error +// if input string contains invalid UTF-8. +func (e *Encoder) WriteString(s string) error { + e.prepareNext(scalar) + var err error + if e.out, err = appendString(e.out, s); err != nil { + return err + } + return nil +} + +// Sentinel error used for indicating invalid UTF-8. +var errInvalidUTF8 = errors.New("invalid UTF-8") + +func appendString(out []byte, in string) ([]byte, error) { + out = append(out, '"') + i := indexNeedEscapeInString(in) + in, out = in[i:], append(out, in[:i]...) + for len(in) > 0 { + switch r, n := utf8.DecodeRuneInString(in); { + case r == utf8.RuneError && n == 1: + return out, errInvalidUTF8 + case r < ' ' || r == '"' || r == '\\': + out = append(out, '\\') + switch r { + case '"', '\\': + out = append(out, byte(r)) + case '\b': + out = append(out, 'b') + case '\f': + out = append(out, 'f') + case '\n': + out = append(out, 'n') + case '\r': + out = append(out, 'r') + case '\t': + out = append(out, 't') + default: + out = append(out, 'u') + out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...) + out = strconv.AppendUint(out, uint64(r), 16) + } + in = in[n:] + default: + i := indexNeedEscapeInString(in[n:]) + in, out = in[n+i:], append(out, in[:n+i]...) + } + } + out = append(out, '"') + return out, nil +} + +// indexNeedEscapeInString returns the index of the character that needs +// escaping. If no characters need escaping, this returns the input length. +func indexNeedEscapeInString(s string) int { + for i, r := range s { + if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError { + return i + } + } + return len(s) +} + +// WriteFloat writes out the given float and bitSize in JSON number value. +func (e *Encoder) WriteFloat(n float64, bitSize int) { + e.prepareNext(scalar) + e.out = appendFloat(e.out, n, bitSize) +} + +// appendFloat formats given float in bitSize, and appends to the given []byte. +func appendFloat(out []byte, n float64, bitSize int) []byte { + switch { + case math.IsNaN(n): + return append(out, `"NaN"`...) + case math.IsInf(n, +1): + return append(out, `"Infinity"`...) + case math.IsInf(n, -1): + return append(out, `"-Infinity"`...) + } + + // JSON number formatting logic based on encoding/json. + // See floatEncoder.encode for reference. + fmt := byte('f') + if abs := math.Abs(n); abs != 0 { + if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || + bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) { + fmt = 'e' + } + } + out = strconv.AppendFloat(out, n, fmt, -1, bitSize) + if fmt == 'e' { + n := len(out) + if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' { + out[n-2] = out[n-1] + out = out[:n-1] + } + } + return out +} + +// WriteInt writes out the given signed integer in JSON number value. +func (e *Encoder) WriteInt(n int64) { + e.prepareNext(scalar) + e.out = append(e.out, strconv.FormatInt(n, 10)...) +} + +// WriteUint writes out the given unsigned integer in JSON number value. +func (e *Encoder) WriteUint(n uint64) { + e.prepareNext(scalar) + e.out = append(e.out, strconv.FormatUint(n, 10)...) +} + +// StartObject writes out the '{' symbol. +func (e *Encoder) StartObject() { + e.prepareNext(objectOpen) + e.out = append(e.out, '{') +} + +// EndObject writes out the '}' symbol. +func (e *Encoder) EndObject() { + e.prepareNext(objectClose) + e.out = append(e.out, '}') +} + +// WriteName writes out the given string in JSON string value and the name +// separator ':'. Returns error if input string contains invalid UTF-8, which +// should not be likely as protobuf field names should be valid. +func (e *Encoder) WriteName(s string) error { + e.prepareNext(name) + var err error + // Append to output regardless of error. + e.out, err = appendString(e.out, s) + e.out = append(e.out, ':') + return err +} + +// StartArray writes out the '[' symbol. +func (e *Encoder) StartArray() { + e.prepareNext(arrayOpen) + e.out = append(e.out, '[') +} + +// EndArray writes out the ']' symbol. +func (e *Encoder) EndArray() { + e.prepareNext(arrayClose) + e.out = append(e.out, ']') +} + +// prepareNext adds possible comma and indentation for the next value based +// on last type and indent option. It also updates lastKind to next. +func (e *Encoder) prepareNext(next kind) { + defer func() { + // Set lastKind to next. + e.lastKind = next + }() + + if len(e.indent) == 0 { + // Need to add comma on the following condition. + if e.lastKind&(scalar|objectClose|arrayClose) != 0 && + next&(name|scalar|objectOpen|arrayOpen) != 0 { + e.out = append(e.out, ',') + // For single-line output, add a random extra space after each + // comma to make output unstable. + if detrand.Bool() { + e.out = append(e.out, ' ') + } + } + return + } + + switch { + case e.lastKind&(objectOpen|arrayOpen) != 0: + // If next type is NOT closing, add indent and newline. + if next&(objectClose|arrayClose) == 0 { + e.indents = append(e.indents, e.indent...) + e.out = append(e.out, '\n') + e.out = append(e.out, e.indents...) + } + + case e.lastKind&(scalar|objectClose|arrayClose) != 0: + switch { + // If next type is either a value or name, add comma and newline. + case next&(name|scalar|objectOpen|arrayOpen) != 0: + e.out = append(e.out, ',', '\n') + + // If next type is a closing object or array, adjust indentation. + case next&(objectClose|arrayClose) != 0: + e.indents = e.indents[:len(e.indents)-len(e.indent)] + e.out = append(e.out, '\n') + } + e.out = append(e.out, e.indents...) + + case e.lastKind&name != 0: + e.out = append(e.out, ' ') + // For multi-line output, add a random extra space after key: to make + // output unstable. + if detrand.Bool() { + e.out = append(e.out, ' ') + } + } +} diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go index eb10ea10..37803773 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go @@ -381,7 +381,7 @@ func (d *Decoder) currentOpenKind() (Kind, byte) { case '[': return ListOpen, ']' } - panic(fmt.Sprintf("Decoder: openStack contains invalid byte %s", string(openCh))) + panic(fmt.Sprintf("Decoder: openStack contains invalid byte %c", openCh)) } func (d *Decoder) pushOpenStack(ch byte) { diff --git a/vendor/google.golang.org/protobuf/internal/errors/is_go112.go b/vendor/google.golang.org/protobuf/internal/errors/is_go112.go index f90e909b..fbcd3492 100644 --- a/vendor/google.golang.org/protobuf/internal/errors/is_go112.go +++ b/vendor/google.golang.org/protobuf/internal/errors/is_go112.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.13 // +build !go1.13 package errors diff --git a/vendor/google.golang.org/protobuf/internal/errors/is_go113.go b/vendor/google.golang.org/protobuf/internal/errors/is_go113.go index dc05f419..5e72f1cd 100644 --- a/vendor/google.golang.org/protobuf/internal/errors/is_go113.go +++ b/vendor/google.golang.org/protobuf/internal/errors/is_go113.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 // +build go1.13 package errors diff --git a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go index a72995f0..bda8e8cf 100644 --- a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go +++ b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !protolegacy // +build !protolegacy package flags diff --git a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go index 772e2f0e..6d8d9bd6 100644 --- a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go +++ b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build protolegacy // +build protolegacy package flags diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go index 2706bb67..4b15493f 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.12 // +build !go1.12 package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go index 1533ef60..0b31b66e 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.12 // +build go1.12 package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go b/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go index 90705e3a..145c577b 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go b/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go index e118af1e..757642e2 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/decode.go b/vendor/google.golang.org/protobuf/internal/impl/decode.go index 949dc49a..c65b0325 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/decode.go +++ b/vendor/google.golang.org/protobuf/internal/impl/decode.go @@ -18,6 +18,7 @@ import ( ) var errDecode = errors.New("cannot parse invalid wire-format data") +var errRecursionDepth = errors.New("exceeded maximum recursion depth") type unmarshalOptions struct { flags protoiface.UnmarshalInputFlags @@ -25,6 +26,7 @@ type unmarshalOptions struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + depth int } func (o unmarshalOptions) Options() proto.UnmarshalOptions { @@ -44,6 +46,7 @@ func (o unmarshalOptions) IsDefault() bool { var lazyUnmarshalOptions = unmarshalOptions{ resolver: preg.GlobalTypes, + depth: protowire.DefaultRecursionLimit, } type unmarshalOutput struct { @@ -62,6 +65,7 @@ func (mi *MessageInfo) unmarshal(in piface.UnmarshalInput) (piface.UnmarshalOutp out, err := mi.unmarshalPointer(in.Buf, p, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) var flags piface.UnmarshalOutputFlags if out.initialized { @@ -82,6 +86,10 @@ var errUnknown = errors.New("unknown") func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() + opts.depth-- + if opts.depth < 0 { + return out, errRecursionDepth + } if flags.ProtoLegacy && mi.isMessageSet { return unmarshalMessageSet(mi, b, p, opts) } diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go index 9e3ed821..4c491bdf 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go index 9ecf23a8..ee0e0573 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go b/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go index 85e074c9..a1f6f333 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package strs diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go index 2160c701..56a8a4ed 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package strs diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index 14e774fb..3d40d524 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,8 +52,8 @@ import ( // 10. Send out the CL for review and submit it. const ( Major = 1 - Minor = 27 - Patch = 1 + Minor = 28 + Patch = 0 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/proto/decode.go b/vendor/google.golang.org/protobuf/proto/decode.go index 49f9b8c8..11bf7173 100644 --- a/vendor/google.golang.org/protobuf/proto/decode.go +++ b/vendor/google.golang.org/protobuf/proto/decode.go @@ -42,18 +42,25 @@ type UnmarshalOptions struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + + // RecursionLimit limits how deeply messages may be nested. + // If zero, a default limit is applied. + RecursionLimit int } // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). func Unmarshal(b []byte, m Message) error { - _, err := UnmarshalOptions{}.unmarshal(b, m.ProtoReflect()) + _, err := UnmarshalOptions{RecursionLimit: protowire.DefaultRecursionLimit}.unmarshal(b, m.ProtoReflect()) return err } // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { + if o.RecursionLimit == 0 { + o.RecursionLimit = protowire.DefaultRecursionLimit + } _, err := o.unmarshal(b, m.ProtoReflect()) return err } @@ -63,6 +70,9 @@ func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { // This method permits fine-grained control over the unmarshaler. // Most users should use Unmarshal instead. func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + if o.RecursionLimit == 0 { + o.RecursionLimit = protowire.DefaultRecursionLimit + } return o.unmarshal(in.Buf, in.Message) } @@ -86,12 +96,17 @@ func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out proto Message: m, Buf: b, Resolver: o.Resolver, + Depth: o.RecursionLimit, } if o.DiscardUnknown { in.Flags |= protoiface.UnmarshalDiscardUnknown } out, err = methods.Unmarshal(in) } else { + o.RecursionLimit-- + if o.RecursionLimit < 0 { + return out, errors.New("exceeded max recursion depth") + } err = o.unmarshalMessageSlow(b, m) } if err != nil { diff --git a/vendor/google.golang.org/protobuf/proto/proto_methods.go b/vendor/google.golang.org/protobuf/proto/proto_methods.go index d8dd604f..465e057b 100644 --- a/vendor/google.golang.org/protobuf/proto/proto_methods.go +++ b/vendor/google.golang.org/protobuf/proto/proto_methods.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // The protoreflect build tag disables use of fast-path methods. +//go:build !protoreflect // +build !protoreflect package proto diff --git a/vendor/google.golang.org/protobuf/proto/proto_reflect.go b/vendor/google.golang.org/protobuf/proto/proto_reflect.go index b103d432..494d6cee 100644 --- a/vendor/google.golang.org/protobuf/proto/proto_reflect.go +++ b/vendor/google.golang.org/protobuf/proto/proto_reflect.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // The protoreflect build tag disables use of fast-path methods. +//go:build protoreflect // +build protoreflect package proto diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go index 6be5d16e..d5d5af6e 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go @@ -53,6 +53,7 @@ type ( FindExtensionByName(field FullName) (ExtensionType, error) FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error) } + Depth int } unmarshalOutput = struct { pragma.NoUnkeyedLiterals diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go index 918e685e..7ced876f 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package protoreflect diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go index 5a341472..eb7764c3 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go @@ -41,6 +41,31 @@ import ( // Converting to/from a Value and a concrete Go value panics on type mismatch. // For example, ValueOf("hello").Int() panics because this attempts to // retrieve an int64 from a string. +// +// List, Map, and Message Values are called "composite" values. +// +// A composite Value may alias (reference) memory at some location, +// such that changes to the Value updates the that location. +// A composite value acquired with a Mutable method, such as Message.Mutable, +// always references the source object. +// +// For example: +// // Append a 0 to a "repeated int32" field. +// // Since the Value returned by Mutable is guaranteed to alias +// // the source message, modifying the Value modifies the message. +// message.Mutable(fieldDesc).(List).Append(protoreflect.ValueOfInt32(0)) +// +// // Assign [0] to a "repeated int32" field by creating a new Value, +// // modifying it, and assigning it. +// list := message.NewField(fieldDesc).(List) +// list.Append(protoreflect.ValueOfInt32(0)) +// message.Set(fieldDesc, list) +// // ERROR: Since it is not defined whether Set aliases the source, +// // appending to the List here may or may not modify the message. +// list.Append(protoreflect.ValueOfInt32(0)) +// +// Some operations, such as Message.Get, may return an "empty, read-only" +// composite Value. Modifying an empty, read-only value panics. type Value value // The protoreflect API uses a custom Value union type instead of interface{} diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go index c45debdc..702ddf22 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package protoreflect diff --git a/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go b/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go index 32c04f67..44cf467d 100644 --- a/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go +++ b/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go @@ -103,6 +103,7 @@ type UnmarshalInput = struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + Depth int } // UnmarshalOutput is output from the Unmarshal method. diff --git a/vendor/google.golang.org/protobuf/types/known/fieldmaskpb/field_mask.pb.go b/vendor/google.golang.org/protobuf/types/known/fieldmaskpb/field_mask.pb.go new file mode 100644 index 00000000..7f94443d --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/fieldmaskpb/field_mask.pb.go @@ -0,0 +1,591 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/field_mask.proto + +// Package fieldmaskpb contains generated types for google/protobuf/field_mask.proto. +// +// The FieldMask message represents a set of symbolic field paths. +// The paths are specific to some target message type, +// which is not stored within the FieldMask message itself. +// +// +// Constructing a FieldMask +// +// The New function is used construct a FieldMask: +// +// var messageType *descriptorpb.DescriptorProto +// fm, err := fieldmaskpb.New(messageType, "field.name", "field.number") +// if err != nil { +// ... // handle error +// } +// ... // make use of fm +// +// The "field.name" and "field.number" paths are valid paths according to the +// google.protobuf.DescriptorProto message. Use of a path that does not correlate +// to valid fields reachable from DescriptorProto would result in an error. +// +// Once a FieldMask message has been constructed, +// the Append method can be used to insert additional paths to the path set: +// +// var messageType *descriptorpb.DescriptorProto +// if err := fm.Append(messageType, "options"); err != nil { +// ... // handle error +// } +// +// +// Type checking a FieldMask +// +// In order to verify that a FieldMask represents a set of fields that are +// reachable from some target message type, use the IsValid method: +// +// var messageType *descriptorpb.DescriptorProto +// if fm.IsValid(messageType) { +// ... // make use of fm +// } +// +// IsValid needs to be passed the target message type as an input since the +// FieldMask message itself does not store the message type that the set of paths +// are for. +package fieldmaskpb + +import ( + proto "google.golang.org/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sort "sort" + strings "strings" + sync "sync" +) + +// `FieldMask` represents a set of symbolic field paths, for example: +// +// paths: "f.a" +// paths: "f.b.d" +// +// Here `f` represents a field in some root message, `a` and `b` +// fields in the message found in `f`, and `d` a field found in the +// message in `f.b`. +// +// Field masks are used to specify a subset of fields that should be +// returned by a get operation or modified by an update operation. +// Field masks also have a custom JSON encoding (see below). +// +// # Field Masks in Projections +// +// When used in the context of a projection, a response message or +// sub-message is filtered by the API to only contain those fields as +// specified in the mask. For example, if the mask in the previous +// example is applied to a response message as follows: +// +// f { +// a : 22 +// b { +// d : 1 +// x : 2 +// } +// y : 13 +// } +// z: 8 +// +// The result will not contain specific values for fields x,y and z +// (their value will be set to the default, and omitted in proto text +// output): +// +// +// f { +// a : 22 +// b { +// d : 1 +// } +// } +// +// A repeated field is not allowed except at the last position of a +// paths string. +// +// If a FieldMask object is not present in a get operation, the +// operation applies to all fields (as if a FieldMask of all fields +// had been specified). +// +// Note that a field mask does not necessarily apply to the +// top-level response message. In case of a REST get operation, the +// field mask applies directly to the response, but in case of a REST +// list operation, the mask instead applies to each individual message +// in the returned resource list. In case of a REST custom method, +// other definitions may be used. Where the mask applies will be +// clearly documented together with its declaration in the API. In +// any case, the effect on the returned resource/resources is required +// behavior for APIs. +// +// # Field Masks in Update Operations +// +// A field mask in update operations specifies which fields of the +// targeted resource are going to be updated. The API is required +// to only change the values of the fields as specified in the mask +// and leave the others untouched. If a resource is passed in to +// describe the updated values, the API ignores the values of all +// fields not covered by the mask. +// +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. +// +// If a sub-message is specified in the last position of the field mask for an +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: +// +// f { +// b { +// d: 1 +// x: 2 +// } +// c: [1] +// } +// +// And an update message: +// +// f { +// b { +// d: 10 +// } +// c: [2] +// } +// +// then if the field mask is: +// +// paths: ["f.b", "f.c"] +// +// then the result will be: +// +// f { +// b { +// d: 10 +// x: 2 +// } +// c: [1, 2] +// } +// +// An implementation may provide options to override this default behavior for +// repeated and message fields. +// +// In order to reset a field's value to the default, the field must +// be in the mask and set to the default value in the provided resource. +// Hence, in order to reset all fields of a resource, provide a default +// instance of the resource and set all fields in the mask, or do +// not provide a mask as described below. +// +// If a field mask is not present on update, the operation applies to +// all fields (as if a field mask of all fields has been specified). +// Note that in the presence of schema evolution, this may mean that +// fields the client does not know and has therefore not filled into +// the request will be reset to their default. If this is unwanted +// behavior, a specific service may require a client to always specify +// a field mask, producing an error if not. +// +// As with get operations, the location of the resource which +// describes the updated values in the request message depends on the +// operation kind. In any case, the effect of the field mask is +// required to be honored by the API. +// +// ## Considerations for HTTP REST +// +// The HTTP kind of an update operation which uses a field mask must +// be set to PATCH instead of PUT in order to satisfy HTTP semantics +// (PUT must only be used for full updates). +// +// # JSON Encoding of Field Masks +// +// In JSON, a field mask is encoded as a single string where paths are +// separated by a comma. Fields name in each path are converted +// to/from lower-camel naming conventions. +// +// As an example, consider the following message declarations: +// +// message Profile { +// User user = 1; +// Photo photo = 2; +// } +// message User { +// string display_name = 1; +// string address = 2; +// } +// +// In proto a field mask for `Profile` may look as such: +// +// mask { +// paths: "user.display_name" +// paths: "photo" +// } +// +// In JSON, the same mask is represented as below: +// +// { +// mask: "user.displayName,photo" +// } +// +// # Field Masks and Oneof Fields +// +// Field masks treat fields in oneofs just as regular fields. Consider the +// following message: +// +// message SampleMessage { +// oneof test_oneof { +// string name = 4; +// SubMessage sub_message = 9; +// } +// } +// +// The field mask can be: +// +// mask { +// paths: "name" +// } +// +// Or: +// +// mask { +// paths: "sub_message" +// } +// +// Note that oneof type names ("test_oneof" in this case) cannot be used in +// paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is unmappable. +type FieldMask struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of field mask paths. + Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` +} + +// New constructs a field mask from a list of paths and verifies that +// each one is valid according to the specified message type. +func New(m proto.Message, paths ...string) (*FieldMask, error) { + x := new(FieldMask) + return x, x.Append(m, paths...) +} + +// Union returns the union of all the paths in the input field masks. +func Union(mx *FieldMask, my *FieldMask, ms ...*FieldMask) *FieldMask { + var out []string + out = append(out, mx.GetPaths()...) + out = append(out, my.GetPaths()...) + for _, m := range ms { + out = append(out, m.GetPaths()...) + } + return &FieldMask{Paths: normalizePaths(out)} +} + +// Intersect returns the intersection of all the paths in the input field masks. +func Intersect(mx *FieldMask, my *FieldMask, ms ...*FieldMask) *FieldMask { + var ss1, ss2 []string // reused buffers for performance + intersect := func(out, in []string) []string { + ss1 = normalizePaths(append(ss1[:0], in...)) + ss2 = normalizePaths(append(ss2[:0], out...)) + out = out[:0] + for i1, i2 := 0, 0; i1 < len(ss1) && i2 < len(ss2); { + switch s1, s2 := ss1[i1], ss2[i2]; { + case hasPathPrefix(s1, s2): + out = append(out, s1) + i1++ + case hasPathPrefix(s2, s1): + out = append(out, s2) + i2++ + case lessPath(s1, s2): + i1++ + case lessPath(s2, s1): + i2++ + } + } + return out + } + + out := Union(mx, my, ms...).GetPaths() + out = intersect(out, mx.GetPaths()) + out = intersect(out, my.GetPaths()) + for _, m := range ms { + out = intersect(out, m.GetPaths()) + } + return &FieldMask{Paths: normalizePaths(out)} +} + +// IsValid reports whether all the paths are syntactically valid and +// refer to known fields in the specified message type. +// It reports false for a nil FieldMask. +func (x *FieldMask) IsValid(m proto.Message) bool { + paths := x.GetPaths() + return x != nil && numValidPaths(m, paths) == len(paths) +} + +// Append appends a list of paths to the mask and verifies that each one +// is valid according to the specified message type. +// An invalid path is not appended and breaks insertion of subsequent paths. +func (x *FieldMask) Append(m proto.Message, paths ...string) error { + numValid := numValidPaths(m, paths) + x.Paths = append(x.Paths, paths[:numValid]...) + paths = paths[numValid:] + if len(paths) > 0 { + name := m.ProtoReflect().Descriptor().FullName() + return protoimpl.X.NewError("invalid path %q for message %q", paths[0], name) + } + return nil +} + +func numValidPaths(m proto.Message, paths []string) int { + md0 := m.ProtoReflect().Descriptor() + for i, path := range paths { + md := md0 + if !rangeFields(path, func(field string) bool { + // Search the field within the message. + if md == nil { + return false // not within a message + } + fd := md.Fields().ByName(protoreflect.Name(field)) + // The real field name of a group is the message name. + if fd == nil { + gd := md.Fields().ByName(protoreflect.Name(strings.ToLower(field))) + if gd != nil && gd.Kind() == protoreflect.GroupKind && string(gd.Message().Name()) == field { + fd = gd + } + } else if fd.Kind() == protoreflect.GroupKind && string(fd.Message().Name()) != field { + fd = nil + } + if fd == nil { + return false // message has does not have this field + } + + // Identify the next message to search within. + md = fd.Message() // may be nil + + // Repeated fields are only allowed at the last postion. + if fd.IsList() || fd.IsMap() { + md = nil + } + + return true + }) { + return i + } + } + return len(paths) +} + +// Normalize converts the mask to its canonical form where all paths are sorted +// and redundant paths are removed. +func (x *FieldMask) Normalize() { + x.Paths = normalizePaths(x.Paths) +} + +func normalizePaths(paths []string) []string { + sort.Slice(paths, func(i, j int) bool { + return lessPath(paths[i], paths[j]) + }) + + // Elide any path that is a prefix match on the previous. + out := paths[:0] + for _, path := range paths { + if len(out) > 0 && hasPathPrefix(path, out[len(out)-1]) { + continue + } + out = append(out, path) + } + return out +} + +// hasPathPrefix is like strings.HasPrefix, but further checks for either +// an exact matche or that the prefix is delimited by a dot. +func hasPathPrefix(path, prefix string) bool { + return strings.HasPrefix(path, prefix) && (len(path) == len(prefix) || path[len(prefix)] == '.') +} + +// lessPath is a lexicographical comparison where dot is specially treated +// as the smallest symbol. +func lessPath(x, y string) bool { + for i := 0; i < len(x) && i < len(y); i++ { + if x[i] != y[i] { + return (x[i] - '.') < (y[i] - '.') + } + } + return len(x) < len(y) +} + +// rangeFields is like strings.Split(path, "."), but avoids allocations by +// iterating over each field in place and calling a iterator function. +func rangeFields(path string, f func(field string) bool) bool { + for { + var field string + if i := strings.IndexByte(path, '.'); i >= 0 { + field, path = path[:i], path[i:] + } else { + field, path = path, "" + } + + if !f(field) { + return false + } + + if len(path) == 0 { + return true + } + path = strings.TrimPrefix(path, ".") + } +} + +func (x *FieldMask) Reset() { + *x = FieldMask{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_field_mask_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldMask) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldMask) ProtoMessage() {} + +func (x *FieldMask) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_field_mask_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldMask.ProtoReflect.Descriptor instead. +func (*FieldMask) Descriptor() ([]byte, []int) { + return file_google_protobuf_field_mask_proto_rawDescGZIP(), []int{0} +} + +func (x *FieldMask) GetPaths() []string { + if x != nil { + return x.Paths + } + return nil +} + +var File_google_protobuf_field_mask_proto protoreflect.FileDescriptor + +var file_google_protobuf_field_mask_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x22, 0x21, 0x0a, 0x09, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, + 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x42, 0x85, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x0e, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x32, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, + 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x6d, 0x61, + 0x73, 0x6b, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1e, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x57, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_protobuf_field_mask_proto_rawDescOnce sync.Once + file_google_protobuf_field_mask_proto_rawDescData = file_google_protobuf_field_mask_proto_rawDesc +) + +func file_google_protobuf_field_mask_proto_rawDescGZIP() []byte { + file_google_protobuf_field_mask_proto_rawDescOnce.Do(func() { + file_google_protobuf_field_mask_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_field_mask_proto_rawDescData) + }) + return file_google_protobuf_field_mask_proto_rawDescData +} + +var file_google_protobuf_field_mask_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_google_protobuf_field_mask_proto_goTypes = []interface{}{ + (*FieldMask)(nil), // 0: google.protobuf.FieldMask +} +var file_google_protobuf_field_mask_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_google_protobuf_field_mask_proto_init() } +func file_google_protobuf_field_mask_proto_init() { + if File_google_protobuf_field_mask_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_protobuf_field_mask_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FieldMask); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_protobuf_field_mask_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_field_mask_proto_goTypes, + DependencyIndexes: file_google_protobuf_field_mask_proto_depIdxs, + MessageInfos: file_google_protobuf_field_mask_proto_msgTypes, + }.Build() + File_google_protobuf_field_mask_proto = out.File + file_google_protobuf_field_mask_proto_rawDesc = nil + file_google_protobuf_field_mask_proto_goTypes = nil + file_google_protobuf_field_mask_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go b/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go new file mode 100644 index 00000000..895a8049 --- /dev/null +++ b/vendor/google.golang.org/protobuf/types/known/wrapperspb/wrappers.pb.go @@ -0,0 +1,760 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// 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. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. +// +// These wrappers have no meaningful use within repeated fields as they lack +// the ability to detect presence on individual elements. +// These wrappers have no meaningful use within a map or a oneof since +// individual entries of a map or fields of a oneof can already detect presence. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/wrappers.proto + +package wrapperspb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +type DoubleValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The double value. + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Double stores v in a new DoubleValue and returns a pointer to it. +func Double(v float64) *DoubleValue { + return &DoubleValue{Value: v} +} + +func (x *DoubleValue) Reset() { + *x = DoubleValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DoubleValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleValue) ProtoMessage() {} + +func (x *DoubleValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleValue.ProtoReflect.Descriptor instead. +func (*DoubleValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{0} +} + +func (x *DoubleValue) GetValue() float64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +type FloatValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The float value. + Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Float stores v in a new FloatValue and returns a pointer to it. +func Float(v float32) *FloatValue { + return &FloatValue{Value: v} +} + +func (x *FloatValue) Reset() { + *x = FloatValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FloatValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FloatValue) ProtoMessage() {} + +func (x *FloatValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FloatValue.ProtoReflect.Descriptor instead. +func (*FloatValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{1} +} + +func (x *FloatValue) GetValue() float32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +type Int64Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The int64 value. + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Int64 stores v in a new Int64Value and returns a pointer to it. +func Int64(v int64) *Int64Value { + return &Int64Value{Value: v} +} + +func (x *Int64Value) Reset() { + *x = Int64Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Int64Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Int64Value) ProtoMessage() {} + +func (x *Int64Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Int64Value.ProtoReflect.Descriptor instead. +func (*Int64Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{2} +} + +func (x *Int64Value) GetValue() int64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +type UInt64Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The uint64 value. + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// UInt64 stores v in a new UInt64Value and returns a pointer to it. +func UInt64(v uint64) *UInt64Value { + return &UInt64Value{Value: v} +} + +func (x *UInt64Value) Reset() { + *x = UInt64Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UInt64Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UInt64Value) ProtoMessage() {} + +func (x *UInt64Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UInt64Value.ProtoReflect.Descriptor instead. +func (*UInt64Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{3} +} + +func (x *UInt64Value) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +type Int32Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The int32 value. + Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Int32 stores v in a new Int32Value and returns a pointer to it. +func Int32(v int32) *Int32Value { + return &Int32Value{Value: v} +} + +func (x *Int32Value) Reset() { + *x = Int32Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Int32Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Int32Value) ProtoMessage() {} + +func (x *Int32Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Int32Value.ProtoReflect.Descriptor instead. +func (*Int32Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{4} +} + +func (x *Int32Value) GetValue() int32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +type UInt32Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The uint32 value. + Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// UInt32 stores v in a new UInt32Value and returns a pointer to it. +func UInt32(v uint32) *UInt32Value { + return &UInt32Value{Value: v} +} + +func (x *UInt32Value) Reset() { + *x = UInt32Value{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UInt32Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UInt32Value) ProtoMessage() {} + +func (x *UInt32Value) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UInt32Value.ProtoReflect.Descriptor instead. +func (*UInt32Value) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{5} +} + +func (x *UInt32Value) GetValue() uint32 { + if x != nil { + return x.Value + } + return 0 +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +type BoolValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The bool value. + Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Bool stores v in a new BoolValue and returns a pointer to it. +func Bool(v bool) *BoolValue { + return &BoolValue{Value: v} +} + +func (x *BoolValue) Reset() { + *x = BoolValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BoolValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolValue) ProtoMessage() {} + +func (x *BoolValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolValue.ProtoReflect.Descriptor instead. +func (*BoolValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{6} +} + +func (x *BoolValue) GetValue() bool { + if x != nil { + return x.Value + } + return false +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +type StringValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The string value. + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// String stores v in a new StringValue and returns a pointer to it. +func String(v string) *StringValue { + return &StringValue{Value: v} +} + +func (x *StringValue) Reset() { + *x = StringValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringValue) ProtoMessage() {} + +func (x *StringValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringValue.ProtoReflect.Descriptor instead. +func (*StringValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{7} +} + +func (x *StringValue) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +type BytesValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The bytes value. + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +// Bytes stores v in a new BytesValue and returns a pointer to it. +func Bytes(v []byte) *BytesValue { + return &BytesValue{Value: v} +} + +func (x *BytesValue) Reset() { + *x = BytesValue{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_wrappers_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BytesValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BytesValue) ProtoMessage() {} + +func (x *BytesValue) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_wrappers_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BytesValue.ProtoReflect.Descriptor instead. +func (*BytesValue) Descriptor() ([]byte, []int) { + return file_google_protobuf_wrappers_proto_rawDescGZIP(), []int{8} +} + +func (x *BytesValue) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +var File_google_protobuf_wrappers_proto protoreflect.FileDescriptor + +var file_google_protobuf_wrappers_proto_rawDesc = []byte{ + 0x0a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x22, 0x23, 0x0a, 0x0b, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x22, 0x0a, 0x0a, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x22, 0x0a, 0x0a, 0x49, 0x6e, + 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, + 0x0a, 0x0b, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x22, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x55, 0x49, 0x6e, 0x74, 0x33, + 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, + 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x22, 0x22, 0x0a, 0x0a, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x83, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x42, 0x0d, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x31, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, + 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x72, 0x73, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, 0x02, 0x1e, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x57, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_protobuf_wrappers_proto_rawDescOnce sync.Once + file_google_protobuf_wrappers_proto_rawDescData = file_google_protobuf_wrappers_proto_rawDesc +) + +func file_google_protobuf_wrappers_proto_rawDescGZIP() []byte { + file_google_protobuf_wrappers_proto_rawDescOnce.Do(func() { + file_google_protobuf_wrappers_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_wrappers_proto_rawDescData) + }) + return file_google_protobuf_wrappers_proto_rawDescData +} + +var file_google_protobuf_wrappers_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_google_protobuf_wrappers_proto_goTypes = []interface{}{ + (*DoubleValue)(nil), // 0: google.protobuf.DoubleValue + (*FloatValue)(nil), // 1: google.protobuf.FloatValue + (*Int64Value)(nil), // 2: google.protobuf.Int64Value + (*UInt64Value)(nil), // 3: google.protobuf.UInt64Value + (*Int32Value)(nil), // 4: google.protobuf.Int32Value + (*UInt32Value)(nil), // 5: google.protobuf.UInt32Value + (*BoolValue)(nil), // 6: google.protobuf.BoolValue + (*StringValue)(nil), // 7: google.protobuf.StringValue + (*BytesValue)(nil), // 8: google.protobuf.BytesValue +} +var file_google_protobuf_wrappers_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_google_protobuf_wrappers_proto_init() } +func file_google_protobuf_wrappers_proto_init() { + if File_google_protobuf_wrappers_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_protobuf_wrappers_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DoubleValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FloatValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Int64Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UInt64Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Int32Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UInt32Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BoolValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_wrappers_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BytesValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_protobuf_wrappers_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_protobuf_wrappers_proto_goTypes, + DependencyIndexes: file_google_protobuf_wrappers_proto_depIdxs, + MessageInfos: file_google_protobuf_wrappers_proto_msgTypes, + }.Build() + File_google_protobuf_wrappers_proto = out.File + file_google_protobuf_wrappers_proto_rawDesc = nil + file_google_protobuf_wrappers_proto_goTypes = nil + file_google_protobuf_wrappers_proto_depIdxs = nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index b6fe6b65..7830b358 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -149,6 +149,13 @@ github.com/gdamore/tcell/terminfo/x/xterm_kitty # github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 ## explicit github.com/getsentry/raven-go +# github.com/go-logr/logr v1.2.3 +## explicit; go 1.16 +github.com/go-logr/logr +github.com/go-logr/logr/funcr +# github.com/go-logr/stdr v1.2.2 +## explicit; go 1.16 +github.com/go-logr/stdr # github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 ## explicit; go 1.13 github.com/go-task/slim-sprig @@ -184,6 +191,11 @@ github.com/gorilla/mux # github.com/gorilla/websocket v1.4.2 ## explicit; go 1.12 github.com/gorilla/websocket +# github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 +## explicit; go 1.14 +github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule +github.com/grpc-ecosystem/grpc-gateway/v2/runtime +github.com/grpc-ecosystem/grpc-gateway/v2/utilities # github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 ## explicit github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc @@ -325,7 +337,7 @@ github.com/rs/zerolog/log # github.com/russross/blackfriday/v2 v2.1.0 ## explicit github.com/russross/blackfriday/v2 -# github.com/stretchr/testify v1.7.0 +# github.com/stretchr/testify v1.7.1 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/require @@ -333,6 +345,40 @@ github.com/stretchr/testify/require ## explicit; go 1.11 github.com/urfave/cli/v2 github.com/urfave/cli/v2/altsrc +# go.opentelemetry.io/contrib/propagators v0.22.0 +## explicit; go 1.15 +go.opentelemetry.io/contrib/propagators/Jaeger +# go.opentelemetry.io/otel v1.6.3 +## explicit; go 1.16 +go.opentelemetry.io/otel +go.opentelemetry.io/otel/attribute +go.opentelemetry.io/otel/baggage +go.opentelemetry.io/otel/codes +go.opentelemetry.io/otel/internal +go.opentelemetry.io/otel/internal/baggage +go.opentelemetry.io/otel/internal/global +go.opentelemetry.io/otel/propagation +go.opentelemetry.io/otel/semconv/v1.7.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3 +## explicit; go 1.16 +go.opentelemetry.io/otel/exporters/otlp/otlptrace +go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform +# go.opentelemetry.io/otel/sdk v1.6.3 +## explicit; go 1.16 +go.opentelemetry.io/otel/sdk/instrumentation +go.opentelemetry.io/otel/sdk/internal +go.opentelemetry.io/otel/sdk/internal/env +go.opentelemetry.io/otel/sdk/resource +go.opentelemetry.io/otel/sdk/trace +# go.opentelemetry.io/otel/trace v1.6.3 +## explicit; go 1.16 +go.opentelemetry.io/otel/trace +# go.opentelemetry.io/proto/otlp v0.15.0 +## explicit; go 1.14 +go.opentelemetry.io/proto/otlp/collector/trace/v1 +go.opentelemetry.io/proto/otlp/common/v1 +go.opentelemetry.io/proto/otlp/resource/v1 +go.opentelemetry.io/proto/otlp/trace/v1 # go.uber.org/automaxprocs v1.4.0 ## explicit; go 1.13 go.uber.org/automaxprocs/internal/cgroups @@ -447,8 +493,10 @@ google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/urlfetch # google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb ## explicit; go 1.11 +google.golang.org/genproto/googleapis/api/httpbody google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.43.0 +google.golang.org/genproto/protobuf/field_mask +# google.golang.org/grpc v1.45.0 ## explicit; go 1.14 google.golang.org/grpc google.golang.org/grpc/attributes @@ -495,14 +543,16 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# google.golang.org/protobuf v1.27.1 -## explicit; go 1.9 +# google.golang.org/protobuf v1.28.0 +## explicit; go 1.11 +google.golang.org/protobuf/encoding/protojson google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire google.golang.org/protobuf/internal/descfmt google.golang.org/protobuf/internal/descopts google.golang.org/protobuf/internal/detrand google.golang.org/protobuf/internal/encoding/defval +google.golang.org/protobuf/internal/encoding/json google.golang.org/protobuf/internal/encoding/messageset google.golang.org/protobuf/internal/encoding/tag google.golang.org/protobuf/internal/encoding/text @@ -526,7 +576,9 @@ google.golang.org/protobuf/runtime/protoimpl google.golang.org/protobuf/types/descriptorpb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb +google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/timestamppb +google.golang.org/protobuf/types/known/wrapperspb # gopkg.in/coreos/go-oidc.v2 v2.2.1 ## explicit gopkg.in/coreos/go-oidc.v2 From 7a6ab54fcb733479ae328766d90af8beff7904da Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 11 Apr 2022 22:50:37 +0100 Subject: [PATCH 051/238] TUN-6043: Allow UI-managed Tunnels to fallback from QUIC but warn about that --- cmd/cloudflared/tunnel/configuration.go | 18 ++++++++++++++++-- supervisor/tunnel.go | 9 +++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 0226a87e..14fd430c 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -216,6 +216,7 @@ func prepareTunnelConfig( ) transportProtocol := c.String("protocol") + protocolFetcher := edgediscovery.ProtocolPercentage cfg := config.GetConfiguration() if isNamedTunnel { @@ -227,7 +228,20 @@ func prepareTunnelConfig( features := append(c.StringSlice("features"), supervisor.FeatureSerializedHeaders) if c.IsSet(TunnelTokenFlag) { if transportProtocol == connection.AutoSelectFlag { - transportProtocol = connection.QUIC.String() + protocolFetcher = func() (edgediscovery.ProtocolPercents, error) { + // If the Tunnel is remotely managed and no protocol is set, we prefer QUIC, but still allow fall-back. + preferQuic := []edgediscovery.ProtocolPercent{ + { + Protocol: connection.QUIC.String(), + Percentage: 100, + }, + { + Protocol: connection.HTTP2.String(), + Percentage: 100, + }, + } + return preferQuic, nil + } } features = append(features, supervisor.FeatureAllowRemoteConfig) log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API. Defaulting to protocol: quic") @@ -274,7 +288,7 @@ func prepareTunnelConfig( } warpRoutingEnabled := isWarpRoutingEnabled(cfg.WarpRouting, isNamedTunnel) - protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, edgediscovery.ProtocolPercentage, supervisor.ResolveTTL, log) + protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, protocolFetcher, supervisor.ResolveTTL, log) if err != nil { return nil, nil, err } diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index d6a588f4..8683c929 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -235,6 +235,15 @@ func selectNextProtocol( _, hasFallback := selector.Fallback() if protocolBackoff.ReachedMaxRetries() || (hasFallback && isNetworkActivityTimeout) { + if isNetworkActivityTimeout { + connLog.Warn().Msg("If this log occurs persistently, and cloudflared is unable to connect to " + + "Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " + + "UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " + + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips/\n" + + "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work" + + "unless your cloudflared can connect with Cloudflare Network with `quic`.") + } + fallback, hasFallback := selector.Fallback() if !hasFallback { return false From d727d3ade60cc4ed8838faf9cd1359f57bd0bb51 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 12 Apr 2022 15:00:51 +0100 Subject: [PATCH 052/238] Release 2022.4.1 --- RELEASE_NOTES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index a6a3d58b..87ba3c3a 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,10 @@ +2022.4.1 +- 2022-04-11 TUN-6035: Reduce buffer size when proxying data +- 2022-04-11 TUN-6038: Reduce buffer size used for proxying data +- 2022-04-11 TUN-6043: Allow UI-managed Tunnels to fallback from QUIC but warn about that +- 2022-04-07 TUN-6000 add version argument to bump-formula-pr +- 2022-04-06 TUN-5989: Add in-memory otlp exporter + 2022.4.0 - 2022-04-01 TUN-5973: Add backoff for non-recoverable errors as well - 2022-04-05 TUN-5992: Use QUIC protocol for remotely managed tunnels when protocol is unspecified From 8a07a900fde36efe48fd27ad5f974273015e8270 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 12 Apr 2022 17:38:13 +0100 Subject: [PATCH 053/238] TUN-6000: Another fix for publishing to brew core --- .teamcity/update-homebrew-core.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh index d8e976ff..bf2f8b79 100755 --- a/.teamcity/update-homebrew-core.sh +++ b/.teamcity/update-homebrew-core.sh @@ -19,5 +19,8 @@ eval "$(tmp/homebrew/bin/brew shellenv)" brew update --force --quiet chmod -R go-w "$(brew --prefix)/share/zsh" +git config user.name "cloudflare-warp-bot" +git config user.email "warp-bot@cloudflare.com" + # bump formula pr brew bump-formula-pr cloudflared --version="$VERSION" From f81b0ee9e85504861ee2b6d43e71f6174e1b6968 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 11 Apr 2022 12:57:50 -0700 Subject: [PATCH 054/238] TUN-5990: Add otlp span export to response header --- connection/http2.go | 6 ++++++ proxy/proxy.go | 15 +++++++++------ tracing/client.go | 5 +++-- tracing/tracing.go | 30 ++++++++++++++++++++++++++---- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/connection/http2.go b/connection/http2.go index 67c67862..715c827c 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -231,6 +231,12 @@ func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) erro dest[name] = values } + if h2name == tracing.IntCloudflaredTracingHeader { + // Add cf-int-cloudflared-tracing header outside of serialized userHeaders + rp.w.Header()[tracing.CanonicalCloudflaredTracingHeader] = values + continue + } + if !IsControlResponseHeader(h2name) || IsWebsocketClientHeader(h2name) { // User headers, on the other hand, must all be serialized so that // HTTP/2 header validation won't be applied to HTTP/1 header values diff --git a/proxy/proxy.go b/proxy/proxy.go index 76e11fe1..f24f0290 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -87,7 +87,7 @@ func (p *Proxy) ProxyHTTP( case ingress.HTTPOriginProxy: if err := p.proxyHTTPRequest( w, - req, + tr, originProxy, isWebsocket, rule.Config.DisableChunkedEncoding, @@ -159,15 +159,15 @@ func ruleField(ing ingress.Ingress, ruleNum int) (ruleID string, srv string) { // ProxyHTTPRequest proxies requests of underlying type http and websocket to the origin service. func (p *Proxy) proxyHTTPRequest( w connection.ResponseWriter, - req *http.Request, + tr *tracing.TracedRequest, httpService ingress.HTTPOriginProxy, isWebsocket bool, disableChunkedEncoding bool, fields logFields, ) error { - roundTripReq := req + roundTripReq := tr.Request if isWebsocket { - roundTripReq = req.Clone(req.Context()) + roundTripReq = tr.Request.Clone(tr.Request.Context()) roundTripReq.Header.Set("Connection", "Upgrade") roundTripReq.Header.Set("Upgrade", "websocket") roundTripReq.Header.Set("Sec-Websocket-Version", "13") @@ -177,7 +177,7 @@ func (p *Proxy) proxyHTTPRequest( // Support for WSGI Servers by switching transfer encoding from chunked to gzip/deflate if disableChunkedEncoding { roundTripReq.TransferEncoding = []string{"gzip", "deflate"} - cLength, err := strconv.Atoi(req.Header.Get("Content-Length")) + cLength, err := strconv.Atoi(tr.Request.Header.Get("Content-Length")) if err == nil { roundTripReq.ContentLength = int64(cLength) } @@ -197,6 +197,9 @@ func (p *Proxy) proxyHTTPRequest( } defer resp.Body.Close() + // Add spans to response header (if available) + tr.AddSpans(resp.Header, p.log) + err = w.WriteRespHeaders(resp.StatusCode, resp.Header) if err != nil { return errors.Wrap(err, "Error writing response header") @@ -211,7 +214,7 @@ func (p *Proxy) proxyHTTPRequest( eyeballStream := &bidirectionalStream{ writer: w, - reader: req.Body, + reader: tr.Request.Body, } websocket.Stream(eyeballStream, rwc, p.log) diff --git a/tracing/client.go b/tracing/client.go index 6783912e..5ed4efb3 100644 --- a/tracing/client.go +++ b/tracing/client.go @@ -16,7 +16,8 @@ const ( ) var ( - errNoTraces = errors.New("no traces recorded to be exported") + errNoTraces = errors.New("no traces recorded to be exported") + errNoopTracer = errors.New("noop tracer has no traces") ) type InMemoryClient interface { @@ -86,5 +87,5 @@ func (mc *NoopOtlpClient) UploadTraces(_ context.Context, _ []*tracepb.ResourceS // Spans always returns no traces error func (mc *NoopOtlpClient) Spans() (string, error) { - return "", errNoTraces + return "", errNoopTracer } diff --git a/tracing/tracing.go b/tracing/tracing.go index a35d2962..f124e367 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" + "github.com/rs/zerolog" otelContrib "go.opentelemetry.io/contrib/propagators/Jaeger" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -23,11 +24,14 @@ const ( tracerContextName = "cf-trace-id" tracerContextNameOverride = "uber-trace-id" + + IntCloudflaredTracingHeader = "cf-int-cloudflared-tracing" ) var ( - Http2TransportAttribute = trace.WithAttributes(TransportAttributeKey.String("http2")) - QuicTransportAttribute = trace.WithAttributes(TransportAttributeKey.String("quic")) + CanonicalCloudflaredTracingHeader = http.CanonicalHeaderKey(IntCloudflaredTracingHeader) + Http2TransportAttribute = trace.WithAttributes(TransportAttributeKey.String("http2")) + QuicTransportAttribute = trace.WithAttributes(TransportAttributeKey.String("quic")) TransportAttributeKey = attribute.Key("transport") TrafficAttributeKey = attribute.Key("traffic") @@ -75,8 +79,26 @@ func (cft *TracedRequest) Tracer() trace.Tracer { } // Spans returns the spans as base64 encoded protobuf otlp traces. -func (cft *TracedRequest) Spans() (string, error) { - return cft.exporter.Spans() +func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { + enc, err := cft.exporter.Spans() + switch err { + case nil: + break + case errNoTraces: + log.Error().Err(err).Msgf("expected traces to be available") + return + case errNoopTracer: + return // noop tracer has no traces + default: + log.Error().Err(err) + return + } + // No need to add header if no traces + if enc == "" { + log.Error().Msgf("no traces provided and no error from exporter") + return + } + headers[CanonicalCloudflaredTracingHeader] = []string{enc} } // EndWithStatus will set a status for the span and then end it. From e4278bab97706e0037738ac6e970b8c38705d39e Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 19 Apr 2022 12:37:32 +0100 Subject: [PATCH 055/238] TUN-6070: First connection retries other edge IPs if the error is quic timeout(likely due to firewall blocking UDP) --- supervisor/supervisor.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index f1661bf3..8f33e279 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -7,6 +7,7 @@ import ( "time" "github.com/google/uuid" + "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/connection" @@ -264,9 +265,9 @@ func (s *Supervisor) startFirstTunnel( switch err.(type) { case nil: return - // try the next address if it was a dialError(network problem) or + // try the next address if it was a quic.IdleTimeoutError, dialError(network problem) or // dupConnRegisterTunnelError - case edgediscovery.DialError, connection.DupConnRegisterTunnelError: + case *quic.IdleTimeoutError, edgediscovery.DialError, connection.DupConnRegisterTunnelError: edgeErrors++ default: return From 775c2bc93ee75e06f8e10b47586959ce85827dec Mon Sep 17 00:00:00 2001 From: Jasmit Tarang Date: Wed, 20 Apr 2022 15:28:49 -0500 Subject: [PATCH 056/238] VULN-8383 Bump yaml.v2 to yaml.v3 --- config/configuration.go | 4 ++-- go.mod | 2 +- go.sum | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/config/configuration.go b/config/configuration.go index c73a3a46..bb7a10b8 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -15,7 +15,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/urfave/cli/v2" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" "github.com/cloudflare/cloudflared/validation" ) @@ -392,7 +392,7 @@ func ReadConfigFile(c *cli.Context, log *zerolog.Logger) (settings *configFileSe // Parse it again, with strict mode, to find warnings. if file, err := os.Open(configFile); err == nil { decoder := yaml.NewDecoder(file) - decoder.SetStrict(true) + decoder.KnownFields(true) var unusedConfig configFileSettings if err := decoder.Decode(&unusedConfig); err != nil { warnings = err.Error() diff --git a/go.mod b/go.mod index 0e8a0932..356ce37a 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b zombiezen.com/go/capnproto2 v2.18.0+incompatible ) @@ -101,7 +102,6 @@ require ( google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.45.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d diff --git a/go.sum b/go.sum index d4065e58..9e39dfa6 100644 --- a/go.sum +++ b/go.sum @@ -104,6 +104,7 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -343,6 +344,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= @@ -522,6 +524,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= From a97233bb3e3abb695e76935037403f7559012e21 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 11 Apr 2022 16:02:13 -0700 Subject: [PATCH 057/238] TUN-6030: Add ttfb span for origin http request --- cmd/cloudflared/main.go | 2 ++ proxy/proxy.go | 10 ++++++-- tracing/client.go | 4 ++-- tracing/client_test.go | 4 ++-- tracing/tracing.go | 52 ++++++++++++++++++++++++++++++----------- tracing/tracing_test.go | 10 ++++---- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 889e1f64..07271eb4 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -21,6 +21,7 @@ import ( "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" "github.com/cloudflare/cloudflared/overwatch" + "github.com/cloudflare/cloudflared/tracing" "github.com/cloudflare/cloudflared/watcher" ) @@ -86,6 +87,7 @@ func main() { tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command... access.Init(graceShutdownC) updater.Init(Version) + tracing.Init(Version) runApp(app, graceShutdownC) } diff --git a/proxy/proxy.go b/proxy/proxy.go index f24f0290..bcdab8de 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -11,6 +11,8 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" "github.com/cloudflare/cloudflared/carrier" "github.com/cloudflare/cloudflared/cfio" @@ -72,7 +74,8 @@ func (p *Proxy) ProxyHTTP( lbProbe := connection.IsLBProbeRequest(req) p.appendTagHeaders(req) - _, ruleSpan := tr.Tracer().Start(req.Context(), "ingress_match") + _, ruleSpan := tr.Tracer().Start(req.Context(), "ingress_match", + trace.WithAttributes(attribute.String("req-host", req.Host))) rule, ruleNum := p.ingressRules.FindMatchingRule(req.Host, req.URL.Path) logFields := logFields{ cfRay: cfRay, @@ -167,7 +170,7 @@ func (p *Proxy) proxyHTTPRequest( ) error { roundTripReq := tr.Request if isWebsocket { - roundTripReq = tr.Request.Clone(tr.Request.Context()) + roundTripReq = tr.Clone(tr.Request.Context()) roundTripReq.Header.Set("Connection", "Upgrade") roundTripReq.Header.Set("Upgrade", "websocket") roundTripReq.Header.Set("Sec-Websocket-Version", "13") @@ -191,10 +194,13 @@ func (p *Proxy) proxyHTTPRequest( roundTripReq.Header.Set("User-Agent", "") } + _, ttfbSpan := tr.Tracer().Start(tr.Context(), "ttfb_origin") resp, err := httpService.RoundTrip(roundTripReq) if err != nil { + tracing.EndWithStatus(ttfbSpan, codes.Error, "") return errors.Wrap(err, "Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared") } + tracing.EndWithStatus(ttfbSpan, codes.Ok, resp.Status) defer resp.Body.Close() // Add spans to response header (if available) diff --git a/tracing/client.go b/tracing/client.go index 5ed4efb3..ffc477e1 100644 --- a/tracing/client.go +++ b/tracing/client.go @@ -12,7 +12,7 @@ import ( ) const ( - maxTraceAmount = 20 + MaxTraceAmount = 20 ) var ( @@ -46,7 +46,7 @@ func (mc *InMemoryOtlpClient) UploadTraces(_ context.Context, protoSpans []*trac defer mc.mu.Unlock() // Catch to make sure too many traces aren't being added to response header. // Returning nil makes sure we don't fail to send the traces we already recorded. - if len(mc.spans)+len(protoSpans) > maxTraceAmount { + if len(mc.spans)+len(protoSpans) > MaxTraceAmount { return nil } mc.spans = append(mc.spans, protoSpans...) diff --git a/tracing/client_test.go b/tracing/client_test.go index 946ee7f5..175c9112 100644 --- a/tracing/client_test.go +++ b/tracing/client_test.go @@ -108,12 +108,12 @@ func TestSpansNil(t *testing.T) { func TestSpansTooManySpans(t *testing.T) { client := &InMemoryOtlpClient{} - for i := 0; i < maxTraceAmount+1; i++ { + for i := 0; i < MaxTraceAmount+1; i++ { spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) err := client.UploadTraces(context.Background(), spans) assert.NoError(t, err) } - assert.Len(t, client.spans, maxTraceAmount) + assert.Len(t, client.spans, MaxTraceAmount) _, err := client.Spans() assert.NoError(t, err) } diff --git a/tracing/tracing.go b/tracing/tracing.go index f124e367..d3786603 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -3,7 +3,10 @@ package tracing import ( "context" "errors" + "fmt" "net/http" + "os" + "runtime" "github.com/rs/zerolog" otelContrib "go.opentelemetry.io/contrib/propagators/Jaeger" @@ -22,19 +25,26 @@ const ( service = "cloudflared" tracerInstrumentName = "origin" - tracerContextName = "cf-trace-id" - tracerContextNameOverride = "uber-trace-id" + TracerContextName = "cf-trace-id" + TracerContextNameOverride = "uber-trace-id" IntCloudflaredTracingHeader = "cf-int-cloudflared-tracing" ) var ( CanonicalCloudflaredTracingHeader = http.CanonicalHeaderKey(IntCloudflaredTracingHeader) - Http2TransportAttribute = trace.WithAttributes(TransportAttributeKey.String("http2")) - QuicTransportAttribute = trace.WithAttributes(TransportAttributeKey.String("quic")) + Http2TransportAttribute = trace.WithAttributes(transportAttributeKey.String("http2")) + QuicTransportAttribute = trace.WithAttributes(transportAttributeKey.String("quic")) + HostOSAttribute = semconv.HostTypeKey.String(runtime.GOOS) + HostArchAttribute = semconv.HostArchKey.String(runtime.GOARCH) - TransportAttributeKey = attribute.Key("transport") - TrafficAttributeKey = attribute.Key("traffic") + otelVersionAttribute attribute.KeyValue + hostnameAttribute attribute.KeyValue + cloudflaredVersionAttribute attribute.KeyValue + serviceAttribute = semconv.ServiceNameKey.String(service) + + transportAttributeKey = attribute.Key("transport") + otelVersionAttributeKey = attribute.Key("jaeger.version") errNoopTracerProvider = errors.New("noop tracer provider records no spans") ) @@ -42,6 +52,14 @@ var ( func init() { // Register the jaeger propagator globally. otel.SetTextMapPropagator(otelContrib.Jaeger{}) + otelVersionAttribute = otelVersionAttributeKey.String(fmt.Sprintf("go-otel-%s", otel.Version())) + if hostname, err := os.Hostname(); err == nil { + hostnameAttribute = attribute.String("hostname", hostname) + } +} + +func Init(version string) { + cloudflaredVersionAttribute = semconv.ProcessRuntimeVersionKey.String(version) } type TracedRequest struct { @@ -67,7 +85,12 @@ func NewTracedRequest(req *http.Request) *TracedRequest { // Record information about this application in a Resource. tracesdk.WithResource(resource.NewWithAttributes( semconv.SchemaURL, - semconv.ServiceNameKey.String(service), + serviceAttribute, + otelVersionAttribute, + hostnameAttribute, + cloudflaredVersionAttribute, + HostOSAttribute, + HostArchAttribute, )), ) @@ -110,27 +133,28 @@ func EndWithStatus(span trace.Span, code codes.Code, status string) { span.End() } -// extractTrace attempts to check for a cf-trace-id from a request header. +// extractTrace attempts to check for a cf-trace-id from a request and return the +// trace context with the provided http.Request. func extractTrace(req *http.Request) (context.Context, bool) { // Only add tracing for requests with appropriately tagged headers - remoteTraces := req.Header.Values(tracerContextName) + remoteTraces := req.Header.Values(TracerContextName) if len(remoteTraces) <= 0 { // Strip the cf-trace-id header - req.Header.Del(tracerContextName) + req.Header.Del(TracerContextName) return nil, false } - traceHeader := make(map[string]string, 1) + traceHeader := map[string]string{} for _, t := range remoteTraces { // Override the 'cf-trace-id' as 'uber-trace-id' so the jaeger propagator can extract it. // Last entry wins if multiple provided - traceHeader[tracerContextNameOverride] = t + traceHeader[TracerContextNameOverride] = t } // Strip the cf-trace-id header - req.Header.Del(tracerContextName) + req.Header.Del(TracerContextName) - if traceHeader[tracerContextNameOverride] == "" { + if traceHeader[TracerContextNameOverride] == "" { return nil, false } remoteCtx := otel.GetTextMapPropagator().Extract(req.Context(), propagation.MapCarrier(traceHeader)) diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go index 50dcb3ff..1bd23e89 100644 --- a/tracing/tracing_test.go +++ b/tracing/tracing_test.go @@ -12,7 +12,7 @@ import ( func TestNewCfTracer(t *testing.T) { req := httptest.NewRequest("GET", "http://localhost", nil) - req.Header.Add(tracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") + req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) @@ -21,8 +21,8 @@ func TestNewCfTracer(t *testing.T) { func TestNewCfTracerMultiple(t *testing.T) { req := httptest.NewRequest("GET", "http://localhost", nil) - req.Header.Add(tracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") - req.Header.Add(tracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") + req.Header.Add(TracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") + req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) @@ -31,7 +31,7 @@ func TestNewCfTracerMultiple(t *testing.T) { func TestNewCfTracerNilHeader(t *testing.T) { req := httptest.NewRequest("GET", "http://localhost", nil) - req.Header[http.CanonicalHeaderKey(tracerContextName)] = nil + req.Header[http.CanonicalHeaderKey(TracerContextName)] = nil tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) @@ -41,7 +41,7 @@ func TestNewCfTracerNilHeader(t *testing.T) { func TestNewCfTracerInvalidHeaders(t *testing.T) { req := httptest.NewRequest("GET", "http://localhost", nil) for _, test := range [][]string{nil, {""}} { - req.Header[http.CanonicalHeaderKey(tracerContextName)] = test + req.Header[http.CanonicalHeaderKey(TracerContextName)] = test tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) From 8f0498f66a9276361b630d1a199ecb83759d0aec Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 21 Apr 2022 10:52:19 +0100 Subject: [PATCH 058/238] TUN-6123: For a given connection with edge, close all datagram sessions through this connection when it's closed --- datagramsession/manager.go | 48 +++++++++++++++++++++++++-- datagramsession/manager_test.go | 58 ++++++++++++++++++++++++++------- datagramsession/session.go | 14 -------- datagramsession/session_test.go | 31 +++++++----------- 4 files changed, 103 insertions(+), 48 deletions(-) diff --git a/datagramsession/manager.go b/datagramsession/manager.go index c4144279..a691e3f9 100644 --- a/datagramsession/manager.go +++ b/datagramsession/manager.go @@ -2,6 +2,7 @@ package datagramsession import ( "context" + "fmt" "io" "time" @@ -16,6 +17,10 @@ const ( defaultReqTimeout = time.Second * 5 ) +var ( + errSessionManagerClosed = fmt.Errorf("session manager closed") +) + // Manager defines the APIs to manage sessions from the same transport. type Manager interface { // Serve starts the event loop @@ -30,6 +35,7 @@ type manager struct { registrationChan chan *registerSessionEvent unregistrationChan chan *unregisterSessionEvent datagramChan chan *newDatagram + closedChan chan struct{} transport transport sessions map[uuid.UUID]*Session log *zerolog.Logger @@ -43,6 +49,7 @@ func NewManager(transport transport, log *zerolog.Logger) *manager { unregistrationChan: make(chan *unregisterSessionEvent), // datagramChan is buffered, so it can read more datagrams from transport while the event loop is processing other events datagramChan: make(chan *newDatagram, requestChanCapacity), + closedChan: make(chan struct{}), transport: transport, sessions: make(map[uuid.UUID]*Session), log: log, @@ -90,7 +97,24 @@ func (m *manager) Serve(ctx context.Context) error { } } }) - return errGroup.Wait() + err := errGroup.Wait() + close(m.closedChan) + m.shutdownSessions(err) + return err +} + +func (m *manager) shutdownSessions(err error) { + if err == nil { + err = errSessionManagerClosed + } + closeSessionErr := &errClosedSession{ + message: err.Error(), + // Usually connection with remote has been closed, so set this to true to skip unregistering from remote + byRemote: true, + } + for _, s := range m.sessions { + s.close(closeSessionErr) + } } func (m *manager) RegisterSession(ctx context.Context, sessionID uuid.UUID, originProxy io.ReadWriteCloser) (*Session, error) { @@ -104,15 +128,33 @@ func (m *manager) RegisterSession(ctx context.Context, sessionID uuid.UUID, orig case m.registrationChan <- event: session := <-event.resultChan return session, nil + // Once closedChan is closed, manager won't accept more registration because nothing is + // reading from registrationChan and it's an unbuffered channel + case <-m.closedChan: + return nil, errSessionManagerClosed } } func (m *manager) registerSession(ctx context.Context, registration *registerSessionEvent) { - session := newSession(registration.sessionID, m.transport, registration.originProxy, m.log) + session := m.newSession(registration.sessionID, registration.originProxy) m.sessions[registration.sessionID] = session registration.resultChan <- session } +func (m *manager) newSession(id uuid.UUID, dstConn io.ReadWriteCloser) *Session { + return &Session{ + ID: id, + transport: m.transport, + dstConn: dstConn, + // activeAtChan has low capacity. It can be full when there are many concurrent read/write. markActive() will + // drop instead of blocking because last active time only needs to be an approximation + activeAtChan: make(chan time.Time, 2), + // capacity is 2 because close() and dstToTransport routine in Serve() can write to this channel + closeChan: make(chan error, 2), + log: m.log, + } +} + func (m *manager) UnregisterSession(ctx context.Context, sessionID uuid.UUID, message string, byRemote bool) error { ctx, cancel := context.WithTimeout(ctx, m.timeout) defer cancel() @@ -129,6 +171,8 @@ func (m *manager) UnregisterSession(ctx context.Context, sessionID uuid.UUID, me return ctx.Err() case m.unregistrationChan <- event: return nil + case <-m.closedChan: + return errSessionManagerClosed } } diff --git a/datagramsession/manager_test.go b/datagramsession/manager_test.go index 823c55bb..a9361624 100644 --- a/datagramsession/manager_test.go +++ b/datagramsession/manager_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "sync" "testing" "time" @@ -21,12 +22,8 @@ func TestManagerServe(t *testing.T) { msgs = 50 remoteUnregisterMsg = "eyeball closed connection" ) - log := zerolog.Nop() - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(1), - respChan: newDatagramChannel(1), - } - mg := NewManager(transport, &log) + + mg, transport := newTestManager(1) eyeballTracker := make(map[uuid.UUID]*datagramChannel) for i := 0; i < sessions; i++ { @@ -124,12 +121,8 @@ func TestTimeout(t *testing.T) { const ( testTimeout = time.Millisecond * 50 ) - log := zerolog.Nop() - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(1), - respChan: newDatagramChannel(1), - } - mg := NewManager(transport, &log) + + mg, _ := newTestManager(1) mg.timeout = testTimeout ctx := context.Background() sessionID := uuid.New() @@ -142,6 +135,47 @@ func TestTimeout(t *testing.T) { require.ErrorIs(t, err, context.DeadlineExceeded) } +func TestCloseTransportCloseSessions(t *testing.T) { + mg, transport := newTestManager(1) + ctx := context.Background() + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err := mg.Serve(ctx) + require.Error(t, err) + }() + + cfdConn, eyeballConn := net.Pipe() + session, err := mg.RegisterSession(ctx, uuid.New(), cfdConn) + require.NoError(t, err) + require.NotNil(t, session) + + wg.Add(1) + go func() { + defer wg.Done() + _, err := eyeballConn.Write([]byte(t.Name())) + require.NoError(t, err) + transport.close() + }() + + closedByRemote, err := session.Serve(ctx, time.Minute) + require.True(t, closedByRemote) + require.Error(t, err) + + wg.Wait() +} + +func newTestManager(capacity uint) (*manager, *mockQUICTransport) { + log := zerolog.Nop() + transport := &mockQUICTransport{ + reqChan: newDatagramChannel(capacity), + respChan: newDatagramChannel(capacity), + } + return NewManager(transport, &log), transport +} + type mockOrigin struct { expectMsgCount int expectedMsg []byte diff --git a/datagramsession/session.go b/datagramsession/session.go index 0a1199e8..351ae298 100644 --- a/datagramsession/session.go +++ b/datagramsession/session.go @@ -39,20 +39,6 @@ type Session struct { log *zerolog.Logger } -func newSession(id uuid.UUID, transport transport, dstConn io.ReadWriteCloser, log *zerolog.Logger) *Session { - return &Session{ - ID: id, - transport: transport, - dstConn: dstConn, - // activeAtChan has low capacity. It can be full when there are many concurrent read/write. markActive() will - // drop instead of blocking because last active time only needs to be an approximation - activeAtChan: make(chan time.Time, 2), - // capacity is 2 because close() and dstToTransport routine in Serve() can write to this channel - closeChan: make(chan error, 2), - log: log, - } -} - func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (closedByRemote bool, err error) { go func() { // QUIC implementation copies data to another buffer before returning https://github.com/lucas-clemente/quic-go/blob/v0.24.0/session.go#L1967-L1975 diff --git a/datagramsession/session_test.go b/datagramsession/session_test.go index bf1b1e20..db4201f6 100644 --- a/datagramsession/session_test.go +++ b/datagramsession/session_test.go @@ -11,7 +11,6 @@ import ( "time" "github.com/google/uuid" - "github.com/rs/zerolog" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -41,12 +40,9 @@ func testSessionReturns(t *testing.T, closeBy closeMethod, closeAfterIdle time.D sessionID := uuid.New() cfdConn, originConn := net.Pipe() payload := testPayload(sessionID) - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(1), - respChan: newDatagramChannel(1), - } - log := zerolog.Nop() - session := newSession(sessionID, transport, cfdConn, &log) + + mg, _ := newTestManager(1) + session := mg.newSession(sessionID, cfdConn) ctx, cancel := context.WithCancel(context.Background()) sessionDone := make(chan struct{}) @@ -117,12 +113,9 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool) sessionID := uuid.New() cfdConn, originConn := net.Pipe() payload := testPayload(sessionID) - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(100), - respChan: newDatagramChannel(100), - } - log := zerolog.Nop() - session := newSession(sessionID, transport, cfdConn, &log) + + mg, _ := newTestManager(100) + session := mg.newSession(sessionID, cfdConn) startTime := time.Now() activeUntil := startTime.Add(activeTime) @@ -184,7 +177,8 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool) func TestMarkActiveNotBlocking(t *testing.T) { const concurrentCalls = 50 - session := newSession(uuid.New(), nil, nil, nil) + mg, _ := newTestManager(1) + session := mg.newSession(uuid.New(), nil) var wg sync.WaitGroup wg.Add(concurrentCalls) for i := 0; i < concurrentCalls; i++ { @@ -199,12 +193,9 @@ func TestMarkActiveNotBlocking(t *testing.T) { func TestZeroBytePayload(t *testing.T) { sessionID := uuid.New() cfdConn, originConn := net.Pipe() - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(1), - respChan: newDatagramChannel(1), - } - log := zerolog.Nop() - session := newSession(sessionID, transport, cfdConn, &log) + + mg, transport := newTestManager(1) + session := mg.newSession(sessionID, cfdConn) ctx, cancel := context.WithCancel(context.Background()) errGroup, ctx := errgroup.WithContext(ctx) From d22cb4a6ca1e1cae4112f2e519c1cbf577c8fee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 20 Apr 2022 17:52:55 +0100 Subject: [PATCH 059/238] TUN-6015: Add RPC method for pushing local config --- tunnelrpc/pogs/connectionrpc.go | 12 + tunnelrpc/pogs/connectionrpc_test.go | 5 + tunnelrpc/tunnelrpc.capnp | 1 + tunnelrpc/tunnelrpc.capnp.go | 656 ++++++++++++++++++--------- 4 files changed, 454 insertions(+), 220 deletions(-) diff --git a/tunnelrpc/pogs/connectionrpc.go b/tunnelrpc/pogs/connectionrpc.go index 997a9bc3..788a67a8 100644 --- a/tunnelrpc/pogs/connectionrpc.go +++ b/tunnelrpc/pogs/connectionrpc.go @@ -18,6 +18,7 @@ import ( type RegistrationServer interface { RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) UnregisterConnection(ctx context.Context) + UpdateLocalConfiguration(ctx context.Context, config []byte) error } type RegistrationServer_PogsImpl struct { @@ -88,6 +89,17 @@ func (i RegistrationServer_PogsImpl) UnregisterConnection(p tunnelrpc.Registrati return nil } +func (i RegistrationServer_PogsImpl) UpdateLocalConfiguration(c tunnelrpc.RegistrationServer_updateLocalConfiguration) error { + server.Ack(c.Options) + + configBytes, err := c.Params.Config() + if err != nil { + return err + } + + return i.impl.UpdateLocalConfiguration(c.Ctx, configBytes) +} + type RegistrationServer_PogsClient struct { Client capnp.Client Conn *rpc.Conn diff --git a/tunnelrpc/pogs/connectionrpc_test.go b/tunnelrpc/pogs/connectionrpc_test.go index 99a68ab4..c9ec77ca 100644 --- a/tunnelrpc/pogs/connectionrpc_test.go +++ b/tunnelrpc/pogs/connectionrpc_test.go @@ -129,6 +129,11 @@ type testConnectionRegistrationServer struct { err error } +func (t *testConnectionRegistrationServer) UpdateLocalConfiguration(ctx context.Context, config []byte) error { + // do nothing at this point + return nil +} + func (t *testConnectionRegistrationServer) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) { if auth.AccountTag != testAccountTag { panic("bad account tag: " + auth.AccountTag) diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 8d91f6d1..c1d56d6e 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -131,6 +131,7 @@ struct TunnelAuth { interface RegistrationServer { registerConnection @0 (auth :TunnelAuth, tunnelId :Data, connIndex :UInt8, options :ConnectionOptions) -> (result :ConnectionResponse); unregisterConnection @1 () -> (); + updateLocalConfiguration @2 (config :Data) -> (); } interface TunnelServer extends (RegistrationServer) { diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index ac1e152a..4d1af72f 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -1621,11 +1621,35 @@ func (c RegistrationServer) UnregisterConnection(ctx context.Context, params fun } return RegistrationServer_unregisterConnection_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} } +func (c RegistrationServer) UpdateLocalConfiguration(ctx context.Context, params func(RegistrationServer_updateLocalConfiguration_Params) error, opts ...capnp.CallOption) RegistrationServer_updateLocalConfiguration_Results_Promise { + if c.Client == nil { + return RegistrationServer_updateLocalConfiguration_Results_Promise{Pipeline: capnp.NewPipeline(capnp.ErrorAnswer(capnp.ErrNullClient))} + } + call := &capnp.Call{ + Ctx: ctx, + Method: capnp.Method{ + InterfaceID: 0xf71695ec7fe85497, + MethodID: 2, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:RegistrationServer", + MethodName: "updateLocalConfiguration", + }, + Options: capnp.NewCallOptions(opts), + } + if params != nil { + call.ParamsSize = capnp.ObjectSize{DataSize: 0, PointerCount: 1} + call.ParamsFunc = func(s capnp.Struct) error { + return params(RegistrationServer_updateLocalConfiguration_Params{Struct: s}) + } + } + return RegistrationServer_updateLocalConfiguration_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} +} type RegistrationServer_Server interface { RegisterConnection(RegistrationServer_registerConnection) error UnregisterConnection(RegistrationServer_unregisterConnection) error + + UpdateLocalConfiguration(RegistrationServer_updateLocalConfiguration) error } func RegistrationServer_ServerToClient(s RegistrationServer_Server) RegistrationServer { @@ -1635,7 +1659,7 @@ func RegistrationServer_ServerToClient(s RegistrationServer_Server) Registration func RegistrationServer_Methods(methods []server.Method, s RegistrationServer_Server) []server.Method { if cap(methods) == 0 { - methods = make([]server.Method, 0, 2) + methods = make([]server.Method, 0, 3) } methods = append(methods, server.Method{ @@ -1666,6 +1690,20 @@ func RegistrationServer_Methods(methods []server.Method, s RegistrationServer_Se ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 0}, }) + methods = append(methods, server.Method{ + Method: capnp.Method{ + InterfaceID: 0xf71695ec7fe85497, + MethodID: 2, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:RegistrationServer", + MethodName: "updateLocalConfiguration", + }, + Impl: func(c context.Context, opts capnp.CallOptions, p, r capnp.Struct) error { + call := RegistrationServer_updateLocalConfiguration{c, opts, RegistrationServer_updateLocalConfiguration_Params{Struct: p}, RegistrationServer_updateLocalConfiguration_Results{Struct: r}} + return s.UpdateLocalConfiguration(call) + }, + ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 0}, + }) + return methods } @@ -1685,6 +1723,14 @@ type RegistrationServer_unregisterConnection struct { Results RegistrationServer_unregisterConnection_Results } +// RegistrationServer_updateLocalConfiguration holds the arguments for a server call to RegistrationServer.updateLocalConfiguration. +type RegistrationServer_updateLocalConfiguration struct { + Ctx context.Context + Options capnp.CallOptions + Params RegistrationServer_updateLocalConfiguration_Params + Results RegistrationServer_updateLocalConfiguration_Results +} + type RegistrationServer_registerConnection_Params struct{ capnp.Struct } // RegistrationServer_registerConnection_Params_TypeID is the unique identifier for the type RegistrationServer_registerConnection_Params. @@ -2014,6 +2060,130 @@ func (p RegistrationServer_unregisterConnection_Results_Promise) Struct() (Regis return RegistrationServer_unregisterConnection_Results{s}, err } +type RegistrationServer_updateLocalConfiguration_Params struct{ capnp.Struct } + +// RegistrationServer_updateLocalConfiguration_Params_TypeID is the unique identifier for the type RegistrationServer_updateLocalConfiguration_Params. +const RegistrationServer_updateLocalConfiguration_Params_TypeID = 0xc5d6e311876a3604 + +func NewRegistrationServer_updateLocalConfiguration_Params(s *capnp.Segment) (RegistrationServer_updateLocalConfiguration_Params, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return RegistrationServer_updateLocalConfiguration_Params{st}, err +} + +func NewRootRegistrationServer_updateLocalConfiguration_Params(s *capnp.Segment) (RegistrationServer_updateLocalConfiguration_Params, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return RegistrationServer_updateLocalConfiguration_Params{st}, err +} + +func ReadRootRegistrationServer_updateLocalConfiguration_Params(msg *capnp.Message) (RegistrationServer_updateLocalConfiguration_Params, error) { + root, err := msg.RootPtr() + return RegistrationServer_updateLocalConfiguration_Params{root.Struct()}, err +} + +func (s RegistrationServer_updateLocalConfiguration_Params) String() string { + str, _ := text.Marshal(0xc5d6e311876a3604, s.Struct) + return str +} + +func (s RegistrationServer_updateLocalConfiguration_Params) Config() ([]byte, error) { + p, err := s.Struct.Ptr(0) + return []byte(p.Data()), err +} + +func (s RegistrationServer_updateLocalConfiguration_Params) HasConfig() bool { + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s RegistrationServer_updateLocalConfiguration_Params) SetConfig(v []byte) error { + return s.Struct.SetData(0, v) +} + +// RegistrationServer_updateLocalConfiguration_Params_List is a list of RegistrationServer_updateLocalConfiguration_Params. +type RegistrationServer_updateLocalConfiguration_Params_List struct{ capnp.List } + +// NewRegistrationServer_updateLocalConfiguration_Params creates a new list of RegistrationServer_updateLocalConfiguration_Params. +func NewRegistrationServer_updateLocalConfiguration_Params_List(s *capnp.Segment, sz int32) (RegistrationServer_updateLocalConfiguration_Params_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) + return RegistrationServer_updateLocalConfiguration_Params_List{l}, err +} + +func (s RegistrationServer_updateLocalConfiguration_Params_List) At(i int) RegistrationServer_updateLocalConfiguration_Params { + return RegistrationServer_updateLocalConfiguration_Params{s.List.Struct(i)} +} + +func (s RegistrationServer_updateLocalConfiguration_Params_List) Set(i int, v RegistrationServer_updateLocalConfiguration_Params) error { + return s.List.SetStruct(i, v.Struct) +} + +func (s RegistrationServer_updateLocalConfiguration_Params_List) String() string { + str, _ := text.MarshalList(0xc5d6e311876a3604, s.List) + return str +} + +// RegistrationServer_updateLocalConfiguration_Params_Promise is a wrapper for a RegistrationServer_updateLocalConfiguration_Params promised by a client call. +type RegistrationServer_updateLocalConfiguration_Params_Promise struct{ *capnp.Pipeline } + +func (p RegistrationServer_updateLocalConfiguration_Params_Promise) Struct() (RegistrationServer_updateLocalConfiguration_Params, error) { + s, err := p.Pipeline.Struct() + return RegistrationServer_updateLocalConfiguration_Params{s}, err +} + +type RegistrationServer_updateLocalConfiguration_Results struct{ capnp.Struct } + +// RegistrationServer_updateLocalConfiguration_Results_TypeID is the unique identifier for the type RegistrationServer_updateLocalConfiguration_Results. +const RegistrationServer_updateLocalConfiguration_Results_TypeID = 0xe5ceae5d6897d7be + +func NewRegistrationServer_updateLocalConfiguration_Results(s *capnp.Segment) (RegistrationServer_updateLocalConfiguration_Results, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0}) + return RegistrationServer_updateLocalConfiguration_Results{st}, err +} + +func NewRootRegistrationServer_updateLocalConfiguration_Results(s *capnp.Segment) (RegistrationServer_updateLocalConfiguration_Results, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0}) + return RegistrationServer_updateLocalConfiguration_Results{st}, err +} + +func ReadRootRegistrationServer_updateLocalConfiguration_Results(msg *capnp.Message) (RegistrationServer_updateLocalConfiguration_Results, error) { + root, err := msg.RootPtr() + return RegistrationServer_updateLocalConfiguration_Results{root.Struct()}, err +} + +func (s RegistrationServer_updateLocalConfiguration_Results) String() string { + str, _ := text.Marshal(0xe5ceae5d6897d7be, s.Struct) + return str +} + +// RegistrationServer_updateLocalConfiguration_Results_List is a list of RegistrationServer_updateLocalConfiguration_Results. +type RegistrationServer_updateLocalConfiguration_Results_List struct{ capnp.List } + +// NewRegistrationServer_updateLocalConfiguration_Results creates a new list of RegistrationServer_updateLocalConfiguration_Results. +func NewRegistrationServer_updateLocalConfiguration_Results_List(s *capnp.Segment, sz int32) (RegistrationServer_updateLocalConfiguration_Results_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 0}, sz) + return RegistrationServer_updateLocalConfiguration_Results_List{l}, err +} + +func (s RegistrationServer_updateLocalConfiguration_Results_List) At(i int) RegistrationServer_updateLocalConfiguration_Results { + return RegistrationServer_updateLocalConfiguration_Results{s.List.Struct(i)} +} + +func (s RegistrationServer_updateLocalConfiguration_Results_List) Set(i int, v RegistrationServer_updateLocalConfiguration_Results) error { + return s.List.SetStruct(i, v.Struct) +} + +func (s RegistrationServer_updateLocalConfiguration_Results_List) String() string { + str, _ := text.MarshalList(0xe5ceae5d6897d7be, s.List) + return str +} + +// RegistrationServer_updateLocalConfiguration_Results_Promise is a wrapper for a RegistrationServer_updateLocalConfiguration_Results promised by a client call. +type RegistrationServer_updateLocalConfiguration_Results_Promise struct{ *capnp.Pipeline } + +func (p RegistrationServer_updateLocalConfiguration_Results_Promise) Struct() (RegistrationServer_updateLocalConfiguration_Results, error) { + s, err := p.Pipeline.Struct() + return RegistrationServer_updateLocalConfiguration_Results{s}, err +} + type TunnelServer struct{ Client capnp.Client } // TunnelServer_TypeID is the unique identifier for the type TunnelServer. @@ -2181,6 +2351,28 @@ func (c TunnelServer) UnregisterConnection(ctx context.Context, params func(Regi } return RegistrationServer_unregisterConnection_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} } +func (c TunnelServer) UpdateLocalConfiguration(ctx context.Context, params func(RegistrationServer_updateLocalConfiguration_Params) error, opts ...capnp.CallOption) RegistrationServer_updateLocalConfiguration_Results_Promise { + if c.Client == nil { + return RegistrationServer_updateLocalConfiguration_Results_Promise{Pipeline: capnp.NewPipeline(capnp.ErrorAnswer(capnp.ErrNullClient))} + } + call := &capnp.Call{ + Ctx: ctx, + Method: capnp.Method{ + InterfaceID: 0xf71695ec7fe85497, + MethodID: 2, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:RegistrationServer", + MethodName: "updateLocalConfiguration", + }, + Options: capnp.NewCallOptions(opts), + } + if params != nil { + call.ParamsSize = capnp.ObjectSize{DataSize: 0, PointerCount: 1} + call.ParamsFunc = func(s capnp.Struct) error { + return params(RegistrationServer_updateLocalConfiguration_Params{Struct: s}) + } + } + return RegistrationServer_updateLocalConfiguration_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} +} type TunnelServer_Server interface { RegisterTunnel(TunnelServer_registerTunnel) error @@ -2198,6 +2390,8 @@ type TunnelServer_Server interface { RegisterConnection(RegistrationServer_registerConnection) error UnregisterConnection(RegistrationServer_unregisterConnection) error + + UpdateLocalConfiguration(RegistrationServer_updateLocalConfiguration) error } func TunnelServer_ServerToClient(s TunnelServer_Server) TunnelServer { @@ -2207,7 +2401,7 @@ func TunnelServer_ServerToClient(s TunnelServer_Server) TunnelServer { func TunnelServer_Methods(methods []server.Method, s TunnelServer_Server) []server.Method { if cap(methods) == 0 { - methods = make([]server.Method, 0, 8) + methods = make([]server.Method, 0, 9) } methods = append(methods, server.Method{ @@ -2322,6 +2516,20 @@ func TunnelServer_Methods(methods []server.Method, s TunnelServer_Server) []serv ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 0}, }) + methods = append(methods, server.Method{ + Method: capnp.Method{ + InterfaceID: 0xf71695ec7fe85497, + MethodID: 2, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:RegistrationServer", + MethodName: "updateLocalConfiguration", + }, + Impl: func(c context.Context, opts capnp.CallOptions, p, r capnp.Struct) error { + call := RegistrationServer_updateLocalConfiguration{c, opts, RegistrationServer_updateLocalConfiguration_Params{Struct: p}, RegistrationServer_updateLocalConfiguration_Results{Struct: r}} + return s.UpdateLocalConfiguration(call) + }, + ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 0}, + }) + return methods } @@ -4317,224 +4525,230 @@ func CloudflaredServer_Methods(methods []server.Method, s CloudflaredServer_Serv return methods } -const schema_db8274f9144abc7e = "x\xda\xccZ{t\x14\xe7u\xbfwfW#\x81V" + - "\xab\xf1\xac\xd1\x03T\xb5:P\x179\xd8\x06Jk\xab" + - "9\xd1\xc3\x12\xb1d\x03\x9a]\x94\xe3cC\x8eG\xbb" + - "\x9f\xa4Qwg\x96\x99Y\x19\x11\x130\x01c\xfb\xb8" + - "\x8eq\xc0\xb1Ih0.\xed\x01\xdb\xad\x89\xdd\xa6\xee" + - "1\xa7\xa6\xcd\xabq\xc0&\x87\xf4\x90@\x9a&\x84>" + - "8\xb8\xae14\x876\xf1\xf4\xdc\x99\x9d\x87v\x17\x09" + - "\x8c\xff\xc8\x7f\xab;\xdf\xe3\xde\xdf\xf7\xbb\x8f\xef~\xba" + - "\xed\xe6\x9a.nq\xf4\xed:\x00\xf9\xa5h\x95\xcd\xda" + - "\x7f\xb0a\xef\x82\x7f\xdc\x02r3\xa2\xfd\xf97\x06\x12" + - "\x97\xad-\xa7 \xca\x0b\x00KW\x08\x1bPR\x04\x01" + - "@Z+\xfc;\xa0\xfd\xc8\x9cW\xbe\xb6\xbfo\xe7\x17" + - "@l\xe6\x83\xc1\x80K\xbb\xab\x07P\x1a\xaa\xa6\x91r" + - "\xf5v\xe9\x10\xfd\xb2\xef\x16o\xbd?\xf1\xce1\x1a\x1d" + - "^:BK?W\xdd\x8e\xd2\x01g\xc2\xfejZ\xfa" + - "\x93\xb9\xb7\xf7\xfd\xc1\xae\xb7\xb6\x82\xd8\xccMYzG" + - "\xcd\x06\x94\xf6\xd7\xd0\xc8\xe7kV\x01\xda\x1f\xecl|" + - "\xf1\xf9c\xdf\xdd\x06\xe2M\x08EM_\xaf\xf91\x02" + - "JGk\xfe\x0a\xd0>z\xe9\xfe\x8b\xaf}{\xd9#" + - " .\xa4\x01H\x03r\xb3\xda8@i\xdb\xacN@" + - "\xfb\xdc\xf9\xff\xdb\xfe\xb9\x85+\x9f\x02y!r\x00Q" + - "\x8eF\xec\x9f\xd5L#\x0e\xcf\"m:\x97\x1f}\xbd" + - "y\xe93;KTw\x06\xee\x99\xdd\x8e\xd2\xcb\xb3I" + - "\xa1\x03\xb3\x1f\x04\xb4?\xf5\x87\xaf>\xd9\xfb\xcc\xe6]" + - " \xde\xea\xef\x17\xab\xbd\x8fV[XK\xfb\xfdO\xdd" + - "W\x8e\x15\xee\xfc\xc63E\x85\x9cU\xfak\xdbi\x80" + - "RK+\xb4M,x\xe0\x1f\xbe\xf5\xea\x97A^\x84" + - "h\x9f\x1e\xbe\xf9\x87\xfc\x9e\x83\xa7`\x08\x05\xd2o\xe9" + - "\x91\xda}d\xddqg\xec\xdb\x9fx\xe3\xef\x9ezu" + - "\xfbW@\xbe\x09\x11\xc0AsY\xec\x7fi@\x7f\x8c" + - "v\xdby\xf2\xf0\xca\xdc\x8e\xdd\xfb\\|\x9c\xef\xebb" + - "\x1c\x07\x11{k\xff/sC/\xa4^(\"\x17\xa5" + - "O,v\x01\x01\x97N\xc6Z\x11\xd0^\xf6\xe3\xb3\xab" + - "V|}\xe4/Bsw\xd5m\xa0\xb9\xdbG.\x1c" + - "\xa9O\xe6^,A\xc41vG\xddA\x94\x0e\xd49" + - "\x87YG*\xbc\xfc[w\xd7\xac?\xbb\xfc\x15\x10\x17" + - "y\xcb|\xab.I\xcbL|\xef\x85\xdf]\xf0\xbd\x07" + - "\x0f\x81|+\xfa`\x1d\xa1o(\xfd\xa4\x8e\xec\x8b\x9c" + - "Zp\xf0\xf0O\x9f|\xad\x8ccw\xc47\xa0\xb4\"" + - "N\xbb\xf4\xc7?-M\xd2/;\xb2\x96\xffPy\xf6" + - "\xef_+\xe5\xaf\x83\xb1\x12\x1fF\xa9\x10w\x10\x88;" + - "\xf6=~d\xf7\xcd\xd5_\xfb\xe0\xaf+\x9d\xebs\xf5" + - "\xc3(\xbd\\\xef\x9ck=irc?\x9e~sq" + - "\xe4\x1ba\xa2\xd5\x88\xe7\x08\xe9\x16\x91\x88\xd6\xf2nO" + - "L{o\xcb\x9b%\xab9\x03\x0f\x8b\x03(\x1d\x17i" + - "\xb5\xa3\xce\xe0\x81\xfb\xbf\xf4t\xf4\xec\x97\xbeC\x9a\x86" + - "\x18\x1e%\x1fX\xaa\xde`\xa0\xb4\xf1\x06\xfa9yC" + - "\x03\x0fh7\xbf\xf2G\x7f\xd9\x93\xf9\xd1[\x154\x95" + - ".\xdfxA\x8a\xce\xa1_8\x87\x14=\xb3\xe8\xd0\xe7" + - "\xfe\xf3O\x8e\x9f(*\xea`\xbav\x8eC\x89us" + - "\xe8<.\xaf\xd9{\xb7j\xdf{\xaa\x14%\xf7\xf4\xe6" + - "|\x1d\xa5\x03\xcer\xfb\x9d\xe5|\xfeU\x1a]\xd30" + - "\x8eRK\x03\x8dnj\xa0\xb5\xb9\xb3J\xd3\xe6\x7f\xfe" + - "\xd4\xe9\x10eZ\x1a~\x8e\x10\xb1W~\xe6\xfe\xf1\x9a" + - "\x8dg\xce\x84\xd5\x12\x1b\x1c\xfc\x168S\xff\xeb\xcf\xcf" + - "}\xf1|.\xf3o\x0e\xed=\x84\xfb\x1a:\x88\x0ck" + - "\x1b\xc8\x0f\x1bZc}m'\x07\xcf\xb9Dr\x97\xb8" + - "\xa3\xb1\x87\x06\xc8\x8d\xb4\xc4\xb2\x07\xba\xd9\x9a\xdb\xef=" + - "W\xc6\x96u\x8d\x1d(=\xdcH\x1366nGi" + - "WS\x03\x80=\xf17;\xee}\xf1\x9b+/\xb8\x9e" + - "\xe8(\xbb\xadi\x09\x11\xf3\xc9\xcf\xf7\xae\xba\xa3\xed\xc8" + - "\x85\xb0\xb2\x1b\x9b\xc87\xa4\x1dM\xb4\xd3\xc8\xed\xe7?" + - "\xbd\xe0\xc9o_\xa8\xe4\x00\x87\x9a\xdaQ:\xd2D\xa0" + - "\x1c\xa6\xc1\xef-\xff\xd3\x13\xcd\xf1\xe6\x8b%\x00V\xd1" + - "\xd8\x9f5\x8d\xa3t\x89\xc6.}\xbf\xe9;D\xca\xe7" + - "\xffl\xdf\xbf\\>v\xd7\xa52\x1b\xce\xce\x1dF\xe9" + - "\xf2\\Z\xf6\xd2\\A\xba4\xf7&\x00\xfb\x91S\x9f" + - "]\xff\x83/|p\xa9\x94G\x8e\"\xef\xceM\xa2\x84" + - "\xf3h\xc6\xaf\xe7\x12\xeb\xbe\xbc\xfa?6\x9d\xdf5\xe7" + - "\x97ek\xef\x997\x8e\xd2!g\xe4\xcb\xf3\xb6K\xb1" + - "\x16\xf2\xa6w\x84\x17\x16\xf7nz\xebr\xc8o/\xcd" + - "\x1b x\x9e\x11\xbezf\xf3O?\xfb\xab0<\xef" + - "\xcf\xfb9\xc1\x13m!x\x1ez\xef\xb9\xbb\xbe\xb8\xe6" + - "\xa5\x0fC4X\xd0\xb2\x85\xa6Z\x05McY#\x1f" + - "I\xdf\xea\xfdL\xdf\x92V\xf2Z\xbe\xa3\xbb`\x8d1" + - "\xcdR\xd3\x8a\xc5\x92\xac\xd3\xcc\xeb\x9a\xc9\x06\x11\xe5z" + - ">\x02\x10A\x00Q\x19\x07\x90\x1f\xe0Q\xcer(\"" + - "&\x88(\xa2J\xc21\x1ee\x8bC\x91\xe3\x12\x14%" + - "\xc5um\x00r\x96Gy=\x87\xc8'\x90\x07\x10\x0b" + - "O\x03\xc8\xeby\x94\xb7rh\xe7\x99\x91S4\xa6A" + - "\xdc\xea3\x0c\xac\x05\x0ek\x01m\x83Y\xc6\xa42\x9c" + - "\x858\x0b\x89\x85\xf1\x07-\x8c\x01\x871@{L/" + - "\x18\xe6\x90f\xa1\x9aM\xb2\x11\x83\x998\x86U\xc0a" + - "\x15\xe0t\xe6\xa5\x98i\xaa\xba\xb6B\xd1\x94Qf\x00" + - "\x90e\xd5|\x14\xc0\xcf@\xe8\xe5*q\xf1n\xe0\xc4" + - "E\x02\x06\xd9\x02=\xb2\x8a\xbfs\x108\xb1E\xb0\x0d" + - "6\xaa\x9a\x163p(\x93w\xd6\xe6u\xad\x0b\xed\x82" + - "\xe6~@f\xb8\x1f\xe2\xb4k\x17\x0eb\xa0\x1d_\xae" + - "\xdd\x9dY\x95iV\xbc_\x1b\xd1K \x1f\xa8\x04\xf9" + - "@\x11\xf2\xad!\xc8\x1f\xee\x01\x90\x1f\xe2Q~\x94C" + - "\x91/b\xbe\xad\x1d@\xde\xcc\xa3\xfc\x04\x87v\xda\xd9" + - "\xa4?\x03\x00>\x9a#L\xb1\x0a\x063IV\x078" + - "\xc8\xa3\x03z\x1d\xe0\xa6\x09f\x90\xee\xde!\xc4\x15#" + - "=\xe6\x1f\xd44H\xf7\xadWMK\xd5FW;\xf2" + - "\xceA=\xab\xa6'\xc9\xaaZG\xcf\x96\x0e\x00D\xf1" + - "\xc6\xfb\x00\x90\x13\xc5\x1e\x80NuT\xd3\x0dfgT" + - "3\xadk\x1a\x03>mm\x1aV\xb2\x8a\x96f\xfeF" + - "U\xe5\x1b\xb9\x1b\xa4\x981\xc1\x8c[\x94\x10}\xe7\x0f" + - "*\x86\xc2\xe7L\xb9\xd6\xc7\xb1\xef>\x00\xb9\x97Gy" + - "0\x84\xe3\x0a\xc2\xf1\x1e\x1e\xe5{C8\x0e\x11\x8e\x83" + - "<\xcak8\xb4uC\x1dU\xb5;\x19\xf0F\x98\x81" + - "\xa6\xa5)9F\x98\x15\xf1\xd8\xa4\xe7-U\xd7L\xac" + - "\x0fr\x0b \xd6\x87\x90\x12f\xe2\xe4-\x1e\xa5(\x09>" + - "\x8e]=\xf8B4LV\xa2\xe1\x12\x009\xc3\xa3\x9c" + - "\xe7\x10\x8b\xe8\xe5zB\xd1\x80G\x97\x85\xebv\x03\xc8" + - "\x16\x8f\xf2f\x0em\xd3\xdd\xa4\x1f0\xe3!\xda\x9a1" + - "\xad\xfe\xbc\xf7\xd7\xa6\x8ci\x0d\xea\x86\x85\x02p(\x00" + - "\xf1V7Y\xf7\x08\xf9T\x7f&\xcb\xeeRy\xcd\xc2" + - "(p\x18\x85i\x9d\xca\xe5G\x9c\x02\x9b\xeb\xed\x9e5" + - "\x0b\x89\x0c\xbf\xc7\xa3\xfc\xfb!k\x16S\x1c\xbb\x8dG" + - "\xf9\x93\x1c\xdaJ:\xad\x174k5\xf0\xcah\x09\xe7" + - "S\x0c\xe2i\x83\x05t\xf0\xb6\xad\xae\xe0\xd6\xba6\xa2" + - "\x8e\x16\x0c\xc5\x0a\x01^\xc8g\x14\x8bM\xf9\xe4\x9cs" + - "\x96\xbf\x8as\xf6\xab\x87k>g/2\x95\x9ct\xdc" + - "Prf\x18\x9bd%l\xe8T?\xc1\xa3|{\xe5" + - "\x03\xdc\x94c\xa6\xa9\x8c\xb2\xb2\xf0\x10\xad\x88\x89\xc6\xd2" + - "du\x92\xb9I\xe6\x16\x83\x99B!k\x91\x16\xb5\xb6" + - "\xed\xaaA\xdc\x9a\xcf\xa3|\x1b\x871\xfc\xd0v\xf5X" + - "\xf4tpF\xad\xcc0t\x03\xeb\x83$\\\x84$]" + - "\xdc\x00u\xad\x97Y\x8a\x9aErK\xbf\xda,\x01n" + - "\xa6\xb8\x12\xc0\xe6\x8a\xe7w\x92w\xe4\xa6\x9c\x14\xd1\xbb" + - "\x9eGy\x1e\x87\xf6\xa8\xa1\xa4\xd9 3P\xd53+" + - "\x15MO\xf1,]F\xd6\xbak\xdd\xd4\xe1\x87e\x82" + - "?k\xfa\xf9\x06+\x82P\x9c>\xd8\xea\xea\x9c\xf0u" + - "\xde\xd8\x16$c\xff\x98\x1f\x1e\x0e\xb2\x85\x1f\x0f\x1f#" + - "gy\x94Gyg(\xaf\xec\xa0\xc8\xf9\x14\x8f\xf2W" + - "9\x14#\x91\x04F\x00\xc4\xe7\x88%;y\x94\xf7r" + - "SS6\x9b`\x9a\xd5\xab\x8e\x82\xc0\xcc@J*\xf6" + - "\xaa\xa3\x0cx\xf3zck\xf5\x0cx\xe8\xc3\xa6\x9ee" + - "\x16\xebe\xe9\xacB.7\xc1\xdc\xefE2z\x87:" + - "\x1do\x93e\xdeC\xfc\x8d{UR\x88\x0em\x81\xe3" + - "\x0a,T\xdcL\xa3\xad\xbb\xb8\x1b\x0c\xca8\x10xL" + - "\x91\x07h~,A\xc7\xb1\x19\xa78\x7fO\xe0u\x1e" + - ")\x16u\x04\x01\xc1\xaf\x09\"\xc0a\x04\xb03\xed," + - "X\x16\x0a#3i\xd5\xe9\xaa\xe5\x02GE\x98w\x17" + - "E\xef\x02/\x8a\xfb\x80\x13c\x82\xedi\x8e\xde|\xa1" + - "\xac\xa0\x8aL\x17eV\xe5-U\xd05\x93\xf6\x0a\xf1" + - "\xbf\xa3\x12\xff\x8d\x80\xff^B{lK\x98\xfe\xc5\x84" + - "\xb6cw\xc0t1\xc2\xb9\xf4\xdf\xb3\x0f@\xde\xcb\xa3" + - "\xfc\x12\x87\x9dn\xad\x85\xf5A\xe3\xa5HY\xb7\xa2\xb8" + - "G\x87\xd6\xb4\x92\x0d\x92\x9em\xb0|VI\xb3>," + - "VO\x80\x08\x1c\xa2\xe3'\xb9\xbc\xc1L\x13U]\x93" + - "\x0bJV\xe5\xadI\xbf\xe2\xd5\x0a\xb9A\x83M\xa8\xa8" + - "\x17\xccn\xcbb9!o\x99WS\x0f\x07\x00Q\x90" + - "\x14\xd4\xacY\x92#\xdb\x03*\xf8\x00-\x1a\x0f\xf2@" + - "\xbcPP\xfd\x04`g\xf5\xb4s\xb2\x10_\xa9\xe4\xca" + - "\xf3@\xd5\x8c\x01kJ\xb8\xf3\xd2\xd2oR\xfd6\xfd" + - "\x95\x89Lw\xee\x14!\x95)\x0et\xf1(\xdf\x13R" + - "\xb9\x7fI\xc8\x0eO\xe5\x15\xc3\x81\x1d\xc2\x1f\xb3IO" + - "\xabV\x96\xa3\xf4\xe5\x81Y4\xa6\x1b\x84\xbb\x831\xd3" + - "\xe9\x17\x8e*\xab\xf2\xad\x8e\x85\xa4\xe3\xed\x9e\x8e\xd2$" + - "\x0e\x00\xa4\xd6#\x8f\xa9\xad\x18\xa8)=\x8c=\x00\xa9" + - "\x87H\xfe(\x06\x9aJ\xdb\xb0\x19 \xb5\x99\xe4O\xa0" + - "\x7f\xb5\x93\x1e\xc3\x83\x00\xa9'H\xfc,\x0d\x8f\xf0\x8e" + - "KH\xbb\x9c\xe5w\x92|/\xc9\xa3\x91\x04F\x01\xa4" + - "=\xd8\x0e\x90z\x96\xe4\xaf\x91\xbc\x8aK`\x15\x80t" + - "\x08\xc7\x01R\xaf\x90\xfc\x0d\x92\x0b\xd1\x04\xddn\xa5\xd7" + - "\xd1\x00H\xfd-\xc9\xbfI\xf2\xea\xc6\x04V\x03HG" + - "\x1c\xf9\x9b$\xff>\xc9k\x9a\x12X\x03 \xfd\x13n" + - "\x01H}\x97\xe4'H>\x0b\x138\x0b@:\x8e\xbb" + - "\x01R'H\xfe\xaf$\x9f]\x95\xc0\xd9\x00\xd2O\x1c" + - "}N\x92\xfc\x17$\xaf\x8d$\xb0\x16@\xfa\x19\xee\x03" + - "H\xfd\x82\xe4\xffM\xf2\x98\x90\xc0\x18\x80\xf4\xaec\xd7" + - "y\x92Ws%7+\x8fQ%\xd7'^7\xfd#" + - "cE\x1fG\x97\xee\x83z\x9c\xaeH\x18\x0f\x1a\xaf\x80" + - "\x18\x07\xb4\xf3\xba\x9e]9\x95\xa9qK\x195\xbd\xab" + - "Z}\xd0\x9a\x02$\xa1_\xfc@\\\xd7\xfa3~ " + - "(\x8d:\x9e&\xaa\xd9]\xb0\xf4B\x1eZ)\xc8f" + - "\xfc\x98c\x14\xb4\xe5\x86\x9e[\x8d\xcc\xc8\xa9\x9a\x92\x9d" + - "!\x1a\xd5\x00\x875P\x0c\x09\xde\xda\xd3\x87\xa6+_" + - "<}Fs\xa5\x8cn\xcdw\xacVF\xaf&N-" + - "\x09rV\\\x0b\x05\xa4\xd6\x09%[\xf8(\xe1ij" + - "=\x95\xect\xeb\xb1\x99\xcau\xaf\xf7T\x12J*T" + - "\x17C\xe5\xf99\xc9\xccV\xbf\x09\x132\xf8`\x10\x83" + - "={\x97\xb5\x85\xee.Y\xc5b\xa6\xd5\x9d\xc7|V" + - "e\x99\xcf0#\x1eN\xd9\x15+\x92\xc8Le\xfa\xd4" + - "2\x07C]r2\x9c+\x1a|\xd5x\x8e2\xcb\xfd" + - "\xd5\xaf\x8d\xe8T\x87\x08\xe1\xe2\xeb\xdaf'\x99\x19\xbf" + - "\x9a\xb3\x08\x9a\x863_\x9d*\x94c\x15\x8a1\xef&" + - "\x10\xba&\x13\x19\xd7\xf0(\x8f\x85\xc8\xc8\x06*\\\x93" + - "\x93A\x7fL\xe4\xb9b\x83\x8c2W\x9eG\xf9!\x0e" + - "\xe3J\xc1\x1a\xc3\xfa\xe0\xf1c\x8a\xd2S{8\xc4\xcd" + - "~-\xc3\x00\xd7{\xee\x15\xcag~W~\xe6\x9a\xf9" + - "\xea\xcc\xf6\xee\"3\x02\xee\xf7\xaeKv\xbe\xe2U\xbd" + - "\xd3\xdd\x94x\xd6\xe8T\x85^\xd7\x1f\xbd\x8e\xb0xh" + - "\x03p\xe2\x01\x01\x83^7z\xadmq\x8f\x01\x9c\xb8" + - "K@\xce\x7f\x97A\xef\xfdE|\xecq\xe0\xc4m\x02" + - "\xf2\xfe\xb3\x0az]\xd2\xc5\x93\xb3\x108q\xa3\x80\x11" + - "\xff=\x0b\xbd\x1e\xab\xb8n\x1c8Q\x150\xea\xbf\xd8" + - "\xa0\xd7\xe2\x17\xd7n\x01N\x1c\x0az\x81\xd0\xe9\xda\xd1" + - "\x85\xb6\xc7QhuX:\xb53\xe8\x8e\x02\xe8B\xdb" + - "\xbb\x99\xf0W\xba\x9a8\xa3\xbc\xe6\x16\xc4\xd3\x8a\xc5\xba" + - "\xa8Zt\x03\x12\x16#\x12t\xa1\x1c\xc1P\x8b\x19\xe0" + - "z[\x03I\xd6\xea\x9c\xf3G\xad\xe1\xbc\xf9\x1f1F" + - "\xf2\x95\xb4\xa6}\xfc&ih]*Kky\x94\x1b" + - "\xb9\x19+\xd1\xc8\x95\xac\xf0\xc8\x1f\xa7\xc9\xb4\xfeo\xfb" + - "\xeb\x1f\xa7\xf0\xfa}\x1e\xe5\x93!\xb7\xfe!\x09\xdf\xe1" + - "Q>\x1d\xaa\xe4~D\xbe~\x92G\xf9b\xd0\xf7~" + - "\xffq\x00\xf9\"\x8f\xc9Pe$\xfe\x9a\x06\xfe\x8a\xea" + - "\x07\xa7.B\xb7.\x8a\xe2\xd3\x00\xa9j\xaa+\x12N" + - "]\x14q\xeb\"\x11\x87\x01R\xf5$\x9f\x17\xae\x8b\x9a" + - "\xf0>\x80T#\xc9\xe7\xe3\xd4\xdb\xa6P0\x82\xca1" + - "\xab\x8f\xde\xa3j\x15\x93\xad\xd7\x88Gk\xb9\xa2f\x0b" + - "\x06\x83 \xd7\x17\x83Mo\xa8\xfcp;\xf4n3." + - "E$\xcc\xa0\xe97\xea\xae\xe1\x9e?]\xe6\xc9\xea\x85" + - "\xccHV1X&\xc5\x0c\xc1\x0d\x08\x83|T\xae\xc6" + - "\xd0\xab7@\xf0:\x19\"\xfb\xb4\x99\xac\xcf0t4" + - "J\xaa\xf4%A\x95\xee\x17\xe9t\xd9\xb8\x8bGy5" + - "\x1dm\x97{\xb4\xf2pp\xafhM+\x05\x93\x95a" + - "\x02<3\xfc^\x8f9\xa6\x17\xb2\x99$\x03\xc12&" + - "K \x9d\xb1ZO\xb1\xb8\x17\x09\xddG\x0a\xefy\x0e" + - "\xbdW\xb8\xd0#\x85\xf7R\x84\xdeSo\xf9#\x85\x87" + - "A\xd9#\x85\xfb\xc1\xe1\xfc\xd4;\xf5u4)\xdc\xb4" + - "\x18:\x94k\xea\xdd_u\xcb\xdb\xffw\x89\x92\xc8Q" + - "s\xbd\xcd /\xc1\xfd\x7f\x00\x00\x00\xff\xff\xf1\xc3d" + - "\xc6" +const schema_db8274f9144abc7e = "x\xda\xccZ{\x94\x14\xe5\x95\xbf\xb7\xaa{\xaa\x07\xa7" + + "\xa9)\xaaa\xa0\x0f\xb3\xb3\xe10\xebB\x82\x0a,Y" + + "\xc3\xc6\xcc\x0c\x0e\xc4\x19yLuC\x8eG1\xc7\x9a" + + "\xeeofj\xb6\xbb\xaa\xad\xaa\x1e\x81hx\x04D<" + + "j\xc4\x80\x0f\x126\x80\xeb\xee\x09\xc6\xacD\xdc,{" + + "tWvc\x8c\x1a\x89\xe4h\x16\x83\xd9lB\xd8]" + + "9\xb8\xae\xa8\x9b\xc3n\xb4\xf6\xdc\xaa\xae\xc7t73" + + "\x8c\xe4\x8f\xfc\xd7s\xeb{\xdc\xfb\xfb~\xf7\xf1\xddo" + + "\xae2\x1b;\xb9\xf9\xf1\x8d\"\x80r8\xde\xe0\xb0\xb9" + + "?\xd9\xb0\xaf\xfd\x9f\xb7\x80\x92Ft\xbe\xfcLo\xea" + + "\xbc\xbd\xe5$\xc4y\x01`\xe1c\xc2\x06\x94\x8f\x08\x02" + + "\x80\xfc\xb4\xf0\x1f\x80\xce\x9d\xd3\x9e\xfc\xe6cKw}" + + "\x05\xa44\x1f\x0e\x06\\\xf8H\xa2\x17\xe5'\x124\xf2" + + "[\x89\xedrc\xa3\x00\xe0\\/]yS\xea\xd5c" + + "4:\xbat\x8c\x96~71\x17e\xa4a\xf2\x87\x09" + + "Z\xfa\xb3\xc5\x1f\x1f\xf8\xf4\xee\x97\xb7\x82\x94\xe6F-" + + "\xfdV\xe3\x06\x94?tG\x9eo\\\x05\xe8\xbc\xb7k" + + "\xfa\xe3\xfb\x8f\xfdp\x1bH\x97#T4\x95&\xfd\x0c" + + "\x01\xe5\xf6I\x7f\x03\xe8\xbc\xf2\xc1M\xef\x1f\xfe\xc1\xa2" + + ";A\x9aC\x03\x90\x06\x9c\x94\xa6\x11S/#m:\x96\xbd" + + "r$\xbd\xf0\xc1]U\xaa\xbb\x03?\xb8l.\xca\xf1" + + "&R\x08\x9bn\x03t>\xf7\xa7O\xdd\xd7\xfd\xe0\xa6" + + "\xdd ]\x19\xec\xc7\x9an\xa4\xd567\xd1~\xff3" + + "\xf9\xeb\xc7\xca\xd7~\xef\xc1\x8aB\xee*\xfb\x9b\xe6\xd2" + + "\x80#\xee\x0a\xb3F\xdao\xf9\xa7\xe7\x9fz\x08\x94y" + + "\x88\xce\x9b\xfd\x9f|\x9d\xdf{\xf0$\xacA\x81\xf4[" + + "8#y\x80\xac\x9b\x93\xa4\xb1?\xfe\xd43\x7f\x7f\xff" + + "S\xdb\xbf\x0e\xca\xe5\x88\x00.\x9a\xf7&\xff\x97\x06\xec" + + "O\xd2n\xbbN<\xbb\xb2\xb8s\xcf\x01\x0f\x1f\xf7\xfb" + + "\x8bI\x8e\x83\x98\xb3\xb5\xe77\xc55\x8ff\x1f\xad " + + "\x17\xa7O\xcf&\xcf!\xe0\xc2\xe3\xc96\x04t\x16\xfd" + + "\xec\xf4\xaa\x15\xdf\x1d\xf8\xeb\xc8\xdc\xb7'o\xa0\xb9\xdb" + + "\x07\xce\x1dm\xce\x14\x1f\xafB\xc45\xf6\xad\xc9\x07Q" + + "F\xd1=\xcc\xc9\xa4\xc2\x13\x7fp}\xe3\xba\xd3\xcb\x9e" + + "\x04i\x9e\xbfL\xab\x98\xa1eF^z\xf4\x8f\xda_" + + "\xba\xed\x10(Wb\x00\xd6\x0c\xfa\x86\xf2\"\x91\xec\x8b" + + "\x9dl?\xf8\xec/\xee;\\\xc3\xb1\x9d\xe2\x06\x94\x1f" + + "sw\xd9/~^>N\xbf\x9c\xd8\xcd\xfcG\xea\xc3" + + "\xffx\xb8\x9a\xbf.\xc6G\xc4~\x94_\x11]\x04D" + + "\xd7\xbe\xbb\x8f\xee\xf9d\xe2\x9b\xef=]\xef\\\xdfm" + + "\xeeG9.\xb9\xe7*\x91&S{\xf0\xcd\xe7\xe6\xc7" + + "\xbe\x17%\x9a*\x9d!\xa4\xcb\x12\x11\xad\xf5\xed%I" + + "\xfd\x9d-\xcfU\xad\xe6\x0e\x9c:\xa5\x17\xe59Sh" + + "\xb5\xf6)48\xf6\xe9\xe1\xed\xd2\xa9\x9f>\xefa\xe2" + + "\x19~t\xca\xb0\xcb\xca)\x04Z\xefM_{ ~" + + "\xfak/\x90)\x11\x17\x88'\\vN1Q\x96d" + + "\xfa\x99\x94[x@'\xfd\xe4\x9f}gI\xfe\x8d\x97" + + "\xeb\x98\"\xdf;\xed\x9c\xfc\xc84\xfa\xb5{\x1aYr" + + "j\xde\xa1/\xbdu\xef\xf1\xd7*\x96\xb8{\xbf;\xcd" + + "\xe5L\xbc\x85\xf6>\xbfv\xdf\xf5\x9as\xc3\xc9j\x18" + + "\xdd\x91\xed-\xdfE\xf9\x9a\x16Z\xee3-\xb4\\@" + + "\xd0z\xa3\xf7\xb6\x0c\xa3|\xc8\x1d\xfd\x84\xbb6wZ" + + "\x9d\xb1\xe9\xa7\x9f{3\xc2\xa9C-\xbfB\x889+" + + "\xbfp\xd3p\xe3\x1d\xa7NE\xd5z\xac\xc5\x05\xf8\x88" + + ";\xf5\x1f\xfe\xe5\xa1\xa1\x9b\xbfs\xect\x84Go\xb4" + + "\x98\xc4\xa3\xff\xfa\xab3_=[\xcc\xff\xbb\xeb1\xfe" + + "\xe1\xbc\xde\xb2\x98\xe0|\xbb\x85\\\xb8\xa5-\xb9t\xd6" + + "\x89\xbe3Q\xbc_\x9c\xbe\x84\x06\xfcr:-\xbe\xe8" + + "\x96.\xb6\xf6\xea\x1b\xce\xd4\x10\x0dg,FY\x9a\xe1" + + "b=c;\xca\xed\xe9\x16\x00g\xe4ow\xde\xf0\xf8" + + "\xf7W\x9e\xf3\x9c\xd8\xd5ejz\x01\xe9r\xdf\x97\xbb" + + "W}f\xd6\xd1sQ3\x92ir+\xf9\x13i\xda" + + "i\xe0\xea\xb3\x9fo\xbf\xef\x07\xe7\xea\xf9NWz." + + "\xcaJ\x9a\xe0ZA\x83\xdfY\xf6\x17\xaf\xa5\xc5\xf4\xfb" + + "U\xd06\xd0\xd8[\xd3\xc3(o\xa3\xb1\x0b7\xa7_" + + " >\xef\xff\xcb\x03\xffz\xfe\xd8u\x1f\xd4\xd8P\x9e" + + "\xd9\x8f\xf2\x8e\x99\xb4\xec\xb6\x99\x82\xbcm\xe6\xe5\x00\xce" + + "\x9d'\xbf\xb8\xee'_y\xef\x83j\x86\xb9\x8a\xdc1" + + "3\x83\xf2Nw\xc6\xbd3\x89\xb0\x0f\xad\xfe\xcf\x8dg" + + "wO\xfbM\xcd\xda\xf3Z\x87Q\xeej\xa5\x91\xd7\xb4" + + "\xbe \x1f\xa7_\xce\xab\xc2\xa3\xf3\xbb7\xbe|>r" + + "TGZ{\x09\x9e\x07\x85o\x9c\xda\xf4\x8b/\xfe6" + + "\x0a\xcf\xd3\xad\xbf\"x^l%xn\x7f\xe7\x91\xeb" + + "\xbe\xba\xf6\xdb\x1fE\x08\xf2V\xeb\x16\x9aj\x97u\x9d" + + "\x15\xccR,w\xa5\xff3wEN-\xe9\xa5\xc5]" + + "e{\x88\xe9\xb6\x96Sm\x96a\x1dV\xc9\xd0-\xd6" + + "\x87\xa84\xf31\x80\x18\x02H\xea0\x80r\x0b\x8fJ" + + "\x81C\x091ED\x914\x12\x0e\xf1\xa8\xd8\x1cJ\x1c" + + "\x97\xa2\x00+\xdd:\x0b@)\xf0\xa8\xac\xe3\x10\xf9\x14" + + "\xf2\x00R\xf9\x01\x00e\x1d\x8f\xcaV\x0e\x9d\x123\x8b" + + "\xaa\xcet\x10\xed\xa5\xa6\x89M\xc0a\x13\xa0c2\xdb" + + "\\\xaf\xf6\x17@d\x11\xb10|\x9b\x8dI\xe00\x09" + + "\xe8\x0c\x19e\xd3Z\xa3\xdb\xa8\x152l\xc0d\x16\x0e" + + "a\x03p\xd8\x008\x96yYfY\x9a\xa1\xafPu" + + "u\x90\x99\x00dY\x82\x8f\x03\x04\xc9\x0b\xfd4'\xcd" + + "\xdf\x03\x9c4O\xc00\xd1\xa0OV\xe9\x13\x07\x81\x93" + + "Z\x05\xc7d\x83\x9ae3\x13\xd7\xe4K\xee\xda\xbc\xa1" + + "w\xa2S\xd6\xbd\x0f\xc8L\xef\x83H\xbbvb\x1f\x86" + + "\xda\xf1\xb5\xda][\xd0\x98n\x8b=\xfa\x80Q\x05y" + + "o=\xc8{+\x90o\x8d@\xbey\x09\x80r;\x8f" + + "\xca]\x1cJ|\x05\xf3ms\x01\x94M<*\xf7p" + + "\xe8\xe4\xdcMz\xf2\x00\x10\xa09\xc0T\xbbl2\x8b" + + "d\x93\x01\xfbxtA\x9f\x0c\xb8q\x84\x99\xa4\xbb\x7f" + + "\x08\xa2j\xe6\x86\x82\x83\x1a\x03\xe9\xa5\xeb4\xcb\xd6\xf4" + + "\xc1\xd5\xae\xbc\xa3\xcf(h\xb9\xf5dU\x93\xabg\xeb" + + "b\x00Di\xea\x8d\x00\xc8I\xd2\x12\x80\x0emP7" + + "L\xe6\xe45+g\xe8:\x03>go\xecW\x0b\xaa" + + "\x9ec\xc1F\x0d\xb5\x1by\x1bd\x999\xc2\xcc+\xd4" + + "\x08}g\xf7\xa9\xa6\xca\x17-\xa5)\xc0q\xe9\x8d\x00" + + "J7\x8fJ_\x04\xc7\x15\x84\xe3r\x1e\x95\x1b\"8" + + "\xae!\x1c\xfbxT\xd6r\xe8\x18\xa66\xa8\xe9\xd72" + + "\xe0\xcd(\x03-[W\x8b\x8c0\xab\xe0\xb1\xd1(\xd9" + + "\x9a\xa1[\xd8\x1cf\x1d@l\x8e %\x8c\xc7\xc9+" + + "|J\xf9\x8c2\xf4\xd9\x19f\x95\x85\x82m)\xb1\xc0" + + "\x92\xe4b\x00%\xc1\xa3\x92\xe2\xb0\xc3dV\xb9`c" + + "sXM\xfc.v\xf5\xe1\x8b\xd00S\x8f\x86\x0b\x00" + + "\x94<\x8fJ\x89C\xac\xa0W\\\x12\x89\x06\xa2my\xcb\xee)\xf9\x7fm\xcc[v\x9fa\xda" + + "(\x00\x87\x02\x10o\x0d\x8bu\x0d\x90O\xf5\xe4\x0b\xec" + + ":\x8d\xd7m\x8c\x03\x87q\x18\xd3\xa9<~\x88\x14\xd8" + + "g?2" + + "U\x9d\xb4h\xaaE+\x8aM\xa6\x1e6t\xaa\x9f\xe2" + + "Q\xb9\xba\xfe\x01n,2\xcbR\x07YMx\x88\xd7" + + "\xc5Dg9\xb2:\xc3\xbc$s\x85\xc9,\xa1\\\xb0" + + "I\x8b&\xc7\xf1\xd4 n\xcd\xe6Q\xb9\x8a\xc3$~" + + "\xe4xz\xcc{ <\xa36f\x9a\x86\x89\xcda\x12" + + "\xae@\x92\xabl\x80\x86\xde\xcdlU+ \xb9eP" + + "\xa8V\x017^\\\x09a\xf3\xc4\xb3;\xc8;\x8a\xa3" + + "N\x8a\xe8\xdd\xcc\xa32\x93Cg\xd0Ts\xac\x8f\x99" + + "\xa8\x19\xf9\x95\xaandy\x96\xab!\xeb\xe4\x89n\xea" + + "\xf2\xc3\xb6 \x985\xf6|\x93U@\xa8L\xefk\xf3" + + "tN\x05:\xdf1+L\xc6\xc11o\xee\x0f\xb3E" + + "\x10\x0fw\x90\xb3\xdc\xc5\xa3\xb2+\x92WvR\xe4\xbc" + + "\x9fG\xe5\x1b\x1cJ\xb1X\x0ac\x00\xd2#\xc4\x92]" + + "<*\xfb\xb8\xd1)\x9b\x8d0\xdd\xee\xd6\x06A`V" + + "(%\x15\xbb\xb5A\x06\xbcu\xa9\xb151\x0e\x1eF" + + "\xbfe\x14\x98\xcd\xbaY\xae\xa0\x92\xcb\x8d0\xef{\x85" + + "\x8c\xfe\xa1\x8e\xc5\xdbL\x8d\xf7\x10\x7fE\xbfJ\x8a\xd0" + + "aV\xe8\xb8\x02\x8b\x147ch\xeb-\xee\x05\x83\x1a" + + "\x0e\x84\x1eS\xe1\x01Z\xbf\x93\xa0\xe3\xda\x8c\xa3\x9c\x7f" + + "I\xe8u>)\xe6-\x0e\x03BP\x13\xc4\x80\xc3\x18" + + "`G\xce]\xb0&\x14\xc6\xc6\xd3\xaa\xc3S\xcb\x03\x8e" + + "\x8a0\xff\x1a\x8b\xfe\xdd_\x92\x0e\x00'%\x05\xc7\xd7" + + "\x1c\xfd\xf9BMA\x15\x1b+\xca\xac*\xd9\x9a`\xe8" + + "\x16\xed\x15\xe1\xff\xe2z\xfc7C\xfe\xfb\x09m\xc7\x96" + + "(\xfd+\x09m\xe7\x9e\x90\xe9R\x8c\xf3\xe8\xbf\xf7\x00" + + "\x80\xb2\x8fG\xe5\xdb\x1cvx\xb5\x166\x87=\x9b\x0a" + + "e\xbd\x8ab\xb9\x01m9\xb5\x10&=\xc7d\xa5\x82" + + "\x9acK\xb1R=\x01\"p\x88\xae\x9f\x14K&\xb3" + + ",\xd4\x0c])\xab\x05\x8d\xb7\xd7\x07\x15\xaf^.\xf6" + + "\x99lDC\xa3lu\xd96+\x0a%\xdb\xba\x98z" + + "8\x04\x88\x82\xa4\xa0\x15\xac\xaa\x1c97\xa4B\x00\xd0" + + "\xbc\xe10\x0f\x88\xe5\xb2\x16$\x00\xa7`\xe4\xdc\x93\x05" + + "q\xa5Z\xac\xcd\x03\x0d\xe3\x06\xacQ\xe1\xceOK\xbf" + + "O\xf5\xdb\xd8W&2\xdd\xbdSDT\xa68\xd0\xc9" + + "\xa3\xb2<\xa2r\xcf\x82\x88\x1d\xbe\xca+\xfaC;\x84" + + "?g\xeb}\xad\xdaX\x91\xd2\x97\x0ff\xc5\x98.\x10" + + "\xae\x0f\xc7L4\xaa\xb8\xde\xb4\xdc\xc8\xa9\x85\xea` " + + "V\xa7\xb5h\x01r\xf1\x8e\x1e\xddtU\xa9\xcd\x85\x95" + + "\x80\xb9\xda_X^\x8f\xbd\x00\xd9u\xc8cv+\x86" + + "\xd8\xc8\x9bq\x09@\xf6v\x92\xdf\x85!<\xf26L" + + "\x03d7\x91\xfc\x1e\x0c\xee\x93\xf2\x0e<\x08\x90\xbd\x87" + + "\xc4\x0f\xd3\xf0\x18\xef\xfa\xa1\xbc\xdb]~\x17\xc9\xf7\x91" + + "<\x1eKa\x1c@\xde\x8bs\x01\xb2\x0f\x93\xfc0\xc9" + + "\x1b\xb8\x146\x00\xc8\x87p\x18 \xfb$\xc9\x9f!\xb9" + + "\x10O\xd1\x95Z>\x82&@\xf6\xefH\xfe}\x92'" + + "\xa6\xa70\x01 \x1fu\xe5\xcf\x91\xfcG$o\x9c\x91" + + "\xc2F\x00\xf9E\xdc\x02\x90\xfd!\xc9_#\xf9$L" + + "\xe1$\x00\xf98\xee\x01\xc8\xbeF\xf2\x7f#\xf9e\x0d" + + ")\xbc\x0c@\xfe\xb9\xab\xcf\x09\x92\xff\x9a\xe4M\xb1\x14" + + "6\x01\xc8\xbf\xc4\x03\x00\xd9_\x93\xfc\xbfI\x9e\x14R" + + "\x98\x04\x90\xdfv\xed:K\xf2\x04Wu\x9d\xf3i\\" + + "ug\xe3\x0d+\xe0\x09\xab\x04\x16\xf4|\xac\xcf\x10\xe9" + + "^\x86b\xd8(\x06D\x11\xd0)\x19Fa\xe5h\xf7" + + "\x10mu\xd0\xf2\xef\x87\xcda\xa7\x0c\x90\x84A\xc5\x05" + + "\xa2\xa1\xf7\xe4\x83\xe8S\x1d\xea|M4\xab\xabl\x1b" + + "\xe5\x12\xb4\x11\x17\xf3A\xa03\xcb\xfa2\xd3(\xaeF" + + "f\x165]-\x8c\x13\x02\x1b\x81\xc3F\xa8\xc4!\x7f" + + "\xed\xb1\xe3\xe1\x85o\xbb\x01\xa3\xb9jF\xb7\x95\x16\xaf" + + "V\x07/&8.\x08\x13\xa5\xa8G\xa2`\xdb\x88Z" + + "(\x7f\x9c\x988\xba\x88\xcbtxE\xe0xw\x04\xbf" + + "\xe1U\x15\xbf\xea\x944kj\x8b\x82\x0c\xb3\xda\x82\xce" + + "O\xc4\xe0\x83a\xe0\xf7\xed]4+ra*\xa86" + + "\xb3\xec\xae\x12\x96\x0a\x1a\xcb\x7f\x81\x99b\xb4N\xa8[" + + "\x06\xc5\xc6\xbb\x1b\x8c\xae\xad0\xd2\xd5'\xc3\xb9\x8a\xc1" + + "\x17\x8d\xe7 \xb3\xbd_=\xfa\x80A\xc5\x8f\x10\xad\xf8" + + "&6;\xc3,\xf1b\xce\"\xecT\x8e_\xb1N$" + + "ZgX\x9b\xcb\x85\xb1\xee\x7fu\xd6\xabSQ\xfa\xd7" + + "\x99\xc8]\x9f\xc8\xbd\x96Ge(Bn\xd6[\xe7\xae" + + "\x9f\x09\x9b|\x12\xcfU\xba|\x94~K<*\xb7s" + + "(\xaae{\x08\x9b\xc3\xc7\x9fQ \x8cnD\x11\xd7" + + "{\xf4<\x03\\\xe7\xbbk$)\x07\xaf\x12\x1f\x0b\xc6" + + "\x0b\x16\xd2\x16\x8c{\x80Ak\xbej\xe7\x0b\xf6\x1b:" + + "\xbcM\x89\xb7\xd3\xdd\xd2\xd6\x7f\xf5@\xbf\xad-\x1d\xda" + + "\x00\x9c\xf4-\x01\xc3V>\xfa\x9d{i\xaf\x09\x9c\xb4" + + "[@.x\x97B\xff\xfdI\xdaq7p\xd26\x01" + + "\xf9\xe0Y\x09\xfdV\xef\xfc\xf5\x93\x108\xe9\x0e\x01c" + + "\xc1{\x1e\xfa\x8db\xe9\xd6a\xe0$M\xc0x\xf0b" + + "\x85\xfe\x0b\x86t\xf3\x16\xe0\xa45aC\x13:<;" + + ":\xd1\xf19\x0fm.\xebG\xb77\xbdQ\x00\x9d\xe8" + + "\xf8\xd7+\xfeB\xf7+w\x94\xdf\xa1\x031\xa7\xda\xac" + + "\x93J^/\xc0a%\xc2A'*1\x8c\xf4\xc9\x01" + + ".\xb5\xbfQ\xe3'\x13,D\xfd\xf9\x1f3\xe6\xf2\xf5" + + "\xb4\xa6}\x82Nod]\xaa\xad\x9bxT\xa6s\xe3" + + "\x96\xd3\xb1\x0bY\xe1\x93_\xa4\xc9\xb4\xfe\x1f\x06\xeb\x1f" + + "\xa7p\xfd#\x1e\x95\x13\x11\xb7~\x9d\x84\xaf\xf2\xa8\xbc" + + "\x19)G\xdf _?\xc1\xa3\xf2~\xd8\xbc\x7f\xf7n" + + "\x00\xe5}\x1e3\x91JK\xfa\x90\x06\xfe\x96\xea\x11\xb7" + + "\xceB\xaf\xce\x8a\xe3\x03\x00\xd9\x04\xd5))\xb7\xce\x8a" + + "yu\x96\x84\xfd\x00\xd9f\x92\xcf\x8c\xd6Y3\xf0F" + + "\x80\xect\x92\xcf\xc6\xd1Wf\xa1l\x86\xe5o\xc1\x18" + + "\\\xae\xe9u\x93\xb7\xff\x9a\x80\xf62U+\x94M\x06" + + "a\xedP\x096\xdd\x91r\xc6{f\xf0:\x8aY\"" + + "a\x1e\xad\xa0\xdb8\x81f\xc5X\x99\xac`\x94\xf3\x03" + + "\x05\xd5d\xf9,3\x05/ \xf4\xf1q%\x81\x91W" + + "\x7f\x80\xf0u6B\xf613\xe3R\xd34\xd0\xac\xba" + + "j,\x08\xaf\x1a\xc1M\x83nL\xd7\xf1\xa8\xac\xa6\xa3" + + "\xed\xf4\x8eV\xe9\x0f/Gm9\xb5l\xb1\x1aL\x80" + + "gf\xd0\xb0\xb2\x86\x8cr!\x9fa \xd8\xe6\xfa*" + + "H\xc7\xad\xfe\xb3L\xf4#a\x93\x1b\x09\xfd7F\xf4" + + "\x9f\x12%e\x0fp\xd2\x0a\x8a\x84\xfes\x17\xfaO\xdd" + + "R\xd7A\xe0\xa4k(\x12\xfa/\xbd\xe8?_J\xf3" + + "_\x02N\x9a\x1fy\x85\xf1\xf1\xa9y\x85\xf1>\xb8\xfe" + + "@\x1f*\x09\x95\xab\xce\xa8\x14\xa1\xa2-\x85K\xe8\xd1" + + "x\x095r\x9c\x13z\xba\xb8\xe8\x8e\x7f\xf0\x8f&U" + + "1\xa7\xf1R{a~j\xfc\xff\x00\x00\x00\xff\xff\xe5" + + "\xf9\xce\x8f" func init() { schemas.Register(schema_db8274f9144abc7e, @@ -4561,6 +4775,7 @@ func init() { 0xb5f39f082b9ac18a, 0xb70431c0dc014915, 0xc082ef6e0d42ed1d, + 0xc5d6e311876a3604, 0xc793e50592935b4a, 0xcbd96442ae3bb01a, 0xd4d18de97bb12de3, @@ -4568,6 +4783,7 @@ func init() { 0xdbaa9d03d52b62dc, 0xdc3ed6801961e502, 0xe3e37d096a5b564e, + 0xe5ceae5d6897d7be, 0xe6646dec8feaa6ee, 0xea50d822450d1f17, 0xea58385c65416035, From f3244db86151eefb7b8b9f447656152698b7e5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 21 Apr 2022 22:37:16 +0100 Subject: [PATCH 060/238] TUN-6130: Fix vendoring due to case sensitive typo in package --- tracing/tracing.go | 2 +- .../contrib/propagators/{Jaeger => jaeger}/context.go | 0 .../contrib/propagators/{Jaeger => jaeger}/doc.go | 0 .../contrib/propagators/{Jaeger => jaeger}/jaeger_propagator.go | 0 vendor/modules.txt | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) rename vendor/go.opentelemetry.io/contrib/propagators/{Jaeger => jaeger}/context.go (100%) rename vendor/go.opentelemetry.io/contrib/propagators/{Jaeger => jaeger}/doc.go (100%) rename vendor/go.opentelemetry.io/contrib/propagators/{Jaeger => jaeger}/jaeger_propagator.go (100%) diff --git a/tracing/tracing.go b/tracing/tracing.go index d3786603..2cab213b 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -9,7 +9,7 @@ import ( "runtime" "github.com/rs/zerolog" - otelContrib "go.opentelemetry.io/contrib/propagators/Jaeger" + otelContrib "go.opentelemetry.io/contrib/propagators/jaeger" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go b/vendor/go.opentelemetry.io/contrib/propagators/jaeger/context.go similarity index 100% rename from vendor/go.opentelemetry.io/contrib/propagators/Jaeger/context.go rename to vendor/go.opentelemetry.io/contrib/propagators/jaeger/context.go diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go b/vendor/go.opentelemetry.io/contrib/propagators/jaeger/doc.go similarity index 100% rename from vendor/go.opentelemetry.io/contrib/propagators/Jaeger/doc.go rename to vendor/go.opentelemetry.io/contrib/propagators/jaeger/doc.go diff --git a/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go b/vendor/go.opentelemetry.io/contrib/propagators/jaeger/jaeger_propagator.go similarity index 100% rename from vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go rename to vendor/go.opentelemetry.io/contrib/propagators/jaeger/jaeger_propagator.go diff --git a/vendor/modules.txt b/vendor/modules.txt index 7830b358..adc719aa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -347,7 +347,7 @@ github.com/urfave/cli/v2 github.com/urfave/cli/v2/altsrc # go.opentelemetry.io/contrib/propagators v0.22.0 ## explicit; go 1.15 -go.opentelemetry.io/contrib/propagators/Jaeger +go.opentelemetry.io/contrib/propagators/jaeger # go.opentelemetry.io/otel v1.6.3 ## explicit; go 1.16 go.opentelemetry.io/otel From d68ad891591844ba7e6f54ced10d41577676f4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 27 Apr 2022 14:34:11 +0100 Subject: [PATCH 061/238] TUN-6142: Add tunnel details support to RPC This commit adds the tunnel details to RPC register connection response so we can have access to some of the details associacted to the tunnel that only the edge knows. Currently this is limited to knowing if the tunnel is remotely managed or not. In the future we could extend this with more information. --- tunnelrpc/pogs/connectionrpc.go | 7 +- tunnelrpc/tunnelrpc.capnp | 2 + tunnelrpc/tunnelrpc.capnp.go | 463 ++++++++++++++++---------------- 3 files changed, 243 insertions(+), 229 deletions(-) diff --git a/tunnelrpc/pogs/connectionrpc.go b/tunnelrpc/pogs/connectionrpc.go index 788a67a8..d713edc6 100644 --- a/tunnelrpc/pogs/connectionrpc.go +++ b/tunnelrpc/pogs/connectionrpc.go @@ -224,8 +224,9 @@ func (a *TunnelAuth) UnmarshalCapnproto(s tunnelrpc.TunnelAuth) error { } type ConnectionDetails struct { - UUID uuid.UUID - Location string + UUID uuid.UUID + Location string + TunnelIsRemotelyManaged bool } func (details *ConnectionDetails) MarshalCapnproto(s tunnelrpc.ConnectionDetails) error { @@ -235,6 +236,7 @@ func (details *ConnectionDetails) MarshalCapnproto(s tunnelrpc.ConnectionDetails if err := s.SetLocationName(details.Location); err != nil { return err } + s.SetTunnelIsRemotelyManaged(details.TunnelIsRemotelyManaged) return nil } @@ -252,6 +254,7 @@ func (details *ConnectionDetails) UnmarshalCapnproto(s tunnelrpc.ConnectionDetai if err != nil { return err } + details.TunnelIsRemotelyManaged = s.TunnelIsRemotelyManaged() return err } diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index c1d56d6e..3d2d1dd3 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -121,6 +121,8 @@ struct ConnectionDetails { uuid @0 :Data; # airport code of the colo where this connection landed locationName @1 :Text; + # tells if the tunnel is remotely managed + tunnelIsRemotelyManaged @2: Bool; } struct TunnelAuth { diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index 4d1af72f..a198ca15 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -1410,12 +1410,12 @@ type ConnectionDetails struct{ capnp.Struct } const ConnectionDetails_TypeID = 0xb5f39f082b9ac18a func NewConnectionDetails(s *capnp.Segment) (ConnectionDetails, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}) return ConnectionDetails{st}, err } func NewRootConnectionDetails(s *capnp.Segment) (ConnectionDetails, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}) return ConnectionDetails{st}, err } @@ -1462,12 +1462,20 @@ func (s ConnectionDetails) SetLocationName(v string) error { return s.Struct.SetText(1, v) } +func (s ConnectionDetails) TunnelIsRemotelyManaged() bool { + return s.Struct.Bit(0) +} + +func (s ConnectionDetails) SetTunnelIsRemotelyManaged(v bool) { + s.Struct.SetBit(0, v) +} + // ConnectionDetails_List is a list of ConnectionDetails. type ConnectionDetails_List struct{ capnp.List } // NewConnectionDetails creates a new list of ConnectionDetails. func NewConnectionDetails_List(s *capnp.Segment, sz int32) (ConnectionDetails_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}, sz) return ConnectionDetails_List{l}, err } @@ -4525,230 +4533,231 @@ func CloudflaredServer_Methods(methods []server.Method, s CloudflaredServer_Serv return methods } -const schema_db8274f9144abc7e = "x\xda\xccZ{\x94\x14\xe5\x95\xbf\xb7\xaa{\xaa\x07\xa7" + - "\xa9)\xaaa\xa0\x0f\xb3\xb3\xe10\xebB\x82\x0a,Y" + - "\xc3\xc6\xcc\x0c\x0e\xc4\x19yLuC\x8eG1\xc7\x9a" + - "\xeeofj\xb6\xbb\xaa\xad\xaa\x1e\x81hx\x04D<" + - "j\xc4\x80\x0f\x126\x80\xeb\xee\x09\xc6\xacD\xdc,{" + - "tWvc\x8c\x1a\x89\xe4h\x16\x83\xd9lB\xd8]" + - "9\xb8\xae\xa8\x9b\xc3n\xb4\xf6\xdc\xaa\xae\xc7t73" + - "\x8c\xe4\x8f\xfc\xd7s\xeb{\xdc\xfb\xfb~\xf7\xf1\xddo" + - "\xae2\x1b;\xb9\xf9\xf1\x8d\"\x80r8\xde\xe0\xb0\xb9" + - "?\xd9\xb0\xaf\xfd\x9f\xb7\x80\x92Ft\xbe\xfcLo\xea" + - "\xbc\xbd\xe5$\xc4y\x01`\xe1c\xc2\x06\x94\x8f\x08\x02" + - "\x80\xfc\xb4\xf0\x1f\x80\xce\x9d\xd3\x9e\xfc\xe6cKw}" + - "\x05\xa44\x1f\x0e\x06\\\xf8H\xa2\x17\xe5'\x124\xf2" + - "[\x89\xedrc\xa3\x00\xe0\\/]yS\xea\xd5c" + - "4:\xbat\x8c\x96~71\x17e\xa4a\xf2\x87\x09" + - "Z\xfa\xb3\xc5\x1f\x1f\xf8\xf4\xee\x97\xb7\x82\x94\xe6F-" + - "\xfdV\xe3\x06\x94?tG\x9eo\\\x05\xe8\xbc\xb7k" + - "\xfa\xe3\xfb\x8f\xfdp\x1bH\x97#T4\x95&\xfd\x0c" + - "\x01\xe5\xf6I\x7f\x03\xe8\xbc\xf2\xc1M\xef\x1f\xfe\xc1\xa2" + - ";A\x9aC\x03\x90\x06\x9c\x94\xa6\x11S/#m:\x96\xbd" + - "r$\xbd\xf0\xc1]U\xaa\xbb\x03?\xb8l.\xca\xf1" + - "&R\x08\x9bn\x03t>\xf7\xa7O\xdd\xd7\xfd\xe0\xa6" + - "\xdd ]\x19\xec\xc7\x9an\xa4\xd567\xd1~\xff3" + - "\xf9\xeb\xc7\xca\xd7~\xef\xc1\x8aB\xee*\xfb\x9b\xe6\xd2" + - "\x80#\xee\x0a\xb3F\xdao\xf9\xa7\xe7\x9fz\x08\x94y" + - "\x88\xce\x9b\xfd\x9f|\x9d\xdf{\xf0$\xacA\x81\xf4[" + - "8#y\x80\xac\x9b\x93\xa4\xb1?\xfe\xd43\x7f\x7f\xff" + - "S\xdb\xbf\x0e\xca\xe5\x88\x00.\x9a\xf7&\xff\x97\x06\xec" + - "O\xd2n\xbbN<\xbb\xb2\xb8s\xcf\x01\x0f\x1f\xf7\xfb" + - "\x8bI\x8e\x83\x98\xb3\xb5\xe77\xc55\x8ff\x1f\xad " + - "\x17\xa7O\xcf&\xcf!\xe0\xc2\xe3\xc96\x04t\x16\xfd" + - "\xec\xf4\xaa\x15\xdf\x1d\xf8\xeb\xc8\xdc\xb7'o\xa0\xb9\xdb" + - "\x07\xce\x1dm\xce\x14\x1f\xafB\xc45\xf6\xad\xc9\x07Q" + - "F\xd1=\xcc\xc9\xa4\xc2\x13\x7fp}\xe3\xba\xd3\xcb\x9e" + - "\x04i\x9e\xbfL\xab\x98\xa1eF^z\xf4\x8f\xda_" + - "\xba\xed\x10(Wb\x00\xd6\x0c\xfa\x86\xf2\"\x91\xec\x8b" + - "\x9dl?\xf8\xec/\xee;\\\xc3\xb1\x9d\xe2\x06\x94\x1f" + - "sw\xd9/~^>N\xbf\x9c\xd8\xcd\xfcG\xea\xc3" + - "\xffx\xb8\x9a\xbf.\xc6G\xc4~\x94_\x11]\x04D" + - "\xd7\xbe\xbb\x8f\xee\xf9d\xe2\x9b\xef=]\xef\\\xdfm" + - "\xeeG9.\xb9\xe7*\x91&S{\xf0\xcd\xe7\xe6\xc7" + - "\xbe\x17%\x9a*\x9d!\xa4\xcb\x12\x11\xad\xf5\xed%I" + - "\xfd\x9d-\xcfU\xad\xe6\x0e\x9c:\xa5\x17\xe59Sh" + - "\xb5\xf6)48\xf6\xe9\xe1\xed\xd2\xa9\x9f>\xefa\xe2" + - "\x19~t\xca\xb0\xcb\xca)\x04Z\xefM_{ ~" + - "\xfak/\x90)\x11\x17\x88'\\vN1Q\x96d" + - "\xfa\x99\x94[x@'\xfd\xe4\x9f}gI\xfe\x8d\x97" + - "\xeb\x98\"\xdf;\xed\x9c\xfc\xc84\xfa\xb5{\x1aYr" + - "j\xde\xa1/\xbdu\xef\xf1\xd7*\x96\xb8{\xbf;\xcd" + - "\xe5L\xbc\x85\xf6>\xbfv\xdf\xf5\x9as\xc3\xc9j\x18" + - "\xdd\x91\xed-\xdfE\xf9\x9a\x16Z\xee3-\xb4\\@" + - "\xd0z\xa3\xf7\xb6\x0c\xa3|\xc8\x1d\xfd\x84\xbb6wZ" + - "\x9d\xb1\xe9\xa7\x9f{3\xc2\xa9C-\xbfB\x889+" + - "\xbfp\xd3p\xe3\x1d\xa7NE\xd5z\xac\xc5\x05\xf8\x88" + - ";\xf5\x1f\xfe\xe5\xa1\xa1\x9b\xbfs\xect\x84Go\xb4" + - "\x98\xc4\xa3\xff\xfa\xab3_=[\xcc\xff\xbb\xeb1\xfe" + - "\xe1\xbc\xde\xb2\x98\xe0|\xbb\x85\\\xb8\xa5-\xb9t\xd6" + - "\x89\xbe3Q\xbc_\x9c\xbe\x84\x06\xfcr:-\xbe\xe8" + - "\x96.\xb6\xf6\xea\x1b\xce\xd4\x10\x0dg,FY\x9a\xe1" + - "b=c;\xca\xed\xe9\x16\x00g\xe4ow\xde\xf0\xf8" + - "\xf7W\x9e\xf3\x9c\xd8\xd5ejz\x01\xe9r\xdf\x97\xbb" + - "W}f\xd6\xd1sQ3\x92ir+\xf9\x13i\xda" + - "i\xe0\xea\xb3\x9fo\xbf\xef\x07\xe7\xea\xf9NWz." + - "\xcaJ\x9a\xe0ZA\x83\xdfY\xf6\x17\xaf\xa5\xc5\xf4\xfb" + - "U\xd06\xd0\xd8[\xd3\xc3(o\xa3\xb1\x0b7\xa7_" + - " >\xef\xff\xcb\x03\xffz\xfe\xd8u\x1f\xd4\xd8P\x9e" + - "\xd9\x8f\xf2\x8e\x99\xb4\xec\xb6\x99\x82\xbcm\xe6\xe5\x00\xce" + - "\x9d'\xbf\xb8\xee'_y\xef\x83j\x86\xb9\x8a\xdc1" + - "3\x83\xf2Nw\xc6\xbd3\x89\xb0\x0f\xad\xfe\xcf\x8dg" + - "wO\xfbM\xcd\xda\xf3Z\x87Q\xeej\xa5\x91\xd7\xb4" + - "\xbe \x1f\xa7_\xce\xab\xc2\xa3\xf3\xbb7\xbe|>r" + - "TGZ{\x09\x9e\x07\x85o\x9c\xda\xf4\x8b/\xfe6" + - "\x0a\xcf\xd3\xad\xbf\"x^l%xn\x7f\xe7\x91\xeb" + - "\xbe\xba\xf6\xdb\x1fE\x08\xf2V\xeb\x16\x9aj\x97u\x9d" + - "\x15\xccR,w\xa5\xff3wEN-\xe9\xa5\xc5]" + - "e{\x88\xe9\xb6\x96Sm\x96a\x1dV\xc9\xd0-\xd6" + - "\x87\xa84\xf31\x80\x18\x02H\xea0\x80r\x0b\x8fJ" + - "\x81C\x091ED\x914\x12\x0e\xf1\xa8\xd8\x1cJ\x1c" + - "\x97\xa2\x00+\xdd:\x0b@)\xf0\xa8\xac\xe3\x10\xf9\x14" + - "\xf2\x00R\xf9\x01\x00e\x1d\x8f\xcaV\x0e\x9d\x123\x8b" + - "\xaa\xcet\x10\xed\xa5\xa6\x89M\xc0a\x13\xa0c2\xdb" + - "\\\xaf\xf6\x17@d\x11\xb10|\x9b\x8dI\xe00\x09" + - "\xe8\x0c\x19e\xd3Z\xa3\xdb\xa8\x152l\xc0d\x16\x0e" + - "a\x03p\xd8\x008\x96yYfY\x9a\xa1\xafPu" + - "u\x90\x99\x00dY\x82\x8f\x03\x04\xc9\x0b\xfd4'\xcd" + - "\xdf\x03\x9c4O\xc00\xd1\xa0OV\xe9\x13\x07\x81\x93" + - "Z\x05\xc7d\x83\x9ae3\x13\xd7\xe4K\xee\xda\xbc\xa1" + - "w\xa2S\xd6\xbd\x0f\xc8L\xef\x83H\xbbvb\x1f\x86" + - "\xda\xf1\xb5\xda][\xd0\x98n\x8b=\xfa\x80Q\x05y" + - "o=\xc8{+\x90o\x8d@\xbey\x09\x80r;\x8f" + - "\xca]\x1cJ|\x05\xf3ms\x01\x94M<*\xf7p" + - "\xe8\xe4\xdcMz\xf2\x00\x10\xa09\xc0T\xbbl2\x8b" + - "d\x93\x01\xfbxtA\x9f\x0c\xb8q\x84\x99\xa4\xbb\x7f" + - "\x08\xa2j\xe6\x86\x82\x83\x1a\x03\xe9\xa5\xeb4\xcb\xd6\xf4" + - "\xc1\xd5\xae\xbc\xa3\xcf(h\xb9\xf5dU\x93\xabg\xeb" + - "b\x00Di\xea\x8d\x00\xc8I\xd2\x12\x80\x0emP7" + - "L\xe6\xe45+g\xe8:\x03>go\xecW\x0b\xaa" + - "\x9ec\xc1F\x0d\xb5\x1by\x1bd\x999\xc2\xcc+\xd4" + - "\x08}g\xf7\xa9\xa6\xca\x17-\xa5)\xc0q\xe9\x8d\x00" + - "J7\x8fJ_\x04\xc7\x15\x84\xe3r\x1e\x95\x1b\"8" + - "\xae!\x1c\xfbxT\xd6r\xe8\x18\xa66\xa8\xe9\xd72" + - "\xe0\xcd(\x03-[W\x8b\x8c0\xab\xe0\xb1\xd1(\xd9" + - "\x9a\xa1[\xd8\x1cf\x1d@l\x8e %\x8c\xc7\xc9+" + - "|J\xf9\x8c2\xf4\xd9\x19f\x95\x85\x82m)\xb1\xc0" + - "\x92\xe4b\x00%\xc1\xa3\x92\xe2\xb0\xc3dV\xb9`c" + - "sXM\xfc.v\xf5\xe1\x8b\xd00S\x8f\x86\x0b\x00" + - "\x94<\x8fJ\x89C\xac\xa0W\\\x12\x89\x06\xa2my\xcb\xee)\xf9\x7fm\xcc[v\x9fa\xda" + - "(\x00\x87\x02\x10o\x0d\x8bu\x0d\x90O\xf5\xe4\x0b\xec" + - ":\x8d\xd7m\x8c\x03\x87q\x18\xd3\xa9<~\x88\x14\xd8" + - "g?2" + - "U\x9d\xb4h\xaaE+\x8aM\xa6\x1e6t\xaa\x9f\xe2" + - "Q\xb9\xba\xfe\x01n,2\xcbR\x07YMx\x88\xd7" + - "\xc5Dg9\xb2:\xc3\xbc$s\x85\xc9,\xa1\\\xb0" + - "I\x8b&\xc7\xf1\xd4 n\xcd\xe6Q\xb9\x8a\xc3$~" + - "\xe4xz\xcc{ <\xa36f\x9a\x86\x89\xcda\x12" + - "\xae@\x92\xabl\x80\x86\xde\xcdlU+ \xb9eP" + - "\xa8V\x017^\\\x09a\xf3\xc4\xb3;\xc8;\x8a\xa3" + - "N\x8a\xe8\xdd\xcc\xa32\x93Cg\xd0Ts\xac\x8f\x99" + - "\xa8\x19\xf9\x95\xaandy\x96\xab!\xeb\xe4\x89n\xea" + - "\xf2\xc3\xb6 \x985\xf6|\x93U@\xa8L\xefk\xf3" + - "tN\x05:\xdf1+L\xc6\xc11o\xee\x0f\xb3E" + - "\x10\x0fw\x90\xb3\xdc\xc5\xa3\xb2+\x92WvR\xe4\xbc" + - "\x9fG\xe5\x1b\x1cJ\xb1X\x0ac\x00\xd2#\xc4\x92]" + - "<*\xfb\xb8\xd1)\x9b\x8d0\xdd\xee\xd6\x06A`V" + - "(%\x15\xbb\xb5A\x06\xbcu\xa9\xb151\x0e\x1eF" + - "\xbfe\x14\x98\xcd\xbaY\xae\xa0\x92\xcb\x8d0\xef{\x85" + - "\x8c\xfe\xa1\x8e\xc5\xdbL\x8d\xf7\x10\x7fE\xbfJ\x8a\xd0" + - "aV\xe8\xb8\x02\x8b\x147ch\xeb-\xee\x05\x83\x1a" + - "\x0e\x84\x1eS\xe1\x01Z\xbf\x93\xa0\xe3\xda\x8c\xa3\x9c\x7f" + - "I\xe8u>)\xe6-\x0e\x03BP\x13\xc4\x80\xc3\x18" + - "`G\xce]\xb0&\x14\xc6\xc6\xd3\xaa\xc3S\xcb\x03\x8e" + - "\x8a0\xff\x1a\x8b\xfe\xdd_\x92\x0e\x00'%\x05\xc7\xd7" + - "\x1c\xfd\xf9BMA\x15\x1b+\xca\xac*\xd9\x9a`\xe8" + - "\x16\xed\x15\xe1\xff\xe2z\xfc7C\xfe\xfb\x09m\xc7\x96" + - "(\xfd+\x09m\xe7\x9e\x90\xe9R\x8c\xf3\xe8\xbf\xf7\x00" + - "\x80\xb2\x8fG\xe5\xdb\x1cvx\xb5\x166\x87=\x9b\x0a" + - "e\xbd\x8ab\xb9\x01m9\xb5\x10&=\xc7d\xa5\x82" + - "\x9acK\xb1R=\x01\"p\x88\xae\x9f\x14K&\xb3" + - ",\xd4\x0c])\xab\x05\x8d\xb7\xd7\x07\x15\xaf^.\xf6" + - "\x99lDC\xa3lu\xd96+\x0a%\xdb\xba\x98z" + - "8\x04\x88\x82\xa4\xa0\x15\xac\xaa\x1c97\xa4B\x00\xd0" + - "\xbc\xe10\x0f\x88\xe5\xb2\x16$\x00\xa7`\xe4\xdc\x93\x05" + - "q\xa5Z\xac\xcd\x03\x0d\xe3\x06\xacQ\xe1\xceOK\xbf" + - "O\xf5\xdb\xd8W&2\xdd\xbdSDT\xa68\xd0\xc9" + - "\xa3\xb2<\xa2r\xcf\x82\x88\x1d\xbe\xca+\xfaC;\x84" + - "?g\xeb}\xad\xdaX\x91\xd2\x97\x0ff\xc5\x98.\x10" + - "\xae\x0f\xc7L4\xaa\xb8\xde\xb4\xdc\xc8\xa9\x85\xea` " + - "V\xa7\xb5h\x01r\xf1\x8e\x1e\xddtU\xa9\xcd\x85\x95" + - "\x80\xb9\xda_X^\x8f\xbd\x00\xd9u\xc8cv+\x86" + - "\xd8\xc8\x9bq\x09@\xf6v\x92\xdf\x85!<\xf26L" + - "\x03d7\x91\xfc\x1e\x0c\xee\x93\xf2\x0e<\x08\x90\xbd\x87" + - "\xc4\x0f\xd3\xf0\x18\xef\xfa\xa1\xbc\xdb]~\x17\xc9\xf7\x91" + - "<\x1eKa\x1c@\xde\x8bs\x01\xb2\x0f\x93\xfc0\xc9" + - "\x1b\xb8\x146\x00\xc8\x87p\x18 \xfb$\xc9\x9f!\xb9" + - "\x10O\xd1\x95Z>\x82&@\xf6\xefH\xfe}\x92'" + - "\xa6\xa70\x01 \x1fu\xe5\xcf\x91\xfcG$o\x9c\x91" + - "\xc2F\x00\xf9E\xdc\x02\x90\xfd!\xc9_#\xf9$L" + - "\xe1$\x00\xf98\xee\x01\xc8\xbeF\xf2\x7f#\xf9e\x0d" + - ")\xbc\x0c@\xfe\xb9\xab\xcf\x09\x92\xff\x9a\xe4M\xb1\x14" + - "6\x01\xc8\xbf\xc4\x03\x00\xd9_\x93\xfc\xbfI\x9e\x14R" + - "\x98\x04\x90\xdfv\xed:K\xf2\x04Wu\x9d\xf3i\\" + - "ug\xe3\x0d+\xe0\x09\xab\x04\x16\xf4|\xac\xcf\x10\xe9" + - "^\x86b\xd8(\x06D\x11\xd0)\x19Fa\xe5h\xf7" + - "\x10mu\xd0\xf2\xef\x87\xcda\xa7\x0c\x90\x84A\xc5\x05" + - "\xa2\xa1\xf7\xe4\x83\xe8S\x1d\xea|M4\xab\xabl\x1b" + - "\xe5\x12\xb4\x11\x17\xf3A\xa03\xcb\xfa2\xd3(\xaeF" + - "f\x165]-\x8c\x13\x02\x1b\x81\xc3F\xa8\xc4!\x7f" + - "\xed\xb1\xe3\xe1\x85o\xbb\x01\xa3\xb9jF\xb7\x95\x16\xaf" + - "V\x07/&8.\x08\x13\xa5\xa8G\xa2`\xdb\x88Z" + - "(\x7f\x9c\x988\xba\x88\xcbtxE\xe0xw\x04\xbf" + - "\xe1U\x15\xbf\xea\x944kj\x8b\x82\x0c\xb3\xda\x82\xce" + - "O\xc4\xe0\x83a\xe0\xf7\xed]4+ra*\xa86" + - "\xb3\xec\xae\x12\x96\x0a\x1a\xcb\x7f\x81\x99b\xb4N\xa8[" + - "\x06\xc5\xc6\xbb\x1b\x8c\xae\xad0\xd2\xd5'\xc3\xb9\x8a\xc1" + - "\x17\x8d\xe7 \xb3\xbd_=\xfa\x80A\xc5\x8f\x10\xad\xf8" + - "&6;\xc3,\xf1b\xce\"\xecT\x8e_\xb1N$" + - "ZgX\x9b\xcb\x85\xb1\xee\x7fu\xd6\xabSQ\xfa\xd7" + - "\x99\xc8]\x9f\xc8\xbd\x96Ge(Bn\xd6[\xe7\xae" + - "\x9f\x09\x9b|\x12\xcfU\xba|\x94~K<*\xb7s" + - "(\xaae{\x08\x9b\xc3\xc7\x9fQ \x8cnD\x11\xd7" + - "{\xf4<\x03\\\xe7\xbbk$)\x07\xaf\x12\x1f\x0b\xc6" + - "\x0b\x16\xd2\x16\x8c{\x80Ak\xbej\xe7\x0b\xf6\x1b:" + - "\xbcM\x89\xb7\xd3\xdd\xd2\xd6\x7f\xf5@\xbf\xad-\x1d\xda" + - "\x00\x9c\xf4-\x01\xc3V>\xfa\x9d{i\xaf\x09\x9c\xb4" + - "[@.x\x97B\xff\xfdI\xdaq7p\xd26\x01" + - "\xf9\xe0Y\x09\xfdV\xef\xfc\xf5\x93\x108\xe9\x0e\x01c" + - "\xc1{\x1e\xfa\x8db\xe9\xd6a\xe0$M\xc0x\xf0b" + - "\x85\xfe\x0b\x86t\xf3\x16\xe0\xa45aC\x13:<;" + - ":\xd1\xf19\x0fm.\xebG\xb77\xbdQ\x00\x9d\xe8" + - "\xf8\xd7+\xfeB\xf7+w\x94\xdf\xa1\x031\xa7\xda\xac" + - "\x93J^/\xc0a%\xc2A'*1\x8c\xf4\xc9\x01" + - ".\xb5\xbfQ\xe3'\x13,D\xfd\xf9\x1f3\xe6\xf2\xf5" + - "\xb4\xa6}\x82Nod]\xaa\xad\x9bxT\xa6s\xe3" + - "\x96\xd3\xb1\x0bY\xe1\x93_\xa4\xc9\xb4\xfe\x1f\x06\xeb\x1f" + - "\xa7p\xfd#\x1e\x95\x13\x11\xb7~\x9d\x84\xaf\xf2\xa8\xbc" + - "\x19)G\xdf _?\xc1\xa3\xf2~\xd8\xbc\x7f\xf7n" + - "\x00\xe5}\x1e3\x91JK\xfa\x90\x06\xfe\x96\xea\x11\xb7" + - "\xceB\xaf\xce\x8a\xe3\x03\x00\xd9\x04\xd5))\xb7\xce\x8a" + - "yu\x96\x84\xfd\x00\xd9f\x92\xcf\x8c\xd6Y3\xf0F" + - "\x80\xect\x92\xcf\xc6\xd1Wf\xa1l\x86\xe5o\xc1\x18" + - "\\\xae\xe9u\x93\xb7\xff\x9a\x80\xf62U+\x94M\x06" + - "a\xedP\x096\xdd\x91r\xc6{f\xf0:\x8aY\"" + - "a\x1e\xad\xa0\xdb8\x81f\xc5X\x99\xac`\x94\xf3\x03" + - "\x05\xd5d\xf9,3\x05/ \xf4\xf1q%\x81\x91W" + - "\x7f\x80\xf0u6B\xf613\xe3R\xd34\xd0\xac\xba" + - "j,\x08\xaf\x1a\xc1M\x83nL\xd7\xf1\xa8\xac\xa6\xa3" + - "\xed\xf4\x8eV\xe9\x0f/Gm9\xb5l\xb1\x1aL\x80" + - "gf\xd0\xb0\xb2\x86\x8cr!\x9fa \xd8\xe6\xfa*" + - "H\xc7\xad\xfe\xb3L\xf4#a\x93\x1b\x09\xfd7F\xf4" + - "\x9f\x12%e\x0fp\xd2\x0a\x8a\x84\xfes\x17\xfaO\xdd" + - "R\xd7A\xe0\xa4k(\x12\xfa/\xbd\xe8?_J\xf3" + - "_\x02N\x9a\x1fy\x85\xf1\xf1\xa9y\x85\xf1>\xb8\xfe" + - "@\x1f*\x09\x95\xab\xce\xa8\x14\xa1\xa2-\x85K\xe8\xd1" + - "x\x095r\x9c\x13z\xba\xb8\xe8\x8e\x7f\xf0\x8f&U" + - "1\xa7\xf1R{a~j\xfc\xff\x00\x00\x00\xff\xff\xe5" + - "\xf9\xce\x8f" +const schema_db8274f9144abc7e = "x\xda\xccZ}t\x1c\xd5u\xbfwfW#\x19\xad" + + "G\xc3,\x96\xb5\xc7B\x8d\x8e]j'\x06d\xd7)" + + "u\xd3H2\xb2\x83\x84?4\xbbV\x0e1&\x87\xd1" + + "\xee\x934\xea\xee\xcc23+l\x07\xe2\x0fl\x0c\x1c" + + "B0\xb1\xf9pBc\x9b\xd2\x9e\x98$\x85`\x9a\xba" + + "\x07Z\x9c\x86\x10Cpp\x0e\xa4&\x86\xa6\x89\xe3\xb6" + + "\xf8\x98R\x0cn\x8e\xdb\x98\xe9\xb93;\x1f\xda]$" + + "+\xce\x1f\xf9ou\xe7\xbe\xf7\xee\xfb\xbd\xdf\xfdx\xf7" + + "\xe9\xea;\x1a\xba\xb8\x8e\xf8F\x11@9\x10\xafs\xd8" + + "\xbc\x9fl\xd83\xe7\x9f\xb7\x80\x92Bt\xbe\xf8l_" + + "\xf2\x9c\xbd\xe58\xc4y\x01`\xe1\xe3\xc2\x06\x94\x0f\x0a" + + "\x02\x80\xfc\x8c\xf0\x1f\x80\xce\x9d3\x9e\xfc\xfa\xe3Kw" + + "\xde\x01R\x8a\x0f\x95\x01\x17>R\xdf\x87\xf2\xb7\xeaI" + + "\xf3\x1b\xf5\xdb\xe5\x86\x06\x01\xc0\xb9^\xba\xea\xc6\xe4\xab" + + "GH;:u\x8c\xa6~\xaf~\x1e\xcaHj\xf2\xf9" + + "z\x9a\xfaS\x85\x1f\xef\xfb\xe4\xae\x97\xb7\x82\x94\xe2\xc6" + + "M\xfdv\xc3\x06\x94\xcf\xbb\x9a\xe7\x1aV\x01:\xef\xef" + + "\x9c\xf9\xc4\xde#?\xdc\x06\xd2\x15\x08eK\xa5i?" + + "C@y\xce\xb4\xbf\x05t^9{\xe3\x07\x07~\xb0" + + "\xe8N\x90\xe6\x92\x02\x92\xc2\x0b\xd3\xda9@\xf9\xadi" + + "\x9d\x80\xce\xa9\xd3\xff\xb7\xfd\x0bsW\xde\x0f\xca\\\xe4" + + "\x00\xe2\x1ci\x9c\x9f\x96\"\x8d\xcb.!k:\x97\xbd" + + "r0\xb5\xf0\xc1\x9d\x15\xa6\xbb\x8ag/\x99\x87r\xbc" + + "\x91\x0c\xc2\xc6[\x01\x9dO\xff\xc9\xd3\xf7\xf5<\xb8i" + + "\x17HW\x05\xeb\xb1\xc654\xdb\xe6FZ\xef\x7f\xa6" + + "\x7f\xf5H\xe9\xda\xef>X6\xc8\x9deo\xe36\xe7\xe6\xef\xbd\xf0\xf4C\xa0\xccG" + + "t\xde\x1c\xfc\xf8\xeb\xfc\xa3\xfb\x8f\xc3\x00\x0ad\xdf\xc2" + + "\x96\xc4>\xda\xdd\xdc\x04\xe9\xfe\xf8\x13\xcf\xfe\xc3\xfdO" + + "o\xff*(W \x02\xb8h~)\xf1\xbf\xa4\xb07" + + "A\xab\xed<\xf6\xdc\xca\xc2\x8e\xdd\xfb<|\xdc\xef\x87" + + "\x13\x1c\x071gk\xef\xaf\x0b\x03\x8fe\x1e+#\x17" + + "\xa7O\xcf%\xce \xe0\xc2\xa3\x896\x04t\x16\xfd\xec" + + "\xe4\xaa\x15\xdf\x19\xfa\x9b\xc8\xd8w\xa6o\xa0\xb1\xdb\x87" + + "\xce\x1cjJ\x17\x9e\xa8@\xc4\xdd\xec\xdb\xd3\xf7\xa3\x8c" + + "\xa2{\x98\xd3\xc9\x84o]~}\xc3\xba\x93\xcb\x9e\x04" + + "i\xbe?M\xab\x98\xa6i\xc6^z\xec\x0f\xe7\xbct" + + "\xebS\xa0\\\x85\x01X-\xf4\x0d\xe5E\"\xed/v" + + "|\xce\xfe\xe7~~\xdf\x81*\x8e\xed\x107\xa0\xfc\xb8" + + "\xbb\xca^\xf13\xf2Q\xfa\xe5\xc4n\xe2?T\x1f\xfe" + + "\xa7\x03\x95\xfcu1>(\x0e\xa2\xfc\x8a\xe8\" \xba" + + "\xfb\xbb\xe7\xd0\xee\x8f\xd7\x7f\xfd\xfdgj\xaa\xbf\xd74" + + "\x88r\\r\x0fV\"\"]\xd6\x8bo>\xdf\x11\xfb" + + "n\x94i\x8fK\xa7\x08\xea\x83\xaeB\xeb;K\x12\xfa" + + "\xbb[\x9e\xaf\x00\xc5U\xfc\xdc\xa5}(\x17.\xa5\xd9" + + "\xb4KI9\xf6\xc9\xd1\xed\xd2\x89\x9f\xbe\xe0\x81\xe2\xed" + + "<.\x8f\xd2\xce?&\x13j}7~\xe5\x81\xf8\xc9" + + "\xaf\xbcH\xc6E| Nn\xb5\xb0[6Q\x1e\x90" + + "\xe9\xa7\"7\xf3\x80N\xea\xc9?\xfb\xf6\x92\xdc\x1b/" + + "\xd7\xe0\xa8|t\xc6\x19\xf9\xad\x19\xf4\xeb\x8d\x19\x04\xea" + + "\x89\xf9O}\xe1\xed/\x1d}\xad\xbc\x13w\xedE\xcd" + + ".iz\x9bi\xedsk\xf7\\\xaf97\x1c\xaf\x04" + + "\xc6\xd5\xd4\x9a\xbf\x83\xf2\xe6f\x9a\xee\xf6f\x9a.`" + + "h-\xed_4\x8f\xa2|\xd6\xd5~\xcf\x9d\x9b;\xa9" + + "\xb6l\xfa\xe9\xa7\xdf\x8c\x90\xeal\xf3/\x11b\xce\xca" + + "\xcf\xde8\xdap\xfb\x89\x13Q\xb3\xdenv\x01>\xef" + + "\x0e\xfd\xc7\x7fyh\xe4\xa6o\x1f9\x19%\xd2L\x93" + + "\x88\xf4_\x7f}\xea\xcb\xa7\x0b\xb9\x7fw]\xc6?\x9c" + + "\x96\x99\x8b\x09\xce\x8e\x99\xe4\xc3\xcdm\x89\xa5\xed\xc7\xfa" + + "OE\xf1N\xb4,!\x859-4\xf9\xa2\x9b\xbb\xd9" + + "\xdakn8U\xc5\xb4\xa5-\x8bQ\x1ehq\xb1n" + + "\xd9\x8e\xb2\x96j\x06p\xc6\xfen\xc7\x0dO|\x7f\xe5" + + "\x19\xcf\x8b][>\x97Z@\xb6\xdc\xf7\xc5\x9eU\x7f" + + "\xda~\xe8Lt\x1bJ\x8a\xfcJf)Zi\xe8\x9a" + + "\xd3\x9f\x99s\xdf\x0f\xce\xd4r\x9em\xa9y(\xefJ" + + "\x11\\;H\xf9\xdde\x7f\xf9ZJL}P\x01m" + + "\x1d\xe9>\x93\x1aE\xf9p\xca\x0dh\xa9\x17\x89\xd0{" + + "\xffj\xdf\xbf\x9e;r\xdd\xd9\xaa=\x1c\x9cE\xdc\x9f" + + "E\xd3\x1e\x9e%\xc8\x87g]\x01\xe0\xdcy\xfc\xf3\xeb" + + "~r\xc7\xfbg+\x19\xe6\x1arhV\x1a\xe5\xd7\xdd" + + "\x11Gg\x11a\x1fZ\xfd\x9f\x1bO\xef\x9a\xf1\xeb\xaa" + + "\xb9oi\x1dEy[+inn}Q\xbe\xecr" + + "\xf2\xc4W\x85\xc7:z6\xbe|.rT\xe7[\xfb" + + "\x08\x9e\x07\x85\xaf\x9d\xd8\xf4\xf3\xcf\xff&\x0a\xcf\xb9\xd6" + + "_\x12<\x89\xcb\x09\x9e\xdb\xde}\xe4\xba/\xaf\xfd\xe6" + + "\x87\x11\x82\xcc\xbf|\x0b\x0d\xb5K\xba\xce\xf2f1\x96" + + "\xbd\xca\xff\x99\xbd2\xab\x16\xf5\xe2\xe2\xee\x92=\xc2t" + + "[\xcb\xaa6K\xb3N\xabh\xe8\x16\xebGT\x9a\xf8" + + "\x18@\x0c\x01$u\x14@\xb9\x99G%\xcf\xa1\x84\x98" + + "$\xa2H\x1a\x09GxTl\x0e%\x8eKR\x84\x95" + + "ni\x07P\xf2<*\xeb8D>\x89<\x80Tz" + + "\x00@Y\xc7\xa3\xb2\x95C\xa7\xc8\xcc\x82\xaa3\x1dD" + + "{\xa9ib#p\xd8\x08\xe8\x98\xcc6\xd7\xab\x83y" + + "\x10YD,\x8c\xdejc\x028L\x00:#F\xc9" + + "\xb4\x06t\x1b\xb5|\x9a\x0d\x99\xcc\xc2\x11\xac\x03\x0e\xeb" + + "\x00'\xda^\x86Y\x96f\xe8+T]\x1df&\x00" + + "\xed\xac\x9e\x8f\x03\x04\xd9\x0b\xfd<'u\xec\x06N\x9a" + + "/`\x98i\xd0'\xab\xf4\xb1\xfd\xc0I\xad\x82c\xb2" + + "a\xcd\xb2\x99\x89\x03\xb9\xa2;7o\xe8]\xe8\x94t" + + "\xef\x032\xd3\xfb \xd2\xaa]\xd8\x8f\xa1u|\xb5u" + + "\xd7\xe65\xa6\xdbb\xaf>dT@\xdeW\x0b\xf2\xbe" + + "2\xe4[#\x90o^\x02\xa0\xdc\xc6\xa3r\x17\x87\x12" + + "_\xc6|\xdb<\x00e\x13\x8f\xca\xbd\x1c:Yw\x91" + + "\xde\x1c\x00\x04h\x0e1\xd5.\x99\xcc\"\xd9t\xc0~" + + "\x1e]\xd0\xa7\x03n\x1cc&\xd9\xee\x1f\x82\xa8\x9a\xd9" + + "\x91\xe0\xa0&@z\xe9:\xcd\xb25}x\xb5+\xef" + + "\xec7\xf2Zv=\xed\xaa\xd1\xb5\xb3u1\x00\xa2t" + + "\xd9\x1a\x00\xe4$i\x09@\xa76\xac\x1b&sr\x9a" + + "\x955t\x9d\x01\x9f\xb57\x0e\xaayU\xcf\xb2`\xa1" + + "\xba\xea\x85\xbc\x052\xcc\x1cc\xe6\x95j\x84\xbe\xb3\xfb" + + "US\xe5\x0b\x96\xd2\x18\xe0\xb8t\x0d\x80\xd2\xc3\xa3\xd2" + + "\x1f\xc1q\x05\xe1\xb8\x9cG\xe5\x86\x08\x8e\x03\x84c?" + + "\x8f\xcaZ\x0e\x1d\xc3\xd4\x865\xfdZ\x06\xbc\x19e\xa0" + + "e\xebj\x81\x11fe<6\x1aE[3t\x0b\x9b" + + "\xc2\xac\x03\x88M\x11\xa4\x84\xc98y\xa5O)\x9fQ" + + "\x86>;\xcd\xac\x92\x90\xb7-%\x16\xec$\xb1\x18@" + + "\xa9\xe7QIr\xd8i2\xab\x94\xb7\xb1),'~" + + "\x17\xab\xfa\xf0Eh\x98\xaeE\xc3\x05\x00J\x8eG\xa5" + + "\xc8!\x96\xd1+,\x89D\x03\x1e=\x16\xde\xb2\x1b@" + + "\xb1yT6q\xe8X\xde\"\xbd\x809\x1f\xd1\xb6\x9c" + + "e\xf7\x16\xfd\xbf6\xe6,\xbb\xdf0m\x14\x80C\x01" + + "\x88\xb7\x86\xc5\xba\x87\xc8\xa7zsyv\x9d\xc6\xeb6" + + "\xc6\x81\xc38L\xe8T\x1e?D\x0al\x9e\xb7\xfb\xbb" + + "\x99Kd\xf8#\x1e\x95?\x8e\xec\xa6\x83\xe2\xd8\xd5<" + + "*\x9f\xe2\xd0Q\xb3Y\xa3\xa4\xdb\xab\x81W\x87+8" + + "\x9fa fM\x16\xd2\xc1_\xb6\xbe\x86[\x1b\xfa\x90" + + "6\\2U;\x02x\xa9\x98Sm6\xee\x93{\xce" + + "y\xfe\x02\xce9\xa8+\xa6|\xce~d\xaa8i\xd1" + + "T\x0bV\x14\x9bt-l\xe8T?\xc1\xa3rM\xed" + + "\x03\xdcX`\x96\xa5\x0e\xb3\xaa\xf0\x10\xaf\x89\x89\xce\xb2" + + "\xb4\xeb4\xf3\x92\xcc\x95&\xb3\x84R\xde&+\x1a\x1d" + + "\xc73\x83\xb85\x9bG\xe5j\x0e\x13\xf8\xa1\xe3\xd91" + + "\xff\x81\xf0\x8c\xda\x98i\x1a&6\x85I\xb8\x0cI\xb6" + + "\xbc\x00\x1az\x0f\xb3U-\x8f\xe4\x96A\xa5Z\x01\xdc" + + "dq%\x84\xcd\x13\xcf\xee$\xef(\x8c;)\xa2w" + + "\x13\x8f\xca,\x0e\x9daS\xcd\xb2~f\xa2f\xe4V" + + "\xaa\xba\x91\xe1Y\xb6\x8a\xac\xd3\xa7\xba\xa8\xcb\x0f\xdb\x82" + + "`\xd4\xc4\xe3MV\x06\xa1<\xbc\xbf\xcd\xb39\x19\xd8" + + "|{{\x98\x8c\x83c\xde<\x18f\x8b \x1e\xdeM" + + "\xcer\x17\x8f\xca\xceH^\xd9A\x91\xf3~\x1e\x95\xaf" + + "q(\xc5bI\x8c\x01H\x8f\x10Kv\xf2\xa8\xec\xe1" + + "\xc6\xa7l6\xc6t\xbbG\x1b\x06\x81Y\xa1\x94L\xec" + + "\xd1\x86\x19\xf0\xd6\xc5\xc6\xd6\xfaI\xf00\x06-#\xcf" + + "l\xd6\xc3\xb2y\x95\\n\x8cy\xdf\xcbd\xf4\x0fu" + + "\"\xde\xa6\xab\xbc\x87\xf8+\xfaUR\x84\x0e\xed\xa1\xe3" + + "\x0a,R\xdcL`\xad7\xb9\x17\x0c\xaa8\x10zL" + + "\x99\x07h\xfdN\x82\x8e\xbbg\x1c\xe7\xfcKB\xaf\xf3" + + "I1\x7fq\x18\x10\x82\x9a \x06\x1c\xc6\x00;\xb3\xee" + + "\x84U\xa106\x99U\x9d\x9eY\x1epT\x84\xf9\xf7" + + "X\xf4/\xff\x92\xb4\x0f8)!8\xbe\xe5\xe8\x8f\x17" + + "\xaa\x0a\xaa\xd8DQfU\xd1\xd6\x04C\xb7h\xad\x08" + + "\xff\x17\xd7\xe2\xbf\x19\xf2\xdfOhwo\x89\xd2\xbf\x9c" + + "\xd0v\xec\x0e\x99.\xc58\x8f\xfe\x8f\xee\x03P\xf6\xf0" + + "\xa8|\x93\xc3N\xaf\xd6\xc2\xa6\xb0iS\xa6\xacWQ" + + ",7\xa0-\xab\xe6\xc3\xa4\xe7\x98\xac\x98W\xb3l)" + + "\x96\xab'@\x04\x0e\xd1\xf5\x93B\xd1d\x96\x85\x9a\xa1" + + "+%5\xaf\xf1\xf6\xfa\xa0\xe2\xd5K\x85~\x93\x8di" + + "h\x94\xacn\xdbf\x05\xa1h[\x17R\x0f\x87\x00Q" + + "\x90\x14\xb4\xbc\x0bP\xa4`\xa2\xca\xb1\x8bGey\x04" + + "\xa0^\xca\x91\xd7\xf1\xa8\xac\x0e\x01R\xbe\x07\xa0\xac\xe6" + + "Q\xb9\x99C\xb1T\xd2\x82\xa4\xe0\xe4\x8d\xac{\xda " + + "\xaeT\x0b\x95\xb9\xa1\xd7\xe2\xd2\xac`\xd8,\xbf\xde\xe3" + + "h.\xdc\xf1\x85\xc6\xe6\x8a \xe9'\xb3\xdf\xa7\xaao" + + "\xe2\x8b\x16\x81\x03\x15\xb8\xb7\xd7\xc2}Ad\x1f\xbe\xc9" + + "+\x06\xc3}\x08\x7f\xc1\xd6\xfbV\xb5\xb1\x02%=\x1f" + + "\xee\xf2f\xbaA\xb8>\xd4\x99j,r}p\xb9\x91" + + "U\xf3\x95!D\xacL\x86\xd1\xb2\xe5\xc2\xc3Ct\xd1" + + "U\xc56\x17V\x02\xe6\x1a\x7fby=\xf6\x01d\xd6" + + "!\x8f\x99\xad\x18b#o\xc6%\x00\x99\xdbH~\x17" + + "\x86\xf0\xc8\xdb0\x05\x90\xd9D\xf2{1\xb8\x85\xcaw" + + "\xe3~\x80\xcc\xbd$~\x98\xd4c\xbc\xeb\xbd\xf2.w" + + "\xfa\x9d$\xdfC\xf2x,\x89q\x00\xf9Q\x9c\x07\x90" + + "y\x98\xe4\x07H^\xc7%\xb1\x0e@~\x0aG\x012" + + "O\x92\xfcY\x92\x0b\xf1$]\xc4\xe5\x83h\x02d\xfe" + + "\x9e\xe4\xdf'y\xfd\xcc$\xd6\x03\xc8\x87\\\xf9\xf3$" + + "\xff\x11\xc9\x1bZ\x92\xd8\x00 \x1f\xc6-\x00\x99\x1f\x92" + + "\xfc5\x92O\xc3$N\x03\x90\x8f\xe2n\x80\xcck$" + + "\xff7\x92_R\x97\xc4K\x00\xe4\xb7\\{\x8e\x91\xfc" + + "W$o\x8c%\xb1\x11@\xfe\x05\xee\x03\xc8\xfc\x8a\xe4" + + "\xffM\xf2\x84\x90\xc4\x04\x80\xfc\x8e\xbb\xaf\xd3$\xaf\xe7" + + "*.\x81>\x8d+nz\xbca\x05dP\xc9$D\xeb\xc4\xa9\x8d" + + "N3K\xbc\x90\xb3\x08\xfb\x9b\x93\xd7\xb9S\x89\xd6i" + + "\xd6\xe6ra\xa2[c\x8d\xf9j\xd4\xa1\xfe%(\xd2" + + "! r\xaf\xe5Q\x19\x89\x90\x9b\xf5\xd5\xe8\x10\xa4\xc3" + + "\xd6\xa0\xc4s\xe5\xde \xa5\xdf\"\x8f\xcam\x1c\x8aj" + + "\xc9\x1e\xc1\xa6\xf0\xcdh\x1c\x08\xe3\xdbW\xc4\xf5^=" + + "\xc7\x00\xd7\xf9\xee\x1aI\xca\xc1c\xc6o\x05\xe3G\x96" + + "\xdf\x16Lz\x80AC\xbfb\xe5\x8f\xecRtz\x8b" + + "\x12og\xba\x05\xb1\xffV\x82~3\\zj\x03p" + + "\xd27\x04\x0c\x1f\x00\xd0\xef\xf7K\x8f\x9a\xc0I\xbb\x04" + + "\xe4\x82\xe7,\xf4\x9f\xad\xa4\xbb\xef\x01N\xda& \x1f" + + "\xbcF\xa1\xdf \xeeX?\x0d\x81\x93n\x170\x16<" + + "\x03\xa2\xdf^\x96n\x19\x05N\xd2\x04\x8c\x07\x0f]\xe8" + + "\xbf{H7m\x01N\x1a\x08\xdb\xa0\xd0\xe9\xed\xa3\x0b" + + "\x1d\x9f\xf3\xd0\xe6\xb2~|S\xd4\xd3\x02\xe8B\xc7\xbf" + + "\x94\xf1\x1fu+s\xb5\xfc\xbe\x1e\x88Y\xd5f]T" + + "({\x01\x0e\xcb\x11\x0e\xbaP\x89a\xa4\xbb\x0ep\xb1" + + "]\x91*?\x99b!\xea\x8f\xff-c._\xcbj" + + "Z'\xe8\x0fG\xe6\xa5\x8a\xbc\x91Ge&7I\xc1" + + "]3tz\x06\xfb\xe4\x17i0\xcd\xff\x07\xc1\xfcG" + + ")\\\xff\x88G\xe5X\xc4\xad_'\xe1\xab<*o" + + "F\xca\xd17\xc8\xd7\x8f\xf1\xa8|\x10\xb6\xfc\xdf\xbb\x07" + + "@\xf9\x80\xc7t\xa4\xd2\x92\xce\x93\xe2o\xa8\x1eq\xeb" + + ",\xf4\xea\xac8>\x00\x90\xa9\xa7:%\xe9\xd6Y1" + + "\xaf\xce\x92p\x10 \xd3D\xf2Y\xd1:\xab\x05\xd7\x00" + + "df\x92|6\x8e\xbfh\x0b%3,\x7f\xf3\xc6\xf0" + + "rM\xaf\x99\xbc\xfd7\x08\xb4\x97\xa9Z\xbed2\xa8" + + "\xbc\x82\xf4\xf6D\xca\x19\xefq\xc2\xebCf\x88\x849" + + "\xb4\x82\x1e\xe5\x14Z\x1c\x13e\xb2\xbcQ\xca\x0d\xe5U" + + "\x93\xe52\xcc\x14\xbc\x80\xd0\xcf\xc7\x95z\x8c\xfc\xb3\x00" + + "@\xf8\xa8\x1b!\xfb\x84\x99q\xa9i\x1ahV\\5" + + "\x16\x84W\x8d\xe0\xa6\xb1&\xbc\xe1I\\W\xf9\x8a7" + + "\x18^\x8e\xda\xb2j\xc9bU\x98\x00\xcf\xcc\xa0\xcde" + + "\x8d\x18\xa5|.\xcd@\xb0\xcd\xf5U\xb7\xba\xd8d\xd1" + + "W\xf4#a\xa3\x1b\x09\xfd\x97I\xf4\x1f %e7" + + "p\xd2\x0a\x8a\x84\xfe#\x19\xfa/\xe4R\xf7~\xe0\xa4" + + "?\xa7H\xe8\xbf\x0f\xa3\xff\xe8)u\xbc\x04\x9c\xd4\x11" + + "y\xbb\xf1\xf1\xa9z\xbb\xf1>\xb8\xfe@\x1f\xca\x09\x95" + + "\xab\xcc\xa8\x14\xa1\xa2\x8d\x88\x8b\xe8\xecx\x095r\x9c" + + "Sz\xf0\xb8\xe0w\x82\xe0\xffS*bN\xc3\xc5v" + + "\xd0\xfc\xd4\xf8\xff\x01\x00\x00\xff\xff|\xba\xdf\xe8" func init() { schemas.Register(schema_db8274f9144abc7e, From 3254d0817364ac2c40d5d6bf015ea8c9b3c41d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 28 Apr 2022 12:00:47 +0100 Subject: [PATCH 062/238] TUN-6014: Add remote config flag as default feature --- cmd/cloudflared/tunnel/configuration.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 14fd430c..778ec6b3 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -41,7 +41,8 @@ var ( LogFieldHostname = "hostname" - secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} + secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} + defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories @@ -225,7 +226,7 @@ func prepareTunnelConfig( return nil, nil, errors.Wrap(err, "can't generate connector UUID") } log.Info().Msgf("Generated Connector ID: %s", clientUUID) - features := append(c.StringSlice("features"), supervisor.FeatureSerializedHeaders) + features := append(c.StringSlice("features"), defaultFeatures...) if c.IsSet(TunnelTokenFlag) { if transportProtocol == connection.AutoSelectFlag { protocolFetcher = func() (edgediscovery.ProtocolPercents, error) { @@ -243,7 +244,6 @@ func prepareTunnelConfig( return preferQuic, nil } } - features = append(features, supervisor.FeatureAllowRemoteConfig) log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API. Defaulting to protocol: quic") } namedTunnel.Client = tunnelpogs.ClientInfo{ From e8fe34773c61ef7da46423ea06458546299437d2 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 2 May 2022 09:34:39 +0100 Subject: [PATCH 063/238] TUN-6158: Update golang.org/x/crypto --- go.mod | 2 +- go.sum | 4 +- .../x/crypto/chacha20/chacha_s390x.go | 1 + vendor/golang.org/x/crypto/cryptobyte/asn1.go | 7 +- .../golang.org/x/crypto/cryptobyte/builder.go | 14 +- .../curve25519/internal/field/fe_amd64.go | 3 + vendor/golang.org/x/crypto/ed25519/ed25519.go | 188 +- .../x/crypto/ed25519/ed25519_go113.go | 74 - .../ed25519/internal/edwards25519/const.go | 1422 ------------- .../internal/edwards25519/edwards25519.go | 1793 ----------------- .../x/crypto/internal/poly1305/sum_generic.go | 5 +- .../x/crypto/internal/poly1305/sum_s390x.go | 1 + vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 2 +- vendor/golang.org/x/crypto/ssh/certs.go | 85 +- vendor/golang.org/x/crypto/ssh/cipher.go | 10 +- vendor/golang.org/x/crypto/ssh/client.go | 18 +- vendor/golang.org/x/crypto/ssh/client_auth.go | 132 +- vendor/golang.org/x/crypto/ssh/common.go | 76 +- vendor/golang.org/x/crypto/ssh/doc.go | 5 +- vendor/golang.org/x/crypto/ssh/handshake.go | 83 +- vendor/golang.org/x/crypto/ssh/kex.go | 186 +- vendor/golang.org/x/crypto/ssh/keys.go | 149 +- vendor/golang.org/x/crypto/ssh/messages.go | 21 +- vendor/golang.org/x/crypto/ssh/server.go | 46 +- vendor/golang.org/x/crypto/ssh/session.go | 1 + vendor/golang.org/x/crypto/ssh/transport.go | 10 +- vendor/modules.txt | 3 +- 27 files changed, 572 insertions(+), 3769 deletions(-) delete mode 100644 vendor/golang.org/x/crypto/ed25519/ed25519_go113.go delete mode 100644 vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go delete mode 100644 vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go diff --git a/go.mod b/go.mod index 356ce37a..e0d85eed 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( go.opentelemetry.io/otel/trace v1.6.3 go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 diff --git a/go.sum b/go.sum index 9e39dfa6..b43b527d 100644 --- a/go.sum +++ b/go.sum @@ -636,8 +636,9 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -723,6 +724,7 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs= diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go index c5898db4..4652247b 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go @@ -15,6 +15,7 @@ const bufSize = 256 // xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only // be called when the vector facility is available. Implementation in asm_s390x.s. +// //go:noescape func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 83c776de..3a1674a1 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -407,7 +407,12 @@ func (s *String) ReadASN1Enum(out *int) bool { func (s *String) readBase128Int(out *int) bool { ret := 0 for i := 0; len(*s) > 0; i++ { - if i == 4 { + if i == 5 { + return false + } + // Avoid overflowing int on a 32-bit platform. + // We don't want different behavior based on the architecture. + if ret >= 1<<(31-7) { return false } ret <<= 7 diff --git a/vendor/golang.org/x/crypto/cryptobyte/builder.go b/vendor/golang.org/x/crypto/cryptobyte/builder.go index ca7b1db5..c7ded757 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/builder.go +++ b/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -106,13 +106,13 @@ func (b *Builder) AddBytes(v []byte) { // supplied to them. The child builder passed to the continuation can be used // to build the content of the length-prefixed sequence. For example: // -// parent := cryptobyte.NewBuilder() -// parent.AddUint8LengthPrefixed(func (child *Builder) { -// child.AddUint8(42) -// child.AddUint8LengthPrefixed(func (grandchild *Builder) { -// grandchild.AddUint8(5) -// }) -// }) +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) // // It is an error to write more bytes to the child than allowed by the reserved // length prefix. After the continuation returns, the child must be considered diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go index 44dc8e8c..edcf163c 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go @@ -1,13 +1,16 @@ // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. +//go:build amd64 && gc && !purego // +build amd64,gc,!purego package field // feMul sets out = a * b. It works like feMulGeneric. +// //go:noescape func feMul(out *Element, a *Element, b *Element) // feSquare sets out = a * a. It works like feSquareGeneric. +// //go:noescape func feSquare(out *Element, a *Element) diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go index 71ad917d..a7828345 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -1,13 +1,7 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// In Go 1.13, the ed25519 package was promoted to the standard library as -// crypto/ed25519, and this package became a wrapper for the standard library one. -// -//go:build !go1.13 -// +build !go1.13 - // Package ed25519 implements the Ed25519 signature algorithm. See // https://ed25519.cr.yp.to/. // @@ -16,21 +10,15 @@ // representation includes a public key suffix to make multiple signing // operations with the same key more efficient. This package refers to the RFC // 8032 private key as the “seed”. +// +// Beginning with Go 1.13, the functionality of this package was moved to the +// standard library as crypto/ed25519. This package only acts as a compatibility +// wrapper. package ed25519 -// This code is a port of the public domain, “ref10” implementation of ed25519 -// from SUPERCOP. - import ( - "bytes" - "crypto" - cryptorand "crypto/rand" - "crypto/sha512" - "errors" + "crypto/ed25519" "io" - "strconv" - - "golang.org/x/crypto/ed25519/internal/edwards25519" ) const ( @@ -45,57 +33,21 @@ const ( ) // PublicKey is the type of Ed25519 public keys. -type PublicKey []byte +// +// This type is an alias for crypto/ed25519's PublicKey type. +// See the crypto/ed25519 package for the methods on this type. +type PublicKey = ed25519.PublicKey // PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. -type PrivateKey []byte - -// Public returns the PublicKey corresponding to priv. -func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) - copy(publicKey, priv[32:]) - return PublicKey(publicKey) -} - -// Seed returns the private key seed corresponding to priv. It is provided for -// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds -// in this package. -func (priv PrivateKey) Seed() []byte { - seed := make([]byte, SeedSize) - copy(seed, priv[:32]) - return seed -} - -// Sign signs the given message with priv. -// Ed25519 performs two passes over messages to be signed and therefore cannot -// handle pre-hashed messages. Thus opts.HashFunc() must return zero to -// indicate the message hasn't been hashed. This can be achieved by passing -// crypto.Hash(0) as the value for opts. -func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { - if opts.HashFunc() != crypto.Hash(0) { - return nil, errors.New("ed25519: cannot sign hashed message") - } - - return Sign(priv, message), nil -} +// +// This type is an alias for crypto/ed25519's PrivateKey type. +// See the crypto/ed25519 package for the methods on this type. +type PrivateKey = ed25519.PrivateKey // GenerateKey generates a public/private key pair using entropy from rand. // If rand is nil, crypto/rand.Reader will be used. func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { - if rand == nil { - rand = cryptorand.Reader - } - - seed := make([]byte, SeedSize) - if _, err := io.ReadFull(rand, seed); err != nil { - return nil, nil, err - } - - privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[32:]) - - return publicKey, privateKey, nil + return ed25519.GenerateKey(rand) } // NewKeyFromSeed calculates a private key from a seed. It will panic if @@ -103,121 +55,17 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { // with RFC 8032. RFC 8032's private keys correspond to seeds in this // package. func NewKeyFromSeed(seed []byte) PrivateKey { - if l := len(seed); l != SeedSize { - panic("ed25519: bad seed length: " + strconv.Itoa(l)) - } - - digest := sha512.Sum512(seed) - digest[0] &= 248 - digest[31] &= 127 - digest[31] |= 64 - - var A edwards25519.ExtendedGroupElement - var hBytes [32]byte - copy(hBytes[:], digest[:]) - edwards25519.GeScalarMultBase(&A, &hBytes) - var publicKeyBytes [32]byte - A.ToBytes(&publicKeyBytes) - - privateKey := make([]byte, PrivateKeySize) - copy(privateKey, seed) - copy(privateKey[32:], publicKeyBytes[:]) - - return privateKey + return ed25519.NewKeyFromSeed(seed) } // Sign signs the message with privateKey and returns a signature. It will // panic if len(privateKey) is not PrivateKeySize. func Sign(privateKey PrivateKey, message []byte) []byte { - if l := len(privateKey); l != PrivateKeySize { - panic("ed25519: bad private key length: " + strconv.Itoa(l)) - } - - h := sha512.New() - h.Write(privateKey[:32]) - - var digest1, messageDigest, hramDigest [64]byte - var expandedSecretKey [32]byte - h.Sum(digest1[:0]) - copy(expandedSecretKey[:], digest1[:]) - expandedSecretKey[0] &= 248 - expandedSecretKey[31] &= 63 - expandedSecretKey[31] |= 64 - - h.Reset() - h.Write(digest1[32:]) - h.Write(message) - h.Sum(messageDigest[:0]) - - var messageDigestReduced [32]byte - edwards25519.ScReduce(&messageDigestReduced, &messageDigest) - var R edwards25519.ExtendedGroupElement - edwards25519.GeScalarMultBase(&R, &messageDigestReduced) - - var encodedR [32]byte - R.ToBytes(&encodedR) - - h.Reset() - h.Write(encodedR[:]) - h.Write(privateKey[32:]) - h.Write(message) - h.Sum(hramDigest[:0]) - var hramDigestReduced [32]byte - edwards25519.ScReduce(&hramDigestReduced, &hramDigest) - - var s [32]byte - edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) - - signature := make([]byte, SignatureSize) - copy(signature[:], encodedR[:]) - copy(signature[32:], s[:]) - - return signature + return ed25519.Sign(privateKey, message) } // Verify reports whether sig is a valid signature of message by publicKey. It // will panic if len(publicKey) is not PublicKeySize. func Verify(publicKey PublicKey, message, sig []byte) bool { - if l := len(publicKey); l != PublicKeySize { - panic("ed25519: bad public key length: " + strconv.Itoa(l)) - } - - if len(sig) != SignatureSize || sig[63]&224 != 0 { - return false - } - - var A edwards25519.ExtendedGroupElement - var publicKeyBytes [32]byte - copy(publicKeyBytes[:], publicKey) - if !A.FromBytes(&publicKeyBytes) { - return false - } - edwards25519.FeNeg(&A.X, &A.X) - edwards25519.FeNeg(&A.T, &A.T) - - h := sha512.New() - h.Write(sig[:32]) - h.Write(publicKey[:]) - h.Write(message) - var digest [64]byte - h.Sum(digest[:0]) - - var hReduced [32]byte - edwards25519.ScReduce(&hReduced, &digest) - - var R edwards25519.ProjectiveGroupElement - var s [32]byte - copy(s[:], sig[32:]) - - // https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in - // the range [0, order) in order to prevent signature malleability. - if !edwards25519.ScMinimal(&s) { - return false - } - - edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s) - - var checkR [32]byte - R.ToBytes(&checkR) - return bytes.Equal(sig[:32], checkR[:]) + return ed25519.Verify(publicKey, message, sig) } diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go b/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go deleted file mode 100644 index b5974dc8..00000000 --- a/vendor/golang.org/x/crypto/ed25519/ed25519_go113.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.13 -// +build go1.13 - -// Package ed25519 implements the Ed25519 signature algorithm. See -// https://ed25519.cr.yp.to/. -// -// These functions are also compatible with the “Ed25519” function defined in -// RFC 8032. However, unlike RFC 8032's formulation, this package's private key -// representation includes a public key suffix to make multiple signing -// operations with the same key more efficient. This package refers to the RFC -// 8032 private key as the “seed”. -// -// Beginning with Go 1.13, the functionality of this package was moved to the -// standard library as crypto/ed25519. This package only acts as a compatibility -// wrapper. -package ed25519 - -import ( - "crypto/ed25519" - "io" -) - -const ( - // PublicKeySize is the size, in bytes, of public keys as used in this package. - PublicKeySize = 32 - // PrivateKeySize is the size, in bytes, of private keys as used in this package. - PrivateKeySize = 64 - // SignatureSize is the size, in bytes, of signatures generated and verified by this package. - SignatureSize = 64 - // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. - SeedSize = 32 -) - -// PublicKey is the type of Ed25519 public keys. -// -// This type is an alias for crypto/ed25519's PublicKey type. -// See the crypto/ed25519 package for the methods on this type. -type PublicKey = ed25519.PublicKey - -// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. -// -// This type is an alias for crypto/ed25519's PrivateKey type. -// See the crypto/ed25519 package for the methods on this type. -type PrivateKey = ed25519.PrivateKey - -// GenerateKey generates a public/private key pair using entropy from rand. -// If rand is nil, crypto/rand.Reader will be used. -func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { - return ed25519.GenerateKey(rand) -} - -// NewKeyFromSeed calculates a private key from a seed. It will panic if -// len(seed) is not SeedSize. This function is provided for interoperability -// with RFC 8032. RFC 8032's private keys correspond to seeds in this -// package. -func NewKeyFromSeed(seed []byte) PrivateKey { - return ed25519.NewKeyFromSeed(seed) -} - -// Sign signs the message with privateKey and returns a signature. It will -// panic if len(privateKey) is not PrivateKeySize. -func Sign(privateKey PrivateKey, message []byte) []byte { - return ed25519.Sign(privateKey, message) -} - -// Verify reports whether sig is a valid signature of message by publicKey. It -// will panic if len(publicKey) is not PublicKeySize. -func Verify(publicKey PublicKey, message, sig []byte) bool { - return ed25519.Verify(publicKey, message, sig) -} diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go deleted file mode 100644 index e39f086c..00000000 --- a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go +++ /dev/null @@ -1,1422 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package edwards25519 - -// These values are from the public domain, “ref10” implementation of ed25519 -// from SUPERCOP. - -// d is a constant in the Edwards curve equation. -var d = FieldElement{ - -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, -} - -// d2 is 2*d. -var d2 = FieldElement{ - -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, -} - -// SqrtM1 is the square-root of -1 in the field. -var SqrtM1 = FieldElement{ - -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482, -} - -// A is a constant in the Montgomery-form of curve25519. -var A = FieldElement{ - 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, -} - -// bi contains precomputed multiples of the base-point. See the Ed25519 paper -// for a discussion about how these values are used. -var bi = [8]PreComputedGroupElement{ - { - FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, - FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, - FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, - }, - { - FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, - FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, - FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, - }, - { - FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, - FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, - FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, - }, - { - FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, - FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, - FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, - }, - { - FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877}, - FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951}, - FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784}, - }, - { - FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436}, - FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918}, - FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877}, - }, - { - FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800}, - FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305}, - FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300}, - }, - { - FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876}, - FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619}, - FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683}, - }, -} - -// base contains precomputed multiples of the base-point. See the Ed25519 paper -// for a discussion about how these values are used. -var base = [32][8]PreComputedGroupElement{ - { - { - FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, - FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, - FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, - }, - { - FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303}, - FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081}, - FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697}, - }, - { - FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, - FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, - FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, - }, - { - FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540}, - FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397}, - FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325}, - }, - { - FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, - FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, - FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, - }, - { - FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777}, - FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737}, - FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652}, - }, - { - FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, - FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, - FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, - }, - { - FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726}, - FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955}, - FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425}, - }, - }, - { - { - FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171}, - FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510}, - FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660}, - }, - { - FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639}, - FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963}, - FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950}, - }, - { - FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568}, - FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335}, - FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628}, - }, - { - FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007}, - FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772}, - FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653}, - }, - { - FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567}, - FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686}, - FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372}, - }, - { - FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887}, - FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954}, - FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953}, - }, - { - FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833}, - FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532}, - FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876}, - }, - { - FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268}, - FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214}, - FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038}, - }, - }, - { - { - FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800}, - FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645}, - FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664}, - }, - { - FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933}, - FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182}, - FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222}, - }, - { - FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991}, - FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880}, - FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092}, - }, - { - FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295}, - FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788}, - FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553}, - }, - { - FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026}, - FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347}, - FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033}, - }, - { - FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395}, - FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278}, - FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890}, - }, - { - FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995}, - FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596}, - FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891}, - }, - { - FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060}, - FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608}, - FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606}, - }, - }, - { - { - FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389}, - FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016}, - FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341}, - }, - { - FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505}, - FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553}, - FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655}, - }, - { - FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220}, - FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631}, - FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099}, - }, - { - FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556}, - FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749}, - FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930}, - }, - { - FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391}, - FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253}, - FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066}, - }, - { - FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958}, - FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082}, - FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383}, - }, - { - FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521}, - FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807}, - FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948}, - }, - { - FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134}, - FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455}, - FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629}, - }, - }, - { - { - FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069}, - FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746}, - FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919}, - }, - { - FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837}, - FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906}, - FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771}, - }, - { - FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817}, - FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098}, - FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409}, - }, - { - FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504}, - FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727}, - FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420}, - }, - { - FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003}, - FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605}, - FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384}, - }, - { - FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701}, - FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683}, - FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708}, - }, - { - FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563}, - FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260}, - FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387}, - }, - { - FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672}, - FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686}, - FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665}, - }, - }, - { - { - FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182}, - FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277}, - FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628}, - }, - { - FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474}, - FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539}, - FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822}, - }, - { - FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970}, - FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756}, - FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508}, - }, - { - FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683}, - FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655}, - FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158}, - }, - { - FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125}, - FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839}, - FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664}, - }, - { - FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294}, - FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899}, - FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070}, - }, - { - FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294}, - FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949}, - FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083}, - }, - { - FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420}, - FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940}, - FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396}, - }, - }, - { - { - FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567}, - FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127}, - FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294}, - }, - { - FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887}, - FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964}, - FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195}, - }, - { - FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244}, - FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999}, - FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762}, - }, - { - FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274}, - FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236}, - FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605}, - }, - { - FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761}, - FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884}, - FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482}, - }, - { - FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638}, - FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490}, - FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170}, - }, - { - FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736}, - FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124}, - FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392}, - }, - { - FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029}, - FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048}, - FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958}, - }, - }, - { - { - FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593}, - FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071}, - FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692}, - }, - { - FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687}, - FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441}, - FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001}, - }, - { - FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460}, - FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007}, - FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762}, - }, - { - FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005}, - FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674}, - FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035}, - }, - { - FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590}, - FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957}, - FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812}, - }, - { - FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740}, - FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122}, - FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158}, - }, - { - FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885}, - FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140}, - FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857}, - }, - { - FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155}, - FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260}, - FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483}, - }, - }, - { - { - FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677}, - FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815}, - FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751}, - }, - { - FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203}, - FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208}, - FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230}, - }, - { - FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850}, - FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389}, - FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968}, - }, - { - FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689}, - FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880}, - FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304}, - }, - { - FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632}, - FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412}, - FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566}, - }, - { - FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038}, - FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232}, - FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943}, - }, - { - FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856}, - FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738}, - FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971}, - }, - { - FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718}, - FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697}, - FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883}, - }, - }, - { - { - FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912}, - FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358}, - FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849}, - }, - { - FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307}, - FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977}, - FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335}, - }, - { - FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644}, - FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616}, - FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735}, - }, - { - FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099}, - FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341}, - FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336}, - }, - { - FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646}, - FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425}, - FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388}, - }, - { - FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743}, - FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822}, - FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462}, - }, - { - FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985}, - FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702}, - FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797}, - }, - { - FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293}, - FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100}, - FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688}, - }, - }, - { - { - FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186}, - FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610}, - FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707}, - }, - { - FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220}, - FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025}, - FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044}, - }, - { - FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992}, - FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027}, - FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197}, - }, - { - FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901}, - FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952}, - FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878}, - }, - { - FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390}, - FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730}, - FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730}, - }, - { - FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180}, - FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272}, - FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715}, - }, - { - FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970}, - FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772}, - FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865}, - }, - { - FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750}, - FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373}, - FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348}, - }, - }, - { - { - FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144}, - FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195}, - FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086}, - }, - { - FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684}, - FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518}, - FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233}, - }, - { - FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793}, - FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794}, - FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435}, - }, - { - FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921}, - FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518}, - FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563}, - }, - { - FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278}, - FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024}, - FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030}, - }, - { - FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783}, - FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717}, - FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844}, - }, - { - FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333}, - FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048}, - FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760}, - }, - { - FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760}, - FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757}, - FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112}, - }, - }, - { - { - FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468}, - FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184}, - FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289}, - }, - { - FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066}, - FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882}, - FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226}, - }, - { - FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101}, - FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279}, - FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811}, - }, - { - FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709}, - FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714}, - FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121}, - }, - { - FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464}, - FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847}, - FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400}, - }, - { - FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414}, - FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158}, - FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045}, - }, - { - FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415}, - FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459}, - FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079}, - }, - { - FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412}, - FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743}, - FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836}, - }, - }, - { - { - FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022}, - FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429}, - FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065}, - }, - { - FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861}, - FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000}, - FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101}, - }, - { - FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815}, - FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642}, - FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966}, - }, - { - FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574}, - FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742}, - FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689}, - }, - { - FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020}, - FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772}, - FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982}, - }, - { - FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953}, - FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218}, - FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265}, - }, - { - FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073}, - FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325}, - FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798}, - }, - { - FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870}, - FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863}, - FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927}, - }, - }, - { - { - FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267}, - FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663}, - FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862}, - }, - { - FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673}, - FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943}, - FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020}, - }, - { - FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238}, - FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064}, - FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795}, - }, - { - FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052}, - FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904}, - FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531}, - }, - { - FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979}, - FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841}, - FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431}, - }, - { - FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324}, - FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940}, - FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320}, - }, - { - FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184}, - FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114}, - FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878}, - }, - { - FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784}, - FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091}, - FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585}, - }, - }, - { - { - FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208}, - FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864}, - FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661}, - }, - { - FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233}, - FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212}, - FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525}, - }, - { - FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068}, - FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397}, - FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988}, - }, - { - FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889}, - FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038}, - FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697}, - }, - { - FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875}, - FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905}, - FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656}, - }, - { - FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818}, - FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714}, - FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203}, - }, - { - FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931}, - FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024}, - FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084}, - }, - { - FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204}, - FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817}, - FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667}, - }, - }, - { - { - FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504}, - FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768}, - FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255}, - }, - { - FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790}, - FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438}, - FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333}, - }, - { - FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971}, - FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905}, - FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409}, - }, - { - FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409}, - FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499}, - FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363}, - }, - { - FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664}, - FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324}, - FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940}, - }, - { - FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990}, - FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914}, - FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290}, - }, - { - FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257}, - FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433}, - FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236}, - }, - { - FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045}, - FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093}, - FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347}, - }, - }, - { - { - FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191}, - FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507}, - FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906}, - }, - { - FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018}, - FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109}, - FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926}, - }, - { - FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528}, - FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625}, - FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286}, - }, - { - FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033}, - FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866}, - FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896}, - }, - { - FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075}, - FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347}, - FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437}, - }, - { - FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165}, - FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588}, - FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193}, - }, - { - FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017}, - FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883}, - FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961}, - }, - { - FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043}, - FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663}, - FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362}, - }, - }, - { - { - FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860}, - FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466}, - FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063}, - }, - { - FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997}, - FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295}, - FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369}, - }, - { - FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385}, - FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109}, - FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906}, - }, - { - FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424}, - FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185}, - FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962}, - }, - { - FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325}, - FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593}, - FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404}, - }, - { - FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644}, - FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801}, - FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804}, - }, - { - FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884}, - FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577}, - FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849}, - }, - { - FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473}, - FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644}, - FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319}, - }, - }, - { - { - FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599}, - FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768}, - FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084}, - }, - { - FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328}, - FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369}, - FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920}, - }, - { - FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815}, - FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025}, - FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397}, - }, - { - FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448}, - FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981}, - FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165}, - }, - { - FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501}, - FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073}, - FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861}, - }, - { - FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845}, - FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211}, - FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870}, - }, - { - FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096}, - FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803}, - FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168}, - }, - { - FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965}, - FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505}, - FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598}, - }, - }, - { - { - FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782}, - FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900}, - FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479}, - }, - { - FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208}, - FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232}, - FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719}, - }, - { - FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271}, - FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326}, - FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132}, - }, - { - FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300}, - FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570}, - FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670}, - }, - { - FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994}, - FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913}, - FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317}, - }, - { - FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730}, - FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096}, - FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078}, - }, - { - FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411}, - FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905}, - FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654}, - }, - { - FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870}, - FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498}, - FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579}, - }, - }, - { - { - FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677}, - FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647}, - FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743}, - }, - { - FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468}, - FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375}, - FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155}, - }, - { - FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725}, - FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612}, - FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943}, - }, - { - FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944}, - FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928}, - FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406}, - }, - { - FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139}, - FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963}, - FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693}, - }, - { - FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734}, - FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680}, - FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410}, - }, - { - FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931}, - FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654}, - FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710}, - }, - { - FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180}, - FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684}, - FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895}, - }, - }, - { - { - FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501}, - FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413}, - FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880}, - }, - { - FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874}, - FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962}, - FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899}, - }, - { - FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152}, - FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063}, - FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080}, - }, - { - FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146}, - FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183}, - FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133}, - }, - { - FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421}, - FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622}, - FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197}, - }, - { - FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663}, - FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753}, - FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755}, - }, - { - FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862}, - FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118}, - FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171}, - }, - { - FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380}, - FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824}, - FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270}, - }, - }, - { - { - FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438}, - FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584}, - FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562}, - }, - { - FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471}, - FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610}, - FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269}, - }, - { - FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650}, - FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369}, - FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461}, - }, - { - FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462}, - FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793}, - FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218}, - }, - { - FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226}, - FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019}, - FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037}, - }, - { - FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171}, - FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132}, - FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841}, - }, - { - FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181}, - FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210}, - FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040}, - }, - { - FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935}, - FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105}, - FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814}, - }, - }, - { - { - FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852}, - FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581}, - FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646}, - }, - { - FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844}, - FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025}, - FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453}, - }, - { - FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068}, - FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192}, - FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921}, - }, - { - FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259}, - FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426}, - FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072}, - }, - { - FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305}, - FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832}, - FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943}, - }, - { - FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011}, - FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447}, - FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494}, - }, - { - FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245}, - FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859}, - FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915}, - }, - { - FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707}, - FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848}, - FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224}, - }, - }, - { - { - FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391}, - FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215}, - FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101}, - }, - { - FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713}, - FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849}, - FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930}, - }, - { - FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940}, - FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031}, - FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404}, - }, - { - FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243}, - FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116}, - FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525}, - }, - { - FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509}, - FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883}, - FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865}, - }, - { - FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660}, - FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273}, - FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138}, - }, - { - FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560}, - FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135}, - FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941}, - }, - { - FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739}, - FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756}, - FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819}, - }, - }, - { - { - FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347}, - FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028}, - FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075}, - }, - { - FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799}, - FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609}, - FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817}, - }, - { - FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989}, - FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523}, - FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278}, - }, - { - FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045}, - FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377}, - FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480}, - }, - { - FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016}, - FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426}, - FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525}, - }, - { - FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396}, - FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080}, - FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892}, - }, - { - FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275}, - FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074}, - FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140}, - }, - { - FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717}, - FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101}, - FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127}, - }, - }, - { - { - FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632}, - FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415}, - FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160}, - }, - { - FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876}, - FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625}, - FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478}, - }, - { - FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164}, - FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595}, - FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248}, - }, - { - FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858}, - FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193}, - FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184}, - }, - { - FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942}, - FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635}, - FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948}, - }, - { - FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935}, - FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415}, - FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416}, - }, - { - FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018}, - FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778}, - FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659}, - }, - { - FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385}, - FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503}, - FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329}, - }, - }, - { - { - FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056}, - FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838}, - FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948}, - }, - { - FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691}, - FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118}, - FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517}, - }, - { - FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269}, - FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904}, - FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589}, - }, - { - FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193}, - FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910}, - FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930}, - }, - { - FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667}, - FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481}, - FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876}, - }, - { - FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640}, - FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278}, - FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112}, - }, - { - FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272}, - FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012}, - FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221}, - }, - { - FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046}, - FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345}, - FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310}, - }, - }, - { - { - FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937}, - FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636}, - FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008}, - }, - { - FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429}, - FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576}, - FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066}, - }, - { - FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490}, - FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104}, - FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053}, - }, - { - FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275}, - FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511}, - FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095}, - }, - { - FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439}, - FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939}, - FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424}, - }, - { - FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310}, - FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608}, - FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079}, - }, - { - FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101}, - FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418}, - FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576}, - }, - { - FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356}, - FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996}, - FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099}, - }, - }, - { - { - FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728}, - FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658}, - FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242}, - }, - { - FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001}, - FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766}, - FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373}, - }, - { - FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458}, - FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628}, - FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657}, - }, - { - FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062}, - FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616}, - FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014}, - }, - { - FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383}, - FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814}, - FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718}, - }, - { - FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417}, - FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222}, - FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444}, - }, - { - FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597}, - FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970}, - FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799}, - }, - { - FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647}, - FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511}, - FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032}, - }, - }, - { - { - FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834}, - FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461}, - FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062}, - }, - { - FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516}, - FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547}, - FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240}, - }, - { - FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038}, - FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741}, - FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103}, - }, - { - FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747}, - FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323}, - FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016}, - }, - { - FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373}, - FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228}, - FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141}, - }, - { - FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399}, - FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831}, - FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376}, - }, - { - FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313}, - FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958}, - FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577}, - }, - { - FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743}, - FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684}, - FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476}, - }, - }, -} diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go deleted file mode 100644 index fd03c252..00000000 --- a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go +++ /dev/null @@ -1,1793 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package edwards25519 - -import "encoding/binary" - -// This code is a port of the public domain, “ref10” implementation of ed25519 -// from SUPERCOP. - -// FieldElement represents an element of the field GF(2^255 - 19). An element -// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 -// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on -// context. -type FieldElement [10]int32 - -var zero FieldElement - -func FeZero(fe *FieldElement) { - copy(fe[:], zero[:]) -} - -func FeOne(fe *FieldElement) { - FeZero(fe) - fe[0] = 1 -} - -func FeAdd(dst, a, b *FieldElement) { - dst[0] = a[0] + b[0] - dst[1] = a[1] + b[1] - dst[2] = a[2] + b[2] - dst[3] = a[3] + b[3] - dst[4] = a[4] + b[4] - dst[5] = a[5] + b[5] - dst[6] = a[6] + b[6] - dst[7] = a[7] + b[7] - dst[8] = a[8] + b[8] - dst[9] = a[9] + b[9] -} - -func FeSub(dst, a, b *FieldElement) { - dst[0] = a[0] - b[0] - dst[1] = a[1] - b[1] - dst[2] = a[2] - b[2] - dst[3] = a[3] - b[3] - dst[4] = a[4] - b[4] - dst[5] = a[5] - b[5] - dst[6] = a[6] - b[6] - dst[7] = a[7] - b[7] - dst[8] = a[8] - b[8] - dst[9] = a[9] - b[9] -} - -func FeCopy(dst, src *FieldElement) { - copy(dst[:], src[:]) -} - -// Replace (f,g) with (g,g) if b == 1; -// replace (f,g) with (f,g) if b == 0. -// -// Preconditions: b in {0,1}. -func FeCMove(f, g *FieldElement, b int32) { - b = -b - f[0] ^= b & (f[0] ^ g[0]) - f[1] ^= b & (f[1] ^ g[1]) - f[2] ^= b & (f[2] ^ g[2]) - f[3] ^= b & (f[3] ^ g[3]) - f[4] ^= b & (f[4] ^ g[4]) - f[5] ^= b & (f[5] ^ g[5]) - f[6] ^= b & (f[6] ^ g[6]) - f[7] ^= b & (f[7] ^ g[7]) - f[8] ^= b & (f[8] ^ g[8]) - f[9] ^= b & (f[9] ^ g[9]) -} - -func load3(in []byte) int64 { - var r int64 - r = int64(in[0]) - r |= int64(in[1]) << 8 - r |= int64(in[2]) << 16 - return r -} - -func load4(in []byte) int64 { - var r int64 - r = int64(in[0]) - r |= int64(in[1]) << 8 - r |= int64(in[2]) << 16 - r |= int64(in[3]) << 24 - return r -} - -func FeFromBytes(dst *FieldElement, src *[32]byte) { - h0 := load4(src[:]) - h1 := load3(src[4:]) << 6 - h2 := load3(src[7:]) << 5 - h3 := load3(src[10:]) << 3 - h4 := load3(src[13:]) << 2 - h5 := load4(src[16:]) - h6 := load3(src[20:]) << 7 - h7 := load3(src[23:]) << 5 - h8 := load3(src[26:]) << 4 - h9 := (load3(src[29:]) & 8388607) << 2 - - FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) -} - -// FeToBytes marshals h to s. -// Preconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Write p=2^255-19; q=floor(h/p). -// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). -// -// Proof: -// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. -// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. -// -// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). -// Then 0> 25 - q = (h[0] + q) >> 26 - q = (h[1] + q) >> 25 - q = (h[2] + q) >> 26 - q = (h[3] + q) >> 25 - q = (h[4] + q) >> 26 - q = (h[5] + q) >> 25 - q = (h[6] + q) >> 26 - q = (h[7] + q) >> 25 - q = (h[8] + q) >> 26 - q = (h[9] + q) >> 25 - - // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. - h[0] += 19 * q - // Goal: Output h-2^255 q, which is between 0 and 2^255-20. - - carry[0] = h[0] >> 26 - h[1] += carry[0] - h[0] -= carry[0] << 26 - carry[1] = h[1] >> 25 - h[2] += carry[1] - h[1] -= carry[1] << 25 - carry[2] = h[2] >> 26 - h[3] += carry[2] - h[2] -= carry[2] << 26 - carry[3] = h[3] >> 25 - h[4] += carry[3] - h[3] -= carry[3] << 25 - carry[4] = h[4] >> 26 - h[5] += carry[4] - h[4] -= carry[4] << 26 - carry[5] = h[5] >> 25 - h[6] += carry[5] - h[5] -= carry[5] << 25 - carry[6] = h[6] >> 26 - h[7] += carry[6] - h[6] -= carry[6] << 26 - carry[7] = h[7] >> 25 - h[8] += carry[7] - h[7] -= carry[7] << 25 - carry[8] = h[8] >> 26 - h[9] += carry[8] - h[8] -= carry[8] << 26 - carry[9] = h[9] >> 25 - h[9] -= carry[9] << 25 - // h10 = carry9 - - // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. - // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; - // evidently 2^255 h10-2^255 q = 0. - // Goal: Output h[0]+...+2^230 h[9]. - - s[0] = byte(h[0] >> 0) - s[1] = byte(h[0] >> 8) - s[2] = byte(h[0] >> 16) - s[3] = byte((h[0] >> 24) | (h[1] << 2)) - s[4] = byte(h[1] >> 6) - s[5] = byte(h[1] >> 14) - s[6] = byte((h[1] >> 22) | (h[2] << 3)) - s[7] = byte(h[2] >> 5) - s[8] = byte(h[2] >> 13) - s[9] = byte((h[2] >> 21) | (h[3] << 5)) - s[10] = byte(h[3] >> 3) - s[11] = byte(h[3] >> 11) - s[12] = byte((h[3] >> 19) | (h[4] << 6)) - s[13] = byte(h[4] >> 2) - s[14] = byte(h[4] >> 10) - s[15] = byte(h[4] >> 18) - s[16] = byte(h[5] >> 0) - s[17] = byte(h[5] >> 8) - s[18] = byte(h[5] >> 16) - s[19] = byte((h[5] >> 24) | (h[6] << 1)) - s[20] = byte(h[6] >> 7) - s[21] = byte(h[6] >> 15) - s[22] = byte((h[6] >> 23) | (h[7] << 3)) - s[23] = byte(h[7] >> 5) - s[24] = byte(h[7] >> 13) - s[25] = byte((h[7] >> 21) | (h[8] << 4)) - s[26] = byte(h[8] >> 4) - s[27] = byte(h[8] >> 12) - s[28] = byte((h[8] >> 20) | (h[9] << 6)) - s[29] = byte(h[9] >> 2) - s[30] = byte(h[9] >> 10) - s[31] = byte(h[9] >> 18) -} - -func FeIsNegative(f *FieldElement) byte { - var s [32]byte - FeToBytes(&s, f) - return s[0] & 1 -} - -func FeIsNonZero(f *FieldElement) int32 { - var s [32]byte - FeToBytes(&s, f) - var x uint8 - for _, b := range s { - x |= b - } - x |= x >> 4 - x |= x >> 2 - x |= x >> 1 - return int32(x & 1) -} - -// FeNeg sets h = -f -// -// Preconditions: -// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func FeNeg(h, f *FieldElement) { - h[0] = -f[0] - h[1] = -f[1] - h[2] = -f[2] - h[3] = -f[3] - h[4] = -f[4] - h[5] = -f[5] - h[6] = -f[6] - h[7] = -f[7] - h[8] = -f[8] - h[9] = -f[9] -} - -func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { - var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64 - - /* - |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) - i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 - |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) - i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 - */ - - c0 = (h0 + (1 << 25)) >> 26 - h1 += c0 - h0 -= c0 << 26 - c4 = (h4 + (1 << 25)) >> 26 - h5 += c4 - h4 -= c4 << 26 - /* |h0| <= 2^25 */ - /* |h4| <= 2^25 */ - /* |h1| <= 1.51*2^58 */ - /* |h5| <= 1.51*2^58 */ - - c1 = (h1 + (1 << 24)) >> 25 - h2 += c1 - h1 -= c1 << 25 - c5 = (h5 + (1 << 24)) >> 25 - h6 += c5 - h5 -= c5 << 25 - /* |h1| <= 2^24; from now on fits into int32 */ - /* |h5| <= 2^24; from now on fits into int32 */ - /* |h2| <= 1.21*2^59 */ - /* |h6| <= 1.21*2^59 */ - - c2 = (h2 + (1 << 25)) >> 26 - h3 += c2 - h2 -= c2 << 26 - c6 = (h6 + (1 << 25)) >> 26 - h7 += c6 - h6 -= c6 << 26 - /* |h2| <= 2^25; from now on fits into int32 unchanged */ - /* |h6| <= 2^25; from now on fits into int32 unchanged */ - /* |h3| <= 1.51*2^58 */ - /* |h7| <= 1.51*2^58 */ - - c3 = (h3 + (1 << 24)) >> 25 - h4 += c3 - h3 -= c3 << 25 - c7 = (h7 + (1 << 24)) >> 25 - h8 += c7 - h7 -= c7 << 25 - /* |h3| <= 2^24; from now on fits into int32 unchanged */ - /* |h7| <= 2^24; from now on fits into int32 unchanged */ - /* |h4| <= 1.52*2^33 */ - /* |h8| <= 1.52*2^33 */ - - c4 = (h4 + (1 << 25)) >> 26 - h5 += c4 - h4 -= c4 << 26 - c8 = (h8 + (1 << 25)) >> 26 - h9 += c8 - h8 -= c8 << 26 - /* |h4| <= 2^25; from now on fits into int32 unchanged */ - /* |h8| <= 2^25; from now on fits into int32 unchanged */ - /* |h5| <= 1.01*2^24 */ - /* |h9| <= 1.51*2^58 */ - - c9 = (h9 + (1 << 24)) >> 25 - h0 += c9 * 19 - h9 -= c9 << 25 - /* |h9| <= 2^24; from now on fits into int32 unchanged */ - /* |h0| <= 1.8*2^37 */ - - c0 = (h0 + (1 << 25)) >> 26 - h1 += c0 - h0 -= c0 << 26 - /* |h0| <= 2^25; from now on fits into int32 unchanged */ - /* |h1| <= 1.01*2^24 */ - - h[0] = int32(h0) - h[1] = int32(h1) - h[2] = int32(h2) - h[3] = int32(h3) - h[4] = int32(h4) - h[5] = int32(h5) - h[6] = int32(h6) - h[7] = int32(h7) - h[8] = int32(h8) - h[9] = int32(h9) -} - -// FeMul calculates h = f * g -// Can overlap h with f or g. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -// -// Notes on implementation strategy: -// -// Using schoolbook multiplication. -// Karatsuba would save a little in some cost models. -// -// Most multiplications by 2 and 19 are 32-bit precomputations; -// cheaper than 64-bit postcomputations. -// -// There is one remaining multiplication by 19 in the carry chain; -// one *19 precomputation can be merged into this, -// but the resulting data flow is considerably less clean. -// -// There are 12 carries below. -// 10 of them are 2-way parallelizable and vectorizable. -// Can get away with 11 carries, but then data flow is much deeper. -// -// With tighter constraints on inputs, can squeeze carries into int32. -func FeMul(h, f, g *FieldElement) { - f0 := int64(f[0]) - f1 := int64(f[1]) - f2 := int64(f[2]) - f3 := int64(f[3]) - f4 := int64(f[4]) - f5 := int64(f[5]) - f6 := int64(f[6]) - f7 := int64(f[7]) - f8 := int64(f[8]) - f9 := int64(f[9]) - - f1_2 := int64(2 * f[1]) - f3_2 := int64(2 * f[3]) - f5_2 := int64(2 * f[5]) - f7_2 := int64(2 * f[7]) - f9_2 := int64(2 * f[9]) - - g0 := int64(g[0]) - g1 := int64(g[1]) - g2 := int64(g[2]) - g3 := int64(g[3]) - g4 := int64(g[4]) - g5 := int64(g[5]) - g6 := int64(g[6]) - g7 := int64(g[7]) - g8 := int64(g[8]) - g9 := int64(g[9]) - - g1_19 := int64(19 * g[1]) /* 1.4*2^29 */ - g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */ - g3_19 := int64(19 * g[3]) - g4_19 := int64(19 * g[4]) - g5_19 := int64(19 * g[5]) - g6_19 := int64(19 * g[6]) - g7_19 := int64(19 * g[7]) - g8_19 := int64(19 * g[8]) - g9_19 := int64(19 * g[9]) - - h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19 - h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19 - h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19 - h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19 - h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19 - h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19 - h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19 - h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19 - h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19 - h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0 - - FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) -} - -func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { - f0 := int64(f[0]) - f1 := int64(f[1]) - f2 := int64(f[2]) - f3 := int64(f[3]) - f4 := int64(f[4]) - f5 := int64(f[5]) - f6 := int64(f[6]) - f7 := int64(f[7]) - f8 := int64(f[8]) - f9 := int64(f[9]) - f0_2 := int64(2 * f[0]) - f1_2 := int64(2 * f[1]) - f2_2 := int64(2 * f[2]) - f3_2 := int64(2 * f[3]) - f4_2 := int64(2 * f[4]) - f5_2 := int64(2 * f[5]) - f6_2 := int64(2 * f[6]) - f7_2 := int64(2 * f[7]) - f5_38 := 38 * f5 // 1.31*2^30 - f6_19 := 19 * f6 // 1.31*2^30 - f7_38 := 38 * f7 // 1.31*2^30 - f8_19 := 19 * f8 // 1.31*2^30 - f9_38 := 38 * f9 // 1.31*2^30 - - h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38 - h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19 - h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19 - h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38 - h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38 - h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19 - h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19 - h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38 - h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38 - h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5 - - return -} - -// FeSquare calculates h = f*f. Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. -func FeSquare(h, f *FieldElement) { - h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) - FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) -} - -// FeSquare2 sets h = 2 * f * f -// -// Can overlap h with f. -// -// Preconditions: -// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. -// -// Postconditions: -// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. -// See fe_mul.c for discussion of implementation strategy. -func FeSquare2(h, f *FieldElement) { - h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) - - h0 += h0 - h1 += h1 - h2 += h2 - h3 += h3 - h4 += h4 - h5 += h5 - h6 += h6 - h7 += h7 - h8 += h8 - h9 += h9 - - FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) -} - -func FeInvert(out, z *FieldElement) { - var t0, t1, t2, t3 FieldElement - var i int - - FeSquare(&t0, z) // 2^1 - FeSquare(&t1, &t0) // 2^2 - for i = 1; i < 2; i++ { // 2^3 - FeSquare(&t1, &t1) - } - FeMul(&t1, z, &t1) // 2^3 + 2^0 - FeMul(&t0, &t0, &t1) // 2^3 + 2^1 + 2^0 - FeSquare(&t2, &t0) // 2^4 + 2^2 + 2^1 - FeMul(&t1, &t1, &t2) // 2^4 + 2^3 + 2^2 + 2^1 + 2^0 - FeSquare(&t2, &t1) // 5,4,3,2,1 - for i = 1; i < 5; i++ { // 9,8,7,6,5 - FeSquare(&t2, &t2) - } - FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 - FeSquare(&t2, &t1) // 10..1 - for i = 1; i < 10; i++ { // 19..10 - FeSquare(&t2, &t2) - } - FeMul(&t2, &t2, &t1) // 19..0 - FeSquare(&t3, &t2) // 20..1 - for i = 1; i < 20; i++ { // 39..20 - FeSquare(&t3, &t3) - } - FeMul(&t2, &t3, &t2) // 39..0 - FeSquare(&t2, &t2) // 40..1 - for i = 1; i < 10; i++ { // 49..10 - FeSquare(&t2, &t2) - } - FeMul(&t1, &t2, &t1) // 49..0 - FeSquare(&t2, &t1) // 50..1 - for i = 1; i < 50; i++ { // 99..50 - FeSquare(&t2, &t2) - } - FeMul(&t2, &t2, &t1) // 99..0 - FeSquare(&t3, &t2) // 100..1 - for i = 1; i < 100; i++ { // 199..100 - FeSquare(&t3, &t3) - } - FeMul(&t2, &t3, &t2) // 199..0 - FeSquare(&t2, &t2) // 200..1 - for i = 1; i < 50; i++ { // 249..50 - FeSquare(&t2, &t2) - } - FeMul(&t1, &t2, &t1) // 249..0 - FeSquare(&t1, &t1) // 250..1 - for i = 1; i < 5; i++ { // 254..5 - FeSquare(&t1, &t1) - } - FeMul(out, &t1, &t0) // 254..5,3,1,0 -} - -func fePow22523(out, z *FieldElement) { - var t0, t1, t2 FieldElement - var i int - - FeSquare(&t0, z) - for i = 1; i < 1; i++ { - FeSquare(&t0, &t0) - } - FeSquare(&t1, &t0) - for i = 1; i < 2; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t1, z, &t1) - FeMul(&t0, &t0, &t1) - FeSquare(&t0, &t0) - for i = 1; i < 1; i++ { - FeSquare(&t0, &t0) - } - FeMul(&t0, &t1, &t0) - FeSquare(&t1, &t0) - for i = 1; i < 5; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t0, &t1, &t0) - FeSquare(&t1, &t0) - for i = 1; i < 10; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t1, &t1, &t0) - FeSquare(&t2, &t1) - for i = 1; i < 20; i++ { - FeSquare(&t2, &t2) - } - FeMul(&t1, &t2, &t1) - FeSquare(&t1, &t1) - for i = 1; i < 10; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t0, &t1, &t0) - FeSquare(&t1, &t0) - for i = 1; i < 50; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t1, &t1, &t0) - FeSquare(&t2, &t1) - for i = 1; i < 100; i++ { - FeSquare(&t2, &t2) - } - FeMul(&t1, &t2, &t1) - FeSquare(&t1, &t1) - for i = 1; i < 50; i++ { - FeSquare(&t1, &t1) - } - FeMul(&t0, &t1, &t0) - FeSquare(&t0, &t0) - for i = 1; i < 2; i++ { - FeSquare(&t0, &t0) - } - FeMul(out, &t0, z) -} - -// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 * -// y^2 where d = -121665/121666. -// -// Several representations are used: -// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z -// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT -// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T -// PreComputedGroupElement: (y+x,y-x,2dxy) - -type ProjectiveGroupElement struct { - X, Y, Z FieldElement -} - -type ExtendedGroupElement struct { - X, Y, Z, T FieldElement -} - -type CompletedGroupElement struct { - X, Y, Z, T FieldElement -} - -type PreComputedGroupElement struct { - yPlusX, yMinusX, xy2d FieldElement -} - -type CachedGroupElement struct { - yPlusX, yMinusX, Z, T2d FieldElement -} - -func (p *ProjectiveGroupElement) Zero() { - FeZero(&p.X) - FeOne(&p.Y) - FeOne(&p.Z) -} - -func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) { - var t0 FieldElement - - FeSquare(&r.X, &p.X) - FeSquare(&r.Z, &p.Y) - FeSquare2(&r.T, &p.Z) - FeAdd(&r.Y, &p.X, &p.Y) - FeSquare(&t0, &r.Y) - FeAdd(&r.Y, &r.Z, &r.X) - FeSub(&r.Z, &r.Z, &r.X) - FeSub(&r.X, &t0, &r.Y) - FeSub(&r.T, &r.T, &r.Z) -} - -func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) { - var recip, x, y FieldElement - - FeInvert(&recip, &p.Z) - FeMul(&x, &p.X, &recip) - FeMul(&y, &p.Y, &recip) - FeToBytes(s, &y) - s[31] ^= FeIsNegative(&x) << 7 -} - -func (p *ExtendedGroupElement) Zero() { - FeZero(&p.X) - FeOne(&p.Y) - FeOne(&p.Z) - FeZero(&p.T) -} - -func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) { - var q ProjectiveGroupElement - p.ToProjective(&q) - q.Double(r) -} - -func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) { - FeAdd(&r.yPlusX, &p.Y, &p.X) - FeSub(&r.yMinusX, &p.Y, &p.X) - FeCopy(&r.Z, &p.Z) - FeMul(&r.T2d, &p.T, &d2) -} - -func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) { - FeCopy(&r.X, &p.X) - FeCopy(&r.Y, &p.Y) - FeCopy(&r.Z, &p.Z) -} - -func (p *ExtendedGroupElement) ToBytes(s *[32]byte) { - var recip, x, y FieldElement - - FeInvert(&recip, &p.Z) - FeMul(&x, &p.X, &recip) - FeMul(&y, &p.Y, &recip) - FeToBytes(s, &y) - s[31] ^= FeIsNegative(&x) << 7 -} - -func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool { - var u, v, v3, vxx, check FieldElement - - FeFromBytes(&p.Y, s) - FeOne(&p.Z) - FeSquare(&u, &p.Y) - FeMul(&v, &u, &d) - FeSub(&u, &u, &p.Z) // y = y^2-1 - FeAdd(&v, &v, &p.Z) // v = dy^2+1 - - FeSquare(&v3, &v) - FeMul(&v3, &v3, &v) // v3 = v^3 - FeSquare(&p.X, &v3) - FeMul(&p.X, &p.X, &v) - FeMul(&p.X, &p.X, &u) // x = uv^7 - - fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8) - FeMul(&p.X, &p.X, &v3) - FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8) - - var tmpX, tmp2 [32]byte - - FeSquare(&vxx, &p.X) - FeMul(&vxx, &vxx, &v) - FeSub(&check, &vxx, &u) // vx^2-u - if FeIsNonZero(&check) == 1 { - FeAdd(&check, &vxx, &u) // vx^2+u - if FeIsNonZero(&check) == 1 { - return false - } - FeMul(&p.X, &p.X, &SqrtM1) - - FeToBytes(&tmpX, &p.X) - for i, v := range tmpX { - tmp2[31-i] = v - } - } - - if FeIsNegative(&p.X) != (s[31] >> 7) { - FeNeg(&p.X, &p.X) - } - - FeMul(&p.T, &p.X, &p.Y) - return true -} - -func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) { - FeMul(&r.X, &p.X, &p.T) - FeMul(&r.Y, &p.Y, &p.Z) - FeMul(&r.Z, &p.Z, &p.T) -} - -func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) { - FeMul(&r.X, &p.X, &p.T) - FeMul(&r.Y, &p.Y, &p.Z) - FeMul(&r.Z, &p.Z, &p.T) - FeMul(&r.T, &p.X, &p.Y) -} - -func (p *PreComputedGroupElement) Zero() { - FeOne(&p.yPlusX) - FeOne(&p.yMinusX) - FeZero(&p.xy2d) -} - -func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { - var t0 FieldElement - - FeAdd(&r.X, &p.Y, &p.X) - FeSub(&r.Y, &p.Y, &p.X) - FeMul(&r.Z, &r.X, &q.yPlusX) - FeMul(&r.Y, &r.Y, &q.yMinusX) - FeMul(&r.T, &q.T2d, &p.T) - FeMul(&r.X, &p.Z, &q.Z) - FeAdd(&t0, &r.X, &r.X) - FeSub(&r.X, &r.Z, &r.Y) - FeAdd(&r.Y, &r.Z, &r.Y) - FeAdd(&r.Z, &t0, &r.T) - FeSub(&r.T, &t0, &r.T) -} - -func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { - var t0 FieldElement - - FeAdd(&r.X, &p.Y, &p.X) - FeSub(&r.Y, &p.Y, &p.X) - FeMul(&r.Z, &r.X, &q.yMinusX) - FeMul(&r.Y, &r.Y, &q.yPlusX) - FeMul(&r.T, &q.T2d, &p.T) - FeMul(&r.X, &p.Z, &q.Z) - FeAdd(&t0, &r.X, &r.X) - FeSub(&r.X, &r.Z, &r.Y) - FeAdd(&r.Y, &r.Z, &r.Y) - FeSub(&r.Z, &t0, &r.T) - FeAdd(&r.T, &t0, &r.T) -} - -func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { - var t0 FieldElement - - FeAdd(&r.X, &p.Y, &p.X) - FeSub(&r.Y, &p.Y, &p.X) - FeMul(&r.Z, &r.X, &q.yPlusX) - FeMul(&r.Y, &r.Y, &q.yMinusX) - FeMul(&r.T, &q.xy2d, &p.T) - FeAdd(&t0, &p.Z, &p.Z) - FeSub(&r.X, &r.Z, &r.Y) - FeAdd(&r.Y, &r.Z, &r.Y) - FeAdd(&r.Z, &t0, &r.T) - FeSub(&r.T, &t0, &r.T) -} - -func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { - var t0 FieldElement - - FeAdd(&r.X, &p.Y, &p.X) - FeSub(&r.Y, &p.Y, &p.X) - FeMul(&r.Z, &r.X, &q.yMinusX) - FeMul(&r.Y, &r.Y, &q.yPlusX) - FeMul(&r.T, &q.xy2d, &p.T) - FeAdd(&t0, &p.Z, &p.Z) - FeSub(&r.X, &r.Z, &r.Y) - FeAdd(&r.Y, &r.Z, &r.Y) - FeSub(&r.Z, &t0, &r.T) - FeAdd(&r.T, &t0, &r.T) -} - -func slide(r *[256]int8, a *[32]byte) { - for i := range r { - r[i] = int8(1 & (a[i>>3] >> uint(i&7))) - } - - for i := range r { - if r[i] != 0 { - for b := 1; b <= 6 && i+b < 256; b++ { - if r[i+b] != 0 { - if r[i]+(r[i+b]<= -15 { - r[i] -= r[i+b] << uint(b) - for k := i + b; k < 256; k++ { - if r[k] == 0 { - r[k] = 1 - break - } - r[k] = 0 - } - } else { - break - } - } - } - } - } -} - -// GeDoubleScalarMultVartime sets r = a*A + b*B -// where a = a[0]+256*a[1]+...+256^31 a[31]. -// and b = b[0]+256*b[1]+...+256^31 b[31]. -// B is the Ed25519 base point (x,4/5) with x positive. -func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) { - var aSlide, bSlide [256]int8 - var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A - var t CompletedGroupElement - var u, A2 ExtendedGroupElement - var i int - - slide(&aSlide, a) - slide(&bSlide, b) - - A.ToCached(&Ai[0]) - A.Double(&t) - t.ToExtended(&A2) - - for i := 0; i < 7; i++ { - geAdd(&t, &A2, &Ai[i]) - t.ToExtended(&u) - u.ToCached(&Ai[i+1]) - } - - r.Zero() - - for i = 255; i >= 0; i-- { - if aSlide[i] != 0 || bSlide[i] != 0 { - break - } - } - - for ; i >= 0; i-- { - r.Double(&t) - - if aSlide[i] > 0 { - t.ToExtended(&u) - geAdd(&t, &u, &Ai[aSlide[i]/2]) - } else if aSlide[i] < 0 { - t.ToExtended(&u) - geSub(&t, &u, &Ai[(-aSlide[i])/2]) - } - - if bSlide[i] > 0 { - t.ToExtended(&u) - geMixedAdd(&t, &u, &bi[bSlide[i]/2]) - } else if bSlide[i] < 0 { - t.ToExtended(&u) - geMixedSub(&t, &u, &bi[(-bSlide[i])/2]) - } - - t.ToProjective(r) - } -} - -// equal returns 1 if b == c and 0 otherwise, assuming that b and c are -// non-negative. -func equal(b, c int32) int32 { - x := uint32(b ^ c) - x-- - return int32(x >> 31) -} - -// negative returns 1 if b < 0 and 0 otherwise. -func negative(b int32) int32 { - return (b >> 31) & 1 -} - -func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) { - FeCMove(&t.yPlusX, &u.yPlusX, b) - FeCMove(&t.yMinusX, &u.yMinusX, b) - FeCMove(&t.xy2d, &u.xy2d, b) -} - -func selectPoint(t *PreComputedGroupElement, pos int32, b int32) { - var minusT PreComputedGroupElement - bNegative := negative(b) - bAbs := b - (((-bNegative) & b) << 1) - - t.Zero() - for i := int32(0); i < 8; i++ { - PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1)) - } - FeCopy(&minusT.yPlusX, &t.yMinusX) - FeCopy(&minusT.yMinusX, &t.yPlusX) - FeNeg(&minusT.xy2d, &t.xy2d) - PreComputedGroupElementCMove(t, &minusT, bNegative) -} - -// GeScalarMultBase computes h = a*B, where -// a = a[0]+256*a[1]+...+256^31 a[31] -// B is the Ed25519 base point (x,4/5) with x positive. -// -// Preconditions: -// a[31] <= 127 -func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) { - var e [64]int8 - - for i, v := range a { - e[2*i] = int8(v & 15) - e[2*i+1] = int8((v >> 4) & 15) - } - - // each e[i] is between 0 and 15 and e[63] is between 0 and 7. - - carry := int8(0) - for i := 0; i < 63; i++ { - e[i] += carry - carry = (e[i] + 8) >> 4 - e[i] -= carry << 4 - } - e[63] += carry - // each e[i] is between -8 and 8. - - h.Zero() - var t PreComputedGroupElement - var r CompletedGroupElement - for i := int32(1); i < 64; i += 2 { - selectPoint(&t, i/2, int32(e[i])) - geMixedAdd(&r, h, &t) - r.ToExtended(h) - } - - var s ProjectiveGroupElement - - h.Double(&r) - r.ToProjective(&s) - s.Double(&r) - r.ToProjective(&s) - s.Double(&r) - r.ToProjective(&s) - s.Double(&r) - r.ToExtended(h) - - for i := int32(0); i < 64; i += 2 { - selectPoint(&t, i/2, int32(e[i])) - geMixedAdd(&r, h, &t) - r.ToExtended(h) - } -} - -// The scalars are GF(2^252 + 27742317777372353535851937790883648493). - -// Input: -// a[0]+256*a[1]+...+256^31*a[31] = a -// b[0]+256*b[1]+...+256^31*b[31] = b -// c[0]+256*c[1]+...+256^31*c[31] = c -// -// Output: -// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l -// where l = 2^252 + 27742317777372353535851937790883648493. -func ScMulAdd(s, a, b, c *[32]byte) { - a0 := 2097151 & load3(a[:]) - a1 := 2097151 & (load4(a[2:]) >> 5) - a2 := 2097151 & (load3(a[5:]) >> 2) - a3 := 2097151 & (load4(a[7:]) >> 7) - a4 := 2097151 & (load4(a[10:]) >> 4) - a5 := 2097151 & (load3(a[13:]) >> 1) - a6 := 2097151 & (load4(a[15:]) >> 6) - a7 := 2097151 & (load3(a[18:]) >> 3) - a8 := 2097151 & load3(a[21:]) - a9 := 2097151 & (load4(a[23:]) >> 5) - a10 := 2097151 & (load3(a[26:]) >> 2) - a11 := (load4(a[28:]) >> 7) - b0 := 2097151 & load3(b[:]) - b1 := 2097151 & (load4(b[2:]) >> 5) - b2 := 2097151 & (load3(b[5:]) >> 2) - b3 := 2097151 & (load4(b[7:]) >> 7) - b4 := 2097151 & (load4(b[10:]) >> 4) - b5 := 2097151 & (load3(b[13:]) >> 1) - b6 := 2097151 & (load4(b[15:]) >> 6) - b7 := 2097151 & (load3(b[18:]) >> 3) - b8 := 2097151 & load3(b[21:]) - b9 := 2097151 & (load4(b[23:]) >> 5) - b10 := 2097151 & (load3(b[26:]) >> 2) - b11 := (load4(b[28:]) >> 7) - c0 := 2097151 & load3(c[:]) - c1 := 2097151 & (load4(c[2:]) >> 5) - c2 := 2097151 & (load3(c[5:]) >> 2) - c3 := 2097151 & (load4(c[7:]) >> 7) - c4 := 2097151 & (load4(c[10:]) >> 4) - c5 := 2097151 & (load3(c[13:]) >> 1) - c6 := 2097151 & (load4(c[15:]) >> 6) - c7 := 2097151 & (load3(c[18:]) >> 3) - c8 := 2097151 & load3(c[21:]) - c9 := 2097151 & (load4(c[23:]) >> 5) - c10 := 2097151 & (load3(c[26:]) >> 2) - c11 := (load4(c[28:]) >> 7) - var carry [23]int64 - - s0 := c0 + a0*b0 - s1 := c1 + a0*b1 + a1*b0 - s2 := c2 + a0*b2 + a1*b1 + a2*b0 - s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0 - s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0 - s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0 - s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0 - s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0 - s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0 - s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0 - s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0 - s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0 - s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1 - s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2 - s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3 - s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4 - s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5 - s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6 - s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7 - s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8 - s20 := a9*b11 + a10*b10 + a11*b9 - s21 := a10*b11 + a11*b10 - s22 := a11 * b11 - s23 := int64(0) - - carry[0] = (s0 + (1 << 20)) >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[2] = (s2 + (1 << 20)) >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[4] = (s4 + (1 << 20)) >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[6] = (s6 + (1 << 20)) >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[8] = (s8 + (1 << 20)) >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[10] = (s10 + (1 << 20)) >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - carry[12] = (s12 + (1 << 20)) >> 21 - s13 += carry[12] - s12 -= carry[12] << 21 - carry[14] = (s14 + (1 << 20)) >> 21 - s15 += carry[14] - s14 -= carry[14] << 21 - carry[16] = (s16 + (1 << 20)) >> 21 - s17 += carry[16] - s16 -= carry[16] << 21 - carry[18] = (s18 + (1 << 20)) >> 21 - s19 += carry[18] - s18 -= carry[18] << 21 - carry[20] = (s20 + (1 << 20)) >> 21 - s21 += carry[20] - s20 -= carry[20] << 21 - carry[22] = (s22 + (1 << 20)) >> 21 - s23 += carry[22] - s22 -= carry[22] << 21 - - carry[1] = (s1 + (1 << 20)) >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[3] = (s3 + (1 << 20)) >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[5] = (s5 + (1 << 20)) >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[7] = (s7 + (1 << 20)) >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[9] = (s9 + (1 << 20)) >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[11] = (s11 + (1 << 20)) >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - carry[13] = (s13 + (1 << 20)) >> 21 - s14 += carry[13] - s13 -= carry[13] << 21 - carry[15] = (s15 + (1 << 20)) >> 21 - s16 += carry[15] - s15 -= carry[15] << 21 - carry[17] = (s17 + (1 << 20)) >> 21 - s18 += carry[17] - s17 -= carry[17] << 21 - carry[19] = (s19 + (1 << 20)) >> 21 - s20 += carry[19] - s19 -= carry[19] << 21 - carry[21] = (s21 + (1 << 20)) >> 21 - s22 += carry[21] - s21 -= carry[21] << 21 - - s11 += s23 * 666643 - s12 += s23 * 470296 - s13 += s23 * 654183 - s14 -= s23 * 997805 - s15 += s23 * 136657 - s16 -= s23 * 683901 - s23 = 0 - - s10 += s22 * 666643 - s11 += s22 * 470296 - s12 += s22 * 654183 - s13 -= s22 * 997805 - s14 += s22 * 136657 - s15 -= s22 * 683901 - s22 = 0 - - s9 += s21 * 666643 - s10 += s21 * 470296 - s11 += s21 * 654183 - s12 -= s21 * 997805 - s13 += s21 * 136657 - s14 -= s21 * 683901 - s21 = 0 - - s8 += s20 * 666643 - s9 += s20 * 470296 - s10 += s20 * 654183 - s11 -= s20 * 997805 - s12 += s20 * 136657 - s13 -= s20 * 683901 - s20 = 0 - - s7 += s19 * 666643 - s8 += s19 * 470296 - s9 += s19 * 654183 - s10 -= s19 * 997805 - s11 += s19 * 136657 - s12 -= s19 * 683901 - s19 = 0 - - s6 += s18 * 666643 - s7 += s18 * 470296 - s8 += s18 * 654183 - s9 -= s18 * 997805 - s10 += s18 * 136657 - s11 -= s18 * 683901 - s18 = 0 - - carry[6] = (s6 + (1 << 20)) >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[8] = (s8 + (1 << 20)) >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[10] = (s10 + (1 << 20)) >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - carry[12] = (s12 + (1 << 20)) >> 21 - s13 += carry[12] - s12 -= carry[12] << 21 - carry[14] = (s14 + (1 << 20)) >> 21 - s15 += carry[14] - s14 -= carry[14] << 21 - carry[16] = (s16 + (1 << 20)) >> 21 - s17 += carry[16] - s16 -= carry[16] << 21 - - carry[7] = (s7 + (1 << 20)) >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[9] = (s9 + (1 << 20)) >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[11] = (s11 + (1 << 20)) >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - carry[13] = (s13 + (1 << 20)) >> 21 - s14 += carry[13] - s13 -= carry[13] << 21 - carry[15] = (s15 + (1 << 20)) >> 21 - s16 += carry[15] - s15 -= carry[15] << 21 - - s5 += s17 * 666643 - s6 += s17 * 470296 - s7 += s17 * 654183 - s8 -= s17 * 997805 - s9 += s17 * 136657 - s10 -= s17 * 683901 - s17 = 0 - - s4 += s16 * 666643 - s5 += s16 * 470296 - s6 += s16 * 654183 - s7 -= s16 * 997805 - s8 += s16 * 136657 - s9 -= s16 * 683901 - s16 = 0 - - s3 += s15 * 666643 - s4 += s15 * 470296 - s5 += s15 * 654183 - s6 -= s15 * 997805 - s7 += s15 * 136657 - s8 -= s15 * 683901 - s15 = 0 - - s2 += s14 * 666643 - s3 += s14 * 470296 - s4 += s14 * 654183 - s5 -= s14 * 997805 - s6 += s14 * 136657 - s7 -= s14 * 683901 - s14 = 0 - - s1 += s13 * 666643 - s2 += s13 * 470296 - s3 += s13 * 654183 - s4 -= s13 * 997805 - s5 += s13 * 136657 - s6 -= s13 * 683901 - s13 = 0 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = (s0 + (1 << 20)) >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[2] = (s2 + (1 << 20)) >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[4] = (s4 + (1 << 20)) >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[6] = (s6 + (1 << 20)) >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[8] = (s8 + (1 << 20)) >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[10] = (s10 + (1 << 20)) >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - - carry[1] = (s1 + (1 << 20)) >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[3] = (s3 + (1 << 20)) >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[5] = (s5 + (1 << 20)) >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[7] = (s7 + (1 << 20)) >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[9] = (s9 + (1 << 20)) >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[11] = (s11 + (1 << 20)) >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = s0 >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[1] = s1 >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[2] = s2 >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[3] = s3 >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[4] = s4 >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[5] = s5 >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[6] = s6 >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[7] = s7 >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[8] = s8 >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[9] = s9 >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[10] = s10 >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - carry[11] = s11 >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = s0 >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[1] = s1 >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[2] = s2 >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[3] = s3 >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[4] = s4 >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[5] = s5 >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[6] = s6 >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[7] = s7 >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[8] = s8 >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[9] = s9 >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[10] = s10 >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - - s[0] = byte(s0 >> 0) - s[1] = byte(s0 >> 8) - s[2] = byte((s0 >> 16) | (s1 << 5)) - s[3] = byte(s1 >> 3) - s[4] = byte(s1 >> 11) - s[5] = byte((s1 >> 19) | (s2 << 2)) - s[6] = byte(s2 >> 6) - s[7] = byte((s2 >> 14) | (s3 << 7)) - s[8] = byte(s3 >> 1) - s[9] = byte(s3 >> 9) - s[10] = byte((s3 >> 17) | (s4 << 4)) - s[11] = byte(s4 >> 4) - s[12] = byte(s4 >> 12) - s[13] = byte((s4 >> 20) | (s5 << 1)) - s[14] = byte(s5 >> 7) - s[15] = byte((s5 >> 15) | (s6 << 6)) - s[16] = byte(s6 >> 2) - s[17] = byte(s6 >> 10) - s[18] = byte((s6 >> 18) | (s7 << 3)) - s[19] = byte(s7 >> 5) - s[20] = byte(s7 >> 13) - s[21] = byte(s8 >> 0) - s[22] = byte(s8 >> 8) - s[23] = byte((s8 >> 16) | (s9 << 5)) - s[24] = byte(s9 >> 3) - s[25] = byte(s9 >> 11) - s[26] = byte((s9 >> 19) | (s10 << 2)) - s[27] = byte(s10 >> 6) - s[28] = byte((s10 >> 14) | (s11 << 7)) - s[29] = byte(s11 >> 1) - s[30] = byte(s11 >> 9) - s[31] = byte(s11 >> 17) -} - -// Input: -// s[0]+256*s[1]+...+256^63*s[63] = s -// -// Output: -// s[0]+256*s[1]+...+256^31*s[31] = s mod l -// where l = 2^252 + 27742317777372353535851937790883648493. -func ScReduce(out *[32]byte, s *[64]byte) { - s0 := 2097151 & load3(s[:]) - s1 := 2097151 & (load4(s[2:]) >> 5) - s2 := 2097151 & (load3(s[5:]) >> 2) - s3 := 2097151 & (load4(s[7:]) >> 7) - s4 := 2097151 & (load4(s[10:]) >> 4) - s5 := 2097151 & (load3(s[13:]) >> 1) - s6 := 2097151 & (load4(s[15:]) >> 6) - s7 := 2097151 & (load3(s[18:]) >> 3) - s8 := 2097151 & load3(s[21:]) - s9 := 2097151 & (load4(s[23:]) >> 5) - s10 := 2097151 & (load3(s[26:]) >> 2) - s11 := 2097151 & (load4(s[28:]) >> 7) - s12 := 2097151 & (load4(s[31:]) >> 4) - s13 := 2097151 & (load3(s[34:]) >> 1) - s14 := 2097151 & (load4(s[36:]) >> 6) - s15 := 2097151 & (load3(s[39:]) >> 3) - s16 := 2097151 & load3(s[42:]) - s17 := 2097151 & (load4(s[44:]) >> 5) - s18 := 2097151 & (load3(s[47:]) >> 2) - s19 := 2097151 & (load4(s[49:]) >> 7) - s20 := 2097151 & (load4(s[52:]) >> 4) - s21 := 2097151 & (load3(s[55:]) >> 1) - s22 := 2097151 & (load4(s[57:]) >> 6) - s23 := (load4(s[60:]) >> 3) - - s11 += s23 * 666643 - s12 += s23 * 470296 - s13 += s23 * 654183 - s14 -= s23 * 997805 - s15 += s23 * 136657 - s16 -= s23 * 683901 - s23 = 0 - - s10 += s22 * 666643 - s11 += s22 * 470296 - s12 += s22 * 654183 - s13 -= s22 * 997805 - s14 += s22 * 136657 - s15 -= s22 * 683901 - s22 = 0 - - s9 += s21 * 666643 - s10 += s21 * 470296 - s11 += s21 * 654183 - s12 -= s21 * 997805 - s13 += s21 * 136657 - s14 -= s21 * 683901 - s21 = 0 - - s8 += s20 * 666643 - s9 += s20 * 470296 - s10 += s20 * 654183 - s11 -= s20 * 997805 - s12 += s20 * 136657 - s13 -= s20 * 683901 - s20 = 0 - - s7 += s19 * 666643 - s8 += s19 * 470296 - s9 += s19 * 654183 - s10 -= s19 * 997805 - s11 += s19 * 136657 - s12 -= s19 * 683901 - s19 = 0 - - s6 += s18 * 666643 - s7 += s18 * 470296 - s8 += s18 * 654183 - s9 -= s18 * 997805 - s10 += s18 * 136657 - s11 -= s18 * 683901 - s18 = 0 - - var carry [17]int64 - - carry[6] = (s6 + (1 << 20)) >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[8] = (s8 + (1 << 20)) >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[10] = (s10 + (1 << 20)) >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - carry[12] = (s12 + (1 << 20)) >> 21 - s13 += carry[12] - s12 -= carry[12] << 21 - carry[14] = (s14 + (1 << 20)) >> 21 - s15 += carry[14] - s14 -= carry[14] << 21 - carry[16] = (s16 + (1 << 20)) >> 21 - s17 += carry[16] - s16 -= carry[16] << 21 - - carry[7] = (s7 + (1 << 20)) >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[9] = (s9 + (1 << 20)) >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[11] = (s11 + (1 << 20)) >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - carry[13] = (s13 + (1 << 20)) >> 21 - s14 += carry[13] - s13 -= carry[13] << 21 - carry[15] = (s15 + (1 << 20)) >> 21 - s16 += carry[15] - s15 -= carry[15] << 21 - - s5 += s17 * 666643 - s6 += s17 * 470296 - s7 += s17 * 654183 - s8 -= s17 * 997805 - s9 += s17 * 136657 - s10 -= s17 * 683901 - s17 = 0 - - s4 += s16 * 666643 - s5 += s16 * 470296 - s6 += s16 * 654183 - s7 -= s16 * 997805 - s8 += s16 * 136657 - s9 -= s16 * 683901 - s16 = 0 - - s3 += s15 * 666643 - s4 += s15 * 470296 - s5 += s15 * 654183 - s6 -= s15 * 997805 - s7 += s15 * 136657 - s8 -= s15 * 683901 - s15 = 0 - - s2 += s14 * 666643 - s3 += s14 * 470296 - s4 += s14 * 654183 - s5 -= s14 * 997805 - s6 += s14 * 136657 - s7 -= s14 * 683901 - s14 = 0 - - s1 += s13 * 666643 - s2 += s13 * 470296 - s3 += s13 * 654183 - s4 -= s13 * 997805 - s5 += s13 * 136657 - s6 -= s13 * 683901 - s13 = 0 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = (s0 + (1 << 20)) >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[2] = (s2 + (1 << 20)) >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[4] = (s4 + (1 << 20)) >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[6] = (s6 + (1 << 20)) >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[8] = (s8 + (1 << 20)) >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[10] = (s10 + (1 << 20)) >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - - carry[1] = (s1 + (1 << 20)) >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[3] = (s3 + (1 << 20)) >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[5] = (s5 + (1 << 20)) >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[7] = (s7 + (1 << 20)) >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[9] = (s9 + (1 << 20)) >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[11] = (s11 + (1 << 20)) >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = s0 >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[1] = s1 >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[2] = s2 >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[3] = s3 >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[4] = s4 >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[5] = s5 >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[6] = s6 >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[7] = s7 >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[8] = s8 >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[9] = s9 >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[10] = s10 >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - carry[11] = s11 >> 21 - s12 += carry[11] - s11 -= carry[11] << 21 - - s0 += s12 * 666643 - s1 += s12 * 470296 - s2 += s12 * 654183 - s3 -= s12 * 997805 - s4 += s12 * 136657 - s5 -= s12 * 683901 - s12 = 0 - - carry[0] = s0 >> 21 - s1 += carry[0] - s0 -= carry[0] << 21 - carry[1] = s1 >> 21 - s2 += carry[1] - s1 -= carry[1] << 21 - carry[2] = s2 >> 21 - s3 += carry[2] - s2 -= carry[2] << 21 - carry[3] = s3 >> 21 - s4 += carry[3] - s3 -= carry[3] << 21 - carry[4] = s4 >> 21 - s5 += carry[4] - s4 -= carry[4] << 21 - carry[5] = s5 >> 21 - s6 += carry[5] - s5 -= carry[5] << 21 - carry[6] = s6 >> 21 - s7 += carry[6] - s6 -= carry[6] << 21 - carry[7] = s7 >> 21 - s8 += carry[7] - s7 -= carry[7] << 21 - carry[8] = s8 >> 21 - s9 += carry[8] - s8 -= carry[8] << 21 - carry[9] = s9 >> 21 - s10 += carry[9] - s9 -= carry[9] << 21 - carry[10] = s10 >> 21 - s11 += carry[10] - s10 -= carry[10] << 21 - - out[0] = byte(s0 >> 0) - out[1] = byte(s0 >> 8) - out[2] = byte((s0 >> 16) | (s1 << 5)) - out[3] = byte(s1 >> 3) - out[4] = byte(s1 >> 11) - out[5] = byte((s1 >> 19) | (s2 << 2)) - out[6] = byte(s2 >> 6) - out[7] = byte((s2 >> 14) | (s3 << 7)) - out[8] = byte(s3 >> 1) - out[9] = byte(s3 >> 9) - out[10] = byte((s3 >> 17) | (s4 << 4)) - out[11] = byte(s4 >> 4) - out[12] = byte(s4 >> 12) - out[13] = byte((s4 >> 20) | (s5 << 1)) - out[14] = byte(s5 >> 7) - out[15] = byte((s5 >> 15) | (s6 << 6)) - out[16] = byte(s6 >> 2) - out[17] = byte(s6 >> 10) - out[18] = byte((s6 >> 18) | (s7 << 3)) - out[19] = byte(s7 >> 5) - out[20] = byte(s7 >> 13) - out[21] = byte(s8 >> 0) - out[22] = byte(s8 >> 8) - out[23] = byte((s8 >> 16) | (s9 << 5)) - out[24] = byte(s9 >> 3) - out[25] = byte(s9 >> 11) - out[26] = byte((s9 >> 19) | (s10 << 2)) - out[27] = byte(s10 >> 6) - out[28] = byte((s10 >> 14) | (s11 << 7)) - out[29] = byte(s11 >> 1) - out[30] = byte(s11 >> 9) - out[31] = byte(s11 >> 17) -} - -// order is the order of Curve25519 in little-endian form. -var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000} - -// ScMinimal returns true if the given scalar is less than the order of the -// curve. -func ScMinimal(scalar *[32]byte) bool { - for i := 3; ; i-- { - v := binary.LittleEndian.Uint64(scalar[i*8:]) - if v > order[i] { - return false - } else if v < order[i] { - break - } else if i == 0 { - return false - } - } - - return true -} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go index c942a659..e041da5e 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go @@ -136,7 +136,7 @@ func shiftRightBy2(a uint128) uint128 { // updateGeneric absorbs msg into the state.h accumulator. For each chunk m of // 128 bits of message, it computes // -// h₊ = (h + m) * r mod 2¹³⁰ - 5 +// h₊ = (h + m) * r mod 2¹³⁰ - 5 // // If the msg length is not a multiple of TagSize, it assumes the last // incomplete chunk is the final one. @@ -278,8 +278,7 @@ const ( // finalize completes the modular reduction of h and computes // -// out = h + s mod 2¹²⁸ -// +// out = h + s mod 2¹²⁸ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { h0, h1, h2 := h[0], h[1], h[2] diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go index 62cc9f84..ec959668 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go @@ -14,6 +14,7 @@ import ( // updateVX is an assembly implementation of Poly1305 that uses vector // instructions. It must only be called if the vector facility (vx) is // available. +// //go:noescape func updateVX(state *macState, msg []byte) diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go index 593f6530..904b57e0 100644 --- a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go +++ b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go @@ -32,7 +32,7 @@ import ( // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by // doing: // -// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) +// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) // // Remember to get a good random salt. At least 8 bytes is recommended by the // RFC. diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index 916c840b..a69e2249 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -14,8 +14,10 @@ import ( "time" ) -// These constants from [PROTOCOL.certkeys] represent the algorithm names -// for certificate types supported by this package. +// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear +// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms. +// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't +// appear in the Signature.Format field. const ( CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" @@ -25,6 +27,21 @@ const ( CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com" CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com" + + // CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a + // Certificate.Type (or PublicKey.Type), but only in + // ClientConfig.HostKeyAlgorithms. + CertAlgoRSASHA256v01 = "rsa-sha2-256-cert-v01@openssh.com" + CertAlgoRSASHA512v01 = "rsa-sha2-512-cert-v01@openssh.com" +) + +const ( + // Deprecated: use CertAlgoRSAv01. + CertSigAlgoRSAv01 = CertAlgoRSAv01 + // Deprecated: use CertAlgoRSASHA256v01. + CertSigAlgoRSASHA2256v01 = CertAlgoRSASHA256v01 + // Deprecated: use CertAlgoRSASHA512v01. + CertSigAlgoRSASHA2512v01 = CertAlgoRSASHA512v01 ) // Certificate types distinguish between host and user @@ -423,6 +440,16 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { } c.SignatureKey = authority.PublicKey() + // Default to KeyAlgoRSASHA512 for ssh-rsa signers. + if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA { + sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512) + if err != nil { + return err + } + c.Signature = sig + return nil + } + sig, err := authority.Sign(rand, c.bytesForSigning()) if err != nil { return err @@ -431,26 +458,40 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { return nil } -var certAlgoNames = map[string]string{ - KeyAlgoRSA: CertAlgoRSAv01, - KeyAlgoDSA: CertAlgoDSAv01, - KeyAlgoECDSA256: CertAlgoECDSA256v01, - KeyAlgoECDSA384: CertAlgoECDSA384v01, - KeyAlgoECDSA521: CertAlgoECDSA521v01, - KeyAlgoSKECDSA256: CertAlgoSKECDSA256v01, - KeyAlgoED25519: CertAlgoED25519v01, - KeyAlgoSKED25519: CertAlgoSKED25519v01, +// certKeyAlgoNames is a mapping from known certificate algorithm names to the +// corresponding public key signature algorithm. +var certKeyAlgoNames = map[string]string{ + CertAlgoRSAv01: KeyAlgoRSA, + CertAlgoRSASHA256v01: KeyAlgoRSASHA256, + CertAlgoRSASHA512v01: KeyAlgoRSASHA512, + CertAlgoDSAv01: KeyAlgoDSA, + CertAlgoECDSA256v01: KeyAlgoECDSA256, + CertAlgoECDSA384v01: KeyAlgoECDSA384, + CertAlgoECDSA521v01: KeyAlgoECDSA521, + CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256, + CertAlgoED25519v01: KeyAlgoED25519, + CertAlgoSKED25519v01: KeyAlgoSKED25519, } -// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. -// Panics if a non-certificate algorithm is passed. -func certToPrivAlgo(algo string) string { - for privAlgo, pubAlgo := range certAlgoNames { - if pubAlgo == algo { - return privAlgo +// underlyingAlgo returns the signature algorithm associated with algo (which is +// an advertised or negotiated public key or host key algorithm). These are +// usually the same, except for certificate algorithms. +func underlyingAlgo(algo string) string { + if a, ok := certKeyAlgoNames[algo]; ok { + return a + } + return algo +} + +// certificateAlgo returns the certificate algorithms that uses the provided +// underlying signature algorithm. +func certificateAlgo(algo string) (certAlgo string, ok bool) { + for certName, algoName := range certKeyAlgoNames { + if algoName == algo { + return certName, true } } - panic("unknown cert algorithm") + return "", false } func (cert *Certificate) bytesForSigning() []byte { @@ -494,13 +535,13 @@ func (c *Certificate) Marshal() []byte { return result } -// Type returns the key name. It is part of the PublicKey interface. +// Type returns the certificate algorithm name. It is part of the PublicKey interface. func (c *Certificate) Type() string { - algo, ok := certAlgoNames[c.Key.Type()] + certName, ok := certificateAlgo(c.Key.Type()) if !ok { - panic("unknown cert key type " + c.Key.Type()) + panic("unknown certificate type for key type " + c.Key.Type()) } - return algo + return certName } // Verify verifies a signature against the certificate's public diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go index bddbde5d..770e8a66 100644 --- a/vendor/golang.org/x/crypto/ssh/cipher.go +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -394,6 +394,10 @@ func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) } c.incIV() + if len(plain) == 0 { + return nil, errors.New("ssh: empty packet") + } + padding := plain[0] if padding < 4 { // padding is a byte, so it automatically satisfies @@ -636,7 +640,7 @@ const chacha20Poly1305ID = "chacha20-poly1305@openssh.com" // chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com // AEAD, which is described here: // -// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00 +// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00 // // the methods here also implement padding, which RFC4253 Section 6 // also requires of stream ciphers. @@ -710,6 +714,10 @@ func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([ plain := c.buf[4:contentEnd] s.XORKeyStream(plain, plain) + if len(plain) == 0 { + return nil, errors.New("ssh: empty packet") + } + padding := plain[0] if padding < 4 { // padding is a byte, so it automatically satisfies diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index 99f68bd3..bdc356cb 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -113,14 +113,18 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e return c.clientAuthenticate(config) } -// verifyHostKeySignature verifies the host key obtained in the key -// exchange. -func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { +// verifyHostKeySignature verifies the host key obtained in the key exchange. +// algo is the negotiated algorithm, and may be a certificate type. +func verifyHostKeySignature(hostKey PublicKey, algo string, result *kexResult) error { sig, rest, ok := parseSignatureBody(result.Signature) if len(rest) > 0 || !ok { return errors.New("ssh: signature parse error") } + if a := underlyingAlgo(algo); sig.Format != a { + return fmt.Errorf("ssh: invalid signature algorithm %q, expected %q", sig.Format, a) + } + return hostKey.Verify(result.H, sig) } @@ -224,11 +228,11 @@ type ClientConfig struct { // be used for the connection. If empty, a reasonable default is used. ClientVersion string - // HostKeyAlgorithms lists the key types that the client will - // accept from the server as host key, in order of + // HostKeyAlgorithms lists the public key algorithms that the client will + // accept from the server for host key authentication, in order of // preference. If empty, a reasonable default is used. Any - // string returned from PublicKey.Type method may be used, or - // any of the CertAlgoXxxx and KeyAlgoXxxx constants. + // string returned from a PublicKey.Type method may be used, or + // any of the CertAlgo and KeyAlgo constants. HostKeyAlgorithms []string // Timeout is the maximum amount of time for the TCP connection to establish. diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index c611aeb6..409b5ea1 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "strings" ) type authResult int @@ -29,6 +30,33 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { if err != nil { return err } + // The server may choose to send a SSH_MSG_EXT_INFO at this point (if we + // advertised willingness to receive one, which we always do) or not. See + // RFC 8308, Section 2.4. + extensions := make(map[string][]byte) + if len(packet) > 0 && packet[0] == msgExtInfo { + var extInfo extInfoMsg + if err := Unmarshal(packet, &extInfo); err != nil { + return err + } + payload := extInfo.Payload + for i := uint32(0); i < extInfo.NumExtensions; i++ { + name, rest, ok := parseString(payload) + if !ok { + return parseError(msgExtInfo) + } + value, rest, ok := parseString(rest) + if !ok { + return parseError(msgExtInfo) + } + extensions[string(name)] = value + payload = rest + } + packet, err = c.transport.readPacket() + if err != nil { + return err + } + } var serviceAccept serviceAcceptMsg if err := Unmarshal(packet, &serviceAccept); err != nil { return err @@ -41,7 +69,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { sessionID := c.transport.getSessionID() for auth := AuthMethod(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) + ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions) if err != nil { return err } @@ -93,7 +121,7 @@ type AuthMethod interface { // If authentication is not successful, a []string of alternative // method names is returned. If the slice is nil, it will be ignored // and the previous set of possible methods will be reused. - auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error) + auth(session []byte, user string, p packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) // method returns the RFC 4252 method name. method() string @@ -102,7 +130,7 @@ type AuthMethod interface { // "none" authentication, RFC 4252 section 5.2. type noneAuth int -func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { +func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) { if err := c.writePacket(Marshal(&userAuthRequestMsg{ User: user, Service: serviceSSH, @@ -122,7 +150,7 @@ func (n *noneAuth) method() string { // a function call, e.g. by prompting the user. type passwordCallback func() (password string, err error) -func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { +func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) { type passwordAuthMsg struct { User string `sshtype:"50"` Service string @@ -189,7 +217,46 @@ func (cb publicKeyCallback) method() string { return "publickey" } -func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { +func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as AlgorithmSigner, algo string) { + keyFormat := signer.PublicKey().Type() + + // Like in sendKexInit, if the public key implements AlgorithmSigner we + // assume it supports all algorithms, otherwise only the key format one. + as, ok := signer.(AlgorithmSigner) + if !ok { + return algorithmSignerWrapper{signer}, keyFormat + } + + extPayload, ok := extensions["server-sig-algs"] + if !ok { + // If there is no "server-sig-algs" extension, fall back to the key + // format algorithm. + return as, keyFormat + } + + // The server-sig-algs extension only carries underlying signature + // algorithm, but we are trying to select a protocol-level public key + // algorithm, which might be a certificate type. Extend the list of server + // supported algorithms to include the corresponding certificate algorithms. + serverAlgos := strings.Split(string(extPayload), ",") + for _, algo := range serverAlgos { + if certAlgo, ok := certificateAlgo(algo); ok { + serverAlgos = append(serverAlgos, certAlgo) + } + } + + keyAlgos := algorithmsForKeyFormat(keyFormat) + algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos) + if err != nil { + // If there is no overlap, try the key anyway with the key format + // algorithm, to support servers that fail to list all supported + // algorithms. + return as, keyFormat + } + return as, algo +} + +func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) { // Authentication is performed by sending an enquiry to test if a key is // acceptable to the remote. If the key is acceptable, the client will // attempt to authenticate with the valid key. If not the client will repeat @@ -201,7 +268,10 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand } var methods []string for _, signer := range signers { - ok, err := validateKey(signer.PublicKey(), user, c) + pub := signer.PublicKey() + as, algo := pickSignatureAlgorithm(signer, extensions) + + ok, err := validateKey(pub, algo, user, c) if err != nil { return authFailure, nil, err } @@ -209,13 +279,13 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand continue } - pub := signer.PublicKey() pubKey := pub.Marshal() - sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + data := buildDataSignedForAuth(session, userAuthRequestMsg{ User: user, Service: serviceSSH, Method: cb.method(), - }, []byte(pub.Type()), pubKey)) + }, algo, pubKey) + sign, err := as.SignWithAlgorithm(rand, data, underlyingAlgo(algo)) if err != nil { return authFailure, nil, err } @@ -229,7 +299,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand Service: serviceSSH, Method: cb.method(), HasSig: true, - Algoname: pub.Type(), + Algoname: algo, PubKey: pubKey, Sig: sig, } @@ -266,26 +336,25 @@ func containsMethod(methods []string, method string) bool { } // validateKey validates the key provided is acceptable to the server. -func validateKey(key PublicKey, user string, c packetConn) (bool, error) { +func validateKey(key PublicKey, algo string, user string, c packetConn) (bool, error) { pubKey := key.Marshal() msg := publickeyAuthMsg{ User: user, Service: serviceSSH, Method: "publickey", HasSig: false, - Algoname: key.Type(), + Algoname: algo, PubKey: pubKey, } if err := c.writePacket(Marshal(&msg)); err != nil { return false, err } - return confirmKeyAck(key, c) + return confirmKeyAck(key, algo, c) } -func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { +func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) { pubKey := key.Marshal() - algoname := key.Type() for { packet, err := c.readPacket() @@ -302,14 +371,14 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { if err := Unmarshal(packet, &msg); err != nil { return false, err } - if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { + if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) { return false, nil } return true, nil case msgUserAuthFailure: return false, nil default: - return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0]) } } } @@ -330,6 +399,7 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet // along with a list of remaining authentication methods to try next and // an error if an unexpected response was received. func handleAuthResponse(c packetConn) (authResult, []string, error) { + gotMsgExtInfo := false for { packet, err := c.readPacket() if err != nil { @@ -341,6 +411,12 @@ func handleAuthResponse(c packetConn) (authResult, []string, error) { if err := handleBannerResponse(c, packet); err != nil { return authFailure, nil, err } + case msgExtInfo: + // Ignore post-authentication RFC 8308 extensions, once. + if gotMsgExtInfo { + return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + gotMsgExtInfo = true case msgUserAuthFailure: var msg userAuthFailureMsg if err := Unmarshal(packet, &msg); err != nil { @@ -380,10 +456,10 @@ func handleBannerResponse(c packetConn, packet []byte) error { // disabling echoing (e.g. for passwords), and return all the answers. // Challenge may be called multiple times in a single session. After // successful authentication, the server may send a challenge with no -// questions, for which the user and instruction messages should be +// questions, for which the name and instruction messages should be // printed. RFC 4256 section 3.3 details how the UI should behave for // both CLI and GUI environments. -type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) +type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error) // KeyboardInteractive returns an AuthMethod using a prompt/response // sequence controlled by the server. @@ -395,7 +471,7 @@ func (cb KeyboardInteractiveChallenge) method() string { return "keyboard-interactive" } -func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { +func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) { type initiateMsg struct { User string `sshtype:"50"` Service string @@ -412,6 +488,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe return authFailure, nil, err } + gotMsgExtInfo := false for { packet, err := c.readPacket() if err != nil { @@ -425,6 +502,13 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe return authFailure, nil, err } continue + case msgExtInfo: + // Ignore post-authentication RFC 8308 extensions, once. + if gotMsgExtInfo { + return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) + } + gotMsgExtInfo = true + continue case msgUserAuthInfoRequest: // OK case msgUserAuthFailure: @@ -465,7 +549,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs") } - answers, err := cb(msg.User, msg.Instruction, prompts, echos) + answers, err := cb(msg.Name, msg.Instruction, prompts, echos) if err != nil { return authFailure, nil, err } @@ -497,9 +581,9 @@ type retryableAuthMethod struct { maxTries int } -func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, methods []string, err error) { +func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (ok authResult, methods []string, err error) { for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ { - ok, methods, err = r.authMethod.auth(session, user, c, rand) + ok, methods, err = r.authMethod.auth(session, user, c, rand, extensions) if ok != authFailure || err != nil { // either success, partial success or error terminate return ok, methods, err } @@ -542,7 +626,7 @@ type gssAPIWithMICCallback struct { target string } -func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { +func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader, _ map[string][]byte) (authResult, []string, error) { m := &userAuthRequestMsg{ User: user, Service: serviceSSH, diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index 290382d0..2a47a61d 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -44,11 +44,11 @@ var preferredCiphers = []string{ // supportedKexAlgos specifies the supported key-exchange algorithms in // preference order. var supportedKexAlgos = []string{ - kexAlgoCurve25519SHA256, + kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH, // P384 and P521 are not constant-time yet, but since we don't // reuse ephemeral keys, using them for ECDH should be OK. kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, - kexAlgoDH14SHA1, kexAlgoDH1SHA1, + kexAlgoDH14SHA256, kexAlgoDH14SHA1, kexAlgoDH1SHA1, } // serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden @@ -61,18 +61,20 @@ var serverForbiddenKexAlgos = map[string]struct{}{ // preferredKexAlgos specifies the default preference for key-exchange algorithms // in preference order. var preferredKexAlgos = []string{ - kexAlgoCurve25519SHA256, + kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH, kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, - kexAlgoDH14SHA1, + kexAlgoDH14SHA256, kexAlgoDH14SHA1, } // supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods // of authenticating servers) in preference order. var supportedHostKeyAlgos = []string{ + CertAlgoRSASHA512v01, CertAlgoRSASHA256v01, CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, + KeyAlgoRSASHA512, KeyAlgoRSASHA256, KeyAlgoRSA, KeyAlgoDSA, KeyAlgoED25519, @@ -87,19 +89,33 @@ var supportedMACs = []string{ var supportedCompressions = []string{compressionNone} -// hashFuncs keeps the mapping of supported algorithms to their respective -// hashes needed for signature verification. +// hashFuncs keeps the mapping of supported signature algorithms to their +// respective hashes needed for signing and verification. var hashFuncs = map[string]crypto.Hash{ - KeyAlgoRSA: crypto.SHA1, - KeyAlgoDSA: crypto.SHA1, - KeyAlgoECDSA256: crypto.SHA256, - KeyAlgoECDSA384: crypto.SHA384, - KeyAlgoECDSA521: crypto.SHA512, - CertAlgoRSAv01: crypto.SHA1, - CertAlgoDSAv01: crypto.SHA1, - CertAlgoECDSA256v01: crypto.SHA256, - CertAlgoECDSA384v01: crypto.SHA384, - CertAlgoECDSA521v01: crypto.SHA512, + KeyAlgoRSA: crypto.SHA1, + KeyAlgoRSASHA256: crypto.SHA256, + KeyAlgoRSASHA512: crypto.SHA512, + KeyAlgoDSA: crypto.SHA1, + KeyAlgoECDSA256: crypto.SHA256, + KeyAlgoECDSA384: crypto.SHA384, + KeyAlgoECDSA521: crypto.SHA512, + // KeyAlgoED25519 doesn't pre-hash. + KeyAlgoSKECDSA256: crypto.SHA256, + KeyAlgoSKED25519: crypto.SHA256, +} + +// algorithmsForKeyFormat returns the supported signature algorithms for a given +// public key format (PublicKey.Type), in order of preference. See RFC 8332, +// Section 2. See also the note in sendKexInit on backwards compatibility. +func algorithmsForKeyFormat(keyFormat string) []string { + switch keyFormat { + case KeyAlgoRSA: + return []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA} + case CertAlgoRSAv01: + return []string{CertAlgoRSASHA256v01, CertAlgoRSASHA512v01, CertAlgoRSAv01} + default: + return []string{keyFormat} + } } // unexpectedMessageError results when the SSH message that we received didn't @@ -146,6 +162,11 @@ func (a *directionAlgorithms) rekeyBytes() int64 { return 1 << 30 } +var aeadCiphers = map[string]bool{ + gcmCipherID: true, + chacha20Poly1305ID: true, +} + type algorithms struct { kex string hostKey string @@ -181,14 +202,18 @@ func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMs return } - ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) - if err != nil { - return + if !aeadCiphers[ctos.Cipher] { + ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if err != nil { + return + } } - stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) - if err != nil { - return + if !aeadCiphers[stoc.Cipher] { + stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if err != nil { + return + } } ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) @@ -272,8 +297,9 @@ func (c *Config) SetDefaults() { } // buildDataSignedForAuth returns the data that is signed in order to prove -// possession of a private key. See RFC 4252, section 7. -func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { +// possession of a private key. See RFC 4252, section 7. algo is the advertised +// algorithm, and may be a certificate type. +func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo string, pubKey []byte) []byte { data := struct { Session []byte Type byte @@ -281,7 +307,7 @@ func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubK Service string Method string Sign bool - Algo []byte + Algo string PubKey []byte }{ sessionID, diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go index 67b7322c..f6bff60d 100644 --- a/vendor/golang.org/x/crypto/ssh/doc.go +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -12,8 +12,9 @@ the multiplexed nature of SSH is exposed to users that wish to support others. References: - [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD - [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 + + [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD + [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 This package does not fall under the stability promise of the Go language itself, so its API may be changed when pressing needs arise. diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 2b10b05a..653dc4d2 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -455,14 +455,38 @@ func (t *handshakeTransport) sendKexInit() error { } io.ReadFull(rand.Reader, msg.Cookie[:]) - if len(t.hostKeys) > 0 { + isServer := len(t.hostKeys) > 0 + if isServer { for _, k := range t.hostKeys { - msg.ServerHostKeyAlgos = append( - msg.ServerHostKeyAlgos, k.PublicKey().Type()) + // If k is an AlgorithmSigner, presume it supports all signature algorithms + // associated with the key format. (Ideally AlgorithmSigner would have a + // method to advertise supported algorithms, but it doesn't. This means that + // adding support for a new algorithm is a breaking change, as we will + // immediately negotiate it even if existing implementations don't support + // it. If that ever happens, we'll have to figure something out.) + // If k is not an AlgorithmSigner, we can only assume it only supports the + // algorithms that matches the key format. (This means that Sign can't pick + // a different default.) + keyFormat := k.PublicKey().Type() + if _, ok := k.(AlgorithmSigner); ok { + msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algorithmsForKeyFormat(keyFormat)...) + } else { + msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat) + } } } else { msg.ServerHostKeyAlgos = t.hostKeyAlgorithms + + // As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what + // algorithms the server supports for public key authentication. See RFC + // 8308, Section 2.1. + if firstKeyExchange := t.sessionID == nil; firstKeyExchange { + msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1) + msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) + msg.KexAlgos = append(msg.KexAlgos, "ext-info-c") + } } + packet := Marshal(msg) // writePacket destroys the contents, so save a copy. @@ -582,9 +606,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { var result *kexResult if len(t.hostKeys) > 0 { - result, err = t.server(kex, t.algorithms, &magics) + result, err = t.server(kex, &magics) } else { - result, err = t.client(kex, t.algorithms, &magics) + result, err = t.client(kex, &magics) } if err != nil { @@ -611,19 +635,52 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return nil } -func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { - var hostKey Signer - for _, k := range t.hostKeys { - if algs.hostKey == k.PublicKey().Type() { - hostKey = k +// algorithmSignerWrapper is an AlgorithmSigner that only supports the default +// key format algorithm. +// +// This is technically a violation of the AlgorithmSigner interface, but it +// should be unreachable given where we use this. Anyway, at least it returns an +// error instead of panicing or producing an incorrect signature. +type algorithmSignerWrapper struct { + Signer +} + +func (a algorithmSignerWrapper) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { + if algorithm != underlyingAlgo(a.PublicKey().Type()) { + return nil, errors.New("ssh: internal error: algorithmSignerWrapper invoked with non-default algorithm") + } + return a.Sign(rand, data) +} + +func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner { + for _, k := range hostKeys { + if algo == k.PublicKey().Type() { + return algorithmSignerWrapper{k} + } + k, ok := k.(AlgorithmSigner) + if !ok { + continue + } + for _, a := range algorithmsForKeyFormat(k.PublicKey().Type()) { + if algo == a { + return k + } } } + return nil +} - r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) +func (t *handshakeTransport) server(kex kexAlgorithm, magics *handshakeMagics) (*kexResult, error) { + hostKey := pickHostKey(t.hostKeys, t.algorithms.hostKey) + if hostKey == nil { + return nil, errors.New("ssh: internal error: negotiated unsupported signature type") + } + + r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.hostKey) return r, err } -func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { +func (t *handshakeTransport) client(kex kexAlgorithm, magics *handshakeMagics) (*kexResult, error) { result, err := kex.Client(t.conn, t.config.Rand, magics) if err != nil { return nil, err @@ -634,7 +691,7 @@ func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics * return nil, err } - if err := verifyHostKeySignature(hostKey, result); err != nil { + if err := verifyHostKeySignature(hostKey, t.algorithms.hostKey, result); err != nil { return nil, err } diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index 766e9293..927a90cd 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -20,12 +20,14 @@ import ( ) const ( - kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" - kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" - kexAlgoECDH256 = "ecdh-sha2-nistp256" - kexAlgoECDH384 = "ecdh-sha2-nistp384" - kexAlgoECDH521 = "ecdh-sha2-nistp521" - kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" + kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256" + kexAlgoECDH256 = "ecdh-sha2-nistp256" + kexAlgoECDH384 = "ecdh-sha2-nistp384" + kexAlgoECDH521 = "ecdh-sha2-nistp521" + kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org" + kexAlgoCurve25519SHA256 = "curve25519-sha256" // For the following kex only the client half contains a production // ready implementation. The server half only consists of a minimal @@ -75,8 +77,9 @@ func (m *handshakeMagics) write(w io.Writer) { // kexAlgorithm abstracts different key exchange algorithms. type kexAlgorithm interface { // Server runs server-side key agreement, signing the result - // with a hostkey. - Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) + // with a hostkey. algo is the negotiated algorithm, and may + // be a certificate type. + Server(p packetConn, rand io.Reader, magics *handshakeMagics, s AlgorithmSigner, algo string) (*kexResult, error) // Client runs the client-side key agreement. Caller is // responsible for verifying the host key signature. @@ -86,6 +89,7 @@ type kexAlgorithm interface { // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. type dhGroup struct { g, p, pMinus1 *big.Int + hashFunc crypto.Hash } func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { @@ -96,8 +100,6 @@ func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, } func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { - hashFunc := crypto.SHA1 - var x *big.Int for { var err error @@ -132,7 +134,7 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha return nil, err } - h := hashFunc.New() + h := group.hashFunc.New() magics.write(h) writeString(h, kexDHReply.HostKey) writeInt(h, X) @@ -146,12 +148,11 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha K: K, HostKey: kexDHReply.HostKey, Signature: kexDHReply.Signature, - Hash: crypto.SHA1, + Hash: group.hashFunc, }, nil } -func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { - hashFunc := crypto.SHA1 +func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return @@ -179,7 +180,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha hostKeyBytes := priv.PublicKey().Marshal() - h := hashFunc.New() + h := group.hashFunc.New() magics.write(h) writeString(h, hostKeyBytes) writeInt(h, kexDHInit.X) @@ -193,7 +194,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. - sig, err := signAndMarshal(priv, randSource, H) + sig, err := signAndMarshal(priv, randSource, H, algo) if err != nil { return nil, err } @@ -211,7 +212,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha K: K, HostKey: hostKeyBytes, Signature: sig, - Hash: crypto.SHA1, + Hash: group.hashFunc, }, err } @@ -314,7 +315,7 @@ func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { return true } -func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { +func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return nil, err @@ -359,7 +360,7 @@ func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, p // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. - sig, err := signAndMarshal(priv, rand, H) + sig, err := signAndMarshal(priv, rand, H, algo) if err != nil { return nil, err } @@ -384,39 +385,62 @@ func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, p }, nil } +// ecHash returns the hash to match the given elliptic curve, see RFC +// 5656, section 6.2.1 +func ecHash(curve elliptic.Curve) crypto.Hash { + bitSize := curve.Params().BitSize + switch { + case bitSize <= 256: + return crypto.SHA256 + case bitSize <= 384: + return crypto.SHA384 + } + return crypto.SHA512 +} + var kexAlgoMap = map[string]kexAlgorithm{} func init() { - // This is the group called diffie-hellman-group1-sha1 in RFC - // 4253 and Oakley Group 2 in RFC 2409. + // This is the group called diffie-hellman-group1-sha1 in + // RFC 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + pMinus1: new(big.Int).Sub(p, bigOne), + hashFunc: crypto.SHA1, + } + + // This are the groups called diffie-hellman-group14-sha1 and + // diffie-hellman-group14-sha256 in RFC 4253 and RFC 8268, + // and Oakley Group 14 in RFC 3526. + p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + group14 := &dhGroup{ g: new(big.Int).SetInt64(2), p: p, pMinus1: new(big.Int).Sub(p, bigOne), } - // This is the group called diffie-hellman-group14-sha1 in RFC - // 4253 and Oakley Group 14 in RFC 3526. - p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) - kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, - pMinus1: new(big.Int).Sub(p, bigOne), + g: group14.g, p: group14.p, pMinus1: group14.pMinus1, + hashFunc: crypto.SHA1, + } + kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{ + g: group14.g, p: group14.p, pMinus1: group14.pMinus1, + hashFunc: crypto.SHA256, } kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} + kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{} kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1} kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256} } -// curve25519sha256 implements the curve25519-sha256@libssh.org key -// agreement protocol, as described in -// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt +// curve25519sha256 implements the curve25519-sha256 (formerly known as +// curve25519-sha256@libssh.org) key exchange method, as described in RFC 8731. type curve25519sha256 struct{} type curve25519KeyPair struct { @@ -486,7 +510,7 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh }, nil } -func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { +func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return @@ -527,7 +551,7 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh H := h.Sum(nil) - sig, err := signAndMarshal(priv, rand, H) + sig, err := signAndMarshal(priv, rand, H, algo) if err != nil { return nil, err } @@ -553,7 +577,6 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh // diffie-hellman-group-exchange-sha256 key agreement protocols, // as described in RFC 4419 type dhGEXSHA struct { - g, p *big.Int hashFunc crypto.Hash } @@ -563,14 +586,7 @@ const ( dhGroupExchangeMaximumBits = 8192 ) -func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { - if theirPublic.Sign() <= 0 || theirPublic.Cmp(gex.p) >= 0 { - return nil, fmt.Errorf("ssh: DH parameter out of bounds") - } - return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil -} - -func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { +func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { // Send GexRequest kexDHGexRequest := kexDHGexRequestMsg{ MinBits: dhGroupExchangeMinimumBits, @@ -587,35 +603,29 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake return nil, err } - var kexDHGexGroup kexDHGexGroupMsg - if err = Unmarshal(packet, &kexDHGexGroup); err != nil { + var msg kexDHGexGroupMsg + if err = Unmarshal(packet, &msg); err != nil { return nil, err } // reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits - if kexDHGexGroup.P.BitLen() < dhGroupExchangeMinimumBits || kexDHGexGroup.P.BitLen() > dhGroupExchangeMaximumBits { - return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", kexDHGexGroup.P.BitLen()) + if msg.P.BitLen() < dhGroupExchangeMinimumBits || msg.P.BitLen() > dhGroupExchangeMaximumBits { + return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", msg.P.BitLen()) } - gex.p = kexDHGexGroup.P - gex.g = kexDHGexGroup.G - - // Check if g is safe by verifing that g > 1 and g < p - 1 - one := big.NewInt(1) - var pMinusOne = &big.Int{} - pMinusOne.Sub(gex.p, one) - if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 { + // Check if g is safe by verifying that 1 < g < p-1 + pMinusOne := new(big.Int).Sub(msg.P, bigOne) + if msg.G.Cmp(bigOne) <= 0 || msg.G.Cmp(pMinusOne) >= 0 { return nil, fmt.Errorf("ssh: server provided gex g is not safe") } // Send GexInit - var pHalf = &big.Int{} - pHalf.Rsh(gex.p, 1) + pHalf := new(big.Int).Rsh(msg.P, 1) x, err := rand.Int(randSource, pHalf) if err != nil { return nil, err } - X := new(big.Int).Exp(gex.g, x, gex.p) + X := new(big.Int).Exp(msg.G, x, msg.P) kexDHGexInit := kexDHGexInitMsg{ X: X, } @@ -634,13 +644,13 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake return nil, err } - kInt, err := gex.diffieHellman(kexDHGexReply.Y, x) - if err != nil { - return nil, err + if kexDHGexReply.Y.Cmp(bigOne) <= 0 || kexDHGexReply.Y.Cmp(pMinusOne) >= 0 { + return nil, errors.New("ssh: DH parameter out of bounds") } + kInt := new(big.Int).Exp(kexDHGexReply.Y, x, msg.P) - // Check if k is safe by verifing that k > 1 and k < p - 1 - if kInt.Cmp(one) != 1 && kInt.Cmp(pMinusOne) != -1 { + // Check if k is safe by verifying that k > 1 and k < p - 1 + if kInt.Cmp(bigOne) <= 0 || kInt.Cmp(pMinusOne) >= 0 { return nil, fmt.Errorf("ssh: derived k is not safe") } @@ -650,8 +660,8 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits)) binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits)) binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits)) - writeInt(h, gex.p) - writeInt(h, gex.g) + writeInt(h, msg.P) + writeInt(h, msg.G) writeInt(h, X) writeInt(h, kexDHGexReply.Y) K := make([]byte, intLength(kInt)) @@ -670,7 +680,7 @@ func (gex dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshake // Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256. // // This is a minimal implementation to satisfy the automated tests. -func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { +func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) { // Receive GexRequest packet, err := c.readPacket() if err != nil { @@ -681,35 +691,17 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake return } - // smoosh the user's preferred size into our own limits - if kexDHGexRequest.PreferedBits > dhGroupExchangeMaximumBits { - kexDHGexRequest.PreferedBits = dhGroupExchangeMaximumBits - } - if kexDHGexRequest.PreferedBits < dhGroupExchangeMinimumBits { - kexDHGexRequest.PreferedBits = dhGroupExchangeMinimumBits - } - // fix min/max if they're inconsistent. technically, we could just pout - // and hang up, but there's no harm in giving them the benefit of the - // doubt and just picking a bitsize for them. - if kexDHGexRequest.MinBits > kexDHGexRequest.PreferedBits { - kexDHGexRequest.MinBits = kexDHGexRequest.PreferedBits - } - if kexDHGexRequest.MaxBits < kexDHGexRequest.PreferedBits { - kexDHGexRequest.MaxBits = kexDHGexRequest.PreferedBits - } - // Send GexGroup // This is the group called diffie-hellman-group14-sha1 in RFC // 4253 and Oakley Group 14 in RFC 3526. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) - gex.p = p - gex.g = big.NewInt(2) + g := big.NewInt(2) - kexDHGexGroup := kexDHGexGroupMsg{ - P: gex.p, - G: gex.g, + msg := &kexDHGexGroupMsg{ + P: p, + G: g, } - if err := c.writePacket(Marshal(&kexDHGexGroup)); err != nil { + if err := c.writePacket(Marshal(msg)); err != nil { return nil, err } @@ -723,19 +715,19 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake return } - var pHalf = &big.Int{} - pHalf.Rsh(gex.p, 1) + pHalf := new(big.Int).Rsh(p, 1) y, err := rand.Int(randSource, pHalf) if err != nil { return } + Y := new(big.Int).Exp(g, y, p) - Y := new(big.Int).Exp(gex.g, y, gex.p) - kInt, err := gex.diffieHellman(kexDHGexInit.X, y) - if err != nil { - return nil, err + pMinusOne := new(big.Int).Sub(p, bigOne) + if kexDHGexInit.X.Cmp(bigOne) <= 0 || kexDHGexInit.X.Cmp(pMinusOne) >= 0 { + return nil, errors.New("ssh: DH parameter out of bounds") } + kInt := new(big.Int).Exp(kexDHGexInit.X, y, p) hostKeyBytes := priv.PublicKey().Marshal() @@ -745,8 +737,8 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits)) binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits)) binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits)) - writeInt(h, gex.p) - writeInt(h, gex.g) + writeInt(h, p) + writeInt(h, g) writeInt(h, kexDHGexInit.X) writeInt(h, Y) @@ -758,7 +750,7 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. - sig, err := signAndMarshal(priv, randSource, H) + sig, err := signAndMarshal(priv, randSource, H, algo) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index 31f26349..1c7de1a6 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -30,8 +30,9 @@ import ( "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf" ) -// These constants represent the algorithm names for key types supported by this -// package. +// Public key algorithms names. These values can appear in PublicKey.Type, +// ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner +// arguments. const ( KeyAlgoRSA = "ssh-rsa" KeyAlgoDSA = "ssh-dss" @@ -41,16 +42,21 @@ const ( KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" KeyAlgoED25519 = "ssh-ed25519" KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com" + + // KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not + // public key formats, so they can't appear as a PublicKey.Type. The + // corresponding PublicKey.Type is KeyAlgoRSA. See RFC 8332, Section 2. + KeyAlgoRSASHA256 = "rsa-sha2-256" + KeyAlgoRSASHA512 = "rsa-sha2-512" ) -// These constants represent non-default signature algorithms that are supported -// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See -// [PROTOCOL.agent] section 4.5.1 and -// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10 const ( - SigAlgoRSA = "ssh-rsa" - SigAlgoRSASHA2256 = "rsa-sha2-256" - SigAlgoRSASHA2512 = "rsa-sha2-512" + // Deprecated: use KeyAlgoRSA. + SigAlgoRSA = KeyAlgoRSA + // Deprecated: use KeyAlgoRSASHA256. + SigAlgoRSASHA2256 = KeyAlgoRSASHA256 + // Deprecated: use KeyAlgoRSASHA512. + SigAlgoRSASHA2512 = KeyAlgoRSASHA512 ) // parsePubKey parses a public key of the given algorithm. @@ -70,7 +76,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err case KeyAlgoSKED25519: return parseSKEd25519(in) case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01: - cert, err := parseCert(in, certToPrivAlgo(algo)) + cert, err := parseCert(in, certKeyAlgoNames[algo]) if err != nil { return nil, nil, err } @@ -289,18 +295,21 @@ func MarshalAuthorizedKey(key PublicKey) []byte { return b.Bytes() } -// PublicKey is an abstraction of different types of public keys. +// PublicKey represents a public key using an unspecified algorithm. +// +// Some PublicKeys provided by this package also implement CryptoPublicKey. type PublicKey interface { - // Type returns the key's type, e.g. "ssh-rsa". + // Type returns the key format name, e.g. "ssh-rsa". Type() string - // Marshal returns the serialized key data in SSH wire format, - // with the name prefix. To unmarshal the returned data, use - // the ParsePublicKey function. + // Marshal returns the serialized key data in SSH wire format, with the name + // prefix. To unmarshal the returned data, use the ParsePublicKey function. Marshal() []byte - // Verify that sig is a signature on the given data using this - // key. This function will hash the data appropriately first. + // Verify that sig is a signature on the given data using this key. This + // method will hash the data appropriately first. sig.Format is allowed to + // be any signature algorithm compatible with the key type, the caller + // should check if it has more stringent requirements. Verify(data []byte, sig *Signature) error } @@ -311,25 +320,32 @@ type CryptoPublicKey interface { } // A Signer can create signatures that verify against a public key. +// +// Some Signers provided by this package also implement AlgorithmSigner. type Signer interface { - // PublicKey returns an associated PublicKey instance. + // PublicKey returns the associated PublicKey. PublicKey() PublicKey - // Sign returns raw signature for the given data. This method - // will apply the hash specified for the keytype to the data. + // Sign returns a signature for the given data. This method will hash the + // data appropriately first. The signature algorithm is expected to match + // the key format returned by the PublicKey.Type method (and not to be any + // alternative algorithm supported by the key format). Sign(rand io.Reader, data []byte) (*Signature, error) } -// A AlgorithmSigner is a Signer that also supports specifying a specific -// algorithm to use for signing. +// An AlgorithmSigner is a Signer that also supports specifying an algorithm to +// use for signing. +// +// An AlgorithmSigner can't advertise the algorithms it supports, so it should +// be prepared to be invoked with every algorithm supported by the public key +// format. type AlgorithmSigner interface { Signer - // SignWithAlgorithm is like Signer.Sign, but allows specification of a - // non-default signing algorithm. See the SigAlgo* constants in this - // package for signature algorithms supported by this package. Callers may - // pass an empty string for the algorithm in which case the AlgorithmSigner - // will use its default algorithm. + // SignWithAlgorithm is like Signer.Sign, but allows specifying a desired + // signing algorithm. Callers may pass an empty string for the algorithm in + // which case the AlgorithmSigner will use a default algorithm. This default + // doesn't currently control any behavior in this package. SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) } @@ -381,17 +397,11 @@ func (r *rsaPublicKey) Marshal() []byte { } func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { - var hash crypto.Hash - switch sig.Format { - case SigAlgoRSA: - hash = crypto.SHA1 - case SigAlgoRSASHA2256: - hash = crypto.SHA256 - case SigAlgoRSASHA2512: - hash = crypto.SHA512 - default: + supportedAlgos := algorithmsForKeyFormat(r.Type()) + if !contains(supportedAlgos, sig.Format) { return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) } + hash := hashFuncs[sig.Format] h := hash.New() h.Write(data) digest := h.Sum(nil) @@ -466,7 +476,7 @@ func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { if sig.Format != k.Type() { return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) } - h := crypto.SHA1.New() + h := hashFuncs[sig.Format].New() h.Write(data) digest := h.Sum(nil) @@ -499,7 +509,7 @@ func (k *dsaPrivateKey) PublicKey() PublicKey { } func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { - return k.SignWithAlgorithm(rand, data, "") + return k.SignWithAlgorithm(rand, data, k.PublicKey().Type()) } func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { @@ -507,7 +517,7 @@ func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) } - h := crypto.SHA1.New() + h := hashFuncs[k.PublicKey().Type()].New() h.Write(data) digest := h.Sum(nil) r, s, err := dsa.Sign(rand, k.PrivateKey, digest) @@ -603,19 +613,6 @@ func supportedEllipticCurve(curve elliptic.Curve) bool { return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() } -// ecHash returns the hash to match the given elliptic curve, see RFC -// 5656, section 6.2.1 -func ecHash(curve elliptic.Curve) crypto.Hash { - bitSize := curve.Params().BitSize - switch { - case bitSize <= 256: - return crypto.SHA256 - case bitSize <= 384: - return crypto.SHA384 - } - return crypto.SHA512 -} - // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { var w struct { @@ -671,7 +668,7 @@ func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) } - h := ecHash(k.Curve).New() + h := hashFuncs[sig.Format].New() h.Write(data) digest := h.Sum(nil) @@ -775,7 +772,7 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) } - h := ecHash(k.Curve).New() + h := hashFuncs[sig.Format].New() h.Write([]byte(k.application)) appDigest := h.Sum(nil) @@ -874,7 +871,7 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { return fmt.Errorf("invalid size %d for Ed25519 public key", l) } - h := sha256.New() + h := hashFuncs[sig.Format].New() h.Write([]byte(k.application)) appDigest := h.Sum(nil) @@ -961,44 +958,20 @@ func (s *wrappedSigner) PublicKey() PublicKey { } func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { - return s.SignWithAlgorithm(rand, data, "") + return s.SignWithAlgorithm(rand, data, s.pubKey.Type()) } func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { - var hashFunc crypto.Hash - - if _, ok := s.pubKey.(*rsaPublicKey); ok { - // RSA keys support a few hash functions determined by the requested signature algorithm - switch algorithm { - case "", SigAlgoRSA: - algorithm = SigAlgoRSA - hashFunc = crypto.SHA1 - case SigAlgoRSASHA2256: - hashFunc = crypto.SHA256 - case SigAlgoRSASHA2512: - hashFunc = crypto.SHA512 - default: - return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) - } - } else { - // The only supported algorithm for all other key types is the same as the type of the key - if algorithm == "" { - algorithm = s.pubKey.Type() - } else if algorithm != s.pubKey.Type() { - return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) - } - - switch key := s.pubKey.(type) { - case *dsaPublicKey: - hashFunc = crypto.SHA1 - case *ecdsaPublicKey: - hashFunc = ecHash(key.Curve) - case ed25519PublicKey: - default: - return nil, fmt.Errorf("ssh: unsupported key type %T", key) - } + if algorithm == "" { + algorithm = s.pubKey.Type() } + supportedAlgos := algorithmsForKeyFormat(s.pubKey.Type()) + if !contains(supportedAlgos, algorithm) { + return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type()) + } + + hashFunc := hashFuncs[algorithm] var digest []byte if hashFunc != 0 { h := hashFunc.New() diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go index ac41a416..19bc67c4 100644 --- a/vendor/golang.org/x/crypto/ssh/messages.go +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -141,6 +141,14 @@ type serviceAcceptMsg struct { Service string `sshtype:"6"` } +// See RFC 8308, section 2.3 +const msgExtInfo = 7 + +type extInfoMsg struct { + NumExtensions uint32 `sshtype:"7"` + Payload []byte `ssh:"rest"` +} + // See RFC 4252, section 5. const msgUserAuthRequest = 50 @@ -180,11 +188,11 @@ const msgUserAuthInfoRequest = 60 const msgUserAuthInfoResponse = 61 type userAuthInfoRequestMsg struct { - User string `sshtype:"60"` - Instruction string - DeprecatedLanguage string - NumPrompts uint32 - Prompts []byte `ssh:"rest"` + Name string `sshtype:"60"` + Instruction string + Language string + NumPrompts uint32 + Prompts []byte `ssh:"rest"` } // See RFC 4254, section 5.1. @@ -782,6 +790,8 @@ func decode(packet []byte) (interface{}, error) { msg = new(serviceRequestMsg) case msgServiceAccept: msg = new(serviceAcceptMsg) + case msgExtInfo: + msg = new(extInfoMsg) case msgKexInit: msg = new(kexInitMsg) case msgKexDHInit: @@ -843,6 +853,7 @@ var packetTypeNames = map[byte]string{ msgDisconnect: "disconnectMsg", msgServiceRequest: "serviceRequestMsg", msgServiceAccept: "serviceAcceptMsg", + msgExtInfo: "extInfoMsg", msgKexInit: "kexInitMsg", msgKexDHInit: "kexDHInitMsg", msgKexDHReply: "kexDHReplyMsg", diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index b6911e83..70045bdf 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -120,7 +120,7 @@ type ServerConfig struct { } // AddHostKey adds a private key as a host key. If an existing host -// key exists with the same algorithm, it is overwritten. Each server +// key exists with the same public key format, it is replaced. Each server // config must have at least one host key. func (s *ServerConfig) AddHostKey(key Signer) { for i, k := range s.hostKeys { @@ -212,9 +212,10 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha } // signAndMarshal signs the data with the appropriate algorithm, -// and serializes the result in SSH wire format. -func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { - sig, err := k.Sign(rand, data) +// and serializes the result in SSH wire format. algo is the negotiate +// algorithm and may be a certificate type. +func signAndMarshal(k AlgorithmSigner, rand io.Reader, data []byte, algo string) ([]byte, error) { + sig, err := k.SignWithAlgorithm(rand, data, underlyingAlgo(algo)) if err != nil { return nil, err } @@ -284,7 +285,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) func isAcceptableAlgo(algo string) bool { switch algo { - case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519, + case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519, CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01: return true } @@ -553,6 +554,7 @@ userAuthLoop: if !ok || len(payload) > 0 { return nil, parseError(msgUserAuthRequest) } + // Ensure the public key algo and signature algo // are supported. Compare the private key // algorithm name that corresponds to algo with @@ -562,7 +564,12 @@ userAuthLoop: authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format) break } - signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) + if underlyingAlgo(algo) != sig.Format { + authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo) + break + } + + signedData := buildDataSignedForAuth(sessionID, userAuthReq, algo, pubKeyData) if err := pubKey.Verify(signedData, sig); err != nil { return nil, err @@ -633,6 +640,30 @@ userAuthLoop: } authFailures++ + if config.MaxAuthTries > 0 && authFailures >= config.MaxAuthTries { + // If we have hit the max attempts, don't bother sending the + // final SSH_MSG_USERAUTH_FAILURE message, since there are + // no more authentication methods which can be attempted, + // and this message may cause the client to re-attempt + // authentication while we send the disconnect message. + // Continue, and trigger the disconnect at the start of + // the loop. + // + // The SSH specification is somewhat confusing about this, + // RFC 4252 Section 5.1 requires each authentication failure + // be responded to with a respective SSH_MSG_USERAUTH_FAILURE + // message, but Section 4 says the server should disconnect + // after some number of attempts, but it isn't explicit which + // message should take precedence (i.e. should there be a failure + // message than a disconnect message, or if we are going to + // disconnect, should we only send that message.) + // + // Either way, OpenSSH disconnects immediately after the last + // failed authnetication attempt, and given they are typically + // considered the golden implementation it seems reasonable + // to match that behavior. + continue + } var failureMsg userAuthFailureMsg if config.PasswordCallback != nil { @@ -670,7 +701,7 @@ type sshClientKeyboardInteractive struct { *connection } -func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { +func (c *sshClientKeyboardInteractive) Challenge(name, instruction string, questions []string, echos []bool) (answers []string, err error) { if len(questions) != len(echos) { return nil, errors.New("ssh: echos and questions must have equal length") } @@ -682,6 +713,7 @@ func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, quest } if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{ + Name: name, Instruction: instruction, NumPrompts: uint32(len(questions)), Prompts: prompts, diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go index d3321f6b..eca31a22 100644 --- a/vendor/golang.org/x/crypto/ssh/session.go +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -85,6 +85,7 @@ const ( IXANY = 39 IXOFF = 40 IMAXBEL = 41 + IUTF8 = 42 // RFC 8160 ISIG = 50 ICANON = 51 XCASE = 52 diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index 49ddc2e7..acf5a21b 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -238,15 +238,19 @@ var ( // (to setup server->client keys) or clientKeys (for client->server keys). func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { cipherMode := cipherModes[algs.Cipher] - macMode := macModes[algs.MAC] iv := make([]byte, cipherMode.ivSize) key := make([]byte, cipherMode.keySize) - macKey := make([]byte, macMode.keySize) generateKeyMaterial(iv, d.ivTag, kex) generateKeyMaterial(key, d.keyTag, kex) - generateKeyMaterial(macKey, d.macKeyTag, kex) + + var macKey []byte + if !aeadCiphers[algs.Cipher] { + macMode := macModes[algs.MAC] + macKey = make([]byte, macMode.keySize) + generateKeyMaterial(macKey, d.macKeyTag, kex) + } return cipherModes[algs.Cipher].create(key, iv, macKey, algs) } diff --git a/vendor/modules.txt b/vendor/modules.txt index adc719aa..5951bced 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -384,7 +384,7 @@ go.opentelemetry.io/proto/otlp/trace/v1 go.uber.org/automaxprocs/internal/cgroups go.uber.org/automaxprocs/internal/runtime go.uber.org/automaxprocs/maxprocs -# golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 +# golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f ## explicit; go 1.17 golang.org/x/crypto/blake2b golang.org/x/crypto/blowfish @@ -395,7 +395,6 @@ golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/curve25519 golang.org/x/crypto/curve25519/internal/field golang.org/x/crypto/ed25519 -golang.org/x/crypto/ed25519/internal/edwards25519 golang.org/x/crypto/hkdf golang.org/x/crypto/internal/poly1305 golang.org/x/crypto/internal/subtle From 7499e5fa00f71f542fd75a98132374baa54d6bd9 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 2 May 2022 17:29:34 +0100 Subject: [PATCH 064/238] Release 2022.5.0 --- RELEASE_NOTES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 87ba3c3a..6d94a14f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,16 @@ +2022.5.0 +- 2022-05-02 TUN-6158: Update golang.org/x/crypto +- 2022-04-20 VULN-8383 Bump yaml.v2 to yaml.v3 +- 2022-04-21 TUN-6123: For a given connection with edge, close all datagram sessions through this connection when it's closed +- 2022-04-20 TUN-6015: Add RPC method for pushing local config +- 2022-04-21 TUN-6130: Fix vendoring due to case sensitive typo in package +- 2022-04-27 TUN-6142: Add tunnel details support to RPC +- 2022-04-28 TUN-6014: Add remote config flag as default feature +- 2022-04-12 TUN-6000: Another fix for publishing to brew core +- 2022-04-11 TUN-5990: Add otlp span export to response header +- 2022-04-19 TUN-6070: First connection retries other edge IPs if the error is quic timeout(likely due to firewall blocking UDP) +- 2022-04-11 TUN-6030: Add ttfb span for origin http request + 2022.4.1 - 2022-04-11 TUN-6035: Reduce buffer size when proxying data - 2022-04-11 TUN-6038: Reduce buffer size used for proxying data From 8250708b3706c9939e6e97129e0949563045bf3a Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 3 May 2022 09:20:26 +0100 Subject: [PATCH 065/238] TUN-6161: Set git user/email for brew core release --- .teamcity/update-homebrew-core.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh index bf2f8b79..adc216d6 100755 --- a/.teamcity/update-homebrew-core.sh +++ b/.teamcity/update-homebrew-core.sh @@ -19,8 +19,8 @@ eval "$(tmp/homebrew/bin/brew shellenv)" brew update --force --quiet chmod -R go-w "$(brew --prefix)/share/zsh" -git config user.name "cloudflare-warp-bot" -git config user.email "warp-bot@cloudflare.com" +git config --global user.name "cloudflare-warp-bot" +git config --global user.email "warp-bot@cloudflare.com" # bump formula pr brew bump-formula-pr cloudflared --version="$VERSION" From 1e71202c894bc13b4408ba261c7fba4bf2e68bbd Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 4 May 2022 09:58:05 +0100 Subject: [PATCH 066/238] TUN-6054: Create and upload deb packages to R2 This PR does the following: 1. Creates packages.gz, signed InRelease files for debs in built_artifacts for configured debian releases. 2. Uploads them to Cloudflare R2. 3. Adds a Workers KV entry that talks about where these assets are uploaded. --- release_pkgs.py | 229 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 release_pkgs.py diff --git a/release_pkgs.py b/release_pkgs.py new file mode 100644 index 00000000..d2b41946 --- /dev/null +++ b/release_pkgs.py @@ -0,0 +1,229 @@ +""" + This is a utility for creating deb and rpm packages, signing them + and uploading them to a storage and adding metadata to workers KV. + + It has two over-arching responsiblities: + 1. Create deb and yum repositories from .deb and .rpm files. + This is also responsible for signing the packages and generally preparing + them to be in an uploadable state. + 2. Upload these packages to a storage and add metadata cross reference + for these to be accessed. +""" +import requests +import subprocess +import os +import io +import shutil +import logging +from hashlib import sha256 + +import boto3 +from botocore.client import Config +from botocore.exceptions import ClientError + +BASE_KV_URL = 'https://api.cloudflare.com/client/v4/accounts/' +# The front facing R2 URL to access assets from. +R2_ASSET_URL = 'https://demo-r2-worker.cloudflare-tunnel.workers.dev/' + +class PkgUploader: + def __init__(self, kv_api_token, namespace, account_id, bucket_name, client_id, client_secret): + self.kv_api_token = kv_api_token + self.namespace = namespace + self.account_id = account_id + self.bucket_name = bucket_name + self.client_id = client_id + self.client_secret = client_secret + + def send_to_kv(self, key, value): + headers = { + "Content-Type": "application/json", + "Authorization": "Bearer " + self.kv_api_token, + } + + kv_url = f"{BASE_KV_URL}{self.account_id}/storage/kv/namespaces/{self.namespace}/values/{key}" + response = requests.put( + kv_url, + headers=headers, + data=value + ) + + if response.status_code != 200: + jsonResponse = response.json() + errors = jsonResponse["errors"] + if len(errors) > 0: + raise Exception("failed to send to workers kv: {0}", errors[0]) + else: + raise Exception("recieved error code: {0}", response.status_code) + + + def send_pkg_info(self, binary, flavor, asset_name, arch, uploaded_package_location): + key = f"pkg_{binary}_{flavor}_{arch}_{asset_name}" + print(f"writing key:{key} , value: {uploaded_package_location}") + self.send_to_kv(key, uploaded_package_location) + + + def upload_pkg_to_r2(self, filename, upload_file_path): + endpoint_url = f"https://{self.account_id}.r2.cloudflarestorage.com" + token_secret_hash = sha256(self.client_secret.encode()).hexdigest() + + config = Config( + s3={ + "addressing_style": "path", + } + ) + + r2 = boto3.client( + "s3", + endpoint_url=endpoint_url, + aws_access_key_id=self.client_id, + aws_secret_access_key=token_secret_hash, + config=config, + ) + + print(f"uploading asset: {filename} to {upload_file_path}...") + try: + r2.upload_file(filename, self.bucket_name, upload_file_path) + except ClientError as e: + raise e + + +class PkgCreator: + """ + The distribution conf is what dictates to reprepro, the debian packaging building + and signing tool we use, what distros to support, what GPG key to use for signing + and what to call the debian binary etc. This function creates it "./conf/distributions". + + origin - name of your package (String) + label - label of your package (could be same as the name) (String) + flavor - flavor you want this to be distributed for (List of Strings) + components - could be a channel like main/stable/beta + archs - Architecture (List of Strings) + description - (String) + gpg_key_id - gpg key id of what you want to use to sign the packages.(String) + """ + def create_distribution_conf(self, + file_path, + origin, + label, + flavors, + archs, + components, + description, + gpg_key_id ): + with open(file_path, "w") as distributions_file: + for flavor in flavors: + distributions_file.write(f"Origin: {origin}\n") + distributions_file.write(f"Label: {label}\n") + distributions_file.write(f"Codename: {flavor}\n") + archs_list = " ".join(archs) + distributions_file.write(f"Architectures: {archs_list}\n") + distributions_file.write(f"Components: {components}\n") + distributions_file.write(f"Description: {description} - {flavor}\n") + distributions_file.write(f"SignWith: {gpg_key_id}\n") + distributions_file.write("\n") + return distributions_file + + """ + Uses the reprepro tool to generate packages, sign them and create the InRelease as specified + by the distribution_conf file. + + This function creates three folders db, pool and dist. + db and pool contain information and metadata about builds. We can ignore these. + dist: contains all the pkgs and signed releases that are necessary for an apt download. + """ + def create_deb_pkgs(self, flavor, deb_file): + self._clean_build_resources() + subprocess.call(("reprepro", "includedeb", flavor, deb_file)) + + """ + This is mostly useful to clear previously built db, dist and pool resources. + """ + def _clean_build_resources(self): + subprocess.call(("reprepro", "clearvanished")) + +def upload_from_directories(pkg_uploader, directory, arch, release): + for root, _ , files in os.walk(directory): + for file in files: + root_elements = root.split("/") + upload_file_name = os.path.join(root, arch, release, file) + flavor_prefix = root_elements[1] + if root_elements[0] == "pool": + upload_file_name = os.path.join(root, file) + flavor_prefix = "deb" + filename = os.path.join(root,file) + try: + pkg_uploader.upload_pkg_to_r2(filename, upload_file_name) + except ClientError as e: + logging.error(e) + return + + # save to workers kv in the following formats + # Example: + # key : pkg_cloudflared_bullseye_InRelease, + # value: https://r2.cloudflarestorage.com/dists/bullseye/amd64/2022_3_4/InRelease + r2_asset_url = f"{R2_ASSET_URL}{upload_file_name}" + pkg_uploader.send_pkg_info("cloudflared", flavor_prefix, upload_file_name, arch, r2_asset_url) + + # TODO https://jira.cfops.it/browse/TUN-6163: Add a key for latest version. + +""" + 1. looks into a built_artifacts folder for cloudflared debs + 2. creates Packages.gz, InRelease (signed) files + 3. uploads them to Cloudflare R2 and + 4. adds a Workers KV reference + + pkg_creator, pkg_uploader: are instantiations of the two classes above. + + gpg_key_id: is an id indicating the key the package should be signed with. The public key of this id will be + uploaded to R2 so it can be presented to apt downloaders. + + release_version: is the cloudflared release version. +""" +def create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, binary_name, arch, package_component, release_version): + # set configuration for package creation. + print(f"initialising configuration for {binary_name} , {arch}") + pkg_creator.create_distribution_conf( + "./conf/distributions", + binary_name, + binary_name, + flavors, + [arch], + package_component, + f"apt repository for {binary_name}", + gpg_key_id) + + # create deb pkgs + for flavor in flavors: + print(f"creating deb pkgs for {flavor} and {arch}...") + pkg_creator.create_deb_pkgs(flavor, f"./built_artifacts/cloudflared-linux-{arch}.deb") + + print("uploading to r2...") + upload_from_directories(pkg_uploader, "dists", arch, release_version) + upload_from_directories(pkg_uploader, "pool", arch, release_version) + + print("cleaning up directories...") + shutil.rmtree("./dists") + shutil.rmtree("./pool") + shutil.rmtree("./db") + +#TODO: https://jira.cfops.it/browse/TUN-6146 will extract this into it's own command line script. +if __name__ == "__main__": + # initialise pkg creator + pkg_creator = PkgCreator() + + # initialise pkg uploader + bucket_name = os.getenv('R2_BUCKET_NAME') + client_id = os.getenv('R2_CLIENT_ID') + client_secret = os.getenv('R2_CLIENT_SECRET') + tunnel_account_id = '5ab4e9dfbd435d24068829fda0077963' + kv_namespace = os.getenv('KV_NAMESPACE') + kv_api_token = os.getenv('KV_API_TOKEN') + release_version = os.getenv('RELEASE_VERSION') + gpg_key_id = os.getenv('GPG_KEY_ID') + + pkg_uploader = PkgUploader(kv_api_token, kv_namespace, tunnel_account_id, bucket_name, client_id, client_secret) + + archs = ["amd64", "386", "arm64"] + flavors = ["bullseye", "buster", "bionic"] + for arch in archs: + create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, "cloudflared", arch, "main", release_version) From 46c147a1b257e54074750604a802e221ade38ef0 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 3 May 2022 18:58:14 +0100 Subject: [PATCH 067/238] TUN-6166: Fix mocked QUIC transport for UDP proxy manager to return expected error --- datagramsession/manager_test.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/datagramsession/manager_test.go b/datagramsession/manager_test.go index a9361624..7b17bf7d 100644 --- a/datagramsession/manager_test.go +++ b/datagramsession/manager_test.go @@ -255,7 +255,10 @@ func (rc *datagramChannel) Send(ctx context.Context, sessionID uuid.UUID, payloa case <-ctx.Done(): return ctx.Err() case <-rc.closedChan: - return fmt.Errorf("datagram channel closed") + return &errClosedSession{ + message: fmt.Errorf("datagram channel closed").Error(), + byRemote: true, + } case rc.datagramChan <- &newDatagram{sessionID: sessionID, payload: payload}: return nil } @@ -266,7 +269,11 @@ func (rc *datagramChannel) Receive(ctx context.Context) (uuid.UUID, []byte, erro case <-ctx.Done(): return uuid.Nil, nil, ctx.Err() case <-rc.closedChan: - return uuid.Nil, nil, fmt.Errorf("datagram channel closed") + err := &errClosedSession{ + message: fmt.Errorf("datagram channel closed").Error(), + byRemote: true, + } + return uuid.Nil, nil, err case msg := <-rc.datagramChan: return msg.sessionID, msg.payload, nil } From 2cf43abe8ccf12476e9ee6ab3eebdc847d79c0f4 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 5 May 2022 16:04:48 +0100 Subject: [PATCH 068/238] TUN-6175: Simply debian packaging by structural upload The way apt works is: 1. It looks at the release file based on the `deb` added to sources.list. 2. It uses this release file to find the relative location of Packages or Packages.gz 3. It uses the pool information from packages to find the relative location of where the .deb file is located and then downloads and installs it. This PR seeks to take advantage of this information by simply arranging the files in a way apt expects thereby eliminating the need for an orchestrating endpoint. --- release_pkgs.py | 103 ++++++++++++++---------------------------------- 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index d2b41946..3cc0949c 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -6,14 +6,10 @@ 1. Create deb and yum repositories from .deb and .rpm files. This is also responsible for signing the packages and generally preparing them to be in an uploadable state. - 2. Upload these packages to a storage and add metadata cross reference - for these to be accessed. + 2. Upload these packages to a storage in a format that apt and yum expect. """ -import requests import subprocess import os -import io -import shutil import logging from hashlib import sha256 @@ -21,52 +17,22 @@ import boto3 from botocore.client import Config from botocore.exceptions import ClientError -BASE_KV_URL = 'https://api.cloudflare.com/client/v4/accounts/' # The front facing R2 URL to access assets from. R2_ASSET_URL = 'https://demo-r2-worker.cloudflare-tunnel.workers.dev/' class PkgUploader: - def __init__(self, kv_api_token, namespace, account_id, bucket_name, client_id, client_secret): - self.kv_api_token = kv_api_token - self.namespace = namespace + def __init__(self, account_id, bucket_name, client_id, client_secret): self.account_id = account_id self.bucket_name = bucket_name self.client_id = client_id self.client_secret = client_secret - def send_to_kv(self, key, value): - headers = { - "Content-Type": "application/json", - "Authorization": "Bearer " + self.kv_api_token, - } - - kv_url = f"{BASE_KV_URL}{self.account_id}/storage/kv/namespaces/{self.namespace}/values/{key}" - response = requests.put( - kv_url, - headers=headers, - data=value - ) - - if response.status_code != 200: - jsonResponse = response.json() - errors = jsonResponse["errors"] - if len(errors) > 0: - raise Exception("failed to send to workers kv: {0}", errors[0]) - else: - raise Exception("recieved error code: {0}", response.status_code) - - - def send_pkg_info(self, binary, flavor, asset_name, arch, uploaded_package_location): - key = f"pkg_{binary}_{flavor}_{arch}_{asset_name}" - print(f"writing key:{key} , value: {uploaded_package_location}") - self.send_to_kv(key, uploaded_package_location) - - def upload_pkg_to_r2(self, filename, upload_file_path): endpoint_url = f"https://{self.account_id}.r2.cloudflarestorage.com" token_secret_hash = sha256(self.client_secret.encode()).hexdigest() config = Config( + region_name = 'auto', s3={ "addressing_style": "path", } @@ -141,15 +107,19 @@ class PkgCreator: def _clean_build_resources(self): subprocess.call(("reprepro", "clearvanished")) -def upload_from_directories(pkg_uploader, directory, arch, release): +""" + Walks through a directory and uploads it's assets to R2. + directory : root directory to walk through (String). + release: release string. If this value is none, a specific release path will not be created + and the release will be uploaded to the default path. + binary: name of the binary to upload +""" +def upload_from_directories(pkg_uploader, directory, release, binary): for root, _ , files in os.walk(directory): for file in files: - root_elements = root.split("/") - upload_file_name = os.path.join(root, arch, release, file) - flavor_prefix = root_elements[1] - if root_elements[0] == "pool": - upload_file_name = os.path.join(root, file) - flavor_prefix = "deb" + upload_file_name = os.path.join(binary, root, file) + if release: + upload_file_name = os.path.join(release, upload_file_name) filename = os.path.join(root,file) try: pkg_uploader.upload_pkg_to_r2(filename, upload_file_name) @@ -157,20 +127,10 @@ def upload_from_directories(pkg_uploader, directory, arch, release): logging.error(e) return - # save to workers kv in the following formats - # Example: - # key : pkg_cloudflared_bullseye_InRelease, - # value: https://r2.cloudflarestorage.com/dists/bullseye/amd64/2022_3_4/InRelease - r2_asset_url = f"{R2_ASSET_URL}{upload_file_name}" - pkg_uploader.send_pkg_info("cloudflared", flavor_prefix, upload_file_name, arch, r2_asset_url) - - # TODO https://jira.cfops.it/browse/TUN-6163: Add a key for latest version. - """ 1. looks into a built_artifacts folder for cloudflared debs 2. creates Packages.gz, InRelease (signed) files - 3. uploads them to Cloudflare R2 and - 4. adds a Workers KV reference + 3. uploads them to Cloudflare R2 pkg_creator, pkg_uploader: are instantiations of the two classes above. @@ -179,32 +139,32 @@ def upload_from_directories(pkg_uploader, directory, arch, release): release_version: is the cloudflared release version. """ -def create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, binary_name, arch, package_component, release_version): +def create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, binary_name, archs, package_component, release_version): # set configuration for package creation. - print(f"initialising configuration for {binary_name} , {arch}") + print(f"initialising configuration for {binary_name} , {archs}") pkg_creator.create_distribution_conf( "./conf/distributions", binary_name, binary_name, flavors, - [arch], + archs, package_component, f"apt repository for {binary_name}", gpg_key_id) # create deb pkgs for flavor in flavors: - print(f"creating deb pkgs for {flavor} and {arch}...") - pkg_creator.create_deb_pkgs(flavor, f"./built_artifacts/cloudflared-linux-{arch}.deb") + for arch in archs: + print(f"creating deb pkgs for {flavor} and {arch}...") + pkg_creator.create_deb_pkgs(flavor, f"./built_artifacts/cloudflared-linux-{arch}.deb") - print("uploading to r2...") - upload_from_directories(pkg_uploader, "dists", arch, release_version) - upload_from_directories(pkg_uploader, "pool", arch, release_version) + print("uploading latest to r2...") + upload_from_directories(pkg_uploader, "dists", None, binary_name) + upload_from_directories(pkg_uploader, "pool", None, binary_name) - print("cleaning up directories...") - shutil.rmtree("./dists") - shutil.rmtree("./pool") - shutil.rmtree("./db") + print(f"uploading versioned release {release_version} to r2...") + upload_from_directories(pkg_uploader, "dists", release_version, binary_name) + upload_from_directories(pkg_uploader, "pool", release_version, binary_name) #TODO: https://jira.cfops.it/browse/TUN-6146 will extract this into it's own command line script. if __name__ == "__main__": @@ -215,15 +175,12 @@ if __name__ == "__main__": bucket_name = os.getenv('R2_BUCKET_NAME') client_id = os.getenv('R2_CLIENT_ID') client_secret = os.getenv('R2_CLIENT_SECRET') - tunnel_account_id = '5ab4e9dfbd435d24068829fda0077963' - kv_namespace = os.getenv('KV_NAMESPACE') - kv_api_token = os.getenv('KV_API_TOKEN') + tunnel_account_id = os.getenv('R2_ACCOUNT_ID') release_version = os.getenv('RELEASE_VERSION') gpg_key_id = os.getenv('GPG_KEY_ID') - pkg_uploader = PkgUploader(kv_api_token, kv_namespace, tunnel_account_id, bucket_name, client_id, client_secret) + pkg_uploader = PkgUploader(tunnel_account_id, bucket_name, client_id, client_secret) archs = ["amd64", "386", "arm64"] flavors = ["bullseye", "buster", "bionic"] - for arch in archs: - create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, "cloudflared", arch, "main", release_version) + create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, "cloudflared", archs, "main", release_version) From 9ef61915159a6b56f8c5f26534e59367ffed7d02 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 5 May 2022 11:33:12 +0100 Subject: [PATCH 069/238] TUN-5945: Added support for Ubuntu releases --- release_pkgs.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index 3cc0949c..d4ee68d7 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -61,7 +61,7 @@ class PkgCreator: origin - name of your package (String) label - label of your package (could be same as the name) (String) - flavor - flavor you want this to be distributed for (List of Strings) + release - release you want this to be distributed for (List of Strings) components - could be a channel like main/stable/beta archs - Architecture (List of Strings) description - (String) @@ -71,20 +71,20 @@ class PkgCreator: file_path, origin, label, - flavors, + releases, archs, components, description, gpg_key_id ): with open(file_path, "w") as distributions_file: - for flavor in flavors: + for release in releases: distributions_file.write(f"Origin: {origin}\n") distributions_file.write(f"Label: {label}\n") - distributions_file.write(f"Codename: {flavor}\n") + distributions_file.write(f"Codename: {release}\n") archs_list = " ".join(archs) distributions_file.write(f"Architectures: {archs_list}\n") distributions_file.write(f"Components: {components}\n") - distributions_file.write(f"Description: {description} - {flavor}\n") + distributions_file.write(f"Description: {description} - {release}\n") distributions_file.write(f"SignWith: {gpg_key_id}\n") distributions_file.write("\n") return distributions_file @@ -97,9 +97,9 @@ class PkgCreator: db and pool contain information and metadata about builds. We can ignore these. dist: contains all the pkgs and signed releases that are necessary for an apt download. """ - def create_deb_pkgs(self, flavor, deb_file): + def create_deb_pkgs(self, release, deb_file): self._clean_build_resources() - subprocess.call(("reprepro", "includedeb", flavor, deb_file)) + subprocess.call(("reprepro", "includedeb", release, deb_file)) """ This is mostly useful to clear previously built db, dist and pool resources. @@ -139,24 +139,24 @@ def upload_from_directories(pkg_uploader, directory, release, binary): release_version: is the cloudflared release version. """ -def create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, binary_name, archs, package_component, release_version): +def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, release_version): # set configuration for package creation. print(f"initialising configuration for {binary_name} , {archs}") pkg_creator.create_distribution_conf( "./conf/distributions", binary_name, binary_name, - flavors, + releases, archs, package_component, f"apt repository for {binary_name}", gpg_key_id) # create deb pkgs - for flavor in flavors: + for release in releases: for arch in archs: - print(f"creating deb pkgs for {flavor} and {arch}...") - pkg_creator.create_deb_pkgs(flavor, f"./built_artifacts/cloudflared-linux-{arch}.deb") + print(f"creating deb pkgs for {release} and {arch}...") + pkg_creator.create_deb_pkgs(release, f"./built_artifacts/cloudflared-linux-{arch}.deb") print("uploading latest to r2...") upload_from_directories(pkg_uploader, "dists", None, binary_name) @@ -182,5 +182,7 @@ if __name__ == "__main__": pkg_uploader = PkgUploader(tunnel_account_id, bucket_name, client_id, client_secret) archs = ["amd64", "386", "arm64"] - flavors = ["bullseye", "buster", "bionic"] - create_deb_packaging(pkg_creator, pkg_uploader, flavors, gpg_key_id, "cloudflared", archs, "main", release_version) + debian_releases = ["bookworm", "bullseye", "buster"] + ubuntu_releases = ["jammy", "impish", "focal", "bionic"] + deb_based_releases = debian_releases + ubuntu_releases + create_deb_packaging(pkg_creator, pkg_uploader, deb_based_releases, gpg_key_id, "cloudflared", archs, "main", release_version) From 0180b6d7338204c393eca78807c59220c9041fcc Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 6 May 2022 15:14:53 +0100 Subject: [PATCH 070/238] TUN-6146: Release_pkgs is now a generic command line script --- release_pkgs.py | 73 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index d4ee68d7..3671676a 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -10,6 +10,7 @@ """ import subprocess import os +import argparse import logging from hashlib import sha256 @@ -166,23 +167,59 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", release_version, binary_name) upload_from_directories(pkg_uploader, "pool", release_version, binary_name) -#TODO: https://jira.cfops.it/browse/TUN-6146 will extract this into it's own command line script. +def parse_args(): + parser = argparse.ArgumentParser( + description="Creates linux releases and uploads them in a packaged format" + ) + + parser.add_argument( + "--bucket", default=os.environ.get("R2_BUCKET_NAME"), help="R2 Bucket name" + ) + parser.add_argument( + "--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID" + ) + parser.add_argument( + "--secret", default=os.environ.get("R2_CLIENT_SECRET"), help="R2 Client Secret" + ) + parser.add_argument( + "--account", default=os.environ.get("R2_ACCOUNT_ID"), help="R2 Account Tag" + ) + parser.add_argument( + "--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\ + prefixed with" + ) + + parser.add_argument( + "--binary", default=os.environ.get("BINARY_NAME"), help="The name of the binary the packages are for" + ) + + parser.add_argument( + "--gpg-key-id", default=os.environ.get("GPG_KEY_ID"), help="gpg key ID that's being used to sign release\ + packages." + ) + + parser.add_argument( + "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic"], + help="list of debian based releases that need to be packaged for" + ) + + parser.add_argument( + "--archs", default=["amd64", "386", "arm64"], help="list of architectures we want to package for. Note that\ + it is the caller's responsiblity to ensure that these debs are already present in a directory. This script\ + will not build binaries or create their debs." + ) + args = parser.parse_args() + + return args + if __name__ == "__main__": - # initialise pkg creator + try: + args = parse_args() + except Exception as e: + logging.exception(e) + exit(1) + pkg_creator = PkgCreator() - - # initialise pkg uploader - bucket_name = os.getenv('R2_BUCKET_NAME') - client_id = os.getenv('R2_CLIENT_ID') - client_secret = os.getenv('R2_CLIENT_SECRET') - tunnel_account_id = os.getenv('R2_ACCOUNT_ID') - release_version = os.getenv('RELEASE_VERSION') - gpg_key_id = os.getenv('GPG_KEY_ID') - - pkg_uploader = PkgUploader(tunnel_account_id, bucket_name, client_id, client_secret) - - archs = ["amd64", "386", "arm64"] - debian_releases = ["bookworm", "bullseye", "buster"] - ubuntu_releases = ["jammy", "impish", "focal", "bionic"] - deb_based_releases = debian_releases + ubuntu_releases - create_deb_packaging(pkg_creator, pkg_uploader, deb_based_releases, gpg_key_id, "cloudflared", archs, "main", release_version) + pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) + create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, args.gpg_key_id, args.binary, + args.archs, "main", args.release_tag) From 99d4e4865689517604314bc8910f5afdd2a369d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 27 Apr 2022 11:51:06 +0100 Subject: [PATCH 071/238] TUN-6016: Push local managed tunnels configuration to the edge --- cmd/cloudflared/tunnel/cmd.go | 6 +- cmd/cloudflared/tunnel/configuration.go | 19 ++++- config/configuration.go | 38 ++++----- connection/connection.go | 1 + connection/connection_test.go | 4 + connection/control.go | 21 ++++- connection/http2.go | 2 +- connection/http2_test.go | 19 +++-- connection/metrics.go | 40 +++++++++ connection/quic.go | 2 +- connection/quic_test.go | 2 +- connection/rpc.go | 26 ++++-- ingress/config.go | 104 +++++++++++++++++++++++- ipaccess/access.go | 12 +++ metrics/metrics.go | 4 +- orchestration/config.go | 53 +++++++++++- orchestration/config_test.go | 82 +++++++++++++++++++ orchestration/orchestrator.go | 24 +++++- orchestration/orchestrator_test.go | 14 ++++ tunnelrpc/pogs/connectionrpc.go | 18 ++++ 20 files changed, 441 insertions(+), 50 deletions(-) create mode 100644 orchestration/config_test.go diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 3eed3436..e9038403 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -343,13 +343,13 @@ func StartServer( observer.SendURL(quickTunnelURL) } - tunnelConfig, dynamicConfig, err := prepareTunnelConfig(c, info, log, logTransport, observer, namedTunnel) + tunnelConfig, orchestratorConfig, err := prepareTunnelConfig(c, info, log, logTransport, observer, namedTunnel) if err != nil { log.Err(err).Msg("Couldn't start tunnel") return err } - orchestrator, err := orchestration.NewOrchestrator(ctx, dynamicConfig, tunnelConfig.Tags, tunnelConfig.Log) + orchestrator, err := orchestration.NewOrchestrator(ctx, orchestratorConfig, tunnelConfig.Tags, tunnelConfig.Log) if err != nil { return err } @@ -388,7 +388,7 @@ func StartServer( info.Version(), hostname, metricsListener.Addr().String(), - dynamicConfig.Ingress, + orchestratorConfig.Ingress, tunnelConfig.HAConnections, ) app := tunnelUI.Launch(ctx, log, logTransport) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 778ec6b3..9a0728c5 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -43,6 +43,8 @@ var ( secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} + + configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq"} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories @@ -348,11 +350,24 @@ func prepareTunnelConfig( ProtocolSelector: protocolSelector, EdgeTLSConfigs: edgeTLSConfigs, } - dynamicConfig := &orchestration.Config{ + orchestratorConfig := &orchestration.Config{ Ingress: &ingressRules, WarpRoutingEnabled: warpRoutingEnabled, + ConfigurationFlags: parseConfigFlags(c), } - return tunnelConfig, dynamicConfig, nil + return tunnelConfig, orchestratorConfig, nil +} + +func parseConfigFlags(c *cli.Context) map[string]string { + result := make(map[string]string) + + for _, flag := range configFlags { + if v := c.String(flag); c.IsSet(flag) && v != "" { + result[flag] = v + } + } + + return result } func gracePeriod(c *cli.Context) (time.Duration, error) { diff --git a/config/configuration.go b/config/configuration.go index bb7a10b8..49395404 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -177,9 +177,9 @@ func ValidateUrl(c *cli.Context, allowURLFromArgs bool) (*url.URL, error) { } type UnvalidatedIngressRule struct { - Hostname string `json:"hostname"` - Path string `json:"path"` - Service string `json:"service"` + Hostname string `json:"hostname,omitempty"` + Path string `json:"path,omitempty"` + Service string `json:"service,omitempty"` OriginRequest OriginRequestConfig `yaml:"originRequest" json:"originRequest"` } @@ -192,41 +192,41 @@ type UnvalidatedIngressRule struct { // - To specify a time.Duration in json, use int64 of the nanoseconds type OriginRequestConfig struct { // HTTP proxy timeout for establishing a new connection - ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout"` + ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"` // HTTP proxy timeout for completing a TLS handshake - TLSTimeout *CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout"` + TLSTimeout *CustomDuration `yaml:"tlsTimeout" json:"tlsTimeout,omitempty"` // HTTP proxy TCP keepalive duration - TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive"` + TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"` // HTTP proxy should disable "happy eyeballs" for IPv4/v6 fallback - NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs"` + NoHappyEyeballs *bool `yaml:"noHappyEyeballs" json:"noHappyEyeballs,omitempty"` // HTTP proxy maximum keepalive connection pool size - KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections"` + KeepAliveConnections *int `yaml:"keepAliveConnections" json:"keepAliveConnections,omitempty"` // HTTP proxy timeout for closing an idle connection - KeepAliveTimeout *CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout"` + KeepAliveTimeout *CustomDuration `yaml:"keepAliveTimeout" json:"keepAliveTimeout,omitempty"` // Sets the HTTP Host header for the local webserver. - HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader"` + HTTPHostHeader *string `yaml:"httpHostHeader" json:"httpHostHeader,omitempty"` // Hostname on the origin server certificate. - OriginServerName *string `yaml:"originServerName" json:"originServerName"` + OriginServerName *string `yaml:"originServerName" json:"originServerName,omitempty"` // Path to the CA for the certificate of your origin. // This option should be used only if your certificate is not signed by Cloudflare. - CAPool *string `yaml:"caPool" json:"caPool"` + CAPool *string `yaml:"caPool" json:"caPool,omitempty"` // Disables TLS verification of the certificate presented by your origin. // Will allow any certificate from the origin to be accepted. // Note: The connection from your machine to Cloudflare's Edge is still encrypted. - NoTLSVerify *bool `yaml:"noTLSVerify" json:"noTLSVerify"` + NoTLSVerify *bool `yaml:"noTLSVerify" json:"noTLSVerify,omitempty"` // Disables chunked transfer encoding. // Useful if you are running a WSGI server. - DisableChunkedEncoding *bool `yaml:"disableChunkedEncoding" json:"disableChunkedEncoding"` + DisableChunkedEncoding *bool `yaml:"disableChunkedEncoding" json:"disableChunkedEncoding,omitempty"` // Runs as jump host - BastionMode *bool `yaml:"bastionMode" json:"bastionMode"` + BastionMode *bool `yaml:"bastionMode" json:"bastionMode,omitempty"` // Listen address for the proxy. - ProxyAddress *string `yaml:"proxyAddress" json:"proxyAddress"` + ProxyAddress *string `yaml:"proxyAddress" json:"proxyAddress,omitempty"` // Listen port for the proxy. - ProxyPort *uint `yaml:"proxyPort" json:"proxyPort"` + ProxyPort *uint `yaml:"proxyPort" json:"proxyPort,omitempty"` // Valid options are 'socks' or empty. - ProxyType *string `yaml:"proxyType" json:"proxyType"` + ProxyType *string `yaml:"proxyType" json:"proxyType,omitempty"` // IP rules for the proxy service - IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules"` + IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules,omitempty"` } type IngressIPRule struct { diff --git a/connection/connection.go b/connection/connection.go index ef60a2f9..aa66e494 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -30,6 +30,7 @@ var switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, type Orchestrator interface { UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse + GetConfigJSON() ([]byte, error) GetOriginProxy() (OriginProxy, error) } diff --git a/connection/connection_test.go b/connection/connection_test.go index 63390165..b8c3b674 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -42,6 +42,10 @@ type mockOrchestrator struct { originProxy OriginProxy } +func (mcr *mockOrchestrator) GetConfigJSON() ([]byte, error) { + return nil, fmt.Errorf("not implemented") +} + func (*mockOrchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.UpdateConfigurationResponse { return &tunnelpogs.UpdateConfigurationResponse{ LastAppliedVersion: version, diff --git a/connection/control.go b/connection/control.go index 2467e80a..1b28ceb1 100644 --- a/connection/control.go +++ b/connection/control.go @@ -30,11 +30,15 @@ type controlStream struct { // ControlStreamHandler registers connections with origintunneld and initiates graceful shutdown. type ControlStreamHandler interface { // ServeControlStream handles the control plane of the transport in the current goroutine calling this - ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions) error + ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions, tunnelConfigGetter TunnelConfigJSONGetter) error // IsStopped tells whether the method above has finished IsStopped() bool } +type TunnelConfigJSONGetter interface { + GetConfigJSON() ([]byte, error) +} + // NewControlStream returns a new instance of ControlStreamHandler func NewControlStream( observer *Observer, @@ -63,15 +67,28 @@ func (c *controlStream) ServeControlStream( ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions, + tunnelConfigGetter TunnelConfigJSONGetter, ) error { rpcClient := c.newRPCClientFunc(ctx, rw, c.observer.log) - if err := rpcClient.RegisterConnection(ctx, c.namedTunnelProperties, connOptions, c.connIndex, c.observer); err != nil { + registrationDetails, err := rpcClient.RegisterConnection(ctx, c.namedTunnelProperties, connOptions, c.connIndex, c.observer) + if err != nil { rpcClient.Close() return err } c.connectedFuse.Connected() + // if conn index is 0 and tunnel is not remotely managed, then send local ingress rules configuration + if c.connIndex == 0 && !registrationDetails.TunnelIsRemotelyManaged { + if tunnelConfig, err := tunnelConfigGetter.GetConfigJSON(); err == nil { + if err := rpcClient.SendLocalConfiguration(ctx, tunnelConfig, c.observer); err != nil { + c.observer.log.Err(err).Msg("unable to send local configuration") + } + } else { + c.observer.log.Err(err).Msg("failed to obtain current configuration") + } + } + c.waitForUnregister(ctx, rpcClient) return nil } diff --git a/connection/http2.go b/connection/http2.go index 715c827c..f7ead1f0 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -117,7 +117,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch connType { case TypeControlStream: - if err := c.controlStreamHandler.ServeControlStream(r.Context(), respWriter, c.connOptions); err != nil { + if err := c.controlStreamHandler.ServeControlStream(r.Context(), respWriter, c.connOptions, c.orchestrator); err != nil { c.controlStreamErr = err c.log.Error().Err(err) respWriter.WriteErrorResponse() diff --git a/connection/http2_test.go b/connection/http2_test.go index 384d29fb..18e688eb 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gobwas/ws/wsutil" + "github.com/google/uuid" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -166,18 +167,26 @@ type mockNamedTunnelRPCClient struct { unregistered chan struct{} } +func (mc mockNamedTunnelRPCClient) SendLocalConfiguration(c context.Context, config []byte, observer *Observer) error { + return nil +} + func (mc mockNamedTunnelRPCClient) RegisterConnection( c context.Context, properties *NamedTunnelProperties, options *tunnelpogs.ConnectionOptions, connIndex uint8, observer *Observer, -) error { +) (*tunnelpogs.ConnectionDetails, error) { if mc.shouldFail != nil { - return mc.shouldFail + return nil, mc.shouldFail } close(mc.registered) - return nil + return &tunnelpogs.ConnectionDetails{ + Location: "LIS", + UUID: uuid.New(), + TunnelIsRemotelyManaged: false, + }, nil } func (mc mockNamedTunnelRPCClient) GracefulShutdown(ctx context.Context, gracePeriod time.Duration) { @@ -477,7 +486,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { select { case <-rpcClientFactory.registered: - break //ok + break // ok case <-time.Tick(time.Second): t.Fatal("timeout out waiting for registration") } @@ -487,7 +496,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { select { case <-rpcClientFactory.unregistered: - break //ok + break // ok case <-time.Tick(time.Second): t.Fatal("timeout out waiting for unregistered signal") } diff --git a/connection/metrics.go b/connection/metrics.go index b52fda0e..a171f1a4 100644 --- a/connection/metrics.go +++ b/connection/metrics.go @@ -13,6 +13,7 @@ const ( MetricsNamespace = "cloudflared" TunnelSubsystem = "tunnel" muxerSubsystem = "muxer" + configSubsystem = "config" ) type muxerMetrics struct { @@ -36,6 +37,11 @@ type muxerMetrics struct { compRateAve *prometheus.GaugeVec } +type localConfigMetrics struct { + pushes prometheus.Counter + pushesErrors prometheus.Counter +} + type tunnelMetrics struct { timerRetries prometheus.Gauge serverLocations *prometheus.GaugeVec @@ -51,6 +57,39 @@ type tunnelMetrics struct { muxerMetrics *muxerMetrics tunnelsHA tunnelsForHA userHostnamesCounts *prometheus.CounterVec + + localConfigMetrics *localConfigMetrics +} + +func newLocalConfigMetrics() *localConfigMetrics { + + pushesMetric := prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: MetricsNamespace, + Subsystem: configSubsystem, + Name: "local_config_pushes", + Help: "Number of local configuration pushes to the edge", + }, + ) + + pushesErrorsMetric := prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: MetricsNamespace, + Subsystem: configSubsystem, + Name: "local_config_pushes_errors", + Help: "Number of errors occurred during local configuration pushes", + }, + ) + + prometheus.MustRegister( + pushesMetric, + pushesErrorsMetric, + ) + + return &localConfigMetrics{ + pushes: pushesMetric, + pushesErrors: pushesErrorsMetric, + } } func newMuxerMetrics() *muxerMetrics { @@ -386,6 +425,7 @@ func initTunnelMetrics() *tunnelMetrics { regFail: registerFail, rpcFail: rpcFail, userHostnamesCounts: userHostnamesCounts, + localConfigMetrics: newLocalConfigMetrics(), } } diff --git a/connection/quic.go b/connection/quic.go index 822925ae..2c04edf3 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -111,7 +111,7 @@ func (q *QUICConnection) Serve(ctx context.Context) error { func (q *QUICConnection) serveControlStream(ctx context.Context, controlStream quic.Stream) error { // This blocks until the control plane is done. - err := q.controlStreamHandler.ServeControlStream(ctx, controlStream, q.connOptions) + err := q.controlStreamHandler.ServeControlStream(ctx, controlStream, q.connOptions, q.orchestrator) if err != nil { // Not wrapping error here to be consistent with the http2 message. return err diff --git a/connection/quic_test.go b/connection/quic_test.go index 50f9863e..05fd64e2 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -163,7 +163,7 @@ type fakeControlStream struct { ControlStreamHandler } -func (fakeControlStream) ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions) error { +func (fakeControlStream) ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions, tunnelConfigGetter TunnelConfigJSONGetter) error { <-ctx.Done() return nil } diff --git a/connection/rpc.go b/connection/rpc.go index 937604b3..4565f396 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -58,6 +58,11 @@ type NamedTunnelRPCClient interface { options *tunnelpogs.ConnectionOptions, connIndex uint8, observer *Observer, + ) (*tunnelpogs.ConnectionDetails, error) + SendLocalConfiguration( + c context.Context, + config []byte, + observer *Observer, ) error GracefulShutdown(ctx context.Context, gracePeriod time.Duration) Close() @@ -90,7 +95,7 @@ func (rsc *registrationServerClient) RegisterConnection( options *tunnelpogs.ConnectionOptions, connIndex uint8, observer *Observer, -) error { +) (*tunnelpogs.ConnectionDetails, error) { conn, err := rsc.client.RegisterConnection( ctx, properties.Credentials.Auth(), @@ -101,10 +106,10 @@ func (rsc *registrationServerClient) RegisterConnection( if err != nil { if err.Error() == DuplicateConnectionError { observer.metrics.regFail.WithLabelValues("dup_edge_conn", "registerConnection").Inc() - return errDuplicationConnection + return nil, errDuplicationConnection } observer.metrics.regFail.WithLabelValues("server_error", "registerConnection").Inc() - return serverRegistrationErrorFromRPC(err) + return nil, serverRegistrationErrorFromRPC(err) } observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() @@ -112,7 +117,18 @@ func (rsc *registrationServerClient) RegisterConnection( observer.logServerInfo(connIndex, conn.Location, fmt.Sprintf("Connection %s registered", conn.UUID)) observer.sendConnectedEvent(connIndex, conn.Location) - return nil + return conn, nil +} + +func (rsc *registrationServerClient) SendLocalConfiguration(ctx context.Context, config []byte, observer *Observer) (err error) { + observer.metrics.localConfigMetrics.pushes.Inc() + defer func() { + if err != nil { + observer.metrics.localConfigMetrics.pushesErrors.Inc() + } + }() + + return rsc.client.SendLocalConfiguration(ctx, config) } func (rsc *registrationServerClient) GracefulShutdown(ctx context.Context, gracePeriod time.Duration) { @@ -274,7 +290,7 @@ func (h *h2muxConnection) registerNamedTunnel( rpcClient := h.newRPCClientFunc(ctx, stream, h.observer.log) defer rpcClient.Close() - if err = rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, h.observer); err != nil { + if _, err = rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, h.observer); err != nil { return err } return nil diff --git a/ingress/config.go b/ingress/config.go index 0b59ecff..bc2a9f6b 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -47,18 +47,26 @@ type RemoteConfig struct { WarpRouting config.WarpRoutingConfig } -type remoteConfigJSON struct { - GlobalOriginRequest config.OriginRequestConfig `json:"originRequest"` +type RemoteConfigJSON struct { + GlobalOriginRequest *config.OriginRequestConfig `json:"originRequest,omitempty"` IngressRules []config.UnvalidatedIngressRule `json:"ingress"` WarpRouting config.WarpRoutingConfig `json:"warp-routing"` } func (rc *RemoteConfig) UnmarshalJSON(b []byte) error { - var rawConfig remoteConfigJSON + var rawConfig RemoteConfigJSON + if err := json.Unmarshal(b, &rawConfig); err != nil { return err } - ingress, err := validateIngress(rawConfig.IngressRules, originRequestFromConfig(rawConfig.GlobalOriginRequest)) + + // if nil, just assume the default values. + globalOriginRequestConfig := rawConfig.GlobalOriginRequest + if globalOriginRequestConfig == nil { + globalOriginRequestConfig = &config.OriginRequestConfig{} + } + + ingress, err := validateIngress(rawConfig.IngressRules, originRequestFromConfig(*globalOriginRequestConfig)) if err != nil { return err } @@ -387,3 +395,91 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi cfg.setIPRules(overrides) return cfg } + +func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig { + var connectTimeout *config.CustomDuration + var tlsTimeout *config.CustomDuration + var tcpKeepAlive *config.CustomDuration + var keepAliveConnections *int + var keepAliveTimeout *config.CustomDuration + var proxyAddress *string + + if c.ConnectTimeout != defaultConnectTimeout { + connectTimeout = &c.ConnectTimeout + } + if c.TLSTimeout != defaultTLSTimeout { + tlsTimeout = &c.TLSTimeout + } + if c.TCPKeepAlive != defaultTCPKeepAlive { + tcpKeepAlive = &c.TCPKeepAlive + } + if c.KeepAliveConnections != defaultKeepAliveConnections { + keepAliveConnections = &c.KeepAliveConnections + } + if c.KeepAliveTimeout != defaultKeepAliveTimeout { + keepAliveTimeout = &c.KeepAliveTimeout + } + if c.ProxyAddress != defaultProxyAddress { + proxyAddress = &c.ProxyAddress + } + + return config.OriginRequestConfig{ + ConnectTimeout: connectTimeout, + TLSTimeout: tlsTimeout, + TCPKeepAlive: tcpKeepAlive, + NoHappyEyeballs: defaultBoolToNil(c.NoHappyEyeballs), + KeepAliveConnections: keepAliveConnections, + KeepAliveTimeout: keepAliveTimeout, + HTTPHostHeader: emptyStringToNil(c.HTTPHostHeader), + OriginServerName: emptyStringToNil(c.OriginServerName), + CAPool: emptyStringToNil(c.CAPool), + NoTLSVerify: defaultBoolToNil(c.NoTLSVerify), + DisableChunkedEncoding: defaultBoolToNil(c.DisableChunkedEncoding), + BastionMode: defaultBoolToNil(c.BastionMode), + ProxyAddress: proxyAddress, + ProxyPort: zeroUIntToNil(c.ProxyPort), + ProxyType: emptyStringToNil(c.ProxyType), + IPRules: convertToRawIPRules(c.IPRules), + } +} + +func convertToRawIPRules(ipRules []ipaccess.Rule) []config.IngressIPRule { + result := make([]config.IngressIPRule, 0) + for _, r := range ipRules { + cidr := r.StringCIDR() + + newRule := config.IngressIPRule{ + Prefix: &cidr, + Ports: r.Ports(), + Allow: r.RulePolicy(), + } + + result = append(result, newRule) + } + + return result +} + +func defaultBoolToNil(b bool) *bool { + if b == false { + return nil + } + + return &b +} + +func emptyStringToNil(s string) *string { + if s == "" { + return nil + } + + return &s +} + +func zeroUIntToNil(v uint) *uint { + if v == 0 { + return nil + } + + return &v +} diff --git a/ipaccess/access.go b/ipaccess/access.go index 3136f2b5..4d41a7a8 100644 --- a/ipaccess/access.go +++ b/ipaccess/access.go @@ -99,3 +99,15 @@ func (ipr *Rule) PortsString() string { } return "all" } + +func (ipr *Rule) Ports() []int { + return ipr.ports +} + +func (ipr *Rule) RulePolicy() bool { + return ipr.allow +} + +func (ipr *Rule) StringCIDR() string { + return ipr.ipNet.String() +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 0129c49a..f8b3b1bc 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -23,7 +23,7 @@ const ( ) type orchestrator interface { - GetConfigJSON() ([]byte, error) + GetVersionedConfigJSON() ([]byte, error) } func newMetricsHandler( @@ -47,7 +47,7 @@ func newMetricsHandler( }) if orchestrator != nil { router.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { - json, err := orchestrator.GetConfigJSON() + json, err := orchestrator.GetVersionedConfigJSON() if err != nil { w.WriteHeader(500) _, _ = fmt.Fprintf(w, "ERR: %v", err) diff --git a/orchestration/config.go b/orchestration/config.go index dff7e701..cfdbd939 100644 --- a/orchestration/config.go +++ b/orchestration/config.go @@ -1,15 +1,66 @@ package orchestration import ( + "encoding/json" + + "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/ingress" ) -type newConfig struct { +type newRemoteConfig struct { ingress.RemoteConfig // Add more fields when we support other settings in tunnel orchestration } +type newLocalConfig struct { + RemoteConfig ingress.RemoteConfig + ConfigurationFlags map[string]string `json:"__configuration_flags,omitempty"` +} + +// Config is the original config as read and parsed by cloudflared. type Config struct { Ingress *ingress.Ingress WarpRoutingEnabled bool + + // Extra settings used to configure this instance but that are not eligible for remotely management + // ie. (--protocol, --loglevel, ...) + ConfigurationFlags map[string]string +} + +func (rc *newLocalConfig) MarshalJSON() ([]byte, error) { + var r = struct { + ConfigurationFlags map[string]string `json:"__configuration_flags,omitempty"` + ingress.RemoteConfigJSON + }{ + ConfigurationFlags: rc.ConfigurationFlags, + RemoteConfigJSON: ingress.RemoteConfigJSON{ + // UI doesn't support top level configs, so we reconcile to individual ingress configs. + GlobalOriginRequest: nil, + IngressRules: convertToUnvalidatedIngressRules(rc.RemoteConfig.Ingress), + WarpRouting: rc.RemoteConfig.WarpRouting, + }, + } + + return json.Marshal(r) +} + +func convertToUnvalidatedIngressRules(i ingress.Ingress) []config.UnvalidatedIngressRule { + result := make([]config.UnvalidatedIngressRule, 0) + for _, rule := range i.Rules { + var path string + if rule.Path != nil { + path = rule.Path.String() + } + + newRule := config.UnvalidatedIngressRule{ + Hostname: rule.Hostname, + Path: path, + Service: rule.Service.String(), + OriginRequest: ingress.ConvertToRawOriginConfig(rule.Config), + } + + result = append(result, newRule) + } + + return result } diff --git a/orchestration/config_test.go b/orchestration/config_test.go new file mode 100644 index 00000000..53d9a23f --- /dev/null +++ b/orchestration/config_test.go @@ -0,0 +1,82 @@ +package orchestration + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cloudflare/cloudflared/ingress" +) + +// TestNewLocalConfig_MarshalJSON tests that we are able to converte a compiled and validated config back +// into an "unvalidated" format which is compatible with Remote Managed configurations. +func TestNewLocalConfig_MarshalJSON(t *testing.T) { + + rawConfig := []byte(` + { + "originRequest": { + "connectTimeout": 160, + "httpHostHeader": "default" + }, + "ingress": [ + { + "hostname": "tun.example.com", + "service": "https://localhost:8000" + }, + { + "hostname": "*", + "service": "https://localhost:8001", + "originRequest": { + "connectTimeout": 121, + "tlsTimeout": 2, + "noHappyEyeballs": false, + "tcpKeepAlive": 2, + "keepAliveConnections": 2, + "keepAliveTimeout": 2, + "httpHostHeader": "def", + "originServerName": "b2", + "caPool": "/tmp/path1", + "noTLSVerify": false, + "disableChunkedEncoding": false, + "bastionMode": false, + "proxyAddress": "interface", + "proxyPort": 200, + "proxyType": "", + "ipRules": [ + { + "prefix": "10.0.0.0/16", + "ports": [3000, 3030], + "allow": false + }, + { + "prefix": "192.16.0.0/24", + "ports": [5000, 5050], + "allow": true + } + ] + } + } + ] + } + `) + + var expectedConfig ingress.RemoteConfig + err := json.Unmarshal(rawConfig, &expectedConfig) + require.NoError(t, err) + + c := &newLocalConfig{ + RemoteConfig: expectedConfig, + ConfigurationFlags: nil, + } + + jsonSerde, err := json.Marshal(c) + require.NoError(t, err) + + var config ingress.RemoteConfig + err = json.Unmarshal(jsonSerde, &config) + require.NoError(t, err) + + require.Equal(t, config.WarpRouting.Enabled, false) + require.Equal(t, config.Ingress.Rules, expectedConfig.Ingress.Rules) +} diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index 9206cc21..60cd1f10 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -54,7 +54,7 @@ func NewOrchestrator(ctx context.Context, config *Config, tags []tunnelpogs.Tag, return o, nil } -// Update creates a new proxy with the new ingress rules +// UpdateConfig creates a new proxy with the new ingress rules func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.UpdateConfigurationResponse { o.lock.Lock() defer o.lock.Unlock() @@ -63,12 +63,12 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up o.log.Debug(). Int32("current_version", o.currentVersion). Int32("received_version", version). - Msg("Current version is equal or newer than receivied version") + Msg("Current version is equal or newer than received version") return &tunnelpogs.UpdateConfigurationResponse{ LastAppliedVersion: o.currentVersion, } } - var newConf newConfig + var newConf newRemoteConfig if err := json.Unmarshal(config, &newConf); err != nil { o.log.Err(err). Int32("version", version). @@ -131,10 +131,26 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEn return nil } -// GetConfigJSON returns the current version and configuration as JSON +// GetConfigJSON returns the current json serialization of the config as the edge understands it func (o *Orchestrator) GetConfigJSON() ([]byte, error) { o.lock.RLock() defer o.lock.RUnlock() + + c := &newLocalConfig{ + RemoteConfig: ingress.RemoteConfig{ + Ingress: *o.config.Ingress, + WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled}, + }, + ConfigurationFlags: o.config.ConfigurationFlags, + } + + return json.Marshal(c) +} + +// GetVersionedConfigJSON returns the current version and configuration as JSON +func (o *Orchestrator) GetVersionedConfigJSON() ([]byte, error) { + o.lock.RLock() + defer o.lock.RUnlock() var currentConfiguration = struct { Version int32 `json:"version"` Config struct { diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index c543d3b6..85f0f83a 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -2,6 +2,7 @@ package orchestration import ( "context" + "encoding/json" "fmt" "io" "io/ioutil" @@ -641,6 +642,19 @@ func TestPersistentConnection(t *testing.T) { wg.Wait() } +func TestSerializeLocalConfig(t *testing.T) { + c := &newLocalConfig{ + RemoteConfig: ingress.RemoteConfig{ + Ingress: ingress.Ingress{}, + WarpRouting: config.WarpRoutingConfig{}, + }, + ConfigurationFlags: map[string]string{"a": "b"}, + } + + result, _ := json.Marshal(c) + fmt.Println(string(result)) +} + func wsEcho(w http.ResponseWriter, r *http.Request) { upgrader := gows.Upgrader{} diff --git a/tunnelrpc/pogs/connectionrpc.go b/tunnelrpc/pogs/connectionrpc.go index d713edc6..e736cd07 100644 --- a/tunnelrpc/pogs/connectionrpc.go +++ b/tunnelrpc/pogs/connectionrpc.go @@ -175,6 +175,24 @@ func (c RegistrationServer_PogsClient) RegisterConnection(ctx context.Context, a return nil, newRPCError("unknown result which %d", result.Which()) } +func (c RegistrationServer_PogsClient) SendLocalConfiguration(ctx context.Context, config []byte) error { + client := tunnelrpc.TunnelServer{Client: c.Client} + promise := client.UpdateLocalConfiguration(ctx, func(p tunnelrpc.RegistrationServer_updateLocalConfiguration_Params) error { + if err := p.SetConfig(config); err != nil { + return err + } + + return nil + }) + + _, err := promise.Struct() + if err != nil { + return wrapRPCError(err) + } + + return nil +} + func (c RegistrationServer_PogsClient) UnregisterConnection(ctx context.Context) error { client := tunnelrpc.TunnelServer{Client: c.Client} promise := client.UnregisterConnection(ctx, func(p tunnelrpc.RegistrationServer_unregisterConnection_Params) error { From fa2234d6391af607826c0f349d418e71c4792d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Fri, 6 May 2022 18:47:03 +0100 Subject: [PATCH 072/238] TUN-6185: Fix tcpOverWSOriginService not using original scheme for String representation --- ingress/origin_service.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ingress/origin_service.go b/ingress/origin_service.go index 239b6e93..c96e4608 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -109,6 +109,7 @@ func (o rawTCPService) MarshalJSON() ([]byte, error) { // tcpOverWSService models TCP origins serving eyeballs connecting over websocket, such as // cloudflared access commands. type tcpOverWSService struct { + scheme string dest string isBastion bool streamHandler streamHandlerFunc @@ -130,7 +131,8 @@ func newTCPOverWSService(url *url.URL) *tcpOverWSService { addPortIfMissing(url, 7864) // just a random port since there isn't a default in this case } return &tcpOverWSService{ - dest: url.Host, + scheme: url.Scheme, + dest: url.Host, } } @@ -160,7 +162,12 @@ func (o *tcpOverWSService) String() string { if o.isBastion { return ServiceBastion } - return o.dest + + if o.scheme != "" { + return fmt.Sprintf("%s://%s", o.scheme, o.dest) + } else { + return o.dest + } } func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error { From f758361730f4891aef365327b2533ef1736e0bd2 Mon Sep 17 00:00:00 2001 From: Albony Cal <67057319+Albonycal@users.noreply.github.com> Date: Tue, 10 May 2022 19:40:31 +0530 Subject: [PATCH 073/238] Warp Private Network link updated The old one was returning 404 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 134e7cc9..353a5663 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Once installed, you can authenticate `cloudflared` into your Cloudflare account * Route traffic to that Tunnel: * Via public [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns) * Or via a public hostname guided by a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb) - * Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/private-networks) + * Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/) ## TryCloudflare From 7bcab138c51cb3af7caa910de6181ccd9b23a015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 11 May 2022 10:31:07 +0100 Subject: [PATCH 074/238] Release 2022.5.1 --- RELEASE_NOTES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 6d94a14f..ac7fff64 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,12 @@ +2022.5.1 +- 2022-05-06 TUN-6146: Release_pkgs is now a generic command line script +- 2022-05-06 TUN-6185: Fix tcpOverWSOriginService not using original scheme for String representation +- 2022-05-05 TUN-6175: Simply debian packaging by structural upload +- 2022-05-05 TUN-5945: Added support for Ubuntu releases +- 2022-05-04 TUN-6054: Create and upload deb packages to R2 +- 2022-05-03 TUN-6161: Set git user/email for brew core release +- 2022-05-03 TUN-6166: Fix mocked QUIC transport for UDP proxy manager to return expected error +- 2022-04-27 TUN-6016: Push local managed tunnels configuration to the edge 2022.5.0 - 2022-05-02 TUN-6158: Update golang.org/x/crypto - 2022-04-20 VULN-8383 Bump yaml.v2 to yaml.v3 From f7fd4ea71c9ba88d989e13ed43d0f2251a6327c2 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 11 May 2022 15:26:05 +0100 Subject: [PATCH 075/238] TUN-6197: Publish to brew core should not try to open the browser The publish to brew core prints a URL with a PR that does the change in github to brew core formula for cloudflared. It then tries to open the browser, which obviously fails in CI. So this adds a flag for it to skip opening the browser. It's not clear how the PR will be opened, it seems like it must be done by a human. But at least this won't fail the build. --- .teamcity/update-homebrew-core.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh index adc216d6..7e6f95e3 100755 --- a/.teamcity/update-homebrew-core.sh +++ b/.teamcity/update-homebrew-core.sh @@ -23,4 +23,4 @@ git config --global user.name "cloudflare-warp-bot" git config --global user.email "warp-bot@cloudflare.com" # bump formula pr -brew bump-formula-pr cloudflared --version="$VERSION" +brew bump-formula-pr cloudflared --version="$VERSION" --no-browse From 4b6437cc60f0f0a7fe732816c549abad36e371fb Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 12 May 2022 08:32:09 +0100 Subject: [PATCH 076/238] TUN-5943: Add RPM support This PR extends release_pkgs.py to now also support uploading rpm based assets to R2. The packages are not signed yet and will be done in a subsequent PR. This PR - Packs the .rpm assets into relevant directories - Calls createrepo on them to make them yum repo ready - Uploads them to R2 --- Makefile | 4 ++++ cfsetup.yaml | 7 ++++++- release_pkgs.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4fda3d6a..f82dbe8f 100644 --- a/Makefile +++ b/Makefile @@ -261,6 +261,10 @@ github-release: cloudflared github-release-built-pkgs: python3 github_release.py --path $(PWD)/built_artifacts --release-version $(VERSION) +.PHONY: release-pkgs-linux +release-pkgs-linux: + python3 ./release_pkgs.py + .PHONY: github-message github-message: python3 github_message.py --release-version $(VERSION) diff --git a/cfsetup.yaml b/cfsetup.yaml index 4dc51ad4..cdad6ce8 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -40,14 +40,19 @@ stretch: &stretch - libffi-dev - python3-setuptools - python3-pip + - reprepro + - createrepo pre-cache: &github_release_pkgs_pre_cache - wget https://github.com/sudarshan-reddy/msitools/releases/download/v0.101b/wixl -P /usr/local/bin - chmod a+x /usr/local/bin/wixl - pip3 install pynacl==1.4.0 - pip3 install pygithub==1.55 + - pip3 install boto3==1.22.9 post-cache: # build all packages (except macos and FIPS) and move them to /cfsetup/built_artifacts - ./build-packages.sh + # publish packages to linux repos + # TODO TUN-6180: Uncomment this - make release-pkgs-linux # release the packages built and moved to /cfsetup/built_artifacts - make github-release-built-pkgs # handle FIPS separately so that we built with gofips compiler @@ -255,4 +260,4 @@ centos-7: - export PATH=$PATH:/usr/local/go/bin - export GOOS=linux - export GOARCH=amd64 - - make publish-rpm \ No newline at end of file + - make publish-rpm diff --git a/release_pkgs.py b/release_pkgs.py index 3671676a..67b1fb3d 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -12,6 +12,7 @@ import subprocess import os import argparse import logging +import shutil from hashlib import sha256 import boto3 @@ -100,13 +101,38 @@ class PkgCreator: """ def create_deb_pkgs(self, release, deb_file): self._clean_build_resources() - subprocess.call(("reprepro", "includedeb", release, deb_file)) + subprocess.call(("reprepro", "includedeb", release, deb_file), timeout=120) """ This is mostly useful to clear previously built db, dist and pool resources. """ def _clean_build_resources(self): - subprocess.call(("reprepro", "clearvanished")) + subprocess.call(("reprepro", "clearvanished"), timeout=120) + + # TODO https://jira.cfops.it/browse/TUN-6209 : Sign these packages. + def create_rpm_pkgs(self, artifacts_path): + self._setup_rpm_pkg_directories(artifacts_path) + subprocess.call(("createrepo", "./rpm"), timeout=120) + + """ + sets up the RPM directories in the following format: + - rpm + - aarch64 + - x86_64 + - 386 + + this assumes the assets are in the format -.rpm + """ + def _setup_rpm_pkg_directories(self, artifacts_path, archs=["aarch64", "x86_64", "386"]): + for arch in archs: + for root, _ , files in os.walk(artifacts_path): + for file in files: + if file.endswith(f"{arch}.rpm"): + new_dir = f"./rpm/{arch}" + os.makedirs(new_dir, exist_ok=True) + old_path = os.path.join(root, file) + new_path = os.path.join(new_dir, file) + shutil.copyfile(old_path, new_path) """ Walks through a directory and uploads it's assets to R2. @@ -167,6 +193,17 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", release_version, binary_name) upload_from_directories(pkg_uploader, "pool", release_version, binary_name) +def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name): + print(f"creating rpm pkgs...") + pkg_creator.create_rpm_pkgs(artifacts_path) + + print("uploading latest to r2...") + upload_from_directories(pkg_uploader, "rpm", None, binary_name) + + print(f"uploading versioned release {release_version} to r2...") + upload_from_directories(pkg_uploader, "rpm", release_version, binary_name) + + def parse_args(): parser = argparse.ArgumentParser( description="Creates linux releases and uploads them in a packaged format" @@ -223,3 +260,5 @@ if __name__ == "__main__": pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, args.gpg_key_id, args.binary, args.archs, "main", args.release_tag) + + create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary ) From 26a7b59f6f9b0de0090875c8aa3340fe75e53100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 18 May 2022 11:11:48 +0100 Subject: [PATCH 077/238] TUN-6248: Fix panic in cloudflared during tracing when origin doesn't provide header map --- proxy/proxy.go | 5 +++++ tracing/tracing.go | 6 ++++++ tracing/tracing_test.go | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index bcdab8de..32e3d678 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -203,6 +203,11 @@ func (p *Proxy) proxyHTTPRequest( tracing.EndWithStatus(ttfbSpan, codes.Ok, resp.Status) defer resp.Body.Close() + // resp headers can be nil + if resp.Header == nil { + resp.Header = make(http.Header) + } + // Add spans to response header (if available) tr.AddSpans(resp.Header, p.log) diff --git a/tracing/tracing.go b/tracing/tracing.go index 2cab213b..927798b3 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -103,6 +103,11 @@ func (cft *TracedRequest) Tracer() trace.Tracer { // Spans returns the spans as base64 encoded protobuf otlp traces. func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { + if headers == nil { + log.Error().Msgf("provided headers map is nil") + return + } + enc, err := cft.exporter.Spans() switch err { case nil: @@ -121,6 +126,7 @@ func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { log.Error().Msgf("no traces provided and no error from exporter") return } + headers[CanonicalCloudflaredTracingHeader] = []string{enc} } diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go index 1bd23e89..68a272fc 100644 --- a/tracing/tracing_test.go +++ b/tracing/tracing_test.go @@ -1,13 +1,16 @@ package tracing import ( + "context" "net/http" "net/http/httptest" "testing" + "github.com/rs/zerolog" "github.com/stretchr/testify/assert" tracesdk "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" + tracepb "go.opentelemetry.io/proto/otlp/trace/v1" ) func TestNewCfTracer(t *testing.T) { @@ -48,3 +51,19 @@ func TestNewCfTracerInvalidHeaders(t *testing.T) { assert.IsType(t, &NoopOtlpClient{}, tr.exporter) } } + +func TestAddingSpansWithNilMap(t *testing.T) { + req := httptest.NewRequest("GET", "http://localhost", nil) + req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") + tr := NewTracedRequest(req) + + exporter := tr.exporter.(*InMemoryOtlpClient) + + // add fake spans + spans := createResourceSpans([]*tracepb.Span{createOtlpSpan(traceId)}) + err := exporter.UploadTraces(context.Background(), spans) + assert.NoError(t, err) + + // a panic shouldn't occur + tr.AddSpans(nil, &zerolog.Logger{}) +} From 6f78ccde04ac33cd16639d1f058a4c4d246232cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 18 May 2022 12:11:38 +0100 Subject: [PATCH 078/238] TUN-6250: Add upstream response status code to tracing span attributes --- ingress/origin_proxy.go | 9 ++++++++- ingress/origin_service.go | 11 +++-------- proxy/proxy.go | 6 +++--- tracing/tracing.go | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/ingress/origin_proxy.go b/ingress/origin_proxy.go index e99e002e..90371dfa 100644 --- a/ingress/origin_proxy.go +++ b/ingress/origin_proxy.go @@ -1,6 +1,7 @@ package ingress import ( + "fmt" "net" "net/http" @@ -49,7 +50,13 @@ func (o *httpService) RoundTrip(req *http.Request) (*http.Response, error) { } func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) { - return o.resp, nil + resp := &http.Response{ + StatusCode: o.code, + Status: fmt.Sprintf("%d %s", o.code, http.StatusText(o.code)), + Body: new(NopReadCloser), + } + + return resp, nil } func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, error) { diff --git a/ingress/origin_service.go b/ingress/origin_service.go index c96e4608..38ceeda5 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -238,20 +238,15 @@ func (o helloWorld) MarshalJSON() ([]byte, error) { // statusCode is an OriginService that just responds with a given HTTP status. // Typical use-case is "user wants the catch-all rule to just respond 404". type statusCode struct { - resp *http.Response + code int } func newStatusCode(status int) statusCode { - resp := &http.Response{ - StatusCode: status, - Status: fmt.Sprintf("%d %s", status, http.StatusText(status)), - Body: new(NopReadCloser), - } - return statusCode{resp: resp} + return statusCode{code: status} } func (o *statusCode) String() string { - return fmt.Sprintf("http_status:%d", o.resp.StatusCode) + return fmt.Sprintf("http_status:%d", o.code) } func (o *statusCode) start( diff --git a/proxy/proxy.go b/proxy/proxy.go index 32e3d678..661ab363 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" "github.com/cloudflare/cloudflared/carrier" @@ -197,10 +196,11 @@ func (p *Proxy) proxyHTTPRequest( _, ttfbSpan := tr.Tracer().Start(tr.Context(), "ttfb_origin") resp, err := httpService.RoundTrip(roundTripReq) if err != nil { - tracing.EndWithStatus(ttfbSpan, codes.Error, "") + tracing.EndWithErrorStatus(ttfbSpan, err) return errors.Wrap(err, "Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared") } - tracing.EndWithStatus(ttfbSpan, codes.Ok, resp.Status) + + tracing.EndWithStatusCode(ttfbSpan, resp.StatusCode) defer resp.Body.Close() // resp headers can be nil diff --git a/tracing/tracing.go b/tracing/tracing.go index 927798b3..e9172cd5 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "net/http" "os" "runtime" @@ -29,6 +30,9 @@ const ( TracerContextNameOverride = "uber-trace-id" IntCloudflaredTracingHeader = "cf-int-cloudflared-tracing" + + MaxErrorDescriptionLen = 100 + traceHttpStatusCodeKey = "upstreamStatusCode" ) var ( @@ -130,12 +134,35 @@ func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { headers[CanonicalCloudflaredTracingHeader] = []string{enc} } -// EndWithStatus will set a status for the span and then end it. -func EndWithStatus(span trace.Span, code codes.Code, status string) { +// EndWithErrorStatus will set a status for the span and then end it. +func EndWithErrorStatus(span trace.Span, err error) { + endSpan(span, -1, codes.Error, err) +} + +// EndWithStatusCode will set a status for the span and then end it. +func EndWithStatusCode(span trace.Span, statusCode int) { + endSpan(span, statusCode, codes.Ok, nil) +} + +// EndWithErrorStatus will set a status for the span and then end it. +func endSpan(span trace.Span, upstreamStatusCode int, spanStatusCode codes.Code, err error) { if span == nil { return } - span.SetStatus(code, status) + + if upstreamStatusCode > 0 { + span.SetAttributes(attribute.Int(traceHttpStatusCodeKey, upstreamStatusCode)) + } + + // add error to status buf cap description + errDescription := "" + if err != nil { + errDescription = err.Error() + l := int(math.Min(float64(len(errDescription)), MaxErrorDescriptionLen)) + errDescription = errDescription[:l] + } + + span.SetStatus(spanStatusCode, errDescription) span.End() } From 7ce2bb8b2f079f252d8ad87a8c94e224dbe5df51 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 23 May 2022 14:51:26 +0100 Subject: [PATCH 079/238] TUN-6270: Import gpg keys from environment variables We now keep the gpg key inputs configurable. This PR imports base64 encoded gpg details into the build environment and uses this information to sign the linux builds. --- cfsetup.yaml | 1 + release_pkgs.py | 31 +++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index cdad6ce8..87bb6390 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -48,6 +48,7 @@ stretch: &stretch - pip3 install pynacl==1.4.0 - pip3 install pygithub==1.55 - pip3 install boto3==1.22.9 + - pip3 install gnupg==2.3.1 post-cache: # build all packages (except macos and FIPS) and move them to /cfsetup/built_artifacts - ./build-packages.sh diff --git a/release_pkgs.py b/release_pkgs.py index 67b1fb3d..cd6a7c85 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -11,10 +11,12 @@ import subprocess import os import argparse +import base64 import logging import shutil from hashlib import sha256 +import gnupg import boto3 from botocore.client import Config from botocore.exceptions import ClientError @@ -133,6 +135,20 @@ class PkgCreator: old_path = os.path.join(root, file) new_path = os.path.join(new_dir, file) shutil.copyfile(old_path, new_path) + + """ + imports gpg keys into the system so reprepro and createrepo can use it to sign packages. + it returns the GPG ID after a successful import + """ + def import_gpg_keys(self, private_key, public_key): + gpg = gnupg.GPG() + private_key = base64.b64decode(private_key) + gpg.import_keys(private_key) + public_key = base64.b64decode(public_key) + gpg.import_keys(public_key) + data = gpg.list_keys(secret=True) + return (data[0]["fingerprint"]) + """ Walks through a directory and uploads it's assets to R2. @@ -231,8 +247,13 @@ def parse_args(): ) parser.add_argument( - "--gpg-key-id", default=os.environ.get("GPG_KEY_ID"), help="gpg key ID that's being used to sign release\ - packages." + "--gpg-private-key", default=os.environ.get("LINUX_SIGNING_PRIVATE_KEY"), help="GPG private key to sign the\ + packages" + ) + + parser.add_argument( + "--gpg-public-key", default=os.environ.get("LINUX_SIGNING_PUBLIC_KEY"), help="GPG public key used for\ + signing packages" ) parser.add_argument( @@ -257,8 +278,10 @@ if __name__ == "__main__": exit(1) pkg_creator = PkgCreator() + gpg_key_id = pkg_creator.import_gpg_keys(args.gpg_private_key, args.gpg_public_key) + pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) - create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, args.gpg_key_id, args.binary, - args.archs, "main", args.release_tag) + create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs, + "main", args.release_tag) create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary ) From 32739e9f98d7ec0e0a3174ca1f3b4f590936a701 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 24 May 2022 13:20:17 +0100 Subject: [PATCH 080/238] TUN-6209: Improve feedback process if release_pkgs to deb and rpm fail This PR mostly raises exceptions so we are aware if release deb or release pkgs fail. It also makes release_version optional if backup pkgs are not needed. --- cfsetup.yaml | 2 +- release_pkgs.py | 39 ++++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 87bb6390..1a8da793 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -48,7 +48,7 @@ stretch: &stretch - pip3 install pynacl==1.4.0 - pip3 install pygithub==1.55 - pip3 install boto3==1.22.9 - - pip3 install gnupg==2.3.1 + - pip3 install python-gnupg==0.4.9 post-cache: # build all packages (except macos and FIPS) and move them to /cfsetup/built_artifacts - ./build-packages.sh diff --git a/release_pkgs.py b/release_pkgs.py index cd6a7c85..2754636d 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -8,7 +8,7 @@ them to be in an uploadable state. 2. Upload these packages to a storage in a format that apt and yum expect. """ -import subprocess +from subprocess import Popen, PIPE import os import argparse import base64 @@ -102,19 +102,21 @@ class PkgCreator: dist: contains all the pkgs and signed releases that are necessary for an apt download. """ def create_deb_pkgs(self, release, deb_file): - self._clean_build_resources() - subprocess.call(("reprepro", "includedeb", release, deb_file), timeout=120) - - """ - This is mostly useful to clear previously built db, dist and pool resources. - """ - def _clean_build_resources(self): - subprocess.call(("reprepro", "clearvanished"), timeout=120) + print(f"creating deb pkgs: {release} : {deb_file}") + p = Popen(["reprepro", "includedeb", release, deb_file], stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + if p.returncode != 0: + print(f"create deb_pkgs result => {out}, {err}") + raise # TODO https://jira.cfops.it/browse/TUN-6209 : Sign these packages. def create_rpm_pkgs(self, artifacts_path): self._setup_rpm_pkg_directories(artifacts_path) - subprocess.call(("createrepo", "./rpm"), timeout=120) + p = Popen(["createrepo", "./rpm"], stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + if p.returncode != 0: + print(f"create rpm_pkgs result => {out}, {err}") + raise """ sets up the RPM directories in the following format: @@ -180,7 +182,7 @@ def upload_from_directories(pkg_uploader, directory, release, binary): gpg_key_id: is an id indicating the key the package should be signed with. The public key of this id will be uploaded to R2 so it can be presented to apt downloaders. - release_version: is the cloudflared release version. + release_version: is the cloudflared release version. Only provide this if you want a permanent backup. """ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, release_version): # set configuration for package creation. @@ -205,9 +207,10 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", None, binary_name) upload_from_directories(pkg_uploader, "pool", None, binary_name) - print(f"uploading versioned release {release_version} to r2...") - upload_from_directories(pkg_uploader, "dists", release_version, binary_name) - upload_from_directories(pkg_uploader, "pool", release_version, binary_name) + if release_version: + print(f"uploading versioned release {release_version} to r2...") + upload_from_directories(pkg_uploader, "dists", release_version, binary_name) + upload_from_directories(pkg_uploader, "pool", release_version, binary_name) def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name): print(f"creating rpm pkgs...") @@ -216,8 +219,9 @@ def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_vers print("uploading latest to r2...") upload_from_directories(pkg_uploader, "rpm", None, binary_name) - print(f"uploading versioned release {release_version} to r2...") - upload_from_directories(pkg_uploader, "rpm", release_version, binary_name) + if release_version: + print(f"uploading versioned release {release_version} to r2...") + upload_from_directories(pkg_uploader, "rpm", release_version, binary_name) def parse_args(): @@ -239,7 +243,7 @@ def parse_args(): ) parser.add_argument( "--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\ - prefixed with" + prefixed with. Leave empty if you don't want tagged release versions backed up to R2." ) parser.add_argument( @@ -281,6 +285,7 @@ if __name__ == "__main__": gpg_key_id = pkg_creator.import_gpg_keys(args.gpg_private_key, args.gpg_public_key) pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) + print(f"signing with gpg_key: {gpg_key_id}") create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs, "main", args.release_tag) From 2c480a72db4dd9fe3046ce3913d719f00a793fd7 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Tue, 24 May 2022 13:41:42 -0500 Subject: [PATCH 081/238] TUN-6280: Don't wrap qlog connection tracer for gatethering QUIC metrics since we're not writing qlog files. --- quic/tracing.go | 89 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/quic/tracing.go b/quic/tracing.go index e0e57ab1..ca14100f 100644 --- a/quic/tracing.go +++ b/quic/tracing.go @@ -3,9 +3,9 @@ package quic import ( "context" "net" + "time" "github.com/lucas-clemente/quic-go/logging" - "github.com/lucas-clemente/quic-go/qlog" "github.com/rs/zerolog" ) @@ -40,75 +40,116 @@ func NewServerTracer(logger *zerolog.Logger) logging.Tracer { } } -func (t *tracer) TracerForConnection(_ context.Context, p logging.Perspective, odcid logging.ConnectionID) logging.ConnectionTracer { - connID := logging.ConnectionID(odcid).String() - ql := &quicLogger{ - logger: t.logger, - connectionID: connID, - } +func (t *tracer) TracerForConnection(_ctx context.Context, _p logging.Perspective, _odcid logging.ConnectionID) logging.ConnectionTracer { if t.config.isClient { - return newConnTracer(ql, p, odcid, newClientCollector(t.config.index)) + return newConnTracer(newClientCollector(t.config.index)) } - return newConnTracer(ql, p, odcid, newServiceCollector()) + return newConnTracer(newServiceCollector()) } func (*tracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {} func (*tracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) { } -// connTracer is a wrapper around https://pkg.go.dev/github.com/lucas-clemente/quic-go@v0.23.0/qlog#NewConnectionTracer to collect metrics +var _ logging.Tracer = (*tracer)(nil) + +// connTracer collects connection level metrics type connTracer struct { - logging.ConnectionTracer metricsCollector MetricsCollector - connectionID string } -func newConnTracer(ql *quicLogger, p logging.Perspective, odcid logging.ConnectionID, metricsCollector MetricsCollector) logging.ConnectionTracer { +var _ logging.ConnectionTracer = (*connTracer)(nil) + +func newConnTracer(metricsCollector MetricsCollector) logging.ConnectionTracer { return &connTracer{ - qlog.NewConnectionTracer(ql, p, odcid), - metricsCollector, - logging.ConnectionID(odcid).String(), + metricsCollector: metricsCollector, } } func (ct *connTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { ct.metricsCollector.startedConnection() - ct.ConnectionTracer.StartedConnection(local, remote, srcConnID, destConnID) } func (ct *connTracer) ClosedConnection(err error) { ct.metricsCollector.closedConnection(err) - ct.ConnectionTracer.ClosedConnection(err) } func (ct *connTracer) SentPacket(hdr *logging.ExtendedHeader, packetSize logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { ct.metricsCollector.sentPackets(packetSize) - ct.ConnectionTracer.SentPacket(hdr, packetSize, ack, frames) } func (ct *connTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, frames []logging.Frame) { ct.metricsCollector.receivedPackets(size) - ct.ConnectionTracer.ReceivedPacket(hdr, size, frames) } func (ct *connTracer) BufferedPacket(pt logging.PacketType) { ct.metricsCollector.bufferedPackets(pt) - ct.ConnectionTracer.BufferedPacket(pt) } func (ct *connTracer) DroppedPacket(pt logging.PacketType, size logging.ByteCount, reason logging.PacketDropReason) { ct.metricsCollector.droppedPackets(pt, size, reason) - ct.ConnectionTracer.DroppedPacket(pt, size, reason) } func (ct *connTracer) LostPacket(level logging.EncryptionLevel, number logging.PacketNumber, reason logging.PacketLossReason) { ct.metricsCollector.lostPackets(reason) - ct.ConnectionTracer.LostPacket(level, number, reason) } func (ct *connTracer) UpdatedMetrics(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) { ct.metricsCollector.updatedRTT(rttStats) - ct.ConnectionTracer.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) +} + +func (ct *connTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { +} + +func (ct *connTracer) SentTransportParameters(parameters *logging.TransportParameters) { +} + +func (ct *connTracer) ReceivedTransportParameters(parameters *logging.TransportParameters) { +} + +func (ct *connTracer) RestoredTransportParameters(parameters *logging.TransportParameters) { +} + +func (ct *connTracer) ReceivedVersionNegotiationPacket(header *logging.Header, numbers []logging.VersionNumber) { +} + +func (ct *connTracer) ReceivedRetry(header *logging.Header) { +} + +func (ct *connTracer) AcknowledgedPacket(level logging.EncryptionLevel, number logging.PacketNumber) { +} + +func (ct *connTracer) UpdatedCongestionState(state logging.CongestionState) { +} + +func (ct *connTracer) UpdatedPTOCount(value uint32) { +} + +func (ct *connTracer) UpdatedKeyFromTLS(level logging.EncryptionLevel, perspective logging.Perspective) { +} + +func (ct *connTracer) UpdatedKey(generation logging.KeyPhase, remote bool) { +} + +func (ct *connTracer) DroppedEncryptionLevel(level logging.EncryptionLevel) { +} + +func (ct *connTracer) DroppedKey(generation logging.KeyPhase) { +} + +func (ct *connTracer) SetLossTimer(timerType logging.TimerType, level logging.EncryptionLevel, time time.Time) { +} + +func (ct *connTracer) LossTimerExpired(timerType logging.TimerType, level logging.EncryptionLevel) { +} + +func (ct *connTracer) LossTimerCanceled() { +} + +func (ct *connTracer) Close() { +} + +func (ct *connTracer) Debug(name, msg string) { } type quicLogger struct { From b2ac88537073b3578624efd80b6091abfbd59f93 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 25 May 2022 13:57:57 +0100 Subject: [PATCH 082/238] TUN-6209: Sign RPM packages This PR uses a provided key to - sign all the .rpms before they are uploaded to R2. - detach signs the repomd.xml after createrepo is run. --- release_pkgs.py | 53 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index 2754636d..7333b842 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -109,17 +109,32 @@ class PkgCreator: print(f"create deb_pkgs result => {out}, {err}") raise - # TODO https://jira.cfops.it/browse/TUN-6209 : Sign these packages. - def create_rpm_pkgs(self, artifacts_path): - self._setup_rpm_pkg_directories(artifacts_path) + def create_rpm_pkgs(self, artifacts_path, gpg_key_name): + self._setup_rpm_pkg_directories(artifacts_path, gpg_key_name) p = Popen(["createrepo", "./rpm"], stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: print(f"create rpm_pkgs result => {out}, {err}") raise + self._sign_repomd() + + def _sign_rpms(self, file_path): + p = Popen(["rpm" , "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + if p.returncode != 0: + print(f"rpm sign result result => {out}, {err}") + raise + + def _sign_repomd(self): + p = Popen(["gpg", "--batch", "--detach-sign", "--armor", "./rpm/repodata/repomd.xml"], stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + if p.returncode != 0: + print(f"sign repomd result => {out}, {err}") + raise + """ - sets up the RPM directories in the following format: + sets up and signs the RPM directories in the following format: - rpm - aarch64 - x86_64 @@ -127,7 +142,7 @@ class PkgCreator: this assumes the assets are in the format -.rpm """ - def _setup_rpm_pkg_directories(self, artifacts_path, archs=["aarch64", "x86_64", "386"]): + def _setup_rpm_pkg_directories(self, artifacts_path, gpg_key_name, archs=["aarch64", "x86_64", "386"]): for arch in archs: for root, _ , files in os.walk(artifacts_path): for file in files: @@ -137,6 +152,7 @@ class PkgCreator: old_path = os.path.join(root, file) new_path = os.path.join(new_dir, file) shutil.copyfile(old_path, new_path) + self._sign_rpms(new_path) """ imports gpg keys into the system so reprepro and createrepo can use it to sign packages. @@ -149,7 +165,23 @@ class PkgCreator: public_key = base64.b64decode(public_key) gpg.import_keys(public_key) data = gpg.list_keys(secret=True) - return (data[0]["fingerprint"]) + return (data[0]["fingerprint"], data[0]["uids"][0]) + + """ + basically rpm --import + This enables us to sign rpms. + """ + def import_rpm_key(self, public_key): + file_name = "pb.key" + with open(file_name, "wb") as f: + public_key = base64.b64decode(public_key) + f.write(public_key) + + p = Popen(["rpm", "--import", file_name], stdout=PIPE, stderr=PIPE) + out, err = p.communicate() + if p.returncode != 0: + print(f"create rpm import result => {out}, {err}") + raise """ @@ -212,9 +244,9 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", release_version, binary_name) upload_from_directories(pkg_uploader, "pool", release_version, binary_name) -def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name): +def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name, gpg_key_name): print(f"creating rpm pkgs...") - pkg_creator.create_rpm_pkgs(artifacts_path) + pkg_creator.create_rpm_pkgs(artifacts_path, gpg_key_name) print("uploading latest to r2...") upload_from_directories(pkg_uploader, "rpm", None, binary_name) @@ -282,11 +314,12 @@ if __name__ == "__main__": exit(1) pkg_creator = PkgCreator() - gpg_key_id = pkg_creator.import_gpg_keys(args.gpg_private_key, args.gpg_public_key) + (gpg_key_id, gpg_key_name) = pkg_creator.import_gpg_keys(args.gpg_private_key, args.gpg_public_key) + pkg_creator.import_rpm_key(args.gpg_public_key) pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) print(f"signing with gpg_key: {gpg_key_id}") create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs, "main", args.release_tag) - create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary ) + create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary, gpg_key_name) From 92f647d45ca3941c1a959699d113c3b206c8de8c Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 25 May 2022 14:01:53 +0100 Subject: [PATCH 083/238] TUN-6285: Upload pkg assets to repos when cloudflared is released. This effectively means that during every release going forwards, we'll have assets for the linux releases and distros that we want to support. --- cfsetup.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 1a8da793..5342f0a9 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -52,10 +52,10 @@ stretch: &stretch post-cache: # build all packages (except macos and FIPS) and move them to /cfsetup/built_artifacts - ./build-packages.sh - # publish packages to linux repos - # TODO TUN-6180: Uncomment this - make release-pkgs-linux # release the packages built and moved to /cfsetup/built_artifacts - make github-release-built-pkgs + # publish packages to linux repos + - make release-pkgs-linux # handle FIPS separately so that we built with gofips compiler github-fips-release-pkgs: build_dir: *build_dir From 7bc2462e36fe1fc67b15fb120dceebb93da7e5dd Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Tue, 24 May 2022 19:26:27 -0500 Subject: [PATCH 084/238] TUN-6282: Upgrade golang to 1.17.10, go-boring to 1.17.9 --- cfsetup.yaml | 8 ++++---- dev.Dockerfile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 5342f0a9..3f546135 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -1,5 +1,5 @@ -pinned_go: &pinned_go go=1.17.5-1 -pinned_go_fips: &pinned_go_fips go-boring=1.17.5-1 +pinned_go: &pinned_go go=1.17.10-1 +pinned_go_fips: &pinned_go_fips go-boring=1.17.9-1 build_dir: &build_dir /cfsetup_build default-flavor: buster @@ -255,8 +255,8 @@ centos-7: pre-cache: - yum install -y fakeroot - yum upgrade -y binutils-2.27-44.base.el7.x86_64 - - wget https://go.dev/dl/go1.17.5.linux-amd64.tar.gz -P /tmp/ - - tar -C /usr/local -xzf /tmp/go1.17.5.linux-amd64.tar.gz + - wget https://go.dev/dl/go1.17.10.linux-amd64.tar.gz -P /tmp/ + - tar -C /usr/local -xzf /tmp/go1.17.10.linux-amd64.tar.gz post-cache: - export PATH=$PATH:/usr/local/go/bin - export GOOS=linux diff --git a/dev.Dockerfile b/dev.Dockerfile index add922fb..64fdc790 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17.5 as builder +FROM golang:1.17.10 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 WORKDIR /go/src/github.com/cloudflare/cloudflared/ @@ -6,4 +6,4 @@ RUN apt-get update COPY . . # compile cloudflared RUN make cloudflared -RUN cp /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ \ No newline at end of file +RUN cp /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ From ec509e114a015cf191881611b3305e68bd8edd08 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 26 May 2022 10:52:39 -0700 Subject: [PATCH 085/238] TUN-6292: Debug builds for cloudflared Allow for cloudflared to be built with debug mode to be used with dlv exec --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index f82dbe8f..bfd64cf5 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,10 @@ ifneq ($(GO_BUILD_TAGS),) GO_BUILD_TAGS := -tags "$(GO_BUILD_TAGS)" endif +ifeq ($(debug), 1) + GO_BUILD_TAGS += -gcflags="all=-N -l" +endif + IMPORT_PATH := github.com/cloudflare/cloudflared PACKAGE_DIR := $(CURDIR)/packaging PREFIX := /usr From a2a4b06eb4be64d7f0bc713e4a7d6e5649e21a00 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Sat, 28 May 2022 08:23:42 +0100 Subject: [PATCH 086/238] TUN-6304: Fixed some file permission issues --- release_pkgs.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index 7333b842..b2e75f6c 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -12,6 +12,7 @@ from subprocess import Popen, PIPE import os import argparse import base64 +from pathlib import Path import logging import shutil from hashlib import sha256 @@ -50,7 +51,7 @@ class PkgUploader: config=config, ) - print(f"uploading asset: {filename} to {upload_file_path}...") + print(f"uploading asset: {filename} to {upload_file_path} in bucket{self.bucket_name}...") try: r2.upload_file(filename, self.bucket_name, upload_file_path) except ClientError as e: @@ -80,7 +81,7 @@ class PkgCreator: components, description, gpg_key_id ): - with open(file_path, "w") as distributions_file: + with open(file_path, "w+") as distributions_file: for release in releases: distributions_file.write(f"Origin: {origin}\n") distributions_file.write(f"Label: {label}\n") @@ -219,6 +220,7 @@ def upload_from_directories(pkg_uploader, directory, release, binary): def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, release_version): # set configuration for package creation. print(f"initialising configuration for {binary_name} , {archs}") + Path("./conf").mkdir(parents=True, exist_ok=True) pkg_creator.create_distribution_conf( "./conf/distributions", binary_name, @@ -262,7 +264,7 @@ def parse_args(): ) parser.add_argument( - "--bucket", default=os.environ.get("R2_BUCKET_NAME"), help="R2 Bucket name" + "--bucket", default=os.environ.get("R2_BUCKET"), help="R2 Bucket name" ) parser.add_argument( "--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID" From 08a8101308a4d32cf53e13940afeea49b49fbc42 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 30 May 2022 09:03:01 +0100 Subject: [PATCH 087/238] Release 2022.5.2 --- RELEASE_NOTES | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ac7fff64..2ae59de5 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,17 @@ +2022.5.2 +- 2022-05-23 TUN-6270: Import gpg keys from environment variables +- 2022-05-24 TUN-6209: Improve feedback process if release_pkgs to deb and rpm fail +- 2022-05-24 TUN-6280: Don't wrap qlog connection tracer for gatethering QUIC metrics since we're not writing qlog files. +- 2022-05-25 TUN-6209: Sign RPM packages +- 2022-05-25 TUN-6285: Upload pkg assets to repos when cloudflared is released. +- 2022-05-24 TUN-6282: Upgrade golang to 1.17.10, go-boring to 1.17.9 +- 2022-05-26 TUN-6292: Debug builds for cloudflared +- 2022-05-28 TUN-6304: Fixed some file permission issues +- 2022-05-11 TUN-6197: Publish to brew core should not try to open the browser +- 2022-05-12 TUN-5943: Add RPM support +- 2022-05-18 TUN-6248: Fix panic in cloudflared during tracing when origin doesn't provide header map +- 2022-05-18 TUN-6250: Add upstream response status code to tracing span attributes + 2022.5.1 - 2022-05-06 TUN-6146: Release_pkgs is now a generic command line script - 2022-05-06 TUN-6185: Fix tcpOverWSOriginService not using original scheme for String representation From baed5f4eeae487a187fa98b0f859a589a5c1a3d7 Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 30 May 2022 13:38:15 +0100 Subject: [PATCH 088/238] TUN-6308: Add debug logs to see if packets are sent/received from edge --- connection/quic.go | 4 ++-- ingress/origin_udp_proxy.go | 11 ++++++++--- quic/datagram.go | 19 +++++++++++-------- quic/datagram_test.go | 7 +++++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 2c04edf3..4e2f4681 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -58,7 +58,7 @@ func NewQUICConnection( return nil, fmt.Errorf("failed to dial to edge: %w", err) } - datagramMuxer, err := quicpogs.NewDatagramMuxer(session) + datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) if err != nil { return nil, err } @@ -217,7 +217,7 @@ func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid. go q.serveUDPSession(session, closeAfterIdleHint) - q.logger.Debug().Msgf("Registered session %v, %v, %v", sessionID, dstIP, dstPort) + q.logger.Debug().Str("sessionID", sessionID.String()).Str("src", originProxy.LocalAddr().String()).Str("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)).Msgf("Registered session") return nil } diff --git a/ingress/origin_udp_proxy.go b/ingress/origin_udp_proxy.go index 3f2b86c7..836489be 100644 --- a/ingress/origin_udp_proxy.go +++ b/ingress/origin_udp_proxy.go @@ -6,11 +6,16 @@ import ( "net" ) -type UDPProxy struct { +type UDPProxy interface { io.ReadWriteCloser + LocalAddr() net.Addr } -func DialUDP(dstIP net.IP, dstPort uint16) (*UDPProxy, error) { +type udpProxy struct { + *net.UDPConn +} + +func DialUDP(dstIP net.IP, dstPort uint16) (UDPProxy, error) { dstAddr := &net.UDPAddr{ IP: dstIP, Port: int(dstPort), @@ -23,5 +28,5 @@ func DialUDP(dstIP net.IP, dstPort uint16) (*UDPProxy, error) { return nil, fmt.Errorf("unable to create UDP proxy to origin (%v:%v): %w", dstIP, dstPort, err) } - return &UDPProxy{udpConn}, nil + return &udpProxy{udpConn}, nil } diff --git a/quic/datagram.go b/quic/datagram.go index 6b3e2bfd..e9b305af 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" + "github.com/rs/zerolog" ) const ( @@ -13,18 +14,14 @@ const ( ) type DatagramMuxer struct { - ID uuid.UUID session quic.Session + logger *zerolog.Logger } -func NewDatagramMuxer(quicSession quic.Session) (*DatagramMuxer, error) { - muxerID, err := uuid.NewRandom() - if err != nil { - return nil, err - } +func NewDatagramMuxer(quicSession quic.Session, logger *zerolog.Logger) (*DatagramMuxer, error) { return &DatagramMuxer{ - ID: muxerID, session: quicSession, + logger: logger, }, nil } @@ -42,6 +39,7 @@ func (dm *DatagramMuxer) SendTo(sessionID uuid.UUID, payload []byte) error { if err := dm.session.SendMessage(msgWithID); err != nil { return errors.Wrap(err, "Failed to send datagram back to edge") } + dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Send datagram back to edge") return nil } @@ -53,7 +51,12 @@ func (dm *DatagramMuxer) ReceiveFrom() (uuid.UUID, []byte, error) { if err != nil { return uuid.Nil, nil, err } - return extractSessionID(msg) + sessionID, payload, err := extractSessionID(msg) + if err != nil { + return uuid.Nil, nil, err + } + dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Received datagram from edge") + return sessionID, payload, nil } // Maximum application payload to send to / receive from QUIC datagram frame diff --git a/quic/datagram_test.go b/quic/datagram_test.go index b8745fa5..3f3eef1a 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -14,6 +14,7 @@ import ( "github.com/google/uuid" "github.com/lucas-clemente/quic-go" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -71,7 +72,8 @@ func TestMaxDatagramPayload(t *testing.T) { return err } - muxer, err := NewDatagramMuxer(quicSession) + logger := zerolog.Nop() + muxer, err := NewDatagramMuxer(quicSession, &logger) if err != nil { return err } @@ -96,7 +98,8 @@ func TestMaxDatagramPayload(t *testing.T) { quicSession, err := quic.DialAddrEarly(quicListener.Addr().String(), tlsClientConfig, quicConfig) require.NoError(t, err) - muxer, err := NewDatagramMuxer(quicSession) + logger := zerolog.Nop() + muxer, err := NewDatagramMuxer(quicSession, &logger) if err != nil { return err } From b8ba5b444c06f373714ecef20492976f5fdc18f6 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 30 May 2022 13:42:56 +0100 Subject: [PATCH 089/238] TUN-6301: Allow to update logger used by UDP session manager --- datagramsession/manager.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/datagramsession/manager.go b/datagramsession/manager.go index a691e3f9..54c5c24d 100644 --- a/datagramsession/manager.go +++ b/datagramsession/manager.go @@ -29,6 +29,8 @@ type Manager interface { RegisterSession(ctx context.Context, sessionID uuid.UUID, dstConn io.ReadWriteCloser) (*Session, error) // UnregisterSession stops tracking the session and terminates it UnregisterSession(ctx context.Context, sessionID uuid.UUID, message string, byRemote bool) error + // UpdateLogger updates the logger used by the Manager + UpdateLogger(log *zerolog.Logger) } type manager struct { @@ -57,6 +59,11 @@ func NewManager(transport transport, log *zerolog.Logger) *manager { } } +func (m *manager) UpdateLogger(log *zerolog.Logger) { + // Benign data race, no problem if the old pointer is read or not concurrently. + m.log = log +} + func (m *manager) Serve(ctx context.Context) error { errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { From 919227fc91ba0be49a14111ffbda6f012fd2f8a2 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 30 May 2022 14:55:27 +0100 Subject: [PATCH 090/238] Release 2022.5.3 --- RELEASE_NOTES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 2ae59de5..4610c3d2 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,7 @@ +2022.5.3 +- 2022-05-30 TUN-6308: Add debug logs to see if packets are sent/received from edge +- 2022-05-30 TUN-6301: Allow to update logger used by UDP session manager + 2022.5.2 - 2022-05-23 TUN-6270: Import gpg keys from environment variables - 2022-05-24 TUN-6209: Improve feedback process if release_pkgs to deb and rpm fail From 5e6f606f4e7714ed912e692b0966e365d162bf93 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 30 May 2022 16:19:51 +0100 Subject: [PATCH 091/238] TUN-6293: Update yaml v3 to latest hotfix This addresses https://security.snyk.io/vuln/SNYK-GOLANG-GOPKGINYAMLV3-2841557 by updating yaml v3 to latest version. It also stops using yaml v2 directly (we were using both v2 and v3 mixed). We still rely on yaml v2 indirectly, via urfave cli, though. Note that the security vulnerability does not affect v2. --- cmd/cloudflared/tunnel/subcommands.go | 2 +- config/configuration_test.go | 22 +- config/manager.go | 2 +- go.mod | 5 +- go.sum | 4 +- ingress/config_test.go | 2 +- ingress/ingress_test.go | 2 +- .../github.com/francoispqt/gojay/.gitignore | 5 - .../github.com/francoispqt/gojay/.travis.yml | 15 - .../github.com/francoispqt/gojay/Gopkg.lock | 163 -- .../github.com/francoispqt/gojay/Gopkg.toml | 23 - vendor/github.com/francoispqt/gojay/LICENSE | 21 - vendor/github.com/francoispqt/gojay/Makefile | 11 - vendor/github.com/francoispqt/gojay/README.md | 855 ----------- vendor/github.com/francoispqt/gojay/decode.go | 386 ----- .../francoispqt/gojay/decode_array.go | 247 --- .../francoispqt/gojay/decode_bool.go | 241 --- .../francoispqt/gojay/decode_embedded_json.go | 85 -- .../francoispqt/gojay/decode_interface.go | 130 -- .../francoispqt/gojay/decode_number.go | 118 -- .../francoispqt/gojay/decode_number_float.go | 516 ------- .../francoispqt/gojay/decode_number_int.go | 1338 ----------------- .../francoispqt/gojay/decode_number_uint.go | 715 --------- .../francoispqt/gojay/decode_object.go | 407 ----- .../francoispqt/gojay/decode_pool.go | 64 - .../francoispqt/gojay/decode_slice.go | 89 -- .../francoispqt/gojay/decode_sqlnull.go | 157 -- .../francoispqt/gojay/decode_stream.go | 115 -- .../francoispqt/gojay/decode_stream_pool.go | 59 - .../francoispqt/gojay/decode_string.go | 260 ---- .../gojay/decode_string_unicode.go | 98 -- .../francoispqt/gojay/decode_time.go | 53 - .../francoispqt/gojay/decode_unsafe.go | 120 -- vendor/github.com/francoispqt/gojay/encode.go | 202 --- .../francoispqt/gojay/encode_array.go | 212 --- .../francoispqt/gojay/encode_bool.go | 164 -- .../francoispqt/gojay/encode_builder.go | 65 - .../francoispqt/gojay/encode_embedded_json.go | 93 -- .../francoispqt/gojay/encode_interface.go | 173 --- .../francoispqt/gojay/encode_null.go | 39 - .../francoispqt/gojay/encode_number.go | 1 - .../francoispqt/gojay/encode_number_float.go | 368 ----- .../francoispqt/gojay/encode_number_int.go | 500 ------ .../francoispqt/gojay/encode_number_uint.go | 362 ----- .../francoispqt/gojay/encode_object.go | 400 ----- .../francoispqt/gojay/encode_pool.go | 50 - .../francoispqt/gojay/encode_slice.go | 113 -- .../francoispqt/gojay/encode_sqlnull.go | 377 ----- .../francoispqt/gojay/encode_stream.go | 205 --- .../francoispqt/gojay/encode_stream_pool.go | 38 - .../francoispqt/gojay/encode_string.go | 186 --- .../francoispqt/gojay/encode_time.go | 68 - vendor/github.com/francoispqt/gojay/errors.go | 88 -- vendor/github.com/francoispqt/gojay/gojay.go | 10 - vendor/github.com/francoispqt/gojay/gojay.png | Bin 44163 -> 0 bytes .../lucas-clemente/quic-go/qlog/event.go | 529 ------- .../lucas-clemente/quic-go/qlog/frame.go | 227 --- .../quic-go/qlog/packet_header.go | 119 -- .../lucas-clemente/quic-go/qlog/qlog.go | 486 ------ .../lucas-clemente/quic-go/qlog/trace.go | 66 - .../lucas-clemente/quic-go/qlog/types.go | 320 ---- vendor/gopkg.in/yaml.v3/decode.go | 78 +- vendor/gopkg.in/yaml.v3/parserc.go | 11 +- vendor/modules.txt | 6 +- 64 files changed, 93 insertions(+), 11793 deletions(-) delete mode 100644 vendor/github.com/francoispqt/gojay/.gitignore delete mode 100644 vendor/github.com/francoispqt/gojay/.travis.yml delete mode 100644 vendor/github.com/francoispqt/gojay/Gopkg.lock delete mode 100644 vendor/github.com/francoispqt/gojay/Gopkg.toml delete mode 100644 vendor/github.com/francoispqt/gojay/LICENSE delete mode 100644 vendor/github.com/francoispqt/gojay/Makefile delete mode 100644 vendor/github.com/francoispqt/gojay/README.md delete mode 100644 vendor/github.com/francoispqt/gojay/decode.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_array.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_bool.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_embedded_json.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_interface.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_number.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_number_float.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_number_int.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_number_uint.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_object.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_pool.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_slice.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_sqlnull.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_stream.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_stream_pool.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_string.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_string_unicode.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_time.go delete mode 100644 vendor/github.com/francoispqt/gojay/decode_unsafe.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_array.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_bool.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_builder.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_embedded_json.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_interface.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_null.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_number.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_number_float.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_number_int.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_number_uint.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_object.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_pool.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_slice.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_sqlnull.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_stream.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_stream_pool.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_string.go delete mode 100644 vendor/github.com/francoispqt/gojay/encode_time.go delete mode 100644 vendor/github.com/francoispqt/gojay/errors.go delete mode 100644 vendor/github.com/francoispqt/gojay/gojay.go delete mode 100644 vendor/github.com/francoispqt/gojay/gojay.png delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/event.go delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/frame.go delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/packet_header.go delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/qlog.go delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/trace.go delete mode 100644 vendor/github.com/lucas-clemente/quic-go/qlog/types.go diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index e9825654..d3549856 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -20,7 +20,7 @@ import ( "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" "golang.org/x/net/idna" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" "github.com/cloudflare/cloudflared/cfapi" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" diff --git a/config/configuration_test.go b/config/configuration_test.go index d870913d..dff785c8 100644 --- a/config/configuration_test.go +++ b/config/configuration_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" ) func TestConfigFileSettings(t *testing.T) { @@ -111,7 +111,7 @@ counters: } -var rawConfig = []byte(` +var rawJsonConfig = []byte(` { "connectTimeout": 10, "tlsTimeout": 30, @@ -148,15 +148,14 @@ func TestMarshalUnmarshalOriginRequest(t *testing.T) { name string marshalFunc func(in interface{}) (out []byte, err error) unMarshalFunc func(in []byte, out interface{}) (err error) - baseUnit time.Duration }{ - {"json", json.Marshal, json.Unmarshal, time.Second}, - {"yaml", yaml.Marshal, yaml.Unmarshal, time.Nanosecond}, + {"json", json.Marshal, json.Unmarshal}, + {"yaml", yaml.Marshal, yaml.Unmarshal}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - assertConfig(t, tc.marshalFunc, tc.unMarshalFunc, tc.baseUnit) + assertConfig(t, tc.marshalFunc, tc.unMarshalFunc) }) } } @@ -165,18 +164,17 @@ func assertConfig( t *testing.T, marshalFunc func(in interface{}) (out []byte, err error), unMarshalFunc func(in []byte, out interface{}) (err error), - baseUnit time.Duration, ) { var config OriginRequestConfig var config2 OriginRequestConfig - assert.NoError(t, unMarshalFunc(rawConfig, &config)) + assert.NoError(t, json.Unmarshal(rawJsonConfig, &config)) - assert.Equal(t, baseUnit*10, config.ConnectTimeout.Duration) - assert.Equal(t, baseUnit*30, config.TLSTimeout.Duration) - assert.Equal(t, baseUnit*30, config.TCPKeepAlive.Duration) + assert.Equal(t, time.Second*10, config.ConnectTimeout.Duration) + assert.Equal(t, time.Second*30, config.TLSTimeout.Duration) + assert.Equal(t, time.Second*30, config.TCPKeepAlive.Duration) assert.Equal(t, true, *config.NoHappyEyeballs) - assert.Equal(t, baseUnit*60, config.KeepAliveTimeout.Duration) + assert.Equal(t, time.Second*60, config.KeepAliveTimeout.Duration) assert.Equal(t, 10, *config.KeepAliveConnections) assert.Equal(t, "app.tunnel.com", *config.HTTPHostHeader) assert.Equal(t, "app.tunnel.com", *config.OriginServerName) diff --git a/config/manager.go b/config/manager.go index ec6f5630..e5002c30 100644 --- a/config/manager.go +++ b/config/manager.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" "github.com/cloudflare/cloudflared/watcher" ) diff --git a/go.mod b/go.mod index e0d85eed..93e4945f 100644 --- a/go.mod +++ b/go.mod @@ -44,8 +44,7 @@ require ( gopkg.in/coreos/go-oidc.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.6.0 - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 zombiezen.com/go/capnproto2 v2.18.0+incompatible ) @@ -64,7 +63,6 @@ require ( github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -102,6 +100,7 @@ require ( google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.45.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d diff --git a/go.sum b/go.sum index b43b527d..c0a1e0d5 100644 --- a/go.sum +++ b/go.sum @@ -184,7 +184,6 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -1110,8 +1109,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/ingress/config_test.go b/ingress/config_test.go index 55229137..93c971fc 100644 --- a/ingress/config_test.go +++ b/ingress/config_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/ipaccess" diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 7ca8965a..1ff5e11c 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/ipaccess" diff --git a/vendor/github.com/francoispqt/gojay/.gitignore b/vendor/github.com/francoispqt/gojay/.gitignore deleted file mode 100644 index 43ebdc4b..00000000 --- a/vendor/github.com/francoispqt/gojay/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -vendor -*.out -*.log -*.test -.vscode diff --git a/vendor/github.com/francoispqt/gojay/.travis.yml b/vendor/github.com/francoispqt/gojay/.travis.yml deleted file mode 100644 index df04aa24..00000000 --- a/vendor/github.com/francoispqt/gojay/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - "1.10.x" - - "1.11.x" - - "1.12.x" - -script: - - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify - - dep ensure -v -vendor-only - - go test ./gojay/codegen/test/... -race - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/francoispqt/gojay/Gopkg.lock b/vendor/github.com/francoispqt/gojay/Gopkg.lock deleted file mode 100644 index d642e9a7..00000000 --- a/vendor/github.com/francoispqt/gojay/Gopkg.lock +++ /dev/null @@ -1,163 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:1a37f9f2ae10d161d9688fb6008ffa14e1631e5068cc3e9698008b9e8d40d575" - name = "cloud.google.com/go" - packages = ["compute/metadata"] - pruneopts = "" - revision = "457ea5c15ccf3b87db582c450e80101989da35f7" - version = "v0.40.0" - -[[projects]] - digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" - name = "github.com/go-errors/errors" - packages = ["."] - pruneopts = "" - revision = "a6af135bd4e28680facf08a3d206b454abc877a4" - version = "v1.0.1" - -[[projects]] - digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d" - name = "github.com/golang/protobuf" - packages = ["proto"] - pruneopts = "" - revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" - version = "v1.3.1" - -[[projects]] - branch = "master" - digest = "1:cae59d7b8243c671c9f544965522ba35c0fec48ee80adb9f1400cd2f33abbbec" - name = "github.com/mailru/easyjson" - packages = [ - ".", - "buffer", - "jlexer", - "jwriter", - ] - pruneopts = "" - revision = "1ea4449da9834f4d333f1cc461c374aea217d249" - -[[projects]] - digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" - -[[projects]] - digest = "1:8d4bbd8ab012efc77ab6b97286f2aff262bcdeac9803bb57d75cf7d0a5e6a877" - name = "github.com/viant/assertly" - packages = ["."] - pruneopts = "" - revision = "04f45e0aeb6f3455884877b047a97bcc95dc9493" - version = "v0.4.8" - -[[projects]] - digest = "1:5913451bc2d274673c0716efe226a137625740cd9380641f4d8300ff4f2d82a0" - name = "github.com/viant/toolbox" - packages = [ - ".", - "cred", - "data", - "storage", - "url", - ] - pruneopts = "" - revision = "1be8e4d172138324f40d55ea61a2aeab0c5ce864" - version = "v0.24.0" - -[[projects]] - branch = "master" - digest = "1:9d150270ca2c3356f2224a0878daa1652e4d0b25b345f18b4f6e156cc4b8ec5e" - name = "golang.org/x/crypto" - packages = [ - "blowfish", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "internal/chacha20", - "internal/subtle", - "poly1305", - "ssh", - ] - pruneopts = "" - revision = "f99c8df09eb5bff426315721bfa5f16a99cad32c" - -[[projects]] - branch = "master" - digest = "1:5a56f211e7c12a65c5585c629457a2fb91d8719844ee8fab92727ea8adb5721c" - name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "websocket", - ] - pruneopts = "" - revision = "461777fb6f67e8cb9d70cda16573678d085a74cf" - -[[projects]] - branch = "master" - digest = "1:01bdbbc604dcd5afb6f66a717f69ad45e9643c72d5bc11678d44ffa5c50f9e42" - name = "golang.org/x/oauth2" - packages = [ - ".", - "google", - "internal", - "jws", - "jwt", - ] - pruneopts = "" - revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33" - -[[projects]] - branch = "master" - digest = "1:8ddb956f67d4c176abbbc42b7514aaeaf9ea30daa24e27d2cf30ad82f9334a2c" - name = "golang.org/x/sys" - packages = ["cpu"] - pruneopts = "" - revision = "1e42afee0f762ed3d76e6dd942e4181855fd1849" - -[[projects]] - digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a" - name = "google.golang.org/appengine" - packages = [ - ".", - "internal", - "internal/app_identity", - "internal/base", - "internal/datastore", - "internal/log", - "internal/modules", - "internal/remote_api", - "internal/urlfetch", - "urlfetch", - ] - pruneopts = "" - revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880" - version = "v1.6.1" - -[[projects]] - digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/go-errors/errors", - "github.com/mailru/easyjson", - "github.com/mailru/easyjson/jlexer", - "github.com/mailru/easyjson/jwriter", - "github.com/viant/assertly", - "github.com/viant/toolbox", - "github.com/viant/toolbox/url", - "golang.org/x/net/websocket", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/francoispqt/gojay/Gopkg.toml b/vendor/github.com/francoispqt/gojay/Gopkg.toml deleted file mode 100644 index fa607923..00000000 --- a/vendor/github.com/francoispqt/gojay/Gopkg.toml +++ /dev/null @@ -1,23 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -ignored = ["github.com/francoispqt/benchmarks*","github.com/stretchr/testify*","github.com/stretchr/testify","github.com/json-iterator/go","github.com/buger/jsonparser"] diff --git a/vendor/github.com/francoispqt/gojay/LICENSE b/vendor/github.com/francoispqt/gojay/LICENSE deleted file mode 100644 index df215964..00000000 --- a/vendor/github.com/francoispqt/gojay/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 gojay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/francoispqt/gojay/Makefile b/vendor/github.com/francoispqt/gojay/Makefile deleted file mode 100644 index ce957239..00000000 --- a/vendor/github.com/francoispqt/gojay/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: test -test: - go test -race -run=^Test -v - -.PHONY: cover -cover: - go test -coverprofile=coverage.out -covermode=atomic - -.PHONY: coverhtml -coverhtml: - go tool cover -html=coverage.out \ No newline at end of file diff --git a/vendor/github.com/francoispqt/gojay/README.md b/vendor/github.com/francoispqt/gojay/README.md deleted file mode 100644 index b2abd291..00000000 --- a/vendor/github.com/francoispqt/gojay/README.md +++ /dev/null @@ -1,855 +0,0 @@ -[![Build Status](https://travis-ci.org/francoispqt/gojay.svg?branch=master)](https://travis-ci.org/francoispqt/gojay) -[![codecov](https://codecov.io/gh/francoispqt/gojay/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/gojay) -[![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/gojay)](https://goreportcard.com/report/github.com/francoispqt/gojay) -[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square -)](https://godoc.org/github.com/francoispqt/gojay) -![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square) -[![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay) -![stability-stable](https://img.shields.io/badge/stability-stable-green.svg) - -# GoJay - - - -GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)). - -It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices. - -Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API. - -There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster. - -# Why another JSON parser? - -I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end. - -Also, I wanted to build a decoder that could consume an io.Reader of line or comma delimited JSON, in a JIT way. To consume a flow of JSON objects from a TCP connection for example or from a standard output. Same way I wanted to build an encoder that could encode a flow of data to a io.Writer. - -This is how GoJay aims to be a very fast, JIT stream parser with 0 reflection, low allocation with a friendly API. - -# Get started - -```bash -go get github.com/francoispqt/gojay -``` - -* [Encoder](#encoding) -* [Decoder](#decoding) -* [Stream API](#stream-api) -* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay) - -## Decoding - -Decoding is done through two different API similar to standard `encoding/json`: -* [Unmarshal](#unmarshal-api) -* [Decode](#decode-api) - - -Example of basic stucture decoding with Unmarshal: -```go -import "github.com/francoispqt/gojay" - -type user struct { - id int - name string - email string -} -// implement gojay.UnmarshalerJSONObject -func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { - switch key { - case "id": - return dec.Int(&u.id) - case "name": - return dec.String(&u.name) - case "email": - return dec.String(&u.email) - } - return nil -} -func (u *user) NKeys() int { - return 3 -} - -func main() { - u := &user{} - d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`) - err := gojay.UnmarshalJSONObject(d, u) - if err != nil { - log.Fatal(err) - } -} -``` - -with Decode: -```go -func main() { - u := &user{} - dec := gojay.NewDecoder(bytes.NewReader([]byte(`{"id":1,"name":"gojay","email":"gojay@email.com"}`))) - err := dec.DecodeObject(d, u) - if err != nil { - log.Fatal(err) - } -} -``` - -### Unmarshal API - -Unmarshal API decodes a `[]byte` to a given pointer with a single function. - -Behind the doors, Unmarshal API borrows a `*gojay.Decoder` resets its settings and decodes the data to the given pointer and releases the `*gojay.Decoder` to the pool when it finishes, whether it encounters an error or not. - -If it cannot find the right Decoding strategy for the type of the given pointer, it returns an `InvalidUnmarshalError`. You can test the error returned by doing `if ok := err.(InvalidUnmarshalError); ok {}`. - -Unmarshal API comes with three functions: -* Unmarshal -```go -func Unmarshal(data []byte, v interface{}) error -``` - -* UnmarshalJSONObject -```go -func UnmarshalJSONObject(data []byte, v gojay.UnmarshalerJSONObject) error -``` - -* UnmarshalJSONArray -```go -func UnmarshalJSONArray(data []byte, v gojay.UnmarshalerJSONArray) error -``` - - -### Decode API - -Decode API decodes a `[]byte` to a given pointer by creating or borrowing a `*gojay.Decoder` with an `io.Reader` and calling `Decode` methods. - -__Getting a *gojay.Decoder or Borrowing__ - -You can either get a fresh `*gojay.Decoder` calling `dec := gojay.NewDecoder(io.Reader)` or borrow one from the pool by calling `dec := gojay.BorrowDecoder(io.Reader)`. - -After using a decoder, you can release it by calling `dec.Release()`. Beware, if you reuse the decoder after releasing it, it will panic with an error of type `InvalidUsagePooledDecoderError`. If you want to fully benefit from the pooling, you must release your decoders after using. - -Example getting a fresh an releasing: -```go -str := "" -dec := gojay.NewDecoder(strings.NewReader(`"test"`)) -defer dec.Release() -if err := dec.Decode(&str); err != nil { - log.Fatal(err) -} -``` -Example borrowing a decoder and releasing: -```go -str := "" -dec := gojay.BorrowDecoder(strings.NewReader(`"test"`)) -defer dec.Release() -if err := dec.Decode(&str); err != nil { - log.Fatal(err) -} -``` - -`*gojay.Decoder` has multiple methods to decode to specific types: -* Decode -```go -func (dec *gojay.Decoder) Decode(v interface{}) error -``` -* DecodeObject -```go -func (dec *gojay.Decoder) DecodeObject(v gojay.UnmarshalerJSONObject) error -``` -* DecodeArray -```go -func (dec *gojay.Decoder) DecodeArray(v gojay.UnmarshalerJSONArray) error -``` -* DecodeInt -```go -func (dec *gojay.Decoder) DecodeInt(v *int) error -``` -* DecodeBool -```go -func (dec *gojay.Decoder) DecodeBool(v *bool) error -``` -* DecodeString -```go -func (dec *gojay.Decoder) DecodeString(v *string) error -``` - -All DecodeXxx methods are used to decode top level JSON values. If you are decoding keys or items of a JSON object or array, don't use the Decode methods. - -Example: -```go -reader := strings.NewReader(`"John Doe"`) -dec := NewDecoder(reader) - -var str string -err := dec.DecodeString(&str) -if err != nil { - log.Fatal(err) -} - -fmt.Println(str) // John Doe -``` - -### Structs and Maps -#### UnmarshalerJSONObject Interface - -To unmarshal a JSON object to a structure, the structure must implement the `UnmarshalerJSONObject` interface: -```go -type UnmarshalerJSONObject interface { - UnmarshalJSONObject(*gojay.Decoder, string) error - NKeys() int -} -``` -`UnmarshalJSONObject` method takes two arguments, the first one is a pointer to the Decoder (*gojay.Decoder) and the second one is the string value of the current key being parsed. If the JSON data is not an object, the UnmarshalJSONObject method will never be called. - -`NKeys` method must return the number of keys to Unmarshal in the JSON object or 0. If zero is returned, all keys will be parsed. - -Example of implementation for a struct: -```go -type user struct { - id int - name string - email string -} -// implement UnmarshalerJSONObject -func (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { - switch key { - case "id": - return dec.Int(&u.id) - case "name": - return dec.String(&u.name) - case "email": - return dec.String(&u.email) - } - return nil -} -func (u *user) NKeys() int { - return 3 -} -``` - -Example of implementation for a `map[string]string`: -```go -// define our custom map type implementing UnmarshalerJSONObject -type message map[string]string - -// Implementing Unmarshaler -func (m message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - str := "" - err := dec.String(&str) - if err != nil { - return err - } - m[k] = str - return nil -} - -// we return 0, it tells the Decoder to decode all keys -func (m message) NKeys() int { - return 0 -} -``` - -### Arrays, Slices and Channels - -To unmarshal a JSON object to a slice an array or a channel, it must implement the UnmarshalerJSONArray interface: -```go -type UnmarshalerJSONArray interface { - UnmarshalJSONArray(*gojay.Decoder) error -} -``` -UnmarshalJSONArray method takes one argument, a pointer to the Decoder (*gojay.Decoder). If the JSON data is not an array, the Unmarshal method will never be called. - -Example of implementation with a slice: -```go -type testSlice []string -// implement UnmarshalerJSONArray -func (t *testSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - str := "" - if err := dec.String(&str); err != nil { - return err - } - *t = append(*t, str) - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) - var slice testSlice - err := dec.DecodeArray(&slice) - if err != nil { - log.Fatal(err) - } - fmt.Println(slice) // [Tom Jim] - dec.Release() -} -``` - -Example of implementation with a channel: -```go -type testChannel chan string -// implement UnmarshalerJSONArray -func (c testChannel) UnmarshalJSONArray(dec *gojay.Decoder) error { - str := "" - if err := dec.String(&str); err != nil { - return err - } - c <- str - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim"]`)) - c := make(testChannel, 2) - err := dec.DecodeArray(c) - if err != nil { - log.Fatal(err) - } - for i := 0; i < 2; i++ { - fmt.Println(<-c) - } - close(c) - dec.Release() -} -``` - -Example of implementation with an array: -```go -type testArray [3]string -// implement UnmarshalerJSONArray -func (a *testArray) UnmarshalJSONArray(dec *Decoder) error { - var str string - if err := dec.String(&str); err != nil { - return err - } - a[dec.Index()] = str - return nil -} - -func main() { - dec := gojay.BorrowDecoder(strings.NewReader(`["Tom", "Jim", "Bob"]`)) - var a testArray - err := dec.DecodeArray(&a) - fmt.Println(a) // [Tom Jim Bob] - dec.Release() -} -``` - -### Other types -To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface. - -Example of encoding strings: -```go -func main() { - json := []byte(`"Jay"`) - var v string - err := gojay.Unmarshal(json, &v) - if err != nil { - log.Fatal(err) - } - fmt.Println(v) // Jay -} -``` - -### Decode values methods -When decoding a JSON object of a JSON array using `UnmarshalerJSONObject` or `UnmarshalerJSONArray` interface, the `gojay.Decoder` provides dozens of methods to Decode multiple types. - -Non exhaustive list of methods available (to see all methods, check the godoc): -```go -dec.Int -dec.Int8 -dec.Int16 -dec.Int32 -dec.Int64 -dec.Uint8 -dec.Uint16 -dec.Uint32 -dec.Uint64 -dec.String -dec.Time -dec.Bool -dec.SQLNullString -dec.SQLNullInt64 -``` - - -## Encoding - -Encoding is done through two different API similar to standard `encoding/json`: -* [Marshal](#marshal-api) -* [Encode](#encode-api) - -Example of basic structure encoding with Marshal: -```go -import "github.com/francoispqt/gojay" - -type user struct { - id int - name string - email string -} - -// implement MarshalerJSONObject -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} - -func main() { - u := &user{1, "gojay", "gojay@email.com"} - b, err := gojay.MarshalJSONObject(u) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(b)) // {"id":1,"name":"gojay","email":"gojay@email.com"} -} -``` - -with Encode: -```go -func main() { - u := &user{1, "gojay", "gojay@email.com"} - b := strings.Builder{} - enc := gojay.NewEncoder(&b) - if err := enc.Encode(u); err != nil { - log.Fatal(err) - } - fmt.Println(b.String()) // {"id":1,"name":"gojay","email":"gojay@email.com"} -} -``` - -### Marshal API - -Marshal API encodes a value to a JSON `[]byte` with a single function. - -Behind the doors, Marshal API borrows a `*gojay.Encoder` resets its settings and encodes the data to an internal byte buffer and releases the `*gojay.Encoder` to the pool when it finishes, whether it encounters an error or not. - -If it cannot find the right Encoding strategy for the type of the given value, it returns an `InvalidMarshalError`. You can test the error returned by doing `if ok := err.(InvalidMarshalError); ok {}`. - -Marshal API comes with three functions: -* Marshal -```go -func Marshal(v interface{}) ([]byte, error) -``` - -* MarshalJSONObject -```go -func MarshalJSONObject(v gojay.MarshalerJSONObject) ([]byte, error) -``` - -* MarshalJSONArray -```go -func MarshalJSONArray(v gojay.MarshalerJSONArray) ([]byte, error) -``` - -### Encode API - -Encode API decodes a value to JSON by creating or borrowing a `*gojay.Encoder` sending it to an `io.Writer` and calling `Encode` methods. - -__Getting a *gojay.Encoder or Borrowing__ - -You can either get a fresh `*gojay.Encoder` calling `enc := gojay.NewEncoder(io.Writer)` or borrow one from the pool by calling `enc := gojay.BorrowEncoder(io.Writer)`. - -After using an encoder, you can release it by calling `enc.Release()`. Beware, if you reuse the encoder after releasing it, it will panic with an error of type `InvalidUsagePooledEncoderError`. If you want to fully benefit from the pooling, you must release your encoders after using. - -Example getting a fresh encoder an releasing: -```go -str := "test" -b := strings.Builder{} -enc := gojay.NewEncoder(&b) -defer enc.Release() -if err := enc.Encode(str); err != nil { - log.Fatal(err) -} -``` -Example borrowing an encoder and releasing: -```go -str := "test" -b := strings.Builder{} -enc := gojay.BorrowEncoder(b) -defer enc.Release() -if err := enc.Encode(str); err != nil { - log.Fatal(err) -} -``` - -`*gojay.Encoder` has multiple methods to encoder specific types to JSON: -* Encode -```go -func (enc *gojay.Encoder) Encode(v interface{}) error -``` -* EncodeObject -```go -func (enc *gojay.Encoder) EncodeObject(v gojay.MarshalerJSONObject) error -``` -* EncodeArray -```go -func (enc *gojay.Encoder) EncodeArray(v gojay.MarshalerJSONArray) error -``` -* EncodeInt -```go -func (enc *gojay.Encoder) EncodeInt(n int) error -``` -* EncodeInt64 -```go -func (enc *gojay.Encoder) EncodeInt64(n int64) error -``` -* EncodeFloat -```go -func (enc *gojay.Encoder) EncodeFloat(n float64) error -``` -* EncodeBool -```go -func (enc *gojay.Encoder) EncodeBool(v bool) error -``` -* EncodeString -```go -func (enc *gojay.Encoder) EncodeString(s string) error -``` - -### Structs and Maps - -To encode a structure, the structure must implement the MarshalerJSONObject interface: -```go -type MarshalerJSONObject interface { - MarshalJSONObject(enc *gojay.Encoder) - IsNil() bool -} -``` -`MarshalJSONObject` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all the keys in the JSON Object by calling Decoder's methods. - -IsNil method returns a boolean indicating if the interface underlying value is nil or not. It is used to safely ensure that the underlying value is not nil without using Reflection. - -Example of implementation for a struct: -```go -type user struct { - id int - name string - email string -} - -// implement MarshalerJSONObject -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} -``` - -Example of implementation for a `map[string]string`: -```go -// define our custom map type implementing MarshalerJSONObject -type message map[string]string - -// Implementing Marshaler -func (m message) MarshalJSONObject(enc *gojay.Encoder) { - for k, v := range m { - enc.StringKey(k, v) - } -} - -func (m message) IsNil() bool { - return m == nil -} -``` - -### Arrays and Slices -To encode an array or a slice, the slice/array must implement the MarshalerJSONArray interface: -```go -type MarshalerJSONArray interface { - MarshalJSONArray(enc *gojay.Encoder) - IsNil() bool -} -``` -`MarshalJSONArray` method takes one argument, a pointer to the Encoder (*gojay.Encoder). The method must add all element in the JSON Array by calling Decoder's methods. - -`IsNil` method returns a boolean indicating if the interface underlying value is nil(empty) or not. It is used to safely ensure that the underlying value is not nil without using Reflection and also to in `OmitEmpty` feature. - -Example of implementation: -```go -type users []*user -// implement MarshalerJSONArray -func (u *users) MarshalJSONArray(enc *gojay.Encoder) { - for _, e := range u { - enc.Object(e) - } -} -func (u *users) IsNil() bool { - return len(u) == 0 -} -``` - -### Other types -To encode other types (string, int, float, booleans), you don't need to implement any interface. - -Example of encoding strings: -```go -func main() { - name := "Jay" - b, err := gojay.Marshal(name) - if err != nil { - log.Fatal(err) - } - fmt.Println(string(b)) // "Jay" -} -``` - -# Stream API - -### Stream Decoding -GoJay ships with a powerful stream decoder. - -It allows to read continuously from an io.Reader stream and do JIT decoding writing unmarshalled JSON to a channel to allow async consuming. - -When using the Stream API, the Decoder implements context.Context to provide graceful cancellation. - -To decode a stream of JSON, you must call `gojay.Stream.DecodeStream` and pass it a `UnmarshalerStream` implementation. - -```go -type UnmarshalerStream interface { - UnmarshalStream(*StreamDecoder) error -} -``` - -Example of implementation of stream reading from a WebSocket connection: -```go -// implement UnmarshalerStream -type ChannelStream chan *user - -func (c ChannelStream) UnmarshalStream(dec *gojay.StreamDecoder) error { - u := &user{} - if err := dec.Object(u); err != nil { - return err - } - c <- u - return nil -} - -func main() { - // get our websocket connection - origin := "http://localhost/" - url := "ws://localhost:12345/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - // create our channel which will receive our objects - streamChan := ChannelStream(make(chan *user)) - // borrow a decoder - dec := gojay.Stream.BorrowDecoder(ws) - // start decoding, it will block until a JSON message is decoded from the WebSocket - // or until Done channel is closed - go dec.DecodeStream(streamChan) - for { - select { - case v := <-streamChan: - // Got something from my websocket! - log.Println(v) - case <-dec.Done(): - log.Println("finished reading from WebSocket") - os.Exit(0) - } - } -} -``` - -### Stream Encoding -GoJay ships with a powerful stream encoder part of the Stream API. - -It allows to write continuously to an io.Writer and do JIT encoding of data fed to a channel to allow async consuming. You can set multiple consumers on the channel to be as performant as possible. Consumers are non blocking and are scheduled individually in their own go routine. - -When using the Stream API, the Encoder implements context.Context to provide graceful cancellation. - -To encode a stream of data, you must call `EncodeStream` and pass it a `MarshalerStream` implementation. - -```go -type MarshalerStream interface { - MarshalStream(enc *gojay.StreamEncoder) -} -``` - -Example of implementation of stream writing to a WebSocket: -```go -// Our structure which will be pushed to our stream -type user struct { - id int - name string - email string -} - -func (u *user) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", u.id) - enc.StringKey("name", u.name) - enc.StringKey("email", u.email) -} -func (u *user) IsNil() bool { - return u == nil -} - -// Our MarshalerStream implementation -type StreamChan chan *user - -func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) { - select { - case <-enc.Done(): - return - case o := <-s: - enc.Object(o) - } -} - -// Our main function -func main() { - // get our websocket connection - origin := "http://localhost/" - url := "ws://localhost:12345/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - // we borrow an encoder set stdout as the writer, - // set the number of consumer to 10 - // and tell the encoder to separate each encoded element - // added to the channel by a new line character - enc := gojay.Stream.BorrowEncoder(ws).NConsumer(10).LineDelimited() - // instantiate our MarshalerStream - s := StreamChan(make(chan *user)) - // start the stream encoder - // will block its goroutine until enc.Cancel(error) is called - // or until something is written to the channel - go enc.EncodeStream(s) - // write to our MarshalerStream - for i := 0; i < 1000; i++ { - s <- &user{i, "username", "user@email.com"} - } - // Wait - <-enc.Done() -} -``` - -# Unsafe API - -Unsafe API has the same functions than the regular API, it only has `Unmarshal API` for now. It is unsafe because it makes assumptions on the quality of the given JSON. - -If you are not sure if your JSON is valid, don't use the Unsafe API. - -Also, the `Unsafe` API does not copy the buffer when using Unmarshal API, which, in case of string decoding, can lead to data corruption if a byte buffer is reused. Using the `Decode` API makes `Unsafe` API safer as the io.Reader relies on `copy` builtin method and `Decoder` will have its own internal buffer :) - -Access the `Unsafe` API this way: -```go -gojay.Unsafe.Unmarshal(b, v) -``` - - -# Benchmarks - -Benchmarks encode and decode three different data based on size (small, medium, large). - -To run benchmark for decoder: -```bash -cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/decoder && make bench -``` - -To run benchmark for encoder: -```bash -cd $GOPATH/src/github.com/francoispqt/gojay/benchmarks/encoder && make bench -``` - -# Benchmark Results -## Decode - - - -### Small Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_small_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|--------------|-----------| -| Std Library | 2547 | 496 | 4 | -| JsonIter | 2046 | 312 | 12 | -| JsonParser | 1408 | 0 | 0 | -| EasyJson | 929 | 240 | 2 | -| **GoJay** | **807** | **256** | **2** | -| **GoJay-unsafe**| **712** | **112** | **1** | - -### Medium Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_medium_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|----------|-----------| -| Std Library | 30148 | 2152 | 496 | -| JsonIter | 16309 | 2976 | 80 | -| JsonParser | 7793 | 0 | 0 | -| EasyJson | 7957 | 232 | 6 | -| **GoJay** | **4984** | **2448** | **8** | -| **GoJay-unsafe**| **4809** | **144** | **7** | - -### Large Payload -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/decoder/decoder_bench_large_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) - -| | ns/op | bytes/op | allocs/op | -|-----------------|-----------|-------------|-----------| -| JsonIter | 210078 | 41712 | 1136 | -| EasyJson | 106626 | 160 | 2 | -| JsonParser | 66813 | 0 | 0 | -| **GoJay** | **52153** | **31241** | **77** | -| **GoJay-unsafe**| **48277** | **2561** | **76** | - -## Encode - - - -### Small Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_small_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_small.go) - -| | ns/op | bytes/op | allocs/op | -|----------------|----------|--------------|-----------| -| Std Library | 1280 | 464 | 3 | -| EasyJson | 871 | 944 | 6 | -| JsonIter | 866 | 272 | 3 | -| **GoJay** | **543** | **112** | **1** | -| **GoJay-func** | **347** | **0** | **0** | - -### Medium Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_medium_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_medium.go) - -| | ns/op | bytes/op | allocs/op | -|-------------|----------|--------------|-----------| -| Std Library | 5006 | 1496 | 25 | -| JsonIter | 2232 | 1544 | 20 | -| EasyJson | 1997 | 1544 | 19 | -| **GoJay** | **1522** | **312** | **14** | - -### Large Struct -[benchmark code is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/encoder/encoder_bench_large_test.go) - -[benchmark data is here](https://github.com/francoispqt/gojay/blob/master/benchmarks/benchmarks_large.go) - -| | ns/op | bytes/op | allocs/op | -|-------------|-----------|--------------|-----------| -| Std Library | 66441 | 20576 | 332 | -| JsonIter | 35247 | 20255 | 328 | -| EasyJson | 32053 | 15474 | 327 | -| **GoJay** | **27847** | **9802** | **318** | - -# Contributing - -Contributions are welcome :) - -If you encounter issues please report it in Github and/or send an email at [francois@parquet.ninja](mailto:francois@parquet.ninja) - diff --git a/vendor/github.com/francoispqt/gojay/decode.go b/vendor/github.com/francoispqt/gojay/decode.go deleted file mode 100644 index fbd07f76..00000000 --- a/vendor/github.com/francoispqt/gojay/decode.go +++ /dev/null @@ -1,386 +0,0 @@ -package gojay - -import ( - "fmt" - "io" -) - -// UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v. -// -// v must implement UnmarshalerJSONArray. -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, UnmarshalJSONArray skips that field and completes the unmarshaling as best it can. -func UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = make([]byte, len(data)) - copy(dec.data, data) - dec.length = len(data) - _, err := dec.decodeArray(v) - if err != nil { - return err - } - if dec.err != nil { - return dec.err - } - return nil -} - -// UnmarshalJSONObject parses the JSON-encoded data and stores the result in the value pointed to by v. -// -// v must implement UnmarshalerJSONObject. -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, UnmarshalJSONObject skips that field and completes the unmarshaling as best it can. -func UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = make([]byte, len(data)) - copy(dec.data, data) - dec.length = len(data) - _, err := dec.decodeObject(v) - if err != nil { - return err - } - if dec.err != nil { - return dec.err - } - return nil -} - -// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. -// If v is nil, not an implementation of UnmarshalerJSONObject or UnmarshalerJSONArray or not one of the following types: -// *string, **string, *int, **int, *int8, **int8, *int16, **int16, *int32, **int32, *int64, **int64, *uint8, **uint8, *uint16, **uint16, -// *uint32, **uint32, *uint64, **uint64, *float64, **float64, *float32, **float32, *bool, **bool -// Unmarshal returns an InvalidUnmarshalError. -// -// -// If a JSON value is not appropriate for a given target type, or if a JSON number -// overflows the target type, Unmarshal skips that field and completes the unmarshaling as best it can. -// If no more serious errors are encountered, Unmarshal returns an UnmarshalTypeError describing the earliest such error. -// In any case, it's not guaranteed that all the remaining fields following the problematic one will be unmarshaled into the target object. -func Unmarshal(data []byte, v interface{}) error { - var err error - var dec *Decoder - switch vt := v.(type) { - case *string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeString(vt) - case **string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeStringNull(vt) - case *int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt(vt) - case **int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeIntNull(vt) - case *int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8(vt) - case **int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8Null(vt) - case *int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16(vt) - case **int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16Null(vt) - case *int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32(vt) - case **int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32Null(vt) - case *int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64(vt) - case **int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64Null(vt) - case *uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8(vt) - case **uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8Null(vt) - case *uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16(vt) - case **uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16Null(vt) - case *uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32(vt) - case **uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32Null(vt) - case *uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64(vt) - case **uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64Null(vt) - case *float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64(vt) - case **float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64Null(vt) - case *float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32(vt) - case **float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32Null(vt) - case *bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBool(vt) - case **bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBoolNull(vt) - case UnmarshalerJSONObject: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - _, err = dec.decodeArray(vt) - case *interface{}: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = make([]byte, len(data)) - copy(dec.data, data) - err = dec.decodeInterface(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - defer dec.Release() - if err != nil { - return err - } - return dec.err -} - -// UnmarshalerJSONObject is the interface to implement to decode a JSON Object. -type UnmarshalerJSONObject interface { - UnmarshalJSONObject(*Decoder, string) error - NKeys() int -} - -// UnmarshalerJSONArray is the interface to implement to decode a JSON Array. -type UnmarshalerJSONArray interface { - UnmarshalJSONArray(*Decoder) error -} - -// A Decoder reads and decodes JSON values from an input stream. -type Decoder struct { - r io.Reader - data []byte - err error - isPooled byte - called byte - child byte - cursor int - length int - keysDone int - arrayIndex int -} - -// Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -// The differences between Decode and Unmarshal are: -// - Decode reads from an io.Reader in the Decoder, whereas Unmarshal reads from a []byte -// - Decode leaves to the user the option of borrowing and releasing a Decoder, whereas Unmarshal internally always borrows a Decoder and releases it when the unmarshaling is completed -func (dec *Decoder) Decode(v interface{}) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - var err error - switch vt := v.(type) { - case *string: - err = dec.decodeString(vt) - case **string: - err = dec.decodeStringNull(vt) - case *int: - err = dec.decodeInt(vt) - case **int: - err = dec.decodeIntNull(vt) - case *int8: - err = dec.decodeInt8(vt) - case **int8: - err = dec.decodeInt8Null(vt) - case *int16: - err = dec.decodeInt16(vt) - case **int16: - err = dec.decodeInt16Null(vt) - case *int32: - err = dec.decodeInt32(vt) - case **int32: - err = dec.decodeInt32Null(vt) - case *int64: - err = dec.decodeInt64(vt) - case **int64: - err = dec.decodeInt64Null(vt) - case *uint8: - err = dec.decodeUint8(vt) - case **uint8: - err = dec.decodeUint8Null(vt) - case *uint16: - err = dec.decodeUint16(vt) - case **uint16: - err = dec.decodeUint16Null(vt) - case *uint32: - err = dec.decodeUint32(vt) - case **uint32: - err = dec.decodeUint32Null(vt) - case *uint64: - err = dec.decodeUint64(vt) - case **uint64: - err = dec.decodeUint64Null(vt) - case *float64: - err = dec.decodeFloat64(vt) - case **float64: - err = dec.decodeFloat64Null(vt) - case *float32: - err = dec.decodeFloat32(vt) - case **float32: - err = dec.decodeFloat32Null(vt) - case *bool: - err = dec.decodeBool(vt) - case **bool: - err = dec.decodeBoolNull(vt) - case UnmarshalerJSONObject: - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - _, err = dec.decodeArray(vt) - case *EmbeddedJSON: - err = dec.decodeEmbeddedJSON(vt) - case *interface{}: - err = dec.decodeInterface(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - if err != nil { - return err - } - return dec.err -} - -// Non exported - -func isDigit(b byte) bool { - switch b { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return true - default: - return false - } -} - -func (dec *Decoder) read() bool { - if dec.r != nil { - // if we reach the end, double the buffer to ensure there's always more space - if len(dec.data) == dec.length { - nLen := dec.length * 2 - if nLen == 0 { - nLen = 512 - } - Buf := make([]byte, nLen, nLen) - copy(Buf, dec.data) - dec.data = Buf - } - var n int - var err error - for n == 0 { - n, err = dec.r.Read(dec.data[dec.length:]) - if err != nil { - if err != io.EOF { - dec.err = err - return false - } - if n == 0 { - return false - } - dec.length = dec.length + n - return true - } - } - dec.length = dec.length + n - return true - } - return false -} - -func (dec *Decoder) nextChar() byte { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - } - d := dec.data[dec.cursor] - return d - } - return 0 -} diff --git a/vendor/github.com/francoispqt/gojay/decode_array.go b/vendor/github.com/francoispqt/gojay/decode_array.go deleted file mode 100644 index 297f2ee7..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_array.go +++ /dev/null @@ -1,247 +0,0 @@ -package gojay - -import "reflect" - -// DecodeArray reads the next JSON-encoded value from the decoder's input (io.Reader) -// and stores it in the value pointed to by v. -// -// v must implement UnmarshalerJSONArray. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - _, err := dec.decodeArray(v) - return err -} -func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) { - // remember last array index in case of nested arrays - lastArrayIndex := dec.arrayIndex - dec.arrayIndex = 0 - defer func() { - dec.arrayIndex = lastArrayIndex - }() - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '[': - dec.cursor = dec.cursor + 1 - // array is open, char is not space start readings - for dec.nextChar() != 0 { - // closing array - if dec.data[dec.cursor] == ']' { - dec.cursor = dec.cursor + 1 - return dec.cursor, nil - } - // calling unmarshall function for each element of the slice - err := arr.UnmarshalJSONArray(dec) - if err != nil { - return 0, err - } - dec.arrayIndex++ - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) - case 'n': - // is null - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - // can't unmarshall to struct - // we skip array and set Error - dec.err = dec.makeInvalidUnmarshalErr(arr) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) { - // remember last array index in case of nested arrays - lastArrayIndex := dec.arrayIndex - dec.arrayIndex = 0 - defer func() { - dec.arrayIndex = lastArrayIndex - }() - vv := reflect.ValueOf(v) - vvt := vv.Type() - if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { - dec.err = ErrUnmarshalPtrExpected - return 0, dec.err - } - // not an array not an error, but do not know what to do - // do not check syntax - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '[': - dec.cursor = dec.cursor + 1 - // create our new type - elt := vv.Elem() - n := reflect.New(elt.Type().Elem()) - var arr UnmarshalerJSONArray - var ok bool - if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok { - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) - return 0, dec.err - } - // array is open, char is not space start readings - for dec.nextChar() != 0 { - // closing array - if dec.data[dec.cursor] == ']' { - elt.Set(n) - dec.cursor = dec.cursor + 1 - return dec.cursor, nil - } - // calling unmarshall function for each element of the slice - err := arr.UnmarshalJSONArray(dec) - if err != nil { - return 0, err - } - dec.arrayIndex++ - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) - case 'n': - // is null - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - // can't unmarshall to struct - // we skip array and set Error - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil)) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipArray() (int, error) { - var arraysOpen = 1 - var arraysClosed = 0 - // var stringOpen byte = 0 - for j := dec.cursor; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case ']': - arraysClosed++ - // everything is closed return - if arraysOpen == arraysClosed { - // add char to object data - return j + 1, nil - } - case '[': - arraysOpen++ - case '"': - j++ - var isInEscapeSeq bool - var isFirstQuote = true - for ; j < dec.length || dec.read(); j++ { - if dec.data[j] != '"' { - continue - } - if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { - break - } else { - isInEscapeSeq = false - } - if isFirstQuote { - isFirstQuote = false - } - // loop backward and count how many anti slash found - // to see if string is effectively escaped - ct := 0 - for i := j - 1; i > 0; i-- { - if dec.data[i] != '\\' { - break - } - ct++ - } - // is pair number of slashes, quote is not escaped - if ct&1 == 0 { - break - } - isInEscapeSeq = true - } - default: - continue - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeArrayFunc is a func type implementing UnmarshalerJSONArray. -// Use it to cast a `func(*Decoder) error` to Unmarshal an array on the fly. - -type DecodeArrayFunc func(*Decoder) error - -// UnmarshalJSONArray implements UnmarshalerJSONArray. -func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error { - return f(dec) -} - -// IsNil implements UnmarshalerJSONArray. -func (f DecodeArrayFunc) IsNil() bool { - return f == nil -} - -// Add Values functions - -// AddArray decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error { - return dec.Array(v) -} - -// AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) AddArrayNull(v interface{}) error { - return dec.ArrayNull(v) -} - -// Array decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -func (dec *Decoder) Array(v UnmarshalerJSONArray) error { - newCursor, err := dec.decodeArray(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.called |= 1 - return nil -} - -// ArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray. -// v should be a pointer to an UnmarshalerJSONArray, -// if `null` value is encountered in JSON, it will leave the value v untouched, -// else it will create a new instance of the UnmarshalerJSONArray behind v. -func (dec *Decoder) ArrayNull(v interface{}) error { - newCursor, err := dec.decodeArrayNull(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.called |= 1 - return nil -} - -// Index returns the index of an array being decoded. -func (dec *Decoder) Index() int { - return dec.arrayIndex -} diff --git a/vendor/github.com/francoispqt/gojay/decode_bool.go b/vendor/github.com/francoispqt/gojay/decode_bool.go deleted file mode 100644 index 1dc304ba..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_bool.go +++ /dev/null @@ -1,241 +0,0 @@ -package gojay - -// DecodeBool reads the next JSON-encoded value from the decoder's input (io.Reader) -// and stores it in the boolean pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeBool(v *bool) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeBool(v) -} -func (dec *Decoder) decodeBool(v *bool) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - *v = true - return nil - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - *v = false - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - *v = false - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} -func (dec *Decoder) decodeBoolNull(v **bool) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - if *v == nil { - *v = new(bool) - } - **v = true - return nil - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - if *v == nil { - *v = new(bool) - } - **v = false - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) assertTrue() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'r' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'u' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 'e' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - switch dec.data[dec.cursor] { - case ' ', '\b', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 3 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) assertNull() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'u' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - switch dec.data[dec.cursor] { - case ' ', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 3 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) assertFalse() error { - i := 0 - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch i { - case 0: - if dec.data[dec.cursor] != 'a' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 1: - if dec.data[dec.cursor] != 'l' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 2: - if dec.data[dec.cursor] != 's' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 3: - if dec.data[dec.cursor] != 'e' { - return dec.raiseInvalidJSONErr(dec.cursor) - } - case 4: - switch dec.data[dec.cursor] { - case ' ', '\t', '\n', ',', ']', '}': - // dec.cursor-- - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - } - i++ - } - if i == 4 { - return nil - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// Add Values functions - -// AddBool decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) AddBool(v *bool) error { - return dec.Bool(v) -} - -// AddBoolNull decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddBoolNull(v **bool) error { - return dec.BoolNull(v) -} - -// Bool decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) Bool(v *bool) error { - err := dec.decodeBool(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// BoolNull decodes the JSON value within an object or an array to a *bool. -// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. -// If next key is null, bool will be false. -func (dec *Decoder) BoolNull(v **bool) error { - err := dec.decodeBoolNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_embedded_json.go b/vendor/github.com/francoispqt/gojay/decode_embedded_json.go deleted file mode 100644 index 67fcc2ea..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_embedded_json.go +++ /dev/null @@ -1,85 +0,0 @@ -package gojay - -// EmbeddedJSON is a raw encoded JSON value. -// It can be used to delay JSON decoding or precompute a JSON encoding. -type EmbeddedJSON []byte - -func (dec *Decoder) decodeEmbeddedJSON(ej *EmbeddedJSON) error { - var err error - if ej == nil { - return InvalidUnmarshalError("Invalid nil pointer given") - } - var beginOfEmbeddedJSON int - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - case 't': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - // is false - case 'f': - beginOfEmbeddedJSON = dec.cursor - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - // is an object - case '{': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - dec.cursor, err = dec.skipObject() - // is string - case '"': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - err = dec.skipString() // why no new dec.cursor in result? - // is array - case '[': - beginOfEmbeddedJSON = dec.cursor - dec.cursor = dec.cursor + 1 - dec.cursor, err = dec.skipArray() - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - beginOfEmbeddedJSON = dec.cursor - dec.cursor, err = dec.skipNumber() - } - break - } - if err == nil { - if dec.cursor-1 >= beginOfEmbeddedJSON { - *ej = append(*ej, dec.data[beginOfEmbeddedJSON:dec.cursor]...) - } - dec.called |= 1 - } - return err -} - -// AddEmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. -// It can be used to delay JSON decoding or precompute a JSON encoding. -func (dec *Decoder) AddEmbeddedJSON(v *EmbeddedJSON) error { - return dec.EmbeddedJSON(v) -} - -// EmbeddedJSON adds an EmbeddedsJSON to the value pointed by v. -// It can be used to delay JSON decoding or precompute a JSON encoding. -func (dec *Decoder) EmbeddedJSON(v *EmbeddedJSON) error { - err := dec.decodeEmbeddedJSON(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_interface.go b/vendor/github.com/francoispqt/gojay/decode_interface.go deleted file mode 100644 index 015790d8..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_interface.go +++ /dev/null @@ -1,130 +0,0 @@ -package gojay - -// TODO @afiune for now we are using the standard json unmarshaling but in -// the future it would be great to implement one here inside this repo -import "encoding/json" - -// DecodeInterface reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by i. -// -// i must be an interface poiter -func (dec *Decoder) DecodeInterface(i *interface{}) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - err := dec.decodeInterface(i) - return err -} - -func (dec *Decoder) decodeInterface(i *interface{}) error { - start, end, err := dec.getObject() - if err != nil { - dec.cursor = start - return err - } - - // if start & end are equal the object is a null, don't unmarshal - if start == end { - return nil - } - - object := dec.data[start:end] - if err = json.Unmarshal(object, i); err != nil { - return err - } - - dec.cursor = end - return nil -} - -// @afiune Maybe return the type as well? -func (dec *Decoder) getObject() (start int, end int, err error) { - // start cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - dec.cursor++ - err = dec.assertNull() - if err != nil { - return - } - // Set start & end to the same cursor to indicate the object - // is a null and should not be unmarshal - start = dec.cursor - end = dec.cursor - return - case 't': - start = dec.cursor - dec.cursor++ - err = dec.assertTrue() - if err != nil { - return - } - end = dec.cursor - dec.cursor++ - return - // is false - case 'f': - start = dec.cursor - dec.cursor++ - err = dec.assertFalse() - if err != nil { - return - } - end = dec.cursor - dec.cursor++ - return - // is an object - case '{': - start = dec.cursor - dec.cursor++ - end, err = dec.skipObject() - dec.cursor = end - return - // is string - case '"': - start = dec.cursor - dec.cursor++ - start, end, err = dec.getString() - start-- - dec.cursor = end - return - // is array - case '[': - start = dec.cursor - dec.cursor++ - end, err = dec.skipArray() - dec.cursor = end - return - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - start = dec.cursor - end, err = dec.skipNumber() - dec.cursor = end - return - default: - err = dec.raiseInvalidJSONErr(dec.cursor) - return - } - } - err = dec.raiseInvalidJSONErr(dec.cursor) - return -} - -// Add Values functions - -// AddInterface decodes the JSON value within an object or an array to a interface{}. -func (dec *Decoder) AddInterface(v *interface{}) error { - return dec.Interface(v) -} - -// Interface decodes the JSON value within an object or an array to an interface{}. -func (dec *Decoder) Interface(value *interface{}) error { - err := dec.decodeInterface(value) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_number.go b/vendor/github.com/francoispqt/gojay/decode_number.go deleted file mode 100644 index 0042b471..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_number.go +++ /dev/null @@ -1,118 +0,0 @@ -package gojay - -import ( - "math" -) - -var digits []int8 - -const maxInt64toMultiply = math.MaxInt64 / 10 -const maxInt32toMultiply = math.MaxInt32 / 10 -const maxInt16toMultiply = math.MaxInt16 / 10 -const maxInt8toMultiply = math.MaxInt8 / 10 -const maxUint8toMultiply = math.MaxUint8 / 10 -const maxUint16toMultiply = math.MaxUint16 / 10 -const maxUint32toMultiply = math.MaxUint32 / 10 -const maxUint64toMultiply = math.MaxUint64 / 10 -const maxUint32Length = 10 -const maxUint64Length = 20 -const maxUint16Length = 5 -const maxUint8Length = 3 -const maxInt32Length = 10 -const maxInt64Length = 19 -const maxInt16Length = 5 -const maxInt8Length = 3 -const invalidNumber = int8(-1) - -var pow10uint64 = [21]uint64{ - 0, - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000, -} - -var skipNumberEndCursorIncrement [256]int - -func init() { - digits = make([]int8, 256) - for i := 0; i < len(digits); i++ { - digits[i] = invalidNumber - } - for i := int8('0'); i <= int8('9'); i++ { - digits[i] = i - int8('0') - } - - for i := 0; i < 256; i++ { - switch i { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-': - skipNumberEndCursorIncrement[i] = 1 - } - } -} - -func (dec *Decoder) skipNumber() (int, error) { - end := dec.cursor + 1 - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - end += skipNumberEndCursorIncrement[dec.data[j]] - - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E', '+', '-', ' ', '\n', '\t', '\r': - continue - case ',', '}', ']': - return end, nil - default: - // invalid json we expect numbers, dot (single one), comma, or spaces - return end, dec.raiseInvalidJSONErr(dec.cursor) - } - } - - return end, nil -} - -func (dec *Decoder) getExponent() (int64, error) { - start := dec.cursor - end := dec.cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { // is positive - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = dec.cursor + 1 - case '-': - dec.cursor++ - exp, err := dec.getExponent() - return -exp, err - case '+': - dec.cursor++ - return dec.getExponent() - default: - // if nothing return 0 - // could raise error - if start == end { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end-1), nil - } - } - if start == end { - - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end-1), nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_number_float.go b/vendor/github.com/francoispqt/gojay/decode_number_float.go deleted file mode 100644 index f76c5861..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_number_float.go +++ /dev/null @@ -1,516 +0,0 @@ -package gojay - -// DecodeFloat64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeFloat64(v *float64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeFloat64(v) -} -func (dec *Decoder) decodeFloat64(v *float64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloatNegative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeFloat64Null(v **float64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat() - if err != nil { - return err - } - if *v == nil { - *v = new(float64) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloatNegative() - if err != nil { - return err - } - if *v == nil { - *v = new(float64) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloatNegative() (float64, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getFloat() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat() (float64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // then we get part after decimal as integer - start = j + 1 - // get number after the decimal point - for i := j + 1; i < dec.length || dec.read(); i++ { - c := dec.data[i] - if isDigit(c) { - end = i - // multiply the before decimal point portion by 10 using bitwise - // make sure it doesn't overflow - if end-start < 18 { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - continue - } else if (c == 'e' || c == 'E') && j < i-1 { - // we have an exponent, convert first the value we got before the exponent - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= len(pow10uint64) || expI < 0 { - expI = len(pow10uint64) - 2 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - // then we add both integers - // then we divide the number by the power found - afterDecimal = dec.atoi64(start, end) - } - dec.cursor = i + 1 - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // absolute exponent - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float64(floatVal) * (1 / float64(pow10uint64[pExp])), nil - } - return float64(floatVal) * float64(pow10uint64[pExp]), nil - } - dec.cursor = i - break - } - if end >= dec.length || end < start { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= len(pow10uint64) || expI < 0 { - expI = 19 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - afterDecimal = dec.atoi64(start, end) - } - - pow := pow10uint64[expI] - // then we add both integers - // then we divide the number by the power found - return float64(beforeDecimal+afterDecimal) / float64(pow), nil - case 'e', 'E': - dec.cursor = j + 1 - // we get part before decimal as integer - beforeDecimal := uint64(dec.atoi64(start, end)) - // get exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float64(beforeDecimal) * (1 / float64(pow10uint64[pExp])), nil - } - return float64(beforeDecimal) * float64(pow10uint64[pExp]), nil - case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal - dec.cursor = j - return float64(dec.atoi64(start, end)), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return float64(dec.atoi64(start, end)), nil -} - -// DecodeFloat32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the float32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeFloat32(v *float32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeFloat32(v) -} -func (dec *Decoder) decodeFloat32(v *float32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeFloat32Null(v **float32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32() - if err != nil { - return err - } - if *v == nil { - *v = new(float32) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(float32) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat32Negative() (float32, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getFloat32() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getFloat32() (float32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // then we get part after decimal as integer - start = j + 1 - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := j + 1; i < dec.length || dec.read(); i++ { - c := dec.data[i] - if isDigit(c) { - end = i - // multiply the before decimal point portion by 10 using bitwise - // make sure it desn't overflow - if end-start < 9 { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - continue - } else if (c == 'e' || c == 'E') && j < i-1 { - // we get the number before decimal - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= 12 || expI < 0 { - expI = 10 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - afterDecimal = dec.atoi64(start, end) - } - dec.cursor = i + 1 - pow := pow10uint64[expI] - // then we add both integers - // then we divide the number by the power found - floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float32(floatVal) * (1 / float32(pow10uint64[pExp])), nil - } - return float32(floatVal) * float32(pow10uint64[pExp]), nil - } - dec.cursor = i - break - } - if end >= dec.length || end < start { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // then we add both integers - // then we divide the number by the power found - var afterDecimal int64 - expI := end - start + 2 - // if exp is too long, it means number is too long, just truncate the number - if expI >= 12 || expI < 0 { - expI = 10 - afterDecimal = dec.atoi64(start, start+expI-2) - } else { - // then we add both integers - // then we divide the number by the power found - afterDecimal = dec.atoi64(start, end) - } - pow := pow10uint64[expI] - return float32(beforeDecimal+afterDecimal) / float32(pow), nil - case 'e', 'E': - dec.cursor = j + 1 - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - // if exponent is negative - if exp < 0 { - return float32(beforeDecimal) * (1 / float32(pow10uint64[pExp])), nil - } - return float32(beforeDecimal) * float32(pow10uint64[pExp]), nil - case ' ', '\n', '\t', '\r', ',', '}', ']': // does not have decimal - dec.cursor = j - return float32(dec.atoi64(start, end)), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return float32(dec.atoi64(start, end)), nil -} - -// Add Values functions - -// AddFloat decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat(v *float64) error { - return dec.Float64(v) -} - -// AddFloatNull decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloatNull(v **float64) error { - return dec.Float64Null(v) -} - -// AddFloat64 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat64(v *float64) error { - return dec.Float64(v) -} - -// AddFloat64Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloat64Null(v **float64) error { - return dec.Float64Null(v) -} - -// AddFloat32 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddFloat32(v *float32) error { - return dec.Float32(v) -} - -// AddFloat32Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddFloat32Null(v **float32) error { - return dec.Float32Null(v) -} - -// Float decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float(v *float64) error { - return dec.Float64(v) -} - -// FloatNull decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) FloatNull(v **float64) error { - return dec.Float64Null(v) -} - -// Float64 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float64(v *float64) error { - err := dec.decodeFloat64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float64Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float64Null(v **float64) error { - err := dec.decodeFloat64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float32 decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float32(v *float32) error { - err := dec.decodeFloat32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Float32Null decodes the JSON value within an object or an array to a *float64. -// If next key value overflows float64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Float32Null(v **float32) error { - err := dec.decodeFloat32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_number_int.go b/vendor/github.com/francoispqt/gojay/decode_number_int.go deleted file mode 100644 index 8429049f..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_number_int.go +++ /dev/null @@ -1,1338 +0,0 @@ -package gojay - -import ( - "fmt" - "math" -) - -// DecodeInt reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt(v *int) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt(v) -} -func (dec *Decoder) decodeInt(v *int) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - *v = int(val) - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - *v = -int(val) - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) decodeIntNull(v **int) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - if *v == nil { - *v = new(int) - } - **v = int(val) - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int) - } - **v = -int(val) - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int16 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt16(v *int16) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt16(v) -} -func (dec *Decoder) decodeInt16(v *int16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt16() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt16Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt16Null(v **int16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt16() - if err != nil { - return err - } - if *v == nil { - *v = new(int16) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt16Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int16) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt16Negative() (int16, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt16() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt16() (int16, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi16(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi16(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int16(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi16(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi16(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt16WithExp(dec.atoi16(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi16(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi16(start, end), nil -} - -func (dec *Decoder) getInt16WithExp(init int16) (int16, error) { - var exp uint16 - var sign = int16(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint16(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - exp = exp + 1 - if exp >= uint16(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int16(pow10uint64[exp])), nil - } - return init * int16(pow10uint64[exp]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - exp = exp + 1 - if exp >= uint16(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int16(pow10uint64[exp])), nil - } - return init * int16(pow10uint64[exp]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int8 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt8(v *int8) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt8(v) -} -func (dec *Decoder) decodeInt8(v *int8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt8() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt8Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt8Null(v **int8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - // we don't look for 0 as leading zeros are invalid per RFC - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt8() - if err != nil { - return err - } - if *v == nil { - *v = new(int8) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt8Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int8) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt8Negative() (int8, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt8() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt8() (int8, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi8(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi8(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int8(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi8(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi8(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt8WithExp(dec.atoi8(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi8(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi8(start, end), nil -} - -func (dec *Decoder) getInt8WithExp(init int8) (int8, error) { - var exp uint8 - var sign = int8(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint8(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint8(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int8(pow10uint64[exp+1])), nil - } - return init * int8(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint8(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int8(pow10uint64[exp+1])), nil - } - return init * int8(pow10uint64[exp+1]), nil - default: - dec.err = dec.raiseInvalidJSONErr(dec.cursor) - return 0, dec.err - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt32(v *int32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt32(v) -} -func (dec *Decoder) decodeInt32(v *int32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt32Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt32Null(v **int32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt32() - if err != nil { - return err - } - if *v == nil { - *v = new(int32) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt32Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int32) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt32Negative() (int32, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt32() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt32() (int32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - // if eg 1.E - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi64(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int32(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi32(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi32(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt32WithExp(dec.atoi32(start, end)) - case ' ', '\n', '\t', '\r', ',', '}', ']': - dec.cursor = j - return dec.atoi32(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi32(start, end), nil -} - -func (dec *Decoder) getInt32WithExp(init int32) (int32, error) { - var exp uint32 - var sign = int32(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint32(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint32(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int32(pow10uint64[exp+1])), nil - } - return init * int32(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint32(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int32(pow10uint64[exp+1])), nil - } - return init * int32(pow10uint64[exp+1]), nil - default: - dec.err = dec.raiseInvalidJSONErr(dec.cursor) - return 0, dec.err - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeInt64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the int64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeInt64(v *int64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeInt64(v) -} - -func (dec *Decoder) decodeInt64(v *int64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - *v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeInt64Null(v **int64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getInt64() - if err != nil { - return err - } - if *v == nil { - *v = new(int64) - } - **v = val - return nil - case '-': - dec.cursor = dec.cursor + 1 - val, err := dec.getInt64Negative() - if err != nil { - return err - } - if *v == nil { - *v = new(int64) - } - **v = -val - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt64Negative() (int64, error) { - // look for following numbers - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '1', '2', '3', '4', '5', '6', '7', '8', '9': - return dec.getInt64() - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getInt64() (int64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\t', '\n', ',', '}', ']': - dec.cursor = j - return dec.atoi64(start, end), nil - case '.': - // if dot is found - // look for exponent (e,E) as exponent can change the - // way number should be parsed to int. - // if no exponent found, just unmarshal the number before decimal point - j++ - startDecimal := j - endDecimal := j - 1 - for ; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - endDecimal = j - continue - case 'e', 'E': - // if eg 1.E - if startDecimal > endDecimal { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - dec.cursor = j + 1 - // can try unmarshalling to int as Exponent might change decimal number to non decimal - // let's get the float value first - // we get part before decimal as integer - beforeDecimal := dec.atoi64(start, end) - // get number after the decimal point - // multiple the before decimal point portion by 10 using bitwise - for i := startDecimal; i <= endDecimal; i++ { - beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1) - } - // then we add both integers - // then we divide the number by the power found - afterDecimal := dec.atoi64(startDecimal, endDecimal) - expI := endDecimal - startDecimal + 2 - if expI >= len(pow10uint64) || expI < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - pow := pow10uint64[expI] - floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) - // we have the floating value, now multiply by the exponent - exp, err := dec.getExponent() - if err != nil { - return 0, err - } - pExp := (exp + (exp >> 31)) ^ (exp >> 31) + 1 // abs - if pExp >= int64(len(pow10uint64)) || pExp < 0 { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - val := floatVal * float64(pow10uint64[pExp]) - return int64(val), nil - case ' ', '\t', '\n', ',', ']', '}': - dec.cursor = j - return dec.atoi64(start, end), nil - default: - dec.cursor = j - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return dec.atoi64(start, end), nil - case 'e', 'E': - // get init n - dec.cursor = j + 1 - return dec.getInt64WithExp(dec.atoi64(start, end)) - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoi64(start, end), nil -} - -func (dec *Decoder) getInt64WithExp(init int64) (int64, error) { - var exp uint64 - var sign = int64(1) - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '+': - continue - case '-': - sign = -1 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - dec.cursor++ - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - uintv := uint64(digits[dec.data[dec.cursor]]) - exp = (exp << 3) + (exp << 1) + uintv - case ' ', '\t', '\n', '}', ',', ']': - if exp+1 >= uint64(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int64(pow10uint64[exp+1])), nil - } - return init * int64(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - if exp+1 >= uint64(len(pow10uint64)) { - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - if sign == -1 { - return init * (1 / int64(pow10uint64[exp+1])), nil - } - return init * int64(pow10uint64[exp+1]), nil - default: - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) atoi64(start, end int) int64 { - var ll = end + 1 - start - var val = int64(digits[dec.data[start]]) - end = end + 1 - if ll < maxInt64Length { - for i := start + 1; i < end; i++ { - intv := int64(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - return val - } else if ll == maxInt64Length { - for i := start + 1; i < end; i++ { - intv := int64(digits[dec.data[i]]) - if val > maxInt64toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt64-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi32(start, end int) int32 { - var ll = end + 1 - start - var val = int32(digits[dec.data[start]]) - end = end + 1 - - // overflowing - if ll < maxInt32Length { - for i := start + 1; i < end; i++ { - intv := int32(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt32Length { - for i := start + 1; i < end; i++ { - intv := int32(digits[dec.data[i]]) - if val > maxInt32toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt32-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi16(start, end int) int16 { - var ll = end + 1 - start - var val = int16(digits[dec.data[start]]) - end = end + 1 - // overflowing - if ll < maxInt16Length { - for i := start + 1; i < end; i++ { - intv := int16(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt16Length { - for i := start + 1; i < end; i++ { - intv := int16(digits[dec.data[i]]) - if val > maxInt16toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt16-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoi8(start, end int) int8 { - var ll = end + 1 - start - var val = int8(digits[dec.data[start]]) - end = end + 1 - // overflowing - if ll < maxInt8Length { - for i := start + 1; i < end; i++ { - intv := int8(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + intv - } - } else if ll == maxInt8Length { - for i := start + 1; i < end; i++ { - intv := int8(digits[dec.data[i]]) - if val > maxInt8toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxInt8-val < intv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += intv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -// Add Values functions - -// AddInt decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt(v *int) error { - return dec.Int(v) -} - -// AddIntNull decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddIntNull(v **int) error { - return dec.IntNull(v) -} - -// AddInt8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt8(v *int8) error { - return dec.Int8(v) -} - -// AddInt8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt8Null(v **int8) error { - return dec.Int8Null(v) -} - -// AddInt16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt16(v *int16) error { - return dec.Int16(v) -} - -// AddInt16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt16Null(v **int16) error { - return dec.Int16Null(v) -} - -// AddInt32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt32(v *int32) error { - return dec.Int32(v) -} - -// AddInt32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt32Null(v **int32) error { - return dec.Int32Null(v) -} - -// AddInt64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddInt64(v *int64) error { - return dec.Int64(v) -} - -// AddInt64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddInt64Null(v **int64) error { - return dec.Int64Null(v) -} - -// Int decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int(v *int) error { - err := dec.decodeInt(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// IntNull decodes the JSON value within an object or an array to an *int. -// If next key value overflows int, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) IntNull(v **int) error { - err := dec.decodeIntNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int8(v *int8) error { - err := dec.decodeInt8(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int8Null(v **int8) error { - err := dec.decodeInt8Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int16(v *int16) error { - err := dec.decodeInt16(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int16Null(v **int16) error { - err := dec.decodeInt16Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int32(v *int32) error { - err := dec.decodeInt32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int32Null(v **int32) error { - err := dec.decodeInt32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int64(v *int64) error { - err := dec.decodeInt64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Int64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows int64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Int64Null(v **int64) error { - err := dec.decodeInt64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_number_uint.go b/vendor/github.com/francoispqt/gojay/decode_number_uint.go deleted file mode 100644 index b57ef7ab..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_number_uint.go +++ /dev/null @@ -1,715 +0,0 @@ -package gojay - -import ( - "math" -) - -// DecodeUint8 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint8 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint8(v *uint8) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint8(v) -} - -func (dec *Decoder) decodeUint8(v *uint8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint8() - if err != nil { - return err - } - *v = val - return nil - case '-': // if negative, we just set it to 0 and set error - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint8Null(v **uint8) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint8() - if err != nil { - return err - } - if *v == nil { - *v = new(uint8) - } - **v = val - return nil - case '-': // if negative, we just set it to 0 and set error - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint8) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint8() (uint8, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui8(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui8(start, end), nil -} - -// DecodeUint16 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint16 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint16(v *uint16) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint16(v) -} - -func (dec *Decoder) decodeUint16(v *uint16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint16() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint16Null(v **uint16) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint16() - if err != nil { - return err - } - if *v == nil { - *v = new(uint16) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint16) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint16() (uint16, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui16(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui16(start, end), nil -} - -// DecodeUint32 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint32 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint32(v *uint32) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint32(v) -} - -func (dec *Decoder) decodeUint32(v *uint32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint32() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint32Null(v **uint32) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint32() - if err != nil { - return err - } - if *v == nil { - *v = new(uint32) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint32) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint32() (uint32, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r': - continue - case '.', ',', '}', ']': - dec.cursor = j - return dec.atoui32(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui32(start, end), nil -} - -// DecodeUint64 reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the uint64 pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeUint64(v *uint64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeUint64(v) -} -func (dec *Decoder) decodeUint64(v *uint64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint64() - if err != nil { - return err - } - *v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} -func (dec *Decoder) decodeUint64Null(v **uint64) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch c := dec.data[dec.cursor]; c { - case ' ', '\n', '\t', '\r', ',': - continue - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getUint64() - if err != nil { - return err - } - if *v == nil { - *v = new(uint64) - } - **v = val - return nil - case '-': - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - if *v == nil { - *v = new(uint64) - } - return nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) getUint64() (uint64, error) { - var end = dec.cursor - var start = dec.cursor - // look for following numbers - for j := dec.cursor + 1; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - end = j - continue - case ' ', '\n', '\t', '\r', '.', ',', '}', ']': - dec.cursor = j - return dec.atoui64(start, end), nil - } - // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.atoui64(start, end), nil -} - -func (dec *Decoder) atoui64(start, end int) uint64 { - var ll = end + 1 - start - var val = uint64(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint64Length { - for i := start + 1; i < end; i++ { - uintv := uint64(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint64Length { - for i := start + 1; i < end; i++ { - uintv := uint64(digits[dec.data[i]]) - if val > maxUint64toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint64-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - return val -} - -func (dec *Decoder) atoui32(start, end int) uint32 { - var ll = end + 1 - start - var val uint32 - val = uint32(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint32Length { - for i := start + 1; i < end; i++ { - uintv := uint32(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint32Length { - for i := start + 1; i < end; i++ { - uintv := uint32(digits[dec.data[i]]) - if val > maxUint32toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint32-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint32Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -func (dec *Decoder) atoui16(start, end int) uint16 { - var ll = end + 1 - start - var val uint16 - val = uint16(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint16Length { - for i := start + 1; i < end; i++ { - uintv := uint16(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint16Length { - for i := start + 1; i < end; i++ { - uintv := uint16(digits[dec.data[i]]) - if val > maxUint16toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint16-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint16Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -func (dec *Decoder) atoui8(start, end int) uint8 { - var ll = end + 1 - start - var val uint8 - val = uint8(digits[dec.data[start]]) - end = end + 1 - if ll < maxUint8Length { - for i := start + 1; i < end; i++ { - uintv := uint8(digits[dec.data[i]]) - val = (val << 3) + (val << 1) + uintv - } - } else if ll == maxUint8Length { - for i := start + 1; i < end; i++ { - uintv := uint8(digits[dec.data[i]]) - if val > maxUint8toMultiply { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val = (val << 3) + (val << 1) - if math.MaxUint8-val < uintv { - dec.err = dec.makeInvalidUnmarshalErr(val) - return 0 - } - val += uintv - } - } else if ll > maxUint8Length { - dec.err = dec.makeInvalidUnmarshalErr(val) - val = 0 - } - return val -} - -// Add Values functions - -// AddUint8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint8(v *uint8) error { - return dec.Uint8(v) -} - -// AddUint8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint8Null(v **uint8) error { - return dec.Uint8Null(v) -} - -// AddUint16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint16(v *uint16) error { - return dec.Uint16(v) -} - -// AddUint16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint16Null(v **uint16) error { - return dec.Uint16Null(v) -} - -// AddUint32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint32(v *uint32) error { - return dec.Uint32(v) -} - -// AddUint32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint32Null(v **uint32) error { - return dec.Uint32Null(v) -} - -// AddUint64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) AddUint64(v *uint64) error { - return dec.Uint64(v) -} - -// AddUint64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddUint64Null(v **uint64) error { - return dec.Uint64Null(v) -} - -// Uint8 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint8(v *uint8) error { - err := dec.decodeUint8(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint8Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint8Null(v **uint8) error { - err := dec.decodeUint8Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint16 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint16(v *uint16) error { - err := dec.decodeUint16(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint16Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint16Null(v **uint16) error { - err := dec.decodeUint16Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint32 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint32(v *uint32) error { - err := dec.decodeUint32(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint32Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint32Null(v **uint32) error { - err := dec.decodeUint32Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint64 decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint64(v *uint64) error { - err := dec.decodeUint64(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// Uint64Null decodes the JSON value within an object or an array to an *int. -// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. -func (dec *Decoder) Uint64Null(v **uint64) error { - err := dec.decodeUint64Null(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_object.go b/vendor/github.com/francoispqt/gojay/decode_object.go deleted file mode 100644 index 0fec9d24..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_object.go +++ /dev/null @@ -1,407 +0,0 @@ -package gojay - -import ( - "reflect" - "unsafe" -) - -// DecodeObject reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v. -// -// v must implement UnmarshalerJSONObject. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeObject(j UnmarshalerJSONObject) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - _, err := dec.decodeObject(j) - return err -} -func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { - keys := j.NKeys() - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - case '{': - dec.cursor = dec.cursor + 1 - // if keys is zero we will parse all keys - // we run two loops for micro optimization - if keys == 0 { - for dec.cursor < dec.length || dec.read() { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } else { - for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } - // will get to that point when keysDone is not lower than keys anymore - // in that case, we make sure cursor goes to the end of object, but we skip - // unmarshalling - if dec.child&1 != 0 { - end, err := dec.skipObject() - dec.cursor = end - return dec.cursor, err - } - return dec.cursor, nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - // can't unmarshal to struct - dec.err = dec.makeInvalidUnmarshalErr(j) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) { - // make sure the value is a pointer - vv := reflect.ValueOf(v) - vvt := vv.Type() - if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr { - dec.err = ErrUnmarshalPtrExpected - return 0, dec.err - } - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - case '{': - elt := vv.Elem() - n := reflect.New(elt.Type().Elem()) - elt.Set(n) - var j UnmarshalerJSONObject - var ok bool - if j, ok = n.Interface().(UnmarshalerJSONObject); !ok { - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) - return 0, dec.err - } - keys := j.NKeys() - dec.cursor = dec.cursor + 1 - // if keys is zero we will parse all keys - // we run two loops for micro optimization - if keys == 0 { - for dec.cursor < dec.length || dec.read() { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } else { - for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys { - k, done, err := dec.nextKey() - if err != nil { - return 0, err - } else if done { - return dec.cursor, nil - } - err = j.UnmarshalJSONObject(dec, k) - if err != nil { - dec.err = err - return 0, err - } else if dec.called&1 == 0 { - err := dec.skipData() - if err != nil { - return 0, err - } - } else { - dec.keysDone++ - } - dec.called &= 0 - } - } - // will get to that point when keysDone is not lower than keys anymore - // in that case, we make sure cursor goes to the end of object, but we skip - // unmarshalling - if dec.child&1 != 0 { - end, err := dec.skipObject() - dec.cursor = end - return dec.cursor, err - } - return dec.cursor, nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return 0, err - } - return dec.cursor, nil - default: - // can't unmarshal to struct - dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil)) - err := dec.skipData() - if err != nil { - return 0, err - } - return dec.cursor, nil - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipObject() (int, error) { - var objectsOpen = 1 - var objectsClosed = 0 - for j := dec.cursor; j < dec.length || dec.read(); j++ { - switch dec.data[j] { - case '}': - objectsClosed++ - // everything is closed return - if objectsOpen == objectsClosed { - // add char to object data - return j + 1, nil - } - case '{': - objectsOpen++ - case '"': - j++ - var isInEscapeSeq bool - var isFirstQuote = true - for ; j < dec.length || dec.read(); j++ { - if dec.data[j] != '"' { - continue - } - if dec.data[j-1] != '\\' || (!isInEscapeSeq && !isFirstQuote) { - break - } else { - isInEscapeSeq = false - } - if isFirstQuote { - isFirstQuote = false - } - // loop backward and count how many anti slash found - // to see if string is effectively escaped - ct := 0 - for i := j - 1; i > 0; i-- { - if dec.data[i] != '\\' { - break - } - ct++ - } - // is pair number of slashes, quote is not escaped - if ct&1 == 0 { - break - } - isInEscapeSeq = true - } - default: - continue - } - } - return 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) nextKey() (string, bool, error) { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - case '"': - dec.cursor = dec.cursor + 1 - start, end, err := dec.getString() - if err != nil { - return "", false, err - } - var found byte - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - if dec.data[dec.cursor] == ':' { - found |= 1 - break - } - } - if found&1 != 0 { - dec.cursor++ - d := dec.data[start : end-1] - return *(*string)(unsafe.Pointer(&d)), false, nil - } - return "", false, dec.raiseInvalidJSONErr(dec.cursor) - case '}': - dec.cursor = dec.cursor + 1 - return "", true, nil - default: - // can't unmarshall to struct - return "", false, dec.raiseInvalidJSONErr(dec.cursor) - } - } - return "", false, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipData() error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - // is null - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - case 't': - dec.cursor++ - err := dec.assertTrue() - if err != nil { - return err - } - return nil - // is false - case 'f': - dec.cursor++ - err := dec.assertFalse() - if err != nil { - return err - } - return nil - // is an object - case '{': - dec.cursor = dec.cursor + 1 - end, err := dec.skipObject() - dec.cursor = end - return err - // is string - case '"': - dec.cursor = dec.cursor + 1 - err := dec.skipString() - return err - // is array - case '[': - dec.cursor = dec.cursor + 1 - end, err := dec.skipArray() - dec.cursor = end - return err - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': - end, err := dec.skipNumber() - dec.cursor = end - return err - } - return dec.raiseInvalidJSONErr(dec.cursor) - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -// DecodeObjectFunc is a func type implementing UnmarshalerJSONObject. -// Use it to cast a `func(*Decoder, k string) error` to Unmarshal an object on the fly. -type DecodeObjectFunc func(*Decoder, string) error - -// UnmarshalJSONObject implements UnmarshalerJSONObject. -func (f DecodeObjectFunc) UnmarshalJSONObject(dec *Decoder, k string) error { - return f(dec, k) -} - -// NKeys implements UnmarshalerJSONObject. -func (f DecodeObjectFunc) NKeys() int { - return 0 -} - -// Add Values functions - -// AddObject decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error { - return dec.Object(v) -} - -// AddObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) AddObjectNull(v interface{}) error { - return dec.ObjectNull(v) -} - -// Object decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -func (dec *Decoder) Object(value UnmarshalerJSONObject) error { - initialKeysDone := dec.keysDone - initialChild := dec.child - dec.keysDone = 0 - dec.called = 0 - dec.child |= 1 - newCursor, err := dec.decodeObject(value) - if err != nil { - return err - } - dec.cursor = newCursor - dec.keysDone = initialKeysDone - dec.child = initialChild - dec.called |= 1 - return nil -} - -// ObjectNull decodes the JSON value within an object or an array to a UnmarshalerJSONObject. -// v should be a pointer to an UnmarshalerJSONObject, -// if `null` value is encountered in JSON, it will leave the value v untouched, -// else it will create a new instance of the UnmarshalerJSONObject behind v. -func (dec *Decoder) ObjectNull(v interface{}) error { - initialKeysDone := dec.keysDone - initialChild := dec.child - dec.keysDone = 0 - dec.called = 0 - dec.child |= 1 - newCursor, err := dec.decodeObjectNull(v) - if err != nil { - return err - } - dec.cursor = newCursor - dec.keysDone = initialKeysDone - dec.child = initialChild - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_pool.go b/vendor/github.com/francoispqt/gojay/decode_pool.go deleted file mode 100644 index 68c57138..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_pool.go +++ /dev/null @@ -1,64 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var decPool = sync.Pool{ - New: newDecoderPool, -} - -func init() { - for i := 0; i < 32; i++ { - decPool.Put(NewDecoder(nil)) - } -} - -// NewDecoder returns a new decoder. -// It takes an io.Reader implementation as data input. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{ - called: 0, - cursor: 0, - keysDone: 0, - err: nil, - r: r, - data: make([]byte, 512), - length: 0, - isPooled: 0, - } -} -func newDecoderPool() interface{} { - return NewDecoder(nil) -} - -// BorrowDecoder borrows a Decoder from the pool. -// It takes an io.Reader implementation as data input. -// -// In order to benefit from the pool, a borrowed decoder must be released after usage. -func BorrowDecoder(r io.Reader) *Decoder { - return borrowDecoder(r, 512) -} -func borrowDecoder(r io.Reader, bufSize int) *Decoder { - dec := decPool.Get().(*Decoder) - dec.called = 0 - dec.keysDone = 0 - dec.cursor = 0 - dec.err = nil - dec.r = r - dec.length = 0 - dec.isPooled = 0 - if bufSize > 0 { - dec.data = make([]byte, bufSize) - } - return dec -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (dec *Decoder) Release() { - dec.isPooled = 1 - decPool.Put(dec) -} diff --git a/vendor/github.com/francoispqt/gojay/decode_slice.go b/vendor/github.com/francoispqt/gojay/decode_slice.go deleted file mode 100644 index dbbb4bf3..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_slice.go +++ /dev/null @@ -1,89 +0,0 @@ -package gojay - -// AddSliceString unmarshals the next JSON array of strings to the given *[]string s -func (dec *Decoder) AddSliceString(s *[]string) error { - return dec.SliceString(s) -} - -// SliceString unmarshals the next JSON array of strings to the given *[]string s -func (dec *Decoder) SliceString(s *[]string) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var str string - if err := dec.String(&str); err != nil { - return err - } - *s = append(*s, str) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddSliceInt unmarshals the next JSON array of integers to the given *[]int s -func (dec *Decoder) AddSliceInt(s *[]int) error { - return dec.SliceInt(s) -} - -// SliceInt unmarshals the next JSON array of integers to the given *[]int s -func (dec *Decoder) SliceInt(s *[]int) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var i int - if err := dec.Int(&i); err != nil { - return err - } - *s = append(*s, i) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s -func (dec *Decoder) AddSliceFloat64(s *[]float64) error { - return dec.SliceFloat64(s) -} - -// SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s -func (dec *Decoder) SliceFloat64(s *[]float64) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var i float64 - if err := dec.Float64(&i); err != nil { - return err - } - *s = append(*s, i) - return nil - })) - - if err != nil { - return err - } - return nil -} - -// AddBool unmarshals the next JSON array of boolegers to the given *[]bool s -func (dec *Decoder) AddSliceBool(s *[]bool) error { - return dec.SliceBool(s) -} - -// SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s -func (dec *Decoder) SliceBool(s *[]bool) error { - err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error { - var b bool - if err := dec.Bool(&b); err != nil { - return err - } - *s = append(*s, b) - return nil - })) - - if err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_sqlnull.go b/vendor/github.com/francoispqt/gojay/decode_sqlnull.go deleted file mode 100644 index c25549f5..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_sqlnull.go +++ /dev/null @@ -1,157 +0,0 @@ -package gojay - -import "database/sql" - -// DecodeSQLNullString decodes a sql.NullString -func (dec *Decoder) DecodeSQLNullString(v *sql.NullString) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullString(v) -} - -func (dec *Decoder) decodeSQLNullString(v *sql.NullString) error { - var str string - if err := dec.decodeString(&str); err != nil { - return err - } - v.String = str - v.Valid = true - return nil -} - -// DecodeSQLNullInt64 decodes a sql.NullInt64 -func (dec *Decoder) DecodeSQLNullInt64(v *sql.NullInt64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullInt64(v) -} - -func (dec *Decoder) decodeSQLNullInt64(v *sql.NullInt64) error { - var i int64 - if err := dec.decodeInt64(&i); err != nil { - return err - } - v.Int64 = i - v.Valid = true - return nil -} - -// DecodeSQLNullFloat64 decodes a sql.NullString with the given format -func (dec *Decoder) DecodeSQLNullFloat64(v *sql.NullFloat64) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullFloat64(v) -} - -func (dec *Decoder) decodeSQLNullFloat64(v *sql.NullFloat64) error { - var i float64 - if err := dec.decodeFloat64(&i); err != nil { - return err - } - v.Float64 = i - v.Valid = true - return nil -} - -// DecodeSQLNullBool decodes a sql.NullString with the given format -func (dec *Decoder) DecodeSQLNullBool(v *sql.NullBool) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeSQLNullBool(v) -} - -func (dec *Decoder) decodeSQLNullBool(v *sql.NullBool) error { - var b bool - if err := dec.decodeBool(&b); err != nil { - return err - } - v.Bool = b - v.Valid = true - return nil -} - -// Add Values functions - -// AddSQLNullString decodes the JSON value within an object or an array to qn *sql.NullString -func (dec *Decoder) AddSQLNullString(v *sql.NullString) error { - return dec.SQLNullString(v) -} - -// SQLNullString decodes the JSON value within an object or an array to an *sql.NullString -func (dec *Decoder) SQLNullString(v *sql.NullString) error { - var b *string - if err := dec.StringNull(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.String = *b - v.Valid = true - } - return nil -} - -// AddSQLNullInt64 decodes the JSON value within an object or an array to qn *sql.NullInt64 -func (dec *Decoder) AddSQLNullInt64(v *sql.NullInt64) error { - return dec.SQLNullInt64(v) -} - -// SQLNullInt64 decodes the JSON value within an object or an array to an *sql.NullInt64 -func (dec *Decoder) SQLNullInt64(v *sql.NullInt64) error { - var b *int64 - if err := dec.Int64Null(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Int64 = *b - v.Valid = true - } - return nil -} - -// AddSQLNullFloat64 decodes the JSON value within an object or an array to qn *sql.NullFloat64 -func (dec *Decoder) AddSQLNullFloat64(v *sql.NullFloat64) error { - return dec.SQLNullFloat64(v) -} - -// SQLNullFloat64 decodes the JSON value within an object or an array to an *sql.NullFloat64 -func (dec *Decoder) SQLNullFloat64(v *sql.NullFloat64) error { - var b *float64 - if err := dec.Float64Null(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Float64 = *b - v.Valid = true - } - return nil -} - -// AddSQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool -func (dec *Decoder) AddSQLNullBool(v *sql.NullBool) error { - return dec.SQLNullBool(v) -} - -// SQLNullBool decodes the JSON value within an object or an array to an *sql.NullBool -func (dec *Decoder) SQLNullBool(v *sql.NullBool) error { - var b *bool - if err := dec.BoolNull(&b); err != nil { - return err - } - if b == nil { - v.Valid = false - } else { - v.Bool = *b - v.Valid = true - } - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_stream.go b/vendor/github.com/francoispqt/gojay/decode_stream.go deleted file mode 100644 index 74beee4d..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_stream.go +++ /dev/null @@ -1,115 +0,0 @@ -package gojay - -import ( - "sync" - "time" -) - -// UnmarshalerStream is the interface to implement for a slice, an array or a slice -// to decode a line delimited JSON to. -type UnmarshalerStream interface { - UnmarshalStream(*StreamDecoder) error -} - -// Stream is a struct holding the Stream api -var Stream = stream{} - -type stream struct{} - -// A StreamDecoder reads and decodes JSON values from an input stream. -// -// It implements conext.Context and provide a channel to notify interruption. -type StreamDecoder struct { - mux sync.RWMutex - *Decoder - done chan struct{} - deadline *time.Time -} - -// DecodeStream reads the next line delimited JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by c. -// -// c must implement UnmarshalerStream. Ideally c is a channel. See example for implementation. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *StreamDecoder) DecodeStream(c UnmarshalerStream) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - if dec.r == nil { - dec.err = NoReaderError("No reader given to decode stream") - close(dec.done) - return dec.err - } - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - continue - default: - // char is not space start reading - for dec.nextChar() != 0 { - // calling unmarshal stream - err := c.UnmarshalStream(dec) - if err != nil { - dec.err = err - close(dec.done) - return err - } - // garbage collects buffer - // we don't want the buffer to grow extensively - dec.data = dec.data[dec.cursor:] - dec.length = dec.length - dec.cursor - dec.cursor = 0 - } - // close the done channel to signal the end of the job - close(dec.done) - return nil - } - } - close(dec.done) - dec.mux.Lock() - err := dec.raiseInvalidJSONErr(dec.cursor) - dec.mux.Unlock() - return err -} - -// context.Context implementation - -// Done returns a channel that's closed when work is done. -// It implements context.Context -func (dec *StreamDecoder) Done() <-chan struct{} { - return dec.done -} - -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. -func (dec *StreamDecoder) Deadline() (time.Time, bool) { - if dec.deadline != nil { - return *dec.deadline, true - } - return time.Time{}, false -} - -// SetDeadline sets the deadline -func (dec *StreamDecoder) SetDeadline(t time.Time) { - dec.deadline = &t -} - -// Err returns nil if Done is not yet closed. -// If Done is closed, Err returns a non-nil error explaining why. -// It implements context.Context -func (dec *StreamDecoder) Err() error { - select { - case <-dec.done: - dec.mux.RLock() - defer dec.mux.RUnlock() - return dec.err - default: - return nil - } -} - -// Value implements context.Context -func (dec *StreamDecoder) Value(key interface{}) interface{} { - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_stream_pool.go b/vendor/github.com/francoispqt/gojay/decode_stream_pool.go deleted file mode 100644 index 8e1863b9..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_stream_pool.go +++ /dev/null @@ -1,59 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var streamDecPool = sync.Pool{ - New: newStreamDecoderPool, -} - -// NewDecoder returns a new StreamDecoder. -// It takes an io.Reader implementation as data input. -// It initiates the done channel returned by Done(). -func (s stream) NewDecoder(r io.Reader) *StreamDecoder { - dec := NewDecoder(r) - streamDec := &StreamDecoder{ - Decoder: dec, - done: make(chan struct{}, 1), - mux: sync.RWMutex{}, - } - return streamDec -} -func newStreamDecoderPool() interface{} { - return Stream.NewDecoder(nil) -} - -// BorrowDecoder borrows a StreamDecoder from the pool. -// It takes an io.Reader implementation as data input. -// It initiates the done channel returned by Done(). -// -// If no StreamEncoder is available in the pool, it returns a fresh one -func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder { - return s.borrowDecoder(r, 512) -} - -func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder { - streamDec := streamDecPool.Get().(*StreamDecoder) - streamDec.called = 0 - streamDec.keysDone = 0 - streamDec.cursor = 0 - streamDec.err = nil - streamDec.r = r - streamDec.length = 0 - streamDec.isPooled = 0 - streamDec.done = make(chan struct{}, 1) - if bufSize > 0 { - streamDec.data = make([]byte, bufSize) - } - return streamDec -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (dec *StreamDecoder) Release() { - dec.isPooled = 1 - streamDecPool.Put(dec) -} diff --git a/vendor/github.com/francoispqt/gojay/decode_string.go b/vendor/github.com/francoispqt/gojay/decode_string.go deleted file mode 100644 index 694359c7..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_string.go +++ /dev/null @@ -1,260 +0,0 @@ -package gojay - -import ( - "unsafe" -) - -// DecodeString reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the string pointed to by v. -// -// See the documentation for Unmarshal for details about the conversion of JSON into a Go value. -func (dec *Decoder) DecodeString(v *string) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeString(v) -} -func (dec *Decoder) decodeString(v *string) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - // is string - continue - case '"': - dec.cursor++ - start, end, err := dec.getString() - if err != nil { - return err - } - // we do minus one to remove the last quote - d := dec.data[start : end-1] - *v = *(*string)(unsafe.Pointer(&d)) - dec.cursor = end - return nil - // is nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) decodeStringNull(v **string) error { - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - switch dec.data[dec.cursor] { - case ' ', '\n', '\t', '\r', ',': - // is string - continue - case '"': - dec.cursor++ - start, end, err := dec.getString() - - if err != nil { - return err - } - if *v == nil { - *v = new(string) - } - // we do minus one to remove the last quote - d := dec.data[start : end-1] - **v = *(*string)(unsafe.Pointer(&d)) - dec.cursor = end - return nil - // is nil - case 'n': - dec.cursor++ - err := dec.assertNull() - if err != nil { - return err - } - return nil - default: - dec.err = dec.makeInvalidUnmarshalErr(v) - err := dec.skipData() - if err != nil { - return err - } - return nil - } - } - return nil -} - -func (dec *Decoder) parseEscapedString() error { - if dec.cursor >= dec.length && !dec.read() { - return dec.raiseInvalidJSONErr(dec.cursor) - } - switch dec.data[dec.cursor] { - case '"': - dec.data[dec.cursor] = '"' - case '\\': - dec.data[dec.cursor] = '\\' - case '/': - dec.data[dec.cursor] = '/' - case 'b': - dec.data[dec.cursor] = '\b' - case 'f': - dec.data[dec.cursor] = '\f' - case 'n': - dec.data[dec.cursor] = '\n' - case 'r': - dec.data[dec.cursor] = '\r' - case 't': - dec.data[dec.cursor] = '\t' - case 'u': - start := dec.cursor - dec.cursor++ - str, err := dec.parseUnicode() - if err != nil { - return err - } - diff := dec.cursor - start - dec.data = append(append(dec.data[:start-1], str...), dec.data[dec.cursor:]...) - dec.length = len(dec.data) - dec.cursor += len(str) - diff - 1 - - return nil - default: - return dec.raiseInvalidJSONErr(dec.cursor) - } - - dec.data = append(dec.data[:dec.cursor-1], dec.data[dec.cursor:]...) - dec.length-- - - // Since we've lost a character, our dec.cursor offset is now - // 1 past the escaped character which is precisely where we - // want it. - - return nil -} - -func (dec *Decoder) getString() (int, int, error) { - // extract key - var keyStart = dec.cursor - // var str *Builder - for dec.cursor < dec.length || dec.read() { - switch dec.data[dec.cursor] { - // string found - case '"': - dec.cursor = dec.cursor + 1 - return keyStart, dec.cursor, nil - // slash found - case '\\': - dec.cursor = dec.cursor + 1 - err := dec.parseEscapedString() - if err != nil { - return 0, 0, err - } - default: - dec.cursor = dec.cursor + 1 - continue - } - } - return 0, 0, dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipEscapedString() error { - start := dec.cursor - for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { - if dec.data[dec.cursor] != '\\' { - d := dec.data[dec.cursor] - dec.cursor = dec.cursor + 1 - nSlash := dec.cursor - start - switch d { - case '"': - // nSlash must be odd - if nSlash&1 != 1 { - return dec.raiseInvalidJSONErr(dec.cursor) - } - return nil - case 'u': // is unicode, we skip the following characters and place the cursor one one byte backward to avoid it breaking when returning to skipString - if err := dec.skipString(); err != nil { - return err - } - dec.cursor-- - return nil - case 'n', 'r', 't', '/', 'f', 'b': - return nil - default: - // nSlash must be even - if nSlash&1 == 1 { - return dec.raiseInvalidJSONErr(dec.cursor) - } - return nil - } - } - } - return dec.raiseInvalidJSONErr(dec.cursor) -} - -func (dec *Decoder) skipString() error { - for dec.cursor < dec.length || dec.read() { - switch dec.data[dec.cursor] { - // found the closing quote - // let's return - case '"': - dec.cursor = dec.cursor + 1 - return nil - // solidus found start parsing an escaped string - case '\\': - dec.cursor = dec.cursor + 1 - err := dec.skipEscapedString() - if err != nil { - return err - } - default: - dec.cursor = dec.cursor + 1 - continue - } - } - return dec.raiseInvalidJSONErr(len(dec.data) - 1) -} - -// Add Values functions - -// AddString decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -func (dec *Decoder) AddString(v *string) error { - return dec.String(v) -} - -// AddStringNull decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) AddStringNull(v **string) error { - return dec.StringNull(v) -} - -// String decodes the JSON value within an object or an array to a *string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -func (dec *Decoder) String(v *string) error { - err := dec.decodeString(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} - -// StringNull decodes the JSON value within an object or an array to a **string. -// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. -// If a `null` is encountered, gojay does not change the value of the pointer. -func (dec *Decoder) StringNull(v **string) error { - err := dec.decodeStringNull(v) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_string_unicode.go b/vendor/github.com/francoispqt/gojay/decode_string_unicode.go deleted file mode 100644 index 9e14d52b..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_string_unicode.go +++ /dev/null @@ -1,98 +0,0 @@ -package gojay - -import ( - "unicode/utf16" - "unicode/utf8" -) - -func (dec *Decoder) getUnicode() (rune, error) { - i := 0 - r := rune(0) - for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ { - c := dec.data[dec.cursor] - if c >= '0' && c <= '9' { - r = r*16 + rune(c-'0') - } else if c >= 'a' && c <= 'f' { - r = r*16 + rune(c-'a'+10) - } else if c >= 'A' && c <= 'F' { - r = r*16 + rune(c-'A'+10) - } else { - return 0, InvalidJSONError("Invalid unicode code point") - } - i++ - } - return r, nil -} - -func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) { - switch c { - case 't': - str = append(str, '\t') - case 'n': - str = append(str, '\n') - case 'r': - str = append(str, '\r') - case 'b': - str = append(str, '\b') - case 'f': - str = append(str, '\f') - case '\\': - str = append(str, '\\') - default: - return nil, InvalidJSONError("Invalid JSON") - } - return str, nil -} - -func (dec *Decoder) parseUnicode() ([]byte, error) { - // get unicode after u - r, err := dec.getUnicode() - if err != nil { - return nil, err - } - // no error start making new string - str := make([]byte, 16, 16) - i := 0 - // check if code can be a surrogate utf16 - if utf16.IsSurrogate(r) { - if dec.cursor >= dec.length && !dec.read() { - return nil, dec.raiseInvalidJSONErr(dec.cursor) - } - c := dec.data[dec.cursor] - if c != '\\' { - i += utf8.EncodeRune(str, r) - return str[:i], nil - } - dec.cursor++ - if dec.cursor >= dec.length && !dec.read() { - return nil, dec.raiseInvalidJSONErr(dec.cursor) - } - c = dec.data[dec.cursor] - if c != 'u' { - i += utf8.EncodeRune(str, r) - str, err = dec.appendEscapeChar(str[:i], c) - if err != nil { - dec.err = err - return nil, err - } - i++ - dec.cursor++ - return str[:i], nil - } - dec.cursor++ - r2, err := dec.getUnicode() - if err != nil { - return nil, err - } - combined := utf16.DecodeRune(r, r2) - if combined == '\uFFFD' { - i += utf8.EncodeRune(str, r) - i += utf8.EncodeRune(str, r2) - } else { - i += utf8.EncodeRune(str, combined) - } - return str[:i], nil - } - i += utf8.EncodeRune(str, r) - return str[:i], nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_time.go b/vendor/github.com/francoispqt/gojay/decode_time.go deleted file mode 100644 index 68f906d7..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_time.go +++ /dev/null @@ -1,53 +0,0 @@ -package gojay - -import ( - "time" -) - -// DecodeTime decodes time with the given format -func (dec *Decoder) DecodeTime(v *time.Time, format string) error { - if dec.isPooled == 1 { - panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) - } - return dec.decodeTime(v, format) -} - -func (dec *Decoder) decodeTime(v *time.Time, format string) error { - if format == time.RFC3339 { - var ej = make(EmbeddedJSON, 0, 20) - if err := dec.decodeEmbeddedJSON(&ej); err != nil { - return err - } - if err := v.UnmarshalJSON(ej); err != nil { - return err - } - return nil - } - var str string - if err := dec.decodeString(&str); err != nil { - return err - } - tt, err := time.Parse(format, str) - if err != nil { - return err - } - *v = tt - return nil -} - -// Add Values functions - -// AddTime decodes the JSON value within an object or an array to a *time.Time with the given format -func (dec *Decoder) AddTime(v *time.Time, format string) error { - return dec.Time(v, format) -} - -// Time decodes the JSON value within an object or an array to a *time.Time with the given format -func (dec *Decoder) Time(v *time.Time, format string) error { - err := dec.decodeTime(v, format) - if err != nil { - return err - } - dec.called |= 1 - return nil -} diff --git a/vendor/github.com/francoispqt/gojay/decode_unsafe.go b/vendor/github.com/francoispqt/gojay/decode_unsafe.go deleted file mode 100644 index 54448fba..00000000 --- a/vendor/github.com/francoispqt/gojay/decode_unsafe.go +++ /dev/null @@ -1,120 +0,0 @@ -package gojay - -import ( - "fmt" -) - -// Unsafe is the structure holding the unsafe version of the API. -// The difference between unsafe api and regular api is that the regular API -// copies the buffer passed to Unmarshal functions to a new internal buffer. -// Making it safer because internally GoJay uses unsafe.Pointer to transform slice of bytes into a string. -var Unsafe = decUnsafe{} - -type decUnsafe struct{} - -func (u decUnsafe) UnmarshalJSONArray(data []byte, v UnmarshalerJSONArray) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = data - dec.length = len(data) - _, err := dec.decodeArray(v) - return err -} - -func (u decUnsafe) UnmarshalJSONObject(data []byte, v UnmarshalerJSONObject) error { - dec := borrowDecoder(nil, 0) - defer dec.Release() - dec.data = data - dec.length = len(data) - _, err := dec.decodeObject(v) - return err -} - -func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { - var err error - var dec *Decoder - switch vt := v.(type) { - case *string: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeString(vt) - case *int: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt(vt) - case *int8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt8(vt) - case *int16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt16(vt) - case *int32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt32(vt) - case *int64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeInt64(vt) - case *uint8: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint8(vt) - case *uint16: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint16(vt) - case *uint32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint32(vt) - case *uint64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeUint64(vt) - case *float64: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat64(vt) - case *float32: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeFloat32(vt) - case *bool: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - err = dec.decodeBool(vt) - case UnmarshalerJSONObject: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - _, err = dec.decodeObject(vt) - case UnmarshalerJSONArray: - dec = borrowDecoder(nil, 0) - dec.length = len(data) - dec.data = data - _, err = dec.decodeArray(vt) - default: - return InvalidUnmarshalError(fmt.Sprintf(invalidUnmarshalErrorMsg, vt)) - } - defer dec.Release() - if err != nil { - return err - } - return dec.err -} diff --git a/vendor/github.com/francoispqt/gojay/encode.go b/vendor/github.com/francoispqt/gojay/encode.go deleted file mode 100644 index 92edaafa..00000000 --- a/vendor/github.com/francoispqt/gojay/encode.go +++ /dev/null @@ -1,202 +0,0 @@ -package gojay - -import ( - "encoding/json" - "fmt" - "io" -) - -var nullBytes = []byte("null") - -// MarshalJSONArray returns the JSON encoding of v, an implementation of MarshalerJSONArray. -// -// -// Example: -// type TestSlice []*TestStruct -// -// func (t TestSlice) MarshalJSONArray(enc *Encoder) { -// for _, e := range t { -// enc.AddObject(e) -// } -// } -// -// func main() { -// test := &TestSlice{ -// &TestStruct{123456}, -// &TestStruct{7890}, -// } -// b, _ := Marshal(test) -// fmt.Println(b) // [{"id":123456},{"id":7890}] -// } -func MarshalJSONArray(v MarshalerJSONArray) ([]byte, error) { - enc := BorrowEncoder(nil) - enc.grow(512) - enc.writeByte('[') - v.(MarshalerJSONArray).MarshalJSONArray(enc) - enc.writeByte(']') - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - return enc.buf, nil -} - -// MarshalJSONObject returns the JSON encoding of v, an implementation of MarshalerJSONObject. -// -// Example: -// type Object struct { -// id int -// } -// func (s *Object) MarshalJSONObject(enc *gojay.Encoder) { -// enc.IntKey("id", s.id) -// } -// func (s *Object) IsNil() bool { -// return s == nil -// } -// -// func main() { -// test := &Object{ -// id: 123456, -// } -// b, _ := gojay.Marshal(test) -// fmt.Println(b) // {"id":123456} -// } -func MarshalJSONObject(v MarshalerJSONObject) ([]byte, error) { - enc := BorrowEncoder(nil) - enc.grow(512) - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - return enc.encodeObject(v) -} - -// Marshal returns the JSON encoding of v. -// -// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: -// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool -// Marshal returns an InvalidMarshalError. -func Marshal(v interface{}) ([]byte, error) { - return marshal(v, false) -} - -// MarshalAny returns the JSON encoding of v. -// -// If v is nil, not an implementation MarshalerJSONObject or MarshalerJSONArray or not one of the following types: -// string, int, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float64, float32, bool -// MarshalAny falls back to "json/encoding" package to marshal the value. -func MarshalAny(v interface{}) ([]byte, error) { - return marshal(v, true) -} - -func marshal(v interface{}, any bool) ([]byte, error) { - var ( - enc = BorrowEncoder(nil) - - buf []byte - err error - ) - - defer func() { - enc.buf = make([]byte, 0, 512) - enc.Release() - }() - - buf, err = func() ([]byte, error) { - switch vt := v.(type) { - case MarshalerJSONObject: - return enc.encodeObject(vt) - case MarshalerJSONArray: - return enc.encodeArray(vt) - case string: - return enc.encodeString(vt) - case bool: - return enc.encodeBool(vt) - case int: - return enc.encodeInt(vt) - case int64: - return enc.encodeInt64(vt) - case int32: - return enc.encodeInt(int(vt)) - case int16: - return enc.encodeInt(int(vt)) - case int8: - return enc.encodeInt(int(vt)) - case uint64: - return enc.encodeInt(int(vt)) - case uint32: - return enc.encodeInt(int(vt)) - case uint16: - return enc.encodeInt(int(vt)) - case uint8: - return enc.encodeInt(int(vt)) - case float64: - return enc.encodeFloat(vt) - case float32: - return enc.encodeFloat32(vt) - case *EmbeddedJSON: - return enc.encodeEmbeddedJSON(vt) - default: - if any { - return json.Marshal(vt) - } - - return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - } - }() - return buf, err -} - -// MarshalerJSONObject is the interface to implement for struct to be encoded -type MarshalerJSONObject interface { - MarshalJSONObject(enc *Encoder) - IsNil() bool -} - -// MarshalerJSONArray is the interface to implement -// for a slice or an array to be encoded -type MarshalerJSONArray interface { - MarshalJSONArray(enc *Encoder) - IsNil() bool -} - -// An Encoder writes JSON values to an output stream. -type Encoder struct { - buf []byte - isPooled byte - w io.Writer - err error - hasKeys bool - keys []string -} - -// AppendBytes allows a modular usage by appending bytes manually to the current state of the buffer. -func (enc *Encoder) AppendBytes(b []byte) { - enc.writeBytes(b) -} - -// AppendByte allows a modular usage by appending a single byte manually to the current state of the buffer. -func (enc *Encoder) AppendByte(b byte) { - enc.writeByte(b) -} - -// Buf returns the Encoder's buffer. -func (enc *Encoder) Buf() []byte { - return enc.buf -} - -// Write writes to the io.Writer and resets the buffer. -func (enc *Encoder) Write() (int, error) { - i, err := enc.w.Write(enc.buf) - enc.buf = enc.buf[:0] - return i, err -} - -func (enc *Encoder) getPreviousRune() byte { - last := len(enc.buf) - 1 - return enc.buf[last] -} diff --git a/vendor/github.com/francoispqt/gojay/encode_array.go b/vendor/github.com/francoispqt/gojay/encode_array.go deleted file mode 100644 index 5e9d49e8..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_array.go +++ /dev/null @@ -1,212 +0,0 @@ -package gojay - -// EncodeArray encodes an implementation of MarshalerJSONArray to JSON -func (enc *Encoder) EncodeArray(v MarshalerJSONArray) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeArray(v) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} -func (enc *Encoder) encodeArray(v MarshalerJSONArray) ([]byte, error) { - enc.grow(200) - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') - return enc.buf, enc.err -} - -// AddArray adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) AddArray(v MarshalerJSONArray) { - enc.Array(v) -} - -// AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerAddArrayOmitEmpty -func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) { - enc.ArrayOmitEmpty(v) -} - -// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler, if v is empty, `null` will be encoded` -func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) { - enc.ArrayNullEmpty(v) -} - -// AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key -// value must implement Marshaler -func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) { - enc.ArrayKey(key, v) -} - -// AddArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { - enc.ArrayKeyOmitEmpty(key, v) -} - -// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil. -// Must be called inside an object as it will encode a key. `null` will be encoded` -func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) { - enc.ArrayKeyNullEmpty(key, v) -} - -// Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) Array(v MarshalerJSONArray) { - if v.IsNil() { - enc.grow(3) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - enc.writeByte(']') - return - } - enc.grow(100) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) { - if v.IsNil() { - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler -func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) { - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('[') - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key -// value must implement Marshaler -func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - enc.writeByte(']') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil. -// Must be called inside an object as it will encode a key. -func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyArr) - v.MarshalJSONArray(enc) - enc.writeByte(']') -} - -// EncodeArrayFunc is a custom func type implementing MarshaleArray. -// Use it to cast a func(*Encoder) to Marshal an object. -// -// enc := gojay.NewEncoder(io.Writer) -// enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) { -// enc.AddStringKey("hello", "world") -// })) -type EncodeArrayFunc func(*Encoder) - -// MarshalJSONArray implements MarshalerJSONArray. -func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) { - f(enc) -} - -// IsNil implements MarshalerJSONArray. -func (f EncodeArrayFunc) IsNil() bool { - return f == nil -} diff --git a/vendor/github.com/francoispqt/gojay/encode_bool.go b/vendor/github.com/francoispqt/gojay/encode_bool.go deleted file mode 100644 index 253e0378..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_bool.go +++ /dev/null @@ -1,164 +0,0 @@ -package gojay - -import "strconv" - -// EncodeBool encodes a bool to JSON -func (enc *Encoder) EncodeBool(v bool) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeBool(v) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// encodeBool encodes a bool to JSON -func (enc *Encoder) encodeBool(v bool) ([]byte, error) { - enc.grow(5) - if v { - enc.writeString("true") - } else { - enc.writeString("false") - } - return enc.buf, enc.err -} - -// AddBool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBool(v bool) { - enc.Bool(v) -} - -// AddBoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBoolOmitEmpty(v bool) { - enc.BoolOmitEmpty(v) -} - -// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddBoolNullEmpty(v bool) { - enc.BoolNullEmpty(v) -} - -// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKey(key string, v bool) { - enc.BoolKey(key, v) -} - -// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) { - enc.BoolKeyOmitEmpty(key, v) -} - -// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) { - enc.BoolKeyNullEmpty(key, v) -} - -// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Bool(v bool) { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v { - enc.writeString("true") - } else { - enc.writeString("false") - } -} - -// BoolOmitEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) BoolOmitEmpty(v bool) { - if v == false { - return - } - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeString("true") -} - -// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) BoolNullEmpty(v bool) { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == false { - enc.writeBytes(nullBytes) - return - } - enc.writeString("true") -} - -// BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKey(key string, value bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendBool(enc.buf, value) -} - -// BoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == false { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendBool(enc.buf, v) -} - -// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == false { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendBool(enc.buf, v) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_builder.go b/vendor/github.com/francoispqt/gojay/encode_builder.go deleted file mode 100644 index 2895ba34..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_builder.go +++ /dev/null @@ -1,65 +0,0 @@ -package gojay - -const hex = "0123456789abcdef" - -// grow grows b's capacity, if necessary, to guarantee space for -// another n bytes. After grow(n), at least n bytes can be written to b -// without another allocation. If n is negative, grow panics. -func (enc *Encoder) grow(n int) { - if cap(enc.buf)-len(enc.buf) < n { - Buf := make([]byte, len(enc.buf), 2*cap(enc.buf)+n) - copy(Buf, enc.buf) - enc.buf = Buf - } -} - -// Write appends the contents of p to b's Buffer. -// Write always returns len(p), nil. -func (enc *Encoder) writeBytes(p []byte) { - enc.buf = append(enc.buf, p...) -} - -func (enc *Encoder) writeTwoBytes(b1 byte, b2 byte) { - enc.buf = append(enc.buf, b1, b2) -} - -// WriteByte appends the byte c to b's Buffer. -// The returned error is always nil. -func (enc *Encoder) writeByte(c byte) { - enc.buf = append(enc.buf, c) -} - -// WriteString appends the contents of s to b's Buffer. -// It returns the length of s and a nil error. -func (enc *Encoder) writeString(s string) { - enc.buf = append(enc.buf, s...) -} - -func (enc *Encoder) writeStringEscape(s string) { - l := len(s) - for i := 0; i < l; i++ { - c := s[i] - if c >= 0x20 && c != '\\' && c != '"' { - enc.writeByte(c) - continue - } - switch c { - case '\\', '"': - enc.writeTwoBytes('\\', c) - case '\n': - enc.writeTwoBytes('\\', 'n') - case '\f': - enc.writeTwoBytes('\\', 'f') - case '\b': - enc.writeTwoBytes('\\', 'b') - case '\r': - enc.writeTwoBytes('\\', 'r') - case '\t': - enc.writeTwoBytes('\\', 't') - default: - enc.writeString(`\u00`) - enc.writeTwoBytes(hex[c>>4], hex[c&0xF]) - } - continue - } -} diff --git a/vendor/github.com/francoispqt/gojay/encode_embedded_json.go b/vendor/github.com/francoispqt/gojay/encode_embedded_json.go deleted file mode 100644 index 4c99a057..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_embedded_json.go +++ /dev/null @@ -1,93 +0,0 @@ -package gojay - -// EncodeEmbeddedJSON encodes an embedded JSON. -// is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write() -func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - enc.buf = *v - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) { - enc.writeBytes(*v) - return enc.buf, nil -} - -// AddEmbeddedJSON adds an EmbeddedJSON to be encoded. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) { - enc.grow(len(*v) + 4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(*v) -} - -// AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) { - if v == nil || len(*v) == 0 { - return - } - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(*v) -} - -// AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(*v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(*v) -} - -// AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty. -// -// It basically blindly writes the bytes to the final buffer. Therefore, -// it expects the JSON to be of proper format. -func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == nil || len(*v) == 0 { - return - } - enc.grow(len(key) + len(*v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(*v) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_interface.go b/vendor/github.com/francoispqt/gojay/encode_interface.go deleted file mode 100644 index c4692e5f..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_interface.go +++ /dev/null @@ -1,173 +0,0 @@ -package gojay - -import ( - "fmt" -) - -// Encode encodes a value to JSON. -// -// If Encode cannot find a way to encode the type to JSON -// it will return an InvalidMarshalError. -func (enc *Encoder) Encode(v interface{}) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - switch vt := v.(type) { - case string: - return enc.EncodeString(vt) - case bool: - return enc.EncodeBool(vt) - case MarshalerJSONArray: - return enc.EncodeArray(vt) - case MarshalerJSONObject: - return enc.EncodeObject(vt) - case int: - return enc.EncodeInt(vt) - case int64: - return enc.EncodeInt64(vt) - case int32: - return enc.EncodeInt(int(vt)) - case int8: - return enc.EncodeInt(int(vt)) - case uint64: - return enc.EncodeUint64(vt) - case uint32: - return enc.EncodeInt(int(vt)) - case uint16: - return enc.EncodeInt(int(vt)) - case uint8: - return enc.EncodeInt(int(vt)) - case float64: - return enc.EncodeFloat(vt) - case float32: - return enc.EncodeFloat32(vt) - case *EmbeddedJSON: - return enc.EncodeEmbeddedJSON(vt) - default: - return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - } -} - -// AddInterface adds an interface{} to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInterface(value interface{}) { - switch vt := value.(type) { - case string: - enc.AddString(vt) - case bool: - enc.AddBool(vt) - case MarshalerJSONArray: - enc.AddArray(vt) - case MarshalerJSONObject: - enc.AddObject(vt) - case int: - enc.AddInt(vt) - case int64: - enc.AddInt(int(vt)) - case int32: - enc.AddInt(int(vt)) - case int8: - enc.AddInt(int(vt)) - case uint64: - enc.AddUint64(vt) - case uint32: - enc.AddInt(int(vt)) - case uint16: - enc.AddInt(int(vt)) - case uint8: - enc.AddInt(int(vt)) - case float64: - enc.AddFloat(vt) - case float32: - enc.AddFloat32(vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} - -// AddInterfaceKey adds an interface{} to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInterfaceKey(key string, value interface{}) { - switch vt := value.(type) { - case string: - enc.AddStringKey(key, vt) - case bool: - enc.AddBoolKey(key, vt) - case MarshalerJSONArray: - enc.AddArrayKey(key, vt) - case MarshalerJSONObject: - enc.AddObjectKey(key, vt) - case int: - enc.AddIntKey(key, vt) - case int64: - enc.AddIntKey(key, int(vt)) - case int32: - enc.AddIntKey(key, int(vt)) - case int16: - enc.AddIntKey(key, int(vt)) - case int8: - enc.AddIntKey(key, int(vt)) - case uint64: - enc.AddIntKey(key, int(vt)) - case uint32: - enc.AddIntKey(key, int(vt)) - case uint16: - enc.AddIntKey(key, int(vt)) - case uint8: - enc.AddIntKey(key, int(vt)) - case float64: - enc.AddFloatKey(key, vt) - case float32: - enc.AddFloat32Key(key, vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} - -// AddInterfaceKeyOmitEmpty adds an interface{} to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInterfaceKeyOmitEmpty(key string, v interface{}) { - switch vt := v.(type) { - case string: - enc.AddStringKeyOmitEmpty(key, vt) - case bool: - enc.AddBoolKeyOmitEmpty(key, vt) - case MarshalerJSONArray: - enc.AddArrayKeyOmitEmpty(key, vt) - case MarshalerJSONObject: - enc.AddObjectKeyOmitEmpty(key, vt) - case int: - enc.AddIntKeyOmitEmpty(key, vt) - case int64: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int32: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int16: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case int8: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint64: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint32: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint16: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case uint8: - enc.AddIntKeyOmitEmpty(key, int(vt)) - case float64: - enc.AddFloatKeyOmitEmpty(key, vt) - case float32: - enc.AddFloat32KeyOmitEmpty(key, vt) - default: - if vt != nil { - enc.err = InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, vt)) - return - } - return - } -} diff --git a/vendor/github.com/francoispqt/gojay/encode_null.go b/vendor/github.com/francoispqt/gojay/encode_null.go deleted file mode 100644 index cec4e639..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_null.go +++ /dev/null @@ -1,39 +0,0 @@ -package gojay - -// AddNull adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) AddNull() { - enc.Null() -} - -// Null adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) Null() { - enc.grow(5) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeBytes(nullBytes) -} - -// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) AddNullKey(key string) { - enc.NullKey(key) -} - -// NullKey adds a `null` to be encoded. Must be used while encoding an array.` -func (enc *Encoder) NullKey(key string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.writeBytes(nullBytes) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_number.go b/vendor/github.com/francoispqt/gojay/encode_number.go deleted file mode 100644 index 53affb90..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_number.go +++ /dev/null @@ -1 +0,0 @@ -package gojay diff --git a/vendor/github.com/francoispqt/gojay/encode_number_float.go b/vendor/github.com/francoispqt/gojay/encode_number_float.go deleted file mode 100644 index b45f8442..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_number_float.go +++ /dev/null @@ -1,368 +0,0 @@ -package gojay - -import "strconv" - -// EncodeFloat encodes a float64 to JSON -func (enc *Encoder) EncodeFloat(n float64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeFloat encodes a float64 to JSON -func (enc *Encoder) encodeFloat(n float64) ([]byte, error) { - enc.buf = strconv.AppendFloat(enc.buf, n, 'f', -1, 64) - return enc.buf, nil -} - -// EncodeFloat32 encodes a float32 to JSON -func (enc *Encoder) EncodeFloat32(n float32) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat32(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) { - enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32) - return enc.buf, nil -} - -// AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat(v float64) { - enc.Float64(v) -} - -// AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloatOmitEmpty(v float64) { - enc.Float64OmitEmpty(v) -} - -// AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloatNullEmpty(v float64) { - enc.Float64NullEmpty(v) -} - -// Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float(v float64) { - enc.Float64(v) -} - -// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) FloatOmitEmpty(v float64) { - enc.Float64OmitEmpty(v) -} - -// FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) FloatNullEmpty(v float64) { - enc.Float64NullEmpty(v) -} - -// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKey(key string, v float64) { - enc.Float64Key(key, v) -} - -// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) { - enc.Float64KeyOmitEmpty(key, v) -} - -// AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) { - enc.Float64KeyNullEmpty(key, v) -} - -// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) FloatKey(key string, v float64) { - enc.Float64Key(key, v) -} - -// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) { - enc.Float64KeyOmitEmpty(key, v) -} - -// FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) { - enc.Float64KeyNullEmpty(key, v) -} - -// AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat64(v float64) { - enc.Float(v) -} - -// AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat64OmitEmpty(v float64) { - enc.FloatOmitEmpty(v) -} - -// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float64(v float64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64OmitEmpty(v float64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64NullEmpty(v float64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat64Key(key string, v float64) { - enc.FloatKey(key, v) -} - -// AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) { - enc.FloatKeyOmitEmpty(key, v) -} - -// Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Float64Key(key string, value float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.grow(10) - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64) -} - -// Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) -} - -// AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddFloat32(v float32) { - enc.Float32(v) -} - -// AddFloat32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat32OmitEmpty(v float32) { - enc.Float32OmitEmpty(v) -} - -// AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddFloat32NullEmpty(v float32) { - enc.Float32NullEmpty(v) -} - -// Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Float32(v float32) { - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float32OmitEmpty(v float32) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Float32NullEmpty(v float32) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32Key(key string, v float32) { - enc.Float32Key(key, v) -} - -// AddFloat32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) { - enc.Float32KeyOmitEmpty(key, v) -} - -// AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) { - enc.Float32KeyNullEmpty(key, v) -} - -// Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Float32Key(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeByte('"') - enc.writeByte(':') - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} - -// Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key -func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_number_int.go b/vendor/github.com/francoispqt/gojay/encode_number_int.go deleted file mode 100644 index 2c4bbe34..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_number_int.go +++ /dev/null @@ -1,500 +0,0 @@ -package gojay - -import "strconv" - -// EncodeInt encodes an int to JSON -func (enc *Encoder) EncodeInt(n int) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt encodes an int to JSON -func (enc *Encoder) encodeInt(n int) ([]byte, error) { - enc.buf = strconv.AppendInt(enc.buf, int64(n), 10) - return enc.buf, nil -} - -// EncodeInt64 encodes an int64 to JSON -func (enc *Encoder) EncodeInt64(n int64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt64(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt64 encodes an int to JSON -func (enc *Encoder) encodeInt64(n int64) ([]byte, error) { - enc.buf = strconv.AppendInt(enc.buf, n, 10) - return enc.buf, nil -} - -// AddInt adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt(v int) { - enc.Int(v) -} - -// AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddIntOmitEmpty(v int) { - enc.IntOmitEmpty(v) -} - -// AddIntNullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddIntNullEmpty(v int) { - enc.IntNullEmpty(v) -} - -// Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int(v int) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntOmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) IntOmitEmpty(v int) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntNullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) IntNullEmpty(v int) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddIntKey(key string, v int) { - enc.IntKey(key, v) -} - -// AddIntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) { - enc.IntKeyOmitEmpty(key, v) -} - -// AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) { - enc.IntKeyNullEmpty(key, v) -} - -// IntKey adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) IntKey(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntKeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) IntKeyOmitEmpty(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) IntKeyNullEmpty(key string, v int) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) -} - -// AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt64(v int64) { - enc.Int64(v) -} - -// AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt64OmitEmpty(v int64) { - enc.Int64OmitEmpty(v) -} - -// AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt64NullEmpty(v int64) { - enc.Int64NullEmpty(v) -} - -// Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int64(v int64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int64OmitEmpty(v int64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int64NullEmpty(v int64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt64Key(key string, v int64) { - enc.Int64Key(key, v) -} - -// AddInt64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) { - enc.Int64KeyOmitEmpty(key, v) -} - -// AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) { - enc.Int64KeyNullEmpty(key, v) -} - -// Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int64Key(key string, v int64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64KeyOmitEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) { - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendInt(enc.buf, v, 10) -} - -// AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt32(v int32) { - enc.Int64(int64(v)) -} - -// AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt32OmitEmpty(v int32) { - enc.Int64OmitEmpty(int64(v)) -} - -// AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt32NullEmpty(v int32) { - enc.Int64NullEmpty(int64(v)) -} - -// Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int32(v int32) { - enc.Int64(int64(v)) -} - -// Int32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int32OmitEmpty(v int32) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int32NullEmpty(v int32) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt32Key(key string, v int32) { - enc.Int64Key(key, int64(v)) -} - -// AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int32Key(key string, v int32) { - enc.Int64Key(key, int64(v)) -} - -// Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt16(v int16) { - enc.Int64(int64(v)) -} - -// AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt16OmitEmpty(v int16) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int16(v int16) { - enc.Int64(int64(v)) -} - -// Int16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int16OmitEmpty(v int16) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int16NullEmpty(v int16) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt16Key(key string, v int16) { - enc.Int64Key(key, int64(v)) -} - -// AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int16Key(key string, v int16) { - enc.Int64Key(key, int64(v)) -} - -// Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddInt8(v int8) { - enc.Int64(int64(v)) -} - -// AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt8OmitEmpty(v int8) { - enc.Int64OmitEmpty(int64(v)) -} - -// AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddInt8NullEmpty(v int8) { - enc.Int64NullEmpty(int64(v)) -} - -// Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Int8(v int8) { - enc.Int64(int64(v)) -} - -// Int8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int8OmitEmpty(v int8) { - enc.Int64OmitEmpty(int64(v)) -} - -// Int8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Int8NullEmpty(v int8) { - enc.Int64NullEmpty(int64(v)) -} - -// AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddInt8Key(key string, v int8) { - enc.Int64Key(key, int64(v)) -} - -// AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) { - enc.Int64KeyNullEmpty(key, int64(v)) -} - -// Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Int8Key(key string, v int8) { - enc.Int64Key(key, int64(v)) -} - -// Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) { - enc.Int64KeyOmitEmpty(key, int64(v)) -} - -// Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) { - enc.Int64KeyNullEmpty(key, int64(v)) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_number_uint.go b/vendor/github.com/francoispqt/gojay/encode_number_uint.go deleted file mode 100644 index cd69b13f..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_number_uint.go +++ /dev/null @@ -1,362 +0,0 @@ -package gojay - -import "strconv" - -// EncodeUint64 encodes an int64 to JSON -func (enc *Encoder) EncodeUint64(n uint64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeUint64(n) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeUint64 encodes an int to JSON -func (enc *Encoder) encodeUint64(n uint64) ([]byte, error) { - enc.buf = strconv.AppendUint(enc.buf, n, 10) - return enc.buf, nil -} - -// AddUint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint64(v uint64) { - enc.Uint64(v) -} - -// AddUint64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint64OmitEmpty(v uint64) { - enc.Uint64OmitEmpty(v) -} - -// AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint64NullEmpty(v uint64) { - enc.Uint64NullEmpty(v) -} - -// Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint64(v uint64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint64OmitEmpty(v uint64) { - if v == 0 { - return - } - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint64NullEmpty(v uint64) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint64Key(key string, v uint64) { - enc.Uint64Key(key, v) -} - -// AddUint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) { - enc.Uint64KeyOmitEmpty(key, v) -} - -// AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) { - enc.Uint64KeyNullEmpty(key, v) -} - -// Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint64Key(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == 0 { - return - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == 0 { - enc.writeBytes(nullBytes) - return - } - enc.buf = strconv.AppendUint(enc.buf, v, 10) -} - -// AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint32(v uint32) { - enc.Uint64(uint64(v)) -} - -// AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint32OmitEmpty(v uint32) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint32NullEmpty(v uint32) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint32(v uint32) { - enc.Uint64(uint64(v)) -} - -// Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint32OmitEmpty(v uint32) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint32NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint32NullEmpty(v uint32) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint32Key(key string, v uint32) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint32Key(key string, v uint32) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint16(v uint16) { - enc.Uint64(uint64(v)) -} - -// AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint16OmitEmpty(v uint16) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint16NullEmpty(v uint16) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint16(v uint16) { - enc.Uint64(uint64(v)) -} - -// Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint16OmitEmpty(v uint16) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint16NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint16NullEmpty(v uint16) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint16Key(key string, v uint16) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint16Key(key string, v uint16) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddUint8(v uint8) { - enc.Uint64(uint64(v)) -} - -// AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint8OmitEmpty(v uint8) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) AddUint8NullEmpty(v uint8) { - enc.Uint64NullEmpty(uint64(v)) -} - -// Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Uint8(v uint8) { - enc.Uint64(uint64(v)) -} - -// Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint8OmitEmpty(v uint8) { - enc.Uint64OmitEmpty(uint64(v)) -} - -// Uint8NullEmpty adds an int to be encoded and skips it if its value is 0, -// must be used inside a slice or array encoding (does not encode a key). -func (enc *Encoder) Uint8NullEmpty(v uint8) { - enc.Uint64NullEmpty(uint64(v)) -} - -// AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddUint8Key(key string, v uint8) { - enc.Uint64Key(key, uint64(v)) -} - -// AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} - -// Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) Uint8Key(key string, v uint8) { - enc.Uint64Key(key, uint64(v)) -} - -// Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) { - enc.Uint64KeyOmitEmpty(key, uint64(v)) -} - -// Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. -// Must be used inside an object as it will encode a key. -func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) { - enc.Uint64KeyNullEmpty(key, uint64(v)) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_object.go b/vendor/github.com/francoispqt/gojay/encode_object.go deleted file mode 100644 index 5f2c8cf3..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_object.go +++ /dev/null @@ -1,400 +0,0 @@ -package gojay - -var objKeyStr = []byte(`":"`) -var objKeyObj = []byte(`":{`) -var objKeyArr = []byte(`":[`) -var objKey = []byte(`":`) - -// EncodeObject encodes an object to JSON -func (enc *Encoder) EncodeObject(v MarshalerJSONObject) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, err := enc.encodeObject(v) - if err != nil { - enc.err = err - return err - } - _, err = enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// EncodeObjectKeys encodes an object to JSON -func (enc *Encoder) EncodeObjectKeys(v MarshalerJSONObject, keys []string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - enc.hasKeys = true - enc.keys = keys - _, err := enc.encodeObject(v) - if err != nil { - enc.err = err - return err - } - _, err = enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -func (enc *Encoder) encodeObject(v MarshalerJSONObject) ([]byte, error) { - enc.grow(512) - enc.writeByte('{') - if !v.IsNil() { - v.MarshalJSONObject(enc) - } - if enc.hasKeys { - enc.hasKeys = false - enc.keys = nil - } - enc.writeByte('}') - return enc.buf, enc.err -} - -// AddObject adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObject(v MarshalerJSONObject) { - enc.Object(v) -} - -// AddObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) { - enc.ObjectOmitEmpty(v) -} - -// AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) { - enc.ObjectNullEmpty(v) -} - -// AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) { - enc.ObjectKey(key, v) -} - -// AddObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { - enc.ObjectKeyOmitEmpty(key, v) -} - -// AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) { - enc.ObjectKeyNullEmpty(key, v) -} - -// Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) Object(v MarshalerJSONObject) { - if v.IsNil() { - enc.grow(2) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - enc.writeByte('}') - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject. It will only encode the keys in keys. -func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) { - if v.IsNil() { - enc.grow(2) - r := enc.getPreviousRune() - if r != '{' && r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - enc.writeByte('}') - return - } - enc.grow(4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origKeys = enc.keys - var origHasKeys = enc.hasKeys - enc.hasKeys = true - enc.keys = keys - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) { - if v.IsNil() { - return - } - enc.grow(2) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) { - enc.grow(2) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - enc.writeByte('}') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key. -// Value must implement MarshalerJSONObject. It will only encode the keys in keys. -func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if value.IsNil() { - enc.grow(2 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - enc.writeByte('}') - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - var origKeys = enc.keys - var origHasKeys = enc.hasKeys - enc.hasKeys = true - enc.keys = keys - value.MarshalJSONObject(enc) - enc.hasKeys = origHasKeys - enc.keys = origKeys - enc.writeByte('}') -} - -// ObjectKeyOmitEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v.IsNil() { - return - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKeyObj) - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. -// Must be used inside a slice or array encoding (does not encode a key) -// value must implement MarshalerJSONObject -func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(5 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v.IsNil() { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('{') - - var origHasKeys = enc.hasKeys - var origKeys = enc.keys - enc.hasKeys = false - enc.keys = nil - - v.MarshalJSONObject(enc) - - enc.hasKeys = origHasKeys - enc.keys = origKeys - - enc.writeByte('}') -} - -// EncodeObjectFunc is a custom func type implementing MarshaleObject. -// Use it to cast a func(*Encoder) to Marshal an object. -// -// enc := gojay.NewEncoder(io.Writer) -// enc.EncodeObject(gojay.EncodeObjectFunc(func(enc *gojay.Encoder) { -// enc.AddStringKey("hello", "world") -// })) -type EncodeObjectFunc func(*Encoder) - -// MarshalJSONObject implements MarshalerJSONObject. -func (f EncodeObjectFunc) MarshalJSONObject(enc *Encoder) { - f(enc) -} - -// IsNil implements MarshalerJSONObject. -func (f EncodeObjectFunc) IsNil() bool { - return f == nil -} - -func (enc *Encoder) keyExists(k string) bool { - if enc.keys == nil { - return false - } - for _, key := range enc.keys { - if key == k { - return true - } - } - return false -} diff --git a/vendor/github.com/francoispqt/gojay/encode_pool.go b/vendor/github.com/francoispqt/gojay/encode_pool.go deleted file mode 100644 index 3b263225..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_pool.go +++ /dev/null @@ -1,50 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -var encPool = sync.Pool{ - New: func() interface{} { - return NewEncoder(nil) - }, -} - -var streamEncPool = sync.Pool{ - New: func() interface{} { - return Stream.NewEncoder(nil) - }, -} - -func init() { - for i := 0; i < 32; i++ { - encPool.Put(NewEncoder(nil)) - } - for i := 0; i < 32; i++ { - streamEncPool.Put(Stream.NewEncoder(nil)) - } -} - -// NewEncoder returns a new encoder or borrows one from the pool -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w} -} - -// BorrowEncoder borrows an Encoder from the pool. -func BorrowEncoder(w io.Writer) *Encoder { - enc := encPool.Get().(*Encoder) - enc.w = w - enc.buf = enc.buf[:0] - enc.isPooled = 0 - enc.err = nil - enc.hasKeys = false - enc.keys = nil - return enc -} - -// Release sends back a Encoder to the pool. -func (enc *Encoder) Release() { - enc.isPooled = 1 - encPool.Put(enc) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_slice.go b/vendor/github.com/francoispqt/gojay/encode_slice.go deleted file mode 100644 index 7d964df9..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_slice.go +++ /dev/null @@ -1,113 +0,0 @@ -package gojay - -// AddSliceString marshals the given []string s -func (enc *Encoder) AddSliceString(s []string) { - enc.SliceString(s) -} - -// SliceString marshals the given []string s -func (enc *Encoder) SliceString(s []string) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, str := range s { - enc.String(str) - } - })) -} - -// AddSliceStringKey marshals the given []string s -func (enc *Encoder) AddSliceStringKey(k string, s []string) { - enc.SliceStringKey(k, s) -} - -// SliceStringKey marshals the given []string s -func (enc *Encoder) SliceStringKey(k string, s []string) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, str := range s { - enc.String(str) - } - })) -} - -// AddSliceInt marshals the given []int s -func (enc *Encoder) AddSliceInt(s []int) { - enc.SliceInt(s) -} - -// SliceInt marshals the given []int s -func (enc *Encoder) SliceInt(s []int) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Int(i) - } - })) -} - -// AddSliceIntKey marshals the given []int s -func (enc *Encoder) AddSliceIntKey(k string, s []int) { - enc.SliceIntKey(k, s) -} - -// SliceIntKey marshals the given []int s -func (enc *Encoder) SliceIntKey(k string, s []int) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Int(i) - } - })) -} - -// AddSliceFloat64 marshals the given []float64 s -func (enc *Encoder) AddSliceFloat64(s []float64) { - enc.SliceFloat64(s) -} - -// SliceFloat64 marshals the given []float64 s -func (enc *Encoder) SliceFloat64(s []float64) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Float64(i) - } - })) -} - -// AddSliceFloat64Key marshals the given []float64 s -func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) { - enc.SliceFloat64Key(k, s) -} - -// SliceFloat64Key marshals the given []float64 s -func (enc *Encoder) SliceFloat64Key(k string, s []float64) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Float64(i) - } - })) -} - -// AddSliceBool marshals the given []bool s -func (enc *Encoder) AddSliceBool(s []bool) { - enc.SliceBool(s) -} - -// SliceBool marshals the given []bool s -func (enc *Encoder) SliceBool(s []bool) { - enc.Array(EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Bool(i) - } - })) -} - -// AddSliceBoolKey marshals the given []bool s -func (enc *Encoder) AddSliceBoolKey(k string, s []bool) { - enc.SliceBoolKey(k, s) -} - -// SliceBoolKey marshals the given []bool s -func (enc *Encoder) SliceBoolKey(k string, s []bool) { - enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) { - for _, i := range s { - enc.Bool(i) - } - })) -} diff --git a/vendor/github.com/francoispqt/gojay/encode_sqlnull.go b/vendor/github.com/francoispqt/gojay/encode_sqlnull.go deleted file mode 100644 index 04ff5962..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_sqlnull.go +++ /dev/null @@ -1,377 +0,0 @@ -package gojay - -import "database/sql" - -// EncodeSQLNullString encodes a string to -func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeString(v.String) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullString(v *sql.NullString) { - enc.String(v.String) -} - -// AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringOmitEmpty(v.String) - } -} - -// AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) { - if v != nil && v.Valid { - enc.StringNullEmpty(v.String) - } -} - -// AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) { - enc.StringKey(key, v.String) -} - -// AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringKeyOmitEmpty(key, v.String) - } -} - -// SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullString(v *sql.NullString) { - enc.String(v.String) -} - -// SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.String(v.String) - } -} - -// SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) { - if v != nil && v.Valid { - enc.StringNullEmpty(v.String) - } -} - -// SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) { - enc.StringKey(key, v.String) -} - -// SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid && v.String != "" { - enc.StringKeyOmitEmpty(key, v.String) - } -} - -// SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) { - if v != nil && v.Valid { - enc.StringKeyNullEmpty(key, v.String) - } -} - -// NullInt64 - -// EncodeSQLNullInt64 encodes a string to -func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeInt64(v.Int64) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) { - enc.Int64(v.Int64) -} - -// AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64OmitEmpty(v.Int64) - } -} - -// AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64NullEmpty(v.Int64) - } -} - -// AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) { - enc.Int64Key(key, v.Int64) -} - -// AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64KeyOmitEmpty(key, v.Int64) - } -} - -// AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64KeyNullEmpty(key, v.Int64) - } -} - -// SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) { - enc.Int64(v.Int64) -} - -// SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64(v.Int64) - } -} - -// SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64NullEmpty(v.Int64) - } -} - -// SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) { - enc.Int64Key(key, v.Int64) -} - -// SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid && v.Int64 != 0 { - enc.Int64KeyOmitEmpty(key, v.Int64) - } -} - -// SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { - if v != nil && v.Valid { - enc.Int64KeyNullEmpty(key, v.Int64) - } -} - -// NullFloat64 - -// EncodeSQLNullFloat64 encodes a string to -func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeFloat(v.Float64) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) { - enc.Float64(v.Float64) -} - -// AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64OmitEmpty(v.Float64) - } -} - -// AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64NullEmpty(v.Float64) - } -} - -// AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) { - enc.Float64Key(key, v.Float64) -} - -// AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64KeyOmitEmpty(key, v.Float64) - } -} - -// AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64KeyNullEmpty(key, v.Float64) - } -} - -// SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) { - enc.Float64(v.Float64) -} - -// SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64(v.Float64) - } -} - -// SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64NullEmpty(v.Float64) - } -} - -// SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) { - enc.Float64Key(key, v.Float64) -} - -// SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid && v.Float64 != 0 { - enc.Float64KeyOmitEmpty(key, v.Float64) - } -} - -// SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { - if v != nil && v.Valid { - enc.Float64KeyNullEmpty(key, v.Float64) - } -} - -// NullBool - -// EncodeSQLNullBool encodes a string to -func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeBool(v.Bool) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) { - enc.Bool(v.Bool) -} - -// AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolOmitEmpty(v.Bool) - } -} - -// AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) { - enc.BoolKey(key, v.Bool) -} - -// AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolKeyOmitEmpty(key, v.Bool) - } -} - -// AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolKeyNullEmpty(key, v.Bool) - } -} - -// SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBool(v *sql.NullBool) { - enc.Bool(v.Bool) -} - -// SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.Bool(v.Bool) - } -} - -// SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolNullEmpty(v.Bool) - } -} - -// SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) { - enc.BoolKey(key, v.Bool) -} - -// SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid && v.Bool != false { - enc.BoolKeyOmitEmpty(key, v.Bool) - } -} - -// SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { - if v != nil && v.Valid { - enc.BoolKeyNullEmpty(key, v.Bool) - } -} diff --git a/vendor/github.com/francoispqt/gojay/encode_stream.go b/vendor/github.com/francoispqt/gojay/encode_stream.go deleted file mode 100644 index fae8a17c..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_stream.go +++ /dev/null @@ -1,205 +0,0 @@ -package gojay - -import ( - "strconv" - "sync" - "time" -) - -// MarshalerStream is the interface to implement -// to continuously encode of stream of data. -type MarshalerStream interface { - MarshalStream(enc *StreamEncoder) -} - -// A StreamEncoder reads and encodes values to JSON from an input stream. -// -// It implements conext.Context and provide a channel to notify interruption. -type StreamEncoder struct { - mux *sync.RWMutex - *Encoder - nConsumer int - delimiter byte - deadline *time.Time - done chan struct{} -} - -// EncodeStream spins up a defined number of non blocking consumers of the MarshalerStream m. -// -// m must implement MarshalerStream. Ideally m is a channel. See example for implementation. -// -// See the documentation for Marshal for details about the conversion of Go value to JSON. -func (s *StreamEncoder) EncodeStream(m MarshalerStream) { - // if a single consumer, just use this encoder - if s.nConsumer == 1 { - go consume(s, s, m) - return - } - // else use this Encoder only for first consumer - // and use new encoders for other consumers - // this is to avoid concurrent writing to same buffer - // resulting in a weird JSON - go consume(s, s, m) - for i := 1; i < s.nConsumer; i++ { - s.mux.RLock() - select { - case <-s.done: - default: - ss := Stream.borrowEncoder(s.w) - ss.mux.Lock() - ss.done = s.done - ss.buf = make([]byte, 0, 512) - ss.delimiter = s.delimiter - go consume(s, ss, m) - ss.mux.Unlock() - } - s.mux.RUnlock() - } - return -} - -// LineDelimited sets the delimiter to a new line character. -// -// It will add a new line after each JSON marshaled by the MarshalerStream -func (s *StreamEncoder) LineDelimited() *StreamEncoder { - s.delimiter = '\n' - return s -} - -// CommaDelimited sets the delimiter to a comma. -// -// It will add a new line after each JSON marshaled by the MarshalerStream -func (s *StreamEncoder) CommaDelimited() *StreamEncoder { - s.delimiter = ',' - return s -} - -// NConsumer sets the number of non blocking go routine to consume the stream. -func (s *StreamEncoder) NConsumer(n int) *StreamEncoder { - s.nConsumer = n - return s -} - -// Release sends back a Decoder to the pool. -// If a decoder is used after calling Release -// a panic will be raised with an InvalidUsagePooledDecoderError error. -func (s *StreamEncoder) Release() { - s.isPooled = 1 - streamEncPool.Put(s) -} - -// Done returns a channel that's closed when work is done. -// It implements context.Context -func (s *StreamEncoder) Done() <-chan struct{} { - return s.done -} - -// Err returns nil if Done is not yet closed. -// If Done is closed, Err returns a non-nil error explaining why. -// It implements context.Context -func (s *StreamEncoder) Err() error { - return s.err -} - -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. -func (s *StreamEncoder) Deadline() (time.Time, bool) { - if s.deadline != nil { - return *s.deadline, true - } - return time.Time{}, false -} - -// SetDeadline sets the deadline -func (s *StreamEncoder) SetDeadline(t time.Time) { - s.deadline = &t -} - -// Value implements context.Context -func (s *StreamEncoder) Value(key interface{}) interface{} { - return nil -} - -// Cancel cancels the consumers of the stream, interrupting the stream encoding. -// -// After calling cancel, Done() will return a closed channel. -func (s *StreamEncoder) Cancel(err error) { - s.mux.Lock() - defer s.mux.Unlock() - - select { - case <-s.done: - default: - s.err = err - close(s.done) - } -} - -// AddObject adds an object to be encoded. -// value must implement MarshalerJSONObject. -func (s *StreamEncoder) AddObject(v MarshalerJSONObject) { - if v.IsNil() { - return - } - s.Encoder.writeByte('{') - v.MarshalJSONObject(s.Encoder) - s.Encoder.writeByte('}') - s.Encoder.writeByte(s.delimiter) -} - -// AddString adds a string to be encoded. -func (s *StreamEncoder) AddString(v string) { - s.Encoder.writeByte('"') - s.Encoder.writeString(v) - s.Encoder.writeByte('"') - s.Encoder.writeByte(s.delimiter) -} - -// AddArray adds an implementation of MarshalerJSONArray to be encoded. -func (s *StreamEncoder) AddArray(v MarshalerJSONArray) { - s.Encoder.writeByte('[') - v.MarshalJSONArray(s.Encoder) - s.Encoder.writeByte(']') - s.Encoder.writeByte(s.delimiter) -} - -// AddInt adds an int to be encoded. -func (s *StreamEncoder) AddInt(value int) { - s.buf = strconv.AppendInt(s.buf, int64(value), 10) - s.Encoder.writeByte(s.delimiter) -} - -// AddFloat64 adds a float64 to be encoded. -func (s *StreamEncoder) AddFloat64(value float64) { - s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64) - s.Encoder.writeByte(s.delimiter) -} - -// AddFloat adds a float64 to be encoded. -func (s *StreamEncoder) AddFloat(value float64) { - s.AddFloat64(value) -} - -// Non exposed - -func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) { - defer s.Release() - for { - select { - case <-init.Done(): - return - default: - m.MarshalStream(s) - if s.Encoder.err != nil { - init.Cancel(s.Encoder.err) - return - } - i, err := s.Encoder.Write() - if err != nil || i == 0 { - init.Cancel(err) - return - } - } - } -} diff --git a/vendor/github.com/francoispqt/gojay/encode_stream_pool.go b/vendor/github.com/francoispqt/gojay/encode_stream_pool.go deleted file mode 100644 index 3bb8b1af..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_stream_pool.go +++ /dev/null @@ -1,38 +0,0 @@ -package gojay - -import ( - "io" - "sync" -) - -// NewEncoder returns a new StreamEncoder. -// It takes an io.Writer implementation to output data. -// It initiates the done channel returned by Done(). -func (s stream) NewEncoder(w io.Writer) *StreamEncoder { - enc := BorrowEncoder(w) - return &StreamEncoder{Encoder: enc, nConsumer: 1, done: make(chan struct{}, 1), mux: &sync.RWMutex{}} -} - -// BorrowEncoder borrows a StreamEncoder from the pool. -// It takes an io.Writer implementation to output data. -// It initiates the done channel returned by Done(). -// -// If no StreamEncoder is available in the pool, it returns a fresh one -func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder { - streamEnc := streamEncPool.Get().(*StreamEncoder) - streamEnc.w = w - streamEnc.Encoder.err = nil - streamEnc.done = make(chan struct{}, 1) - streamEnc.Encoder.buf = streamEnc.buf[:0] - streamEnc.nConsumer = 1 - streamEnc.isPooled = 0 - return streamEnc -} - -func (s stream) borrowEncoder(w io.Writer) *StreamEncoder { - streamEnc := streamEncPool.Get().(*StreamEncoder) - streamEnc.isPooled = 0 - streamEnc.w = w - streamEnc.Encoder.err = nil - return streamEnc -} diff --git a/vendor/github.com/francoispqt/gojay/encode_string.go b/vendor/github.com/francoispqt/gojay/encode_string.go deleted file mode 100644 index 438c773f..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_string.go +++ /dev/null @@ -1,186 +0,0 @@ -package gojay - -// EncodeString encodes a string to -func (enc *Encoder) EncodeString(s string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeString(s) - _, err := enc.Write() - if err != nil { - enc.err = err - return err - } - return nil -} - -// encodeString encodes a string to -func (enc *Encoder) encodeString(v string) ([]byte, error) { - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') - return enc.buf, nil -} - -// AppendString appends a string to the buffer -func (enc *Encoder) AppendString(v string) { - enc.grow(len(v) + 2) - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// AddString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddString(v string) { - enc.String(v) -} - -// AddStringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddStringOmitEmpty(v string) { - enc.StringOmitEmpty(v) -} - -// AddStringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddStringNullEmpty(v string) { - enc.StringNullEmpty(v) -} - -// AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKey(key, v string) { - enc.StringKey(key, v) -} - -// AddStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) { - enc.StringKeyOmitEmpty(key, v) -} - -// AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) AddStringKeyNullEmpty(key, v string) { - enc.StringKeyNullEmpty(key, v) -} - -// String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) String(v string) { - enc.grow(len(v) + 4) - r := enc.getPreviousRune() - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) StringOmitEmpty(v string) { - if v == "" { - return - } - r := enc.getPreviousRune() - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) StringNullEmpty(v string) { - r := enc.getPreviousRune() - if v == "" { - if r != '[' { - enc.writeByte(',') - enc.writeBytes(nullBytes) - } else { - enc.writeBytes(nullBytes) - } - return - } - if r != '[' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKey adds a string to be encoded, must be used inside an object as it will encode a key -func (enc *Encoder) StringKey(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) StringKeyOmitEmpty(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - if v == "" { - return - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.writeStringEscape(v) - enc.writeByte('"') -} - -// StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. -// Must be used inside an object as it will encode a key -func (enc *Encoder) StringKeyNullEmpty(key, v string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(len(key) + len(v) + 5) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKey) - if v == "" { - enc.writeBytes(nullBytes) - return - } - enc.writeByte('"') - enc.writeStringEscape(v) - enc.writeByte('"') -} diff --git a/vendor/github.com/francoispqt/gojay/encode_time.go b/vendor/github.com/francoispqt/gojay/encode_time.go deleted file mode 100644 index 6f99e342..00000000 --- a/vendor/github.com/francoispqt/gojay/encode_time.go +++ /dev/null @@ -1,68 +0,0 @@ -package gojay - -import ( - "time" -) - -// EncodeTime encodes a *time.Time to JSON with the given format -func (enc *Encoder) EncodeTime(t *time.Time, format string) error { - if enc.isPooled == 1 { - panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) - } - _, _ = enc.encodeTime(t, format) - _, err := enc.Write() - if err != nil { - return err - } - return nil -} - -// encodeInt encodes an int to JSON -func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) { - enc.writeByte('"') - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') - return enc.buf, nil -} - -// AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key -func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) { - enc.TimeKey(key, t, format) -} - -// TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key -func (enc *Encoder) TimeKey(key string, t *time.Time, format string) { - if enc.hasKeys { - if !enc.keyExists(key) { - return - } - } - enc.grow(10 + len(key)) - r := enc.getPreviousRune() - if r != '{' { - enc.writeTwoBytes(',', '"') - } else { - enc.writeByte('"') - } - enc.writeStringEscape(key) - enc.writeBytes(objKeyStr) - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') -} - -// AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) AddTime(t *time.Time, format string) { - enc.Time(t, format) -} - -// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key) -func (enc *Encoder) Time(t *time.Time, format string) { - enc.grow(10) - r := enc.getPreviousRune() - if r != '[' { - enc.writeByte(',') - } - enc.writeByte('"') - enc.buf = t.AppendFormat(enc.buf, format) - enc.writeByte('"') -} diff --git a/vendor/github.com/francoispqt/gojay/errors.go b/vendor/github.com/francoispqt/gojay/errors.go deleted file mode 100644 index 0fd52e66..00000000 --- a/vendor/github.com/francoispqt/gojay/errors.go +++ /dev/null @@ -1,88 +0,0 @@ -package gojay - -import ( - "errors" - "fmt" -) - -const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d" - -// InvalidJSONError is a type representing an error returned when -// Decoding encounters invalid JSON. -type InvalidJSONError string - -func (err InvalidJSONError) Error() string { - return string(err) -} - -func (dec *Decoder) raiseInvalidJSONErr(pos int) error { - var c byte - if len(dec.data) > pos { - c = dec.data[pos] - } - dec.err = InvalidJSONError( - fmt.Sprintf( - invalidJSONCharErrorMsg, - c, - pos, - ), - ) - return dec.err -} - -const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'" - -// InvalidUnmarshalError is a type representing an error returned when -// Decoding cannot unmarshal JSON to the receiver type for various reasons. -type InvalidUnmarshalError string - -func (err InvalidUnmarshalError) Error() string { - return string(err) -} - -func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error { - return InvalidUnmarshalError( - fmt.Sprintf( - invalidUnmarshalErrorMsg, - v, - ), - ) -} - -const invalidMarshalErrorMsg = "Invalid type %T provided to Marshal" - -// InvalidMarshalError is a type representing an error returned when -// Encoding did not find the proper way to encode -type InvalidMarshalError string - -func (err InvalidMarshalError) Error() string { - return string(err) -} - -// NoReaderError is a type representing an error returned when -// decoding requires a reader and none was given -type NoReaderError string - -func (err NoReaderError) Error() string { - return string(err) -} - -// InvalidUsagePooledDecoderError is a type representing an error returned -// when decoding is called on a still pooled Decoder -type InvalidUsagePooledDecoderError string - -func (err InvalidUsagePooledDecoderError) Error() string { - return string(err) -} - -// InvalidUsagePooledEncoderError is a type representing an error returned -// when decoding is called on a still pooled Encoder -type InvalidUsagePooledEncoderError string - -func (err InvalidUsagePooledEncoderError) Error() string { - return string(err) -} - -// ErrUnmarshalPtrExpected is the error returned when unmarshal expects a pointer value, -// When using `dec.ObjectNull` or `dec.ArrayNull` for example. -var ErrUnmarshalPtrExpected = errors.New("Cannot unmarshal to given value, a pointer is expected") diff --git a/vendor/github.com/francoispqt/gojay/gojay.go b/vendor/github.com/francoispqt/gojay/gojay.go deleted file mode 100644 index d0c542f6..00000000 --- a/vendor/github.com/francoispqt/gojay/gojay.go +++ /dev/null @@ -1,10 +0,0 @@ -// Package gojay implements encoding and decoding of JSON as defined in RFC 7159. -// The mapping between JSON and Go values is described -// in the documentation for the Marshal and Unmarshal functions. -// -// It aims at performance and usability by relying on simple interfaces -// to decode and encode structures, slices, arrays and even channels. -// -// On top of the simple interfaces to implement, gojay provides lots of helpers to decode and encode -// multiple of different types natively such as bit.Int, sql.NullString or time.Time -package gojay diff --git a/vendor/github.com/francoispqt/gojay/gojay.png b/vendor/github.com/francoispqt/gojay/gojay.png deleted file mode 100644 index 21090bdd20877f745003b24a4e8667f57b67a1a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44163 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
  • @ zdYQ4oTFdr*Wtf>~$Xg^?y-0*Rgrfz*Eq`P4$93F`{f2b&C%dowfbain8d><~nR)J} z1^7f^|KY<>UOv9}RimnaZY>>^f@eFM81X#0ew9Q$(mttkfyYT83i=}2xvOGdtmlhL zNzhA4tErj!k2@(Aa-;c}KdTcOrWLy^_9I>zidN_KOGKB!-rC{!yPiIlxnfo>@gA+r z`Fo2SH-;2*c=TY9fDCTuQln)6(LY2M)CxL5t~_}xH#WmLmUY?ynMOvu4JmHU_C4Q- znpMUyggk!u@S%L(@Czu`YLOGh6q#bGv=Ip@e&re^JWNF2voB5ROB4UQmiROd^zKcW z*iACyVWYL7f_2A+3R;2y5Ko=B737Wm&!$to&p+_LAw+?MeSp|uM()0|F*K+qU}q&X zi1x5Oe~o7OahE^Xw0>q8QQ*|cf)sm)F8ju6&BaeDJpQlst}*|^0Zy((Qz$T;mo8Fo z+g6JiuOZO;lB|P3-cBgd+IouIe+(_an2rNflqyrOn|)wO zO7&B}IOPTQ=pjRhq~TbXdXTqt?>As2$^V0;llkiv%m_zQL9eC1SAops?P|**heDUx zEJppaYv~o&A)l47V^q6^(&ebYjuWzB(GyZpwYt!09__&pSr|i*KY)2hy>+6XgOeoB zem3OH) z%P)psCL|<$fcrE5$)KE6Kpq$;?lHAffW^`5u}Q#u+a;@#C7UEQxb19WR|wc>TIKgS z>S~Yf(?YBe?~6z+q$$wl%GT{i$j>cuGETJ z$9sACJUf_D{c!UwsVWn;L*#%}s>fHA7Qz5NWJ&9R6xUz$NekkBSsdK~&dihTyv zjBGXY;_Q&S_fTPpHsXdV^sKBi@6mniIXl#EmVXz`IjN(Un*k_KF^x!*wAxkUSpAz8 z{8ld|(7PnpSrKnq0GeqM2w#nd{TkOu&D!>FGPuw3W0G&b<-f&%@G|`FJI``~P*5{L z2dgs@jp>I?TH>oq!l)m(-dH<@zs?VXEP|$SMJk=2TA9k@Q@U_T3;o8#H3Yt9PEA=k zIXS{V9d?caBS=EOI-%{UQ41eKHu$aTrCk??eNyAHeD36oxY5>#>~sK_SpFRy7JLIm z31!7gRoyD%dI~zm9$d}0n|smdQ7D;>*00CL%zx#Ti<2BRuYB?B}Qb{H^u*ZJAms-Ar|eq4|<$nloN6MZEz5vUlm;*+{k2*(0P6 zCC}aKq*|pazakJjrfvfML|f!2V}QPeE*E03c4`VONfx*+_NGa(k%XPna?}@3oUE$Z z2D2!*@OO6X`!}iZ<(1hk#ubvwQoLp=k(n@I+6Pg6bo0rkSLb3pSLbVATkfO{&+R=~ zz~Q-)5H4@16*0Z`ws_5BxtMlYpxLJE*Zu>^3AFwTra>l9>s5U9rqbQ4ShiQq+9L8) z3cklGW_hF`4WjYnpIt;r*3W z)$9&f5@HdTJSyu&NlTdj)>!Vh`=Q6*QCUKyZVad&0MoX4Z%#A6|J+DJLqk`)*n+ZRb87ND0+K{7@HXyrSI8IjA58my+A)J+T$-f0Jy zJ-|v~0JK2_5K?TsqU6eF+jz$CxGS@QudC$=AeupS5Er#}H$Ng;0+KkBkJxRSf8_D? zBp-cW{I6ZiCnZ71r%m{L_4g~zkr(}d;HaW(AgYN-gkLnfJ+;~yO zP`q@~0@_{|5MSCI?h~fd3kt}E>H04mt5MtuxUR$ zC}DL|H*alG;H$MsK1sh(b+n^b7E~Nsn+vw2Kw#no>=jM1_|hS9q!{HBjSS|j;s8#rSOvU7YysT-QT>-D{jAf{;Oz>K1$xn` zDtR4+3Bj1P!_m)?(xfWVn&75V347f4KDiQZ2|!o$_pE8L-no1ST3DA`dmtUk7{CxJ z8WFnHwgIoyjNZHa=M->F<*qL#+|Z_9({#b(8s8g=>XieOZN7w028VObH%{ZmwV9fN z&%*6Yo7-R(AD!{mM(Te+I2gbO-$PfIewG`pMcMXdG>eY@Oi7Sjbb>mi#($c)|F0LI z-~orfo4e}I^g)gHU&2ZSP@npb9#%V>zao=Nbr{O;H3!gvm2^vD><=Ip0UF`QV_qv; z1|J9(IFz!Iy1nvPVJzL-17uzCZ6BlTFcknDL8bTzJ+tlH6k zX8@Y23DLx`IY{I39+7@L@Ts>t&jJ(Cbpy#DP+s^cTmHME#lNoxb>7WO1kx&g05|#q z0NHC2wLg!aR-RQEJbfCBdVE(8`h-0a#aDY6Srb?AJFz1&31&M#&2{2jV{85NG0)WO zz}5PKwr@ai$RqRkXWkYID+6^pnlC|1wK1#CC`gP^FWyz5|bCv{PO;{)*5`5=(XZ;7#7~!4pNq3G%sCI z0xs|gpq}f*4RSzYAy(8<`F2wDK3=yRaOMQ>()|D;1>{G1V#QN<45}>e;_jh8R zNhgj5KEX2j5<4TIxsw@l6b&Fa3($!ETWpa|`hmX73A&N(2gMT4!`Q?`l*XKo1HqD1 z$^Pb8*lu*-fRbJN*uCH;Sv?^4h@MRUXzm4!e$(AIM^4bUSk+{7c$K#Zl{J1}!f1^G z7JonKn(d-b&GVzVW_qs}=3&RGqDMO{T2`S>Fi)2*`!#)39U=5Md1n zTScWU$xzaf%V7ZY>3G?s7$$w4-H&>1-*~3E$w#rvcGoa&c zNiyO?MuGgoO4$_>ObsL%Y6QlIR=y@-(&6Q+jO1ir2=ygx$il?LM4!m>ztLY`u#$K$ z2XM8%h0+h}xY`MxvVl_fkCAt~$^rs5)sBD3+ty5iKMyzzN2p6acBB#O_|5xs| zkwaP~;ziMDJuAu_J`uV7X>6_3gM}e{5J2}1NYGpGtT0|m8l0GAQIR6Q+pKESO#fZ} z=GI$oq9_W=wrBqVt*1NPf7kVM8c^0;NT|&Qn}tE%^WaDiH&%|7)^{>R@lv^M0z&?! zetOfwp8qCCPC&XIi1>VlshCf@6 z3M|2y%|U%$T72=kN$b;1Cw4{mszR}@3M1atN>&}4(uj+t1Cp%a8OP(k>n)P-RvV-k zyL8>I7xV94&Ga%5N>tQ?cRA{nc*F@58ao&KlzF`5A$i5k*36lxO&?Ls9Ww|4^M&FO zJ2v2grQEL4vFFNZo;$|Kn-kA+LL>+w;Qgrlt%0EP0MhEO(dPrzf^4@L5W20nWl0y$)VJhNVxyVksRKH;@u3ioCkc{ z5H65gvHWK+XlrFXr}7Q*xX61WFAo$HntspKIs)Mlv0lO$!ImQ)i^&5s$&lElcTlg`)depDjhC+I!X|M|ZO0CsOjV_-xOEU}>XLIZ;K*lQJK<6?F=}MJIgJ4ME$>D8l5S z*+bALaDGVVvh^17mUcCtF?T$wK-<%!NGkvbQscApN?GgY4ECrlMebQ8{zMPJwQ({@ zF<$SV@)N)agL%BZZ3lZz=SbTQ7){CCEc^GvGTFrFViTb_;-jOsDMYJ)7+|O+5&trt?hwgyV&SlNzXY>dqF8wZg!R7jvdz>I^?P6-j)z zR{c&^#ZV{L#`-k9L${WoEP3lofJQnpns)}v4olUFim{T!*~?GNIQ)SjBndKE{k>M8 zG#ZR|8Ca#P=u@&60b#GrnZrmuA>a`@ww4}hOMBe#grZ(|)ze^!bjQ|-jCxigETJUs*a|%6;hwLp4u-G{>q6z z{?A0W`$>#&=nHz(cE_LZX3S0ef)Y=u2ay~Tb0$)xeJr43_I>m~M@Tm#Ru})8*1kx7 z4)@l9gJJ&JD#vQ?gMoAl*0}|AGpWAX)QbPUUZTxSRdELAd0^_H#KI+DIkp zl9 z?eKT~d2r-RBYCSQgt>L_^kNMHKf-~8uOwdMvsDC?gr}DZ^$j{Hiw-oXh zE=2C;7pGuqdz*N893vo?>@;L#BNCSy>a|w@WVMfQ$I6+zbeog z{-)}@3)JJaHOWthR}|DrhyG^AB0X=SSV_vq-B9{G*$I)yG7x84HNtGasPj`+YW{fd zNOv%N4*QbNH2v&G5lakaM^N^3^xsO- z|5?iN>YNaDMv=5#=jFR-j5{?8_D{m(KD|;J19IVb?1c|-BkcK?$C+I5%Xq_+d~|S4 z$LZ`AiY3n1(9I+BRsC36HTzn5CdxcRAmHlR@%(UFYvcXRV6U!QRC+E& zF2Ch5&yyv$T!2u`W7T=b6aC8az*3AZTvY2eOPZZa4*gcr`vGOQ`%=I7SOgHRya-mx zS$oJ7Iz->aV&A7NAPeLxA`6*XwQTokbR-e3uum+l;ox9P-%myt=E+%t{&Ok$$dSXJ zLr(4-56o)X#vU2%9Tq8C=&XiwWMN_AKpu&k0IFmEdJD1R7?V|vR>5ZP=nqoAiL*S7Yp(np&8g=3O31KCIdrKK2@ve-lY2QZ1uEq zI1f6$n`fr_=+Ux{nc1{sjq~@MtcDt@pj(loF5}y6L4`(Mr3t$IQCSkq!7UfoZNhJ0 zxl+cz&J%$M?4+aCXHe+=!~H)cmr9)0VGd>`lYF+U{`{%a89BW7BX(voP`G7VrsFI69_r3b!^$s0O@QI=I1ZuvM;~Ke)-`?Kdge5~0V~f+(&O%yh zYCpJgI1_YLVE5l)G2A(%Rip8liALlWB8tu3<^Vh-dP&CM_Et4+-dcRhC|2kFLyOS*v=-*s0=m&d$YNP}zxBtEQH|H)_UROfC7G={o z=^a}tNEKA*)f;zsYxw^>grvc$ojTV@0T`b70v^vq^U&*p)-jp0*+ zo(eR&?@o?N}LROjs zfJU2VBHgjoI=gqBYQ!j$S;=wM%Fr9*L%X8CiLgBJ9 zd82uWD6FKplk8{lv7*bT_*0O#_`4wQ9ElWyEnl&agYQ-GuTl3@gyXBver`-PguFre zp9Wu?1h<4V&TPtGX&uDLz}cxn-j53S=D2~Nrd!POkyG~%4M(hh=_N*bMh(@N*rhD) zm5Q)hsSRij$q+q(k8Oc) zIx0%C!TMfQ4uS0^0i4EX{$dJ5rUKFrH;KW@U+(8~Gk?3tCo>A5+RA>#?WEtkj~9;q zfE@VXRK@&z^+pJ~9RWCCPncV>NBx(o@^{g~>ZYAKpY;JKSu`67wy>V`0QFuHwYjEH5v;>gb-i^QvgON*{p@@q1qAQll6-vi zUm>uKzC9J!kavHW8QYq@1z{@;;-@FixKJM8D2A)Ej8G(GMl$Q984{v#rGC_3-k@{e z_%#XKMaZ}(PZb&8QC6`@)8YB6?;^5*@ekFm<|m9j5#Jdn$H$!+vAWw1A}FlUTGBDs zuZYn2SM{X@CX33$nMY_$dRI<%(Y5-N=NS^Ie>%~v#N*;O+I4F z8-4>0UF8|6gu1qlM0ilOA0<4@hbKl@b7y^YyQXuvTHJPHc9ptDW%+YweQZnOBK4rw z^FmAdxe9r%H>%^|A3Ktj5_IKC_o$&dfCpTm44cKeK7D^@;xk5Dxw7Ufs}esY0tc^N ztSwbm@g*Mi)>meCMivSKTKYREHJwIkrdSYq^A)1-J#Ljkpl^%g-D zi(c~X(r{CW+^l7X6%oh&x36*{JsP}>RFO?sKS2}?Y<&Xn3hw^ZEDAVtcGX5!Jkb2T zwF0KU;*7C=QMw;nD@x{{oipEvc|Zhg<+CEV& z?s}-;e1E%iUqi+fB_X;^6$ zo?9kILBh&_*1AXe7fioaDlg%hIf#@!W zDuB`B0HTwbKT06XXDdsIpmeMxP;S_fo-sCtGY{7` zW5mbGr&FZH^S>?ps~j?lklr>2`B(U1?(nD^q4navj#{|sP?ozkV0jwn(a%*NCG1hG zTz-fI3a}vEb44#~!FB15iE9M{s{sIMb;)S=etf?aq!6pS8lts4(uO?>hDd=)!}OYk z6Bq*sn45{!oE#5DD@##y2jT+lKnL#wIuFwFDS8iSX^^Y?pe-&`4DQqiUoG$Y_iuak z-*Q;%(oRzsj%LYwZFe2ekhi}*6JX0>$DP7IR)2az;G2ofJbKYxvNLfzITZ3N?``rf z%Qy)zovij4YfA60%X^3ZE={;Nl`a!O}=8g62Ya8nSNoWhI)^iq!9a-425M`x~`YW*x$)8=#^kPOS#p`y*By<59 z+rUHZJ?6hfOXM(sxiYAcOXd0v1zHpdiah!Xu`{hgu~HgecI|l+U~hepB{5SbGKOV3;%YffqOAb z+MqvZ6u#p>nC(1o3jWb#z~jY)df4}eP?n`%I@}W3QRP}fwXExKkp@+6Ah#g04u@S@%3(7 z6EViO?e?wec*WJ6rdSmeh3TIUk7?qc%AGSqm1~mgaOlF_mSM)NarYy44Zn>kh!!^vsYEo(V>N-ODw%!(bu5tun_}_FO_U0V zlUPTSV*r*iIdeh?l6kF~U7zEFZ?@(^y6ME~e{k|J224D1n=@fb zhWEM_`VQ_2YMeHeb#!!`jizjefK;x?ah&9FoKzETCM%=cpOG|uBnL@A$ySC>%PPZ< zE%EzO9Q8yBO9k3%{w2S=%u091p?lJ2y|SABqCw6HL+<8y_%n0RvvG~`?>d+DH7OCb z-=>|OsrH^cVi9<7(NqS?6>zQ=i_++2;+~4{Y6yFJ z(8F+YFnq_!g4$r7z%=gE64K(~s{xF)5%&fW--3CrQ?@OS@e)rh{5yU6eq{JHaf1`p zHxA@iMY~1&EB=bL=Fdz22-n?SafIY0ZA9~UGR1+8q?hbRi=qOMPd@jiSW2NhF&krR z$0g+9l+;Cl#Jc$H-{Wqa=DeB@7(Gb}$J21oGf06)x$sE8cSL}(5MHKzd!uMI4)|Z{ z+hylpWh6-EN3HFSJ(a7ipOVx_2NKO;zOV8)##pp+^aA$yx#6hfPXY$Io(gwh*IJNS zCg_d}GFpiMF&X%nMwXL_N2K%#dA&1!Ci_SC*_`HCHM&;(-VrtV&92`h8-V#K>v+0QK?oLNv-M|!nvY}7I4w0hm$2hf?_6}RfIkOsg&Dr zD%a#nD;Y^>zv|QV7s3}=9==v!DnYbr)Cqg!sNeKT`l^B!c)_f^AS)vMY<4Z@WC7C_y((t{aYM0qX&1c9QJEA?OsY&KLE)0p##E0jEx~i zt;*Gizh^=LiG_|qe;50Xsc29wll2~3`ZF@p^|@j~FzF&!(l`H6-&ZsFC7E;7Q<3EU z4#Af(?X=OZpt1qhN_OxG=?5$32Bm7=yf?Y}5aic)Vamv7!VT{wd3=m_ZHXi-Ae%cl zp@V#hTbp(od?fYVbHDFjeA1KG13s2xqHmGD_eYXxJUQI8JG`P=IVF&Xeu}+EP3#j6 z14bcS4+&%rod76$*2lJ#N!c_5a=j9iri!$_PD4YgdQBx3(9t0WH!*kZ8h%I_$@RCfdk_LXWZR5R zA?-m>o`Z)F=WCtWs>Puz2rIk#u34kxJgYQ21) z-nQ!dXr(m(ss&wHv|Mvh<^ce&82GjwB~+B}p!PPjOZ;|CRtWvd(u}D4WOrc-EuQU; zZrG?4)8OAvplit=JCGBiT*jxPCmc&v)XS7fL1?EP%WArRP*(SM7iiwMl69H7^D{^t zcf}t}c4-$Htz^6dPK7IQ-x9o(jNV7zA!~YNI(=u&Nl7q|kf1|ds%H{Y|5z>W&1L<8 z#Y+>GpYPRmf=w$TtMd+$p(eZWs7yH_4vGaGW0PUUJO*%(A#Jz-ik!;JfdQ{zo3A)- z=?J*-da;!lCaIyMc8F6L_pQzr%puMKc^S5J#gG^Qox$?HT6Q%q-f@Sd|&JgeX z{QO2jNpyX}`90Jf$R%O2FplZq*-X&^NJH6R-eOpt23=_2735I*>O8Vo@4f)RtLA|x zWQ2Jx60*4USkFpfweI^<2^oKMc|&+V3QRJvUO}Q-%p3GA50!%ANSJ}0Y(cHyt7l_ zP0#YXy>~76zw~bk;FMqkz;V#s{i}8JpoPjp5)qYPwu7>3_VE2WYOc;uiF%KXcpZ~q z+Ti^Vmsj~_@J$T^pe7t0pDreU;IN31S>ye+mg_IU3`AuHgQc z3-k)rg!ZBG%}F<`vaPHB8o&ZIkrJvdr*WV}yTSt3ch$#+59Ey2wmu%Yf0E@BeCF_$R|C zdV8d|LOFXrDIv~(9L1b4?g_0C?dj7s; zECXy01=Q>*RdEdju6NOVjAMExjP1DqNxUxALx;z6!6Ao^2%4Rq$mLs5@$}(fpiAV; zLdQK{66zripZnA(c&OadShK5;qk7bIb(i$0s~AAf((++J+a3h~l)&YuEx>6{JT z6#W=k1!n&g00E^W_OmQS>AMuNz5h76k%t0$4(DqcEZu2_`Y6}#qId=$sQ|t_33}wI z6_vm4h<`4V%#)X4y9l9L#`bE!O58fPyBbP^j$T%snSx>fdtEc-Iga;P0`JX$0^Wrv z-34n#@J6>$5aL49eF5Xy6#PrH07HPFDqN9f_MdU{1yxzOdQI|q)Ph1Jbea<{RRCrc zo(vzOpY=V%S4&(UWLO#o`31qDh{^cPpg*~|a}@j5aoS=nFi~&i0a6GUqmDJ>Ng_i{ zJA0LVzu!qP`GJ#-*QEhuP48Y~uO{yKQ+@qmFX5XR-tz9Q=Hw)yOAuLNQj&He!wml+ z{gd)mq6n4`TqWE&BYi8OU=$ZWS=~8s6a2;a7nuJ0bNNbE)j3P+RVYGoCcv0y1kke~ z*GC?NAgEu}l?^d+{=vLed0_K>(Fdm6pj)&3wKi`~f-UT3I#wv`^KBBa6 z(UWrp4!M|n>8i*B=+~!q*S?}nSwMWp;AF7qG9YcZ2Qp7s+^FCW z*Ax2!=|$qH>c2~m|NOW3`cfFbbG@IEBvj&qW6xKGHsRa57H3kkO0e3Qy=mFY`GpU0 zi59~5yPho3flSKlq8R0$G3xKlNqJR^#Ek>4>A2i9J+rt*)XI@lPqaeTPqwaJsO^9& z^b5CSa;5+7obMnAti&}ADHe-4z}8=y@9;D^|ESa<<%`Bp<6|SEDWH6^5^Ui%{dU{H z@)_1^n+%#G-j=@!gJ`c0?m#XCZn?nM4jAT^XR0-hB!0#}a6%1If6yf?X+VYIwKbb< zCbD>`zbA$9WK|lGUMmN7)O428O8r!qkculEr`I_c2xhyLf3)#Iztm3`8%d}>U3Z{H z$WvvFMlHna&UHk+08o4E?ke~*ld#o|KQWyA=n7c&?Hr-Vnw1J{z;S#-o=zD%6=1$_SPM7!T`rN)z}RQ_!*y(LpMFNlO0v3-LwWL(!Nxou|qKZ zXNh}SgdL|097bXFEqK{?Xfx5PO8YHiF&H?zJZtY;H@BSN-aB7=6$j3OFWmLReu=&M zYVq`TMgS1rr&W;#t>Sxv#nVm^4k=652bBcbYeRr5_MuMkvRaH!WdFq%pFeY&H`h+i zxSab`lj8r@3q8JJlFb%HVWacnfsRQquL~LRr2T^|Vw18$gFJz(>REyt9XAhd>b!uF z^F|GT`HJj^O?MC8C=A_M%Iywpkr$17hW}aVkXpSIlp)itscche^lk@B#(1S0 z%KlJ>-H*kZV@emQD<-z)f)b`+Oqdomb_ud#-pWAekJlb9`KVvuzFg*JA%n8K7U`!C z4?d7y>kM_2PWS-(AS~gKvTp-MGu}L*So^QM7t;F7uOmS4Bf9_y);<+vve6ccouP6V zr;lrLaflNS2t)b=g}oNhwmLZa>esJdz~eWbwOzFznKpj$yzEpXC0P!ICH$GE?CH5X z6!Ri|_we1)thv}ul1?5;oNMfh5?-IVSdtoAg>*96o9+embMU)ykB%~W@ z1Qmp#B&E?0AuS_azsql}@1L@k@ZP)kp0m$B`|K<0;9_Vg$LlwckiD4f#J^O%y1F`N z9ACkx1x67Zp*Q;7K|l;!?y~&qx?s@pDffwOCr6fC*B`TcNE1H#W8d-TpDK}_cFQBs zjTHu=QXeBK*Qj6E#G;F_n-_t$mBkhqdquX6ESS)Tgfjrfu`UC<{_sHB^J`zZ;+rH- zt$5|!Dc0UTBJ2Dl)O-i#$uE~NM!zd)_BJF0`A<(zRVpu<;=Es)uX6FKN{i#>g9|wc z9XUp28?6Ky<=&ZY@dF8y&|3vM3N)m|?%G^mx?V3}m1q{s;4IWP$U6{^1^qIhhqrh} zUWq!MI70d((~0bc-S~szf#LbcnMA)r$qv;pvpO{gq_@6LbcVFf2zzD=vw)-#j zG4t3Hp4eBf0&h|d&Z}Gl&6L*$8Ipaf6&%ECt$tbyTEm^O>xR>&1g~))cvnQ6s$WQZ zoZGR6(=z>9Xghqr;d`QxT2Oa%#H%whM@q|vyRixOB1J0Vf%8X0VeVY?rx>nqva}bE zB1gtT0#6JGm6=eAeVe{vYRCqdDa-Qo5G>_!#i0t3{1&y(2DD+gJ>U{bjl*vj587Qh zwy+y*25r*|lCfsJmVY%HzClVc$u4!n!fX%aYj#YguNKGY>Wsh@XWX>7y_qBbUO{h< zK5i~EZ^)Ng0-aO%i5A2S^B*&bhITi7g9h(tFPFfGD7NjF{u_uU@@Z|no0XO6b3<5A zQ}@qb9w~xJvW13z3OA)C);DFo&1W~Em-z{U;YS=dPwCP-H*=v=>xZRn)#wa;v#;6HN3u!$xJo+F=R&DsDB8^^X z=$j&3lS)?CSzzxI^z7M6OE)fWAuf#q(x4xH1HIH@B>rorV6vA%O3$ebJn#+H5j-H5gVgk(VSS1+G7XhnXU@6&%~uNT`+5i?_poi& zCwTmMHHS&R$KT@LbOMn^_gBou#EygV501`%qJ!8``i5nERNlRUr`PN#Vj7610=T|7 zGv-LWNp7Q8R(4GeRpshC}{a5*b+SY@QAofLF) zeXCMeNX$zR@^77!rE5wMU-&*#nNq3y4Gz^=>hU%-3gx%MzlGrXS1cC zOL3i`X}FafK@}xDG~>C`XZ6f0*#?r7js^L;jWr(;Z{N$UFrXAENE2&+r36G5IC~H} z)xl`IEyI+_{yONXI*a0s8u7~mufk97^REyGyi>AoOy6UChn*|mNO3057a{cL>{vgF z%$qL0vbnzC_0GqS1TwKJ8YJf;7$I7;Dtt;0mAf9Aav^Odvx<*o6pQRwuM|%c_`OIF zx1QLeN-ZcGlGtQIWg2(+tY2}JZrp#9sJ2>XSGabFJk>=rYONMHKe1c35#bNAof`d8 zFrcy3(m4NFWJsU+-`=&<0`}|d?6ZZEiEEb;T~fz<0Czwd;L^@8yfYKvHUBDHzCbrc zp1#9jZHPCV*>b-2$8W6yE&c|A)m!1rZ#|@4UeQb4jfkR>6W97>(xnupAdO>>;3#sS zGn2Hk(AEjpMLylT`7d?(OEmVRL*#;mF{1oB0%cnA$@P|ZGI-8h=^{h+;q>cc@5(m* z`io2IH0uViis5DipDTe~x&fi1oedF*QZO|FR&d-YzyQf@tV~6I(2`Y!A1^v3yTasy zi(nQ^p!LY93R>|4MBsFHW(EiqR#6Es5Oai}2o- zr!-qPQYB^7cqQjHvkJ!CQ_uBmw|11j|4!5ia$v(57fg&MBt43F@gkwk zPZB0o({r_*k$23jEq#yevGeW8>-HrB@#(`!x~rbev+D~q?kpXpqvY??geH~2Ch$Pu zf0CuDIxbV=PGbw4OC)t%>8jE|tfFb2J%5+*OcKpubmbub!GE|mrFXk>V(x4H5EE$t zYeUpRe3rCOj`v4eTW<6f)D{*zY@>kUBR}ga*&2!Z&ns2cG+rj`?pHnxB^omOzOPWgoP_$(8x{=Cgnr+Qm=H^e5X zT;im6IA=er06X_A^_h!*%VGV4kaXpHgUQHUK38S)U48Df^3yn_b7q%egR)ApR>qm# z{lh%?#i+ zUkVt7L1)=&9hrsoqEoOAPkA@co55Sc674HdUK#c**mzwDxXs= zT)_7Nz^*%eaLo)c}!ruDK>e+mJJ}WGicn5}PCtCO?iNdunGLaO=CCjmO zG#m7C?DRoWI@{)$W?;CY23eI2UWlyPdlL-Z)s!LbD4@Mp<)9Fl2Ct}rvL_8Y@QBt4PPV8d-bS@BdkLyBaL=AqsG|j(5ZR0O`MM}DhB)BYo%W( zR#~M9xXZi<6H9Kb_oeB0(-DI`xWcus#jh3JqlUbNm`?f0AMVtR(>2=Bg*@SiEVOD* zlsMi5`Og-t;E~WGS0UI8>>Y-8tsNX2)5F$|9i=Uk>4*7HZItJ&yC7d>T0DAkQI|8J zJ3b5a7eY|W7XIb%WW*T2o%pr%8my z#qiz8S_x1p#0f1O>4II5f>nxCB9VfEpQJ96>5L2*+-V^>SF_lLnWlS4ch+M>9{Cl* z{2!7Hhkz>KLZbWZffiqDG&YMj_cboqux+u(r|wueswx3nLAc|c)E8imu@9w3mlcmR z*tGGNy&i2b)OP96+;LigR#Rs750?qae z4Iq+8r!P<+P(w~*WjH$=FT)@ts@!U(javmlKj;dVF-cE4Ce#5HSKu53AqfxZmB(o~ z;#@1Vd%CPuo9?9ZpPu}6lw`sZDuYu;0oLs+u7#-M)>4Q_vE~LL`!|17CTxKPm6>Uo zs>kU(x8NLoZO3)oB#Iqs8av3GdE%$<=C&~ms6#4vv0o(*d}&LXHb21m{G;I4ohSUKr$zOzFtEEJQE`w|Vd971C6EpR( zgW30xsVY&lhC&%TrCCeS5qBreefqLss{OZum3_SX5m|dBERk@Q%H}#EtoB>1^TVT` z_Ols;3)OXWo;6pvs+#)r0FDzU?Aq|L?T@Y& zAA2Xxx~Pj7YIHx zgc-l928z89oVZJW=-)_PP{??r|^{Rm(mcV%6* z_&WsuROfq9%HJ}mHo4W3&!B2+tL$}(xq%Jx^&4g;(?@o9wk?R;zjeMLwsu<6`S&A=(#ltK zuRu{jn0ojeeLElN{oSQu&c2D@r7aEyZ4r37Mh(C#$|f^0&y2X2$r;^biP{J}%Z;9y zQw|X8VK2qa4zEe+QwIw&-oi_Mfv70)-!tbsj4@+Rp@ie|}@~H^HL# zM%?LZhiKWK0>-W8;T-qDH&lbO#JyDI)gb3m)X~a#_T0#goA4vUt@%FO;I9RN6$OLd z93WRsOBLt*-V}5lJsEYuoWNHh zf-_Kk2_iusjbFqtPRo+M@Ta_w6LS&cnfs;YkS@CaFEr-;m1tWTbh$#xdSWq zzA#FNVF!stk(x5{nt6BKzn+7rAapH+0(tcZLC0{KZz=Ik3=44$?Pq|gf5Rq26zA8I zBvp2Y4$Q(iupiE45Ltv5R_@RZRAy3=?AO!>E*ur7wE!?H5q!G2x=yo8rLpbVQy2LH zX7<$w+JfiS{&d*9(~#&0V9qm6y0ecXH}WZED8=Yn$rf@!_m;cVfPI+AhXL);?f$cF zxLQ4<*C+e^NtD_u-Icg*d@HRjgdnY0Xq5nOk_3X~gN9d6-^U;Ms9=I2UXqYA@3uv! zXRYYx)OI)Mgl^^HaPJP)+?TG~OIyB=<}(%ZJelnLwXLe7p?O4r<^!{auZ^m?$K@Un zi7FEdRC|)iPoIR_dFrL(c89pHKWPYR=>C0O8Tq+uQvu_I2SCyILxH-w*;+ya#>rXl zzea#R0i=H7y4FJJ&Ah_z@VN#2y+7_fh;|ZX-;Myo?Y${2#rn#_Ao!FGRkMchhlB~j zxBB|}BBtX-3~x@}i$)w?t`!Y1tQd)Q5`-F#gc1V3)2n5IB>KM_^dc}$s+9=d@JV@4KdryNRlwXKkG zJ{{0BNYqxguB&H98i!O|5F?uW#%wW+PIWmMw)!1c1JV&YF8zbzIrDdW5PN)H&OT`X z!#~Zi)w}b_qWE-BOL5j}KUDZ(P?+9>J18bL_?*!_i%XxHT-0^#l-b|4Qo-c!R3v}8 z)wdK&M)S;N=K1-Zw9SWVrHY3yd1N()yX?Dxf__dQ22)cW?8O8kURb(FvS0dnHNa=(mDeXR}t(N8a zrS;%`9y**naqKuB258BM{3$5dLbly!IFZIAOy+ZIXNg8 ztkuUT76=!a(V0e}&tN^=x#_&mP)rSK)0c#|vZl=S4vYYY459yf%ny_1JycbHHZxwD ze`y2?MBv?j=96zr6qbF6^V4x%F)Nc*;1tE<@rpj5-o0~HOQi0(fodb^pn@!L!6pDM zr*Q=?2S5bJYk+>K1BW5YM5bFWBZ?P1IjHLZ(#6AOZsl+M{_B6oTF7djiQbR|M@zs+~c?Y_IZ{lYi9qko9{1Z+FF)sgkF;rf!72kW3JyA8QU2LPWrVp zXLOK3AQ@jb9vS{OB;gK#3()hjRtMz>!A1C=a-p9B`y=ODkf@-FM=R!s&l&0xWrn+i z&QsgygH|LS)Mjo*PtyQJy>%C92Q2Px=%?5O>mm_p;-k)zR!3M|dKZp;k<-?S5oSO4 z5~E1EqaO0bYoIEbmz;}{5aY0XyIPB(*vKm_N$(igq7q&tM8#pZzYFW67^!A%866+3 z%D4m{DEjHlGY$kJ;v=Mevv{45A6geE}` z;)~xsAr%@1P^UorOA?25JtZ~k2grZ2h<3mUI%;UObOR9aK0o1t^y2OD)3NklB$c$v zPe3NAKTCaSr=S65@gISSFN7m1`4;laapWaGv3nG}4z&RGr$EAUbkPCDF@Vdf(PEe* z3vTyF*2a$Ixe=ik(P6Ji5%UJ-hZ7(^s6t)P2H3l6Q}?KTGeNr3!1pW)G%!2d= z*J^@c3Qz$b0PgRU2Tg-L>I%SfPG9|gkl)!$ZCG*2qI(;)%yTDA$ipaNp8{RG+_V1n zQ!h7|sv$HVN!$3J7GQ^TCSVLzlfhhm?bjl(y)-AV@lyTH27xMn9WZeqHabU+B*rg2 zL+NPoDF6-z@~nm9sdg%UDUCKo)WFc#2?D39Kp-d=?c`Ocn_&yUElX^{Q*m{eDZzqp z3~t>B6?O6Fh`tV!ZJ87`=Z_ioIdeg^UGK1g>RAS+32ms*bq2k4APv;F$sjV4WS4-e zv4&c%9L0I!#8^%OcK(`*eDhRU*!avhV*)V-=<4nGl*IUl%mq9NZyc6+!)bXdiibj} zO_~U6U!lF%<90kWxS@Bz1V}Kt{X*~J0XQqKF>QuX4TV>)N~XRrwKDj%31t$zeZs6> zKZcax0A#^+6-*tZsLRjSG*f-AwRim)#6-v^sZ+xM#i^|_0+TR5Q_!<9g35_Svl*X7 z2}sN%@a?c*3Y**L3_u)*vJS}We8QK9B1etLo(dqc3VO0BT$oq|cynn!Bvjm);Mgs` zH$M9whJ$XvaJ|oDIAgwugA95`ddVvPE*G5-lY$3kVVg@TLV0u7L8@CH{cgbiS`E69 zug%inV3JpSLwN1y5)Zs72{Yi+JX=yWp7RtGXVlPeL+N}|AF6?-TF^j5g=XW_A6T{C z7NnW&oTugFoc{1NX((O(3UvA-^aW~j5}^RxNtqgi_0AzPDDQ!!Z?nSh8ffp8}3sNX&)4z6%! z>A;aU6=0{A#9C3`sNDMR&WMD0_`461QFCATgoI`(7Kry=y?XOzi$7dC20M5&7omT` zwmggr1|BCT*T@LBkzt!@#BCL?Qrk?5h<4b~6_`~pMr zXaISUDw)OMJ1Ebte1}fKCO+V$sCsJ}pcUr~#N7i{YAyFXfwKD$LvC4RGfmn>^gDjK_Ov#bdSoeuXV~T>A zNBx&Ie={gFxxrVY(K_~)>s;SE*N|!PNGk|%(E8rIl%v6+37p2Lve!;?Ek|kxR4{K0 zdjH>Bbqe9dc3M?>L8D9$F{;Re%ML<~=Y~LH4~5I#$e-c7c5TnPj<2LS(30(ABs1W! zf#)y*A4w%38^NPdz-pPr+nZIt(&1X7u>W1bS9-7zX^5fcZ<}O1l-=(}=;9<_8(O za_F#8$yU%_HpHw3B^tjk{`4B4G%NsNV9;#LCnV=F^Y7AP>oBKOUrD;wD;IF-7#u4E zyd=asUb{@(i+QfLDn!aLBGE8mkTVZeQbAxzv&K`zutIz4mkk2CVRqz0vV{vY!DWA@w_M1SE)dkN{haB`cC072jfjy{ivseTh^H{;(nPrr!snqYgs+zl{$^ zjR9zEKzKcOne=dXKH^*zm8J1!rr}xNQc%GFe=SucFRC#;dBW$b@68t&`vp5yH|~53 z@^;&648t#tB4N#Z9`Fxj&MFw?(YoHJ=Dw0K9Gxlsa)n~}7+f$QeMZVs*;s6CZ3jqp zsL`_^-kUsPbY^DUU!7q+kJp9#uL;?dqykiS6?ygTm^K?I1!`&GoQb8)N{-8L6o(3O z1VoQ%!2S6-Bz@U-`!jeGwDGT?^AiS+Ps7m0Pp=@t%I-D4!+MBQ{?0Per6}5uK(69~ ztB)s3eK|n-LQ-9o#Cq`?36k0jy`H0 zoRLAGV6Or~#Hv?OzE@9Url9su+I(aimh5RyZOjZ2`rJ{v|Ig>L2nnN-2?gU|EH>8OCeKIk!^D@tu&?#w06qMx9n4X1v;a>2K zeU&F%IuZy=&6L&^i4@lDV|;yK2R0NUvFJm-}xNef?5}|6t6!?MZK=C znch609qxi=9tjt6ho4eLh4nD1V7$~lrs(vkCObJY25D7Vie)87D7*YygeLx^q@;L# zNYH|L-^`uvcAIM?V(h%E*Sw*hz5FbI{h6yHn^pNhd&~|kcU9c*t)}hV#2=yPhp8Z6 zQG6eu8~2K@2(`oJ4x(|v0%#7TH#NEj)6x)no#pwSXEGJk&xafdQ?7K} z>clSNT2+XYA&7q=7QN4L@AOhOI<7LBk#D2lsyfTh%So7Xr>(b z9X3d9f{y@_=e*jl5Wwq)V6cS{glNy{Y!0P!kp-L|*gScQac><-MYrSUf1erDoMsQ^y@ZB#vLS3?;yt#fbHX56A(&jxva`r!2R5}xOyTy3=SQW?* zIs~+$t?#2|AF2&+ZxkpjBVqnASSh;UvmK2$C*Rli-s}Nmz*v=n^*~3|T)wQxM7X#b zAkcPs=rX8m900em!1$le?3D$5o`BQCxz-?>xR@B$F|*#Pu0`grxVX;t=EXl^X`yWD zo#Q`U23SZzff`4cLImvLzKF&q3LS-(8f}#^DZ-lw&jS|UgLeE;*`9hKPeP^^@(4;t zdOvPkyhlNbB}7~1-93ur)**|hRKCKc*yq_K?0zp5)W6N(P-sQaIwES~FdPQWDDE*j@Yn=u_}KNpOcrY%KG*x0SzQsKluf;@9VdbvpW1qA zrs8Y@c@P{XVvfsv*R$??^D#DZvi|8-ABwEK)=>gPJ5+PJlIzMd6UcuwhZEdr%q`#( z-AVh7jIY&*PpwJM^Clog&PRS^9??1evlas(l@X;2&s~BF0$b-mVD+-IwSD7y{~&ig zu(*6(2?4yp^Krp@AY%_QH#kfLmJqvI6t$kpLL!IM#E#BOK;lZ{nmW$Z;R#O# zKu!VFPLX9h#l%j9bi>02y>{WW)X+O@*sWYn&RM30dy_?8+{V2V(U|DfA7f8ii^SX; zt7qDR?pZ9w1N{U8UF-G8fgVC=%jBR6^v@hsw13%;(H&BsM+EzHwpof3D`z%sWhR-` zcQZ0F2|Or4ARds2>S_iQman@;uLd+spK0G*21xIZ)_-+ZvOEDtE&le0QB0ub6!LJ72zeYg04n$2pmM*sf|5Fwqfm5M zegH&@-@di9buF$EED%5|2>gx1h6jPL;4iqrI3dB%vbsAGfDB;o!o?)wJHDoSCN99+9XRmvD!pv9H{MU?64p^m;a>V5s>j`l)XilT|%$ zoMLB_O9^;Q`%4Qb6Z9TEI`V+784XmaX=r5KMfwTcWu@vk1!x_6S)#B8BEZQhq#tgN z=3@voYAV=MNdb-2zJi9^tfJsBrP5wBBYgsukBACmnGpiB zisV8{L9oyHa4=%Hko@e9?2!*!)Qbmg@Gs;)d}#nh@I!IBHoM1xP)jr9EDV`DVCjA(=`iXXidCD)cL=CPeJAD27p*R5ZHh#B=2$ zUMN~~JPt4?XfH?dpv@OeMrr{B>Qi%e{$m9l!2Iux)Z(LyvJ$Fe%!Z7~7aaMQs*v6a zFA5y4E-+6M6xsJD@l2+h8;p`Xh>0Tl?gU% z11W&?L*Ak*;Cj6FNg62FHg!3JikWLQza`sR#nPKOeI>2^bib|s;XZ(7M^azg0s6ir z@Hl^KXV60q)AJqocg>5*0k}g8K&QR9C-7pu>GOKnWI5v>AN`8Q?w_J%#8$m#epy+1 zTxI}#f5t}u?f4-Au>mi6j=_eq{vF1g26un$kyF}pNMP*oivs5mY@-O>9^nwv`B(L@ z-C|iq&3HGXUq#WezrlK3#*a7_WhY^{ii3kg(2b_=R8pYOz6Z4v7x1qsFL%LO@~PXU zbZYL#SBKNy?`<=O*qCxR*`7OIV`q>nE;G3dA&if7?)vYzZ8N+f-0@1>9p7L%jS2Jx zL=nN~+g-pkXgn7Kvlwz@rDI@$at>t^;;J)Hw3TVbj6fS>OIh^?G zpP#^A>|PD_!n8Wj@Q1-pnUq_X%Qt;bd?76un^_5Ez0QD!8w*A?Ra*$vZ)Pc`QzK%HC)Lxc z`Wk0ctXtZ@f}fqV2)O)hg<)C19$l8 zmKMIgAtOMTR|5spEacx6TmGvPe^7`zY@*!1`e)$Z|K+oYP(Ujv zI(gXw$S4~Scus)P;`W@p<%%v&N`E+UrSAl`ex3$S9DE&Ovss%VD$fj;9iCQ{MCzy;% zT!7?}FW_v-j*h}=00v>AE0G4JTU_Cn$zd)28e3r)_1D$fAvigd|8Mbeah}g4OhR9g}aJ>dn`Jf+dq;K?em$E0faV zp*Cv2P{4~QU{)2N3@8@&OlH2~InHY#&W`Hvt8cln9C@d6GXc02_MaXQRD}4{4j&2F zuirP4CH1>ZHD3hc-D$u9Xr%7&Cy1usKJR;%YK`}ULcGWSbai{Q23+=Cs$eRg(4?F@5h;A`F&`wp5I9(F~ z@y0P&pK;bEWcfufhwNhTK|k^#)Lv}54Yq5LWcbfVnnwb3#62xn&t=ZPG5?Z#6#th? z;a^Czy(VgKmm=lp!I2Jpst1<=c{8e5IA8l(Pn;icafeRGXo>G|#S6nHDZ)Qo_99tc z0bumE-aFU;1vCZWnA~ci6TgEC!`pz$xh{bbiIJ(^nmT!JivB0uEcQ~&JFa_ULz$q z!^X;KA^{@~lxKdI$}^T{VSwW-Ma#}DM8DbU;&?S@Jkoesh3se;QLOe~=psiB%BRKYD#$s5|_2KVV~=uj`sf@h>R=1FMAH0sGn3G|adtK3N6h0i}Bd148Q=YCv8Zzl>*8zkb96nVN*?r;GjXe^D%f@14mw0#xI;>E0!r`W$aUuo4 zO0wvOHtAb-TVr8*6^pmY2`PyNdI?X9-bc`S01@}Xf?hM_?&xn|Cd~PSGKfE8U$S_v zF+U->fk(XIa{V`R+DUDs5n%wQdivT~*n#R-NMOrbX;eqwT?$QwtUVnLd5Ul`J4^y{ z1rfS_vRg>FK$3y3Lgmxptg0V!83EJ!2EEfM0g<~!0v{4yCJu!34Ecn7G#lMl2l_G} zD*HCJj<0Jw?U4HReC3V^{*e9nb=X_%1v$h|oTO()^zH}_<-tl}J0yrOUz^{#vJsqH zLmt77{0^YA$%)z`TmU8^q=0~KC0M8pWQYuFT0RDK(PmW7MKdH^(P%AjatX0iKW#X$ zOnzr5tmob1NSGJa?Y&g3)2ZJ4aj8*EEiU+d#W3icX&w z^~O*Y55!LA8c1_TpvI;-6`Ru-9lFarh&!MgZZAIl_qA=0A`~%;oB56{3!nzomiY z^Y=Gl|A3fR=;)RQgG%hJPabD4EREV-Cmc7Z;bx>A!*5SNQga=uoh4NnYd~J{81Sf_oJM zeZ0A6NbkR#>zPN#w3#FQI5skactA+qQ_Z>rsi35rAC-)*W#&wSrEX9^_`BC(f0FgPdsHUW7bd*3GvNjIu2Rl@YKOe-M*7T!Jz;L3^ zwA-DDvOZgS2GYs%>Q^(ay8<a$fAfpiu&**^JJ?3LYt zWZ0*0?1gmYQ~mC#AAA*}No;b`o8mmDkEt3olA^41k;z-;?F%vAn$(*W?Xuj7Ece9u z9iHh-PciOoh#UB@xfah;E?kNnyHzX4-WL-RLCxW`gi|l*(*YVayOQ2ieqg_x2?dJd z{~*2TUFN1|O?Mzrw4{}53)C7@*TU}|l zSa$~*Z{%)x8&l!%@KJf%Cco%Ua?{HFO~BWbOL$A;E9x=+vs}jI$~cF z9#}&6PFRzlPqQ=v4YVZ$qz%@g{5yju99l>Xar`huT~YR2ny6*RLw`EQYMz2?JZ`QwZ>qAABFKB|cDO0& zMbmo+@TXfov>l9sT#4R^oRH|3)d~8!xer8#a?o3Y{Zz~GCtkEU7x#Z4C0yr^iAP+W zaMti#*3kehP^1K8774jKBSMhWv+?^}f$PSOHM3P2!E1A)(q}qzv1eF3>q&1PU%{euvnE zT_-MNCLHk1tM2>`*TEcpZaRh%QUWi+ee-KQh-7VmQWT(R>prKVXc(6o0kK~x8LMRS zB^4OQajj_kSaJ^HD4<%hz&CQkLOi5<*dXvMdIXRT-OkA4GPH{S+9>{( ztX8W@vx0E5#NsrdWTLSZBs(1F;*yX9FwMn?)sSejT~_CmkvU)04Zxk6a`Vi4=?@A| zu&v#cln9eSPpKE`zB4Ev0OFU25x6Kb`zLfw5#uy+2h)-OEbxx`ES6u=(>S^p|eOfUI&y*L!40aw*E-xsRF9)N*L#Na#9iyImZJI)B#I8qrV<{n1pp4SJ{AQx$}diWo4U^ zjm!i=G1JSHzri|c8p8fY!kQ)c+f;VoJI-4)%kaIb>^Cc<_R4<7vvy$^5;@xSVAq{} zIg0q`OBoKyLIA&muc5^@c+Gh+JWhR0FqUj7IBizZ34|dWsIYy(b*_u^5eDv#ped_A zv6r7(;8Uad`toi02{I)R7f*|v^tf&eIrW6BGf7OYZ%p$ z*YLc+9l*ysIo!z2Tx-1%>+Ea5c|m4a_}h5Qw5lIfQX;AomY6Kd&@{NhN>?*{=XxU$_9ow;{pXk}olhjUr{-hD=JHHx zNGVPJ_Hmz(*79{bSS=_{UqmF)P=#Ixguzt?;mNU}0Ol6FT}nW%tOY2uX&Ra4o-|}# zscW~;yxuF(nZI^c2(WyJ-L*JIwKYH3WQ;(LKPz@rgrpK~@rO>oS#Mw--`|wuA{kVD zTr3Kbo<&@D4X|;SDR9m#>iw4n&5U^`oQUI9bVl}_Qui~#Q#_n?He_?W+{vGpQ+?&p zPDYJ>5w|V*^R+@^sZ`A#Ko~=+Ih?GK9v1<{2LV8aQmE10rd2(0FY&IG7sZd^f(=gE zey3`u`dvgusKNv(4O>5YeW~FIX<}~_boh^r&7$9mnVmi4?xROPFJ8Qt(DA}Lrd133@IrL0G4jlhb?fO*h%Aa;YfES7@h7ew-5R*SN`EFWvj zz8zQUy&W4E<+}->?-gp7qA(|B^^E-R>#CQNl~C4oris5 z>=@b)=?)nR|DXt3IBI)d6FH;Z*6Yu#z!LdYOq@ubevXG07)B&WA9CS~`M`CsT2x)1 zzKO;HusdKJb7Bm68p%wM+uWdg4bR0Q|K*miKX?@W1i)2Q&47n*O-@{y-=Dv~7snx; zWA)!;&ivtL-026G)qNm2;5t>PtaJ|{yJ*yF?JNE1uaA1t{Mh0 zmrMNab6LH)?Of*Y4em`*_El9$7Bh1YHEehc(?sV{8zhFQc3$T{!yZ{4t{j3JfWX(X zWbL)o3mV7DT)mq{VH9d&%9H0{2U13sIQ}K*`CWwvmZ`S?{hfvxHQAi6tVE!Hl7&Yz@5m@{9)I*%ccfO1&?f=2CK0A6 zlv8^%dwq1r#Z&3g|F0S-~7rpgR>Hv0o*^hiauDh&e`J5ttxPi zlXu1X~vL_B0I{A=b9^FH@ApJ1#d5W6V80cuJ#ZSZXgKj6Syu!0oJ_{ioDN%G5GuMC`JA5>W~ee;Kiu zj^}HD@?S7F6VhdrwX8zeAdJ(;gz9#n+BdQm_mgIGB;NX1{;mQc+sMc#?g+1bQB^B1 z9!ApP3=AIYRl%T4LUQu<9ZEYv1TX9`8mj~X&}j+#$$X$T!u~=P)z%n!lKMfmK_3!` zUVRNz-JE5-03i@lNOCn*i6|p!I+QF{d2)ea6*_Et{RCnZ$AjA@R(!ie%S%d2E#}|4 z0>RSrwY=c}4i5jx8lkv^O^#+H05xS>a<7(8e2~yH;W4wS+a2>2y>r*p<%Y<|7DNjM zs4brS(;RTD7*fSAaHj(3yMiIj#WNH3P~2xFmJNc+SnMD^ur&gxncNztgG?XHUmnWy z@d63duS>X_fvwPwiUF5KO-l6tEpVom)baY{ocbF8@;+QTQ^7!&-rnDNeHN->yF6J+ z=tDx1Z%`i|7YA*{J?lOyfqEH7 zyy(VPxC2f8sEzqb&o2Pp(tyfdKb6vYdUM~k)WGKQ8|kR8?vP?=pKPhv?SiZ)gCu##+HACioQPf-Ph^ zQ{sAyPN69D_%)UkV6g*7?dR`2cH4e7gnssc#4)S+7arSlzw`J870?%%KkXgwEWU#@ zU={bFb>eprxs||L&X`NdSf-g-N$$<(#wGQIM$;T3V^iZ=t)tT)-+ETn+q#Y!$JN(c z?L;cyT+FT0B&f?RyFz{w{HC7=GGQCdmEWMdcT)P#+p-=gB7)NT73JjGLmVCFq}Zs# z;#~;|Gl~WYL#zV6e}?b+1pO_4^X84;@ovPY@*r_&u)Zlsj^u<=&eBee`Wqt>6WujyM>m!SH5DtuZ1ib~uEBS8pa!avbRQi~(qk`ZAMq`C-fFJ2xVvtA zqI~AhakJZi5j4(hx7LBEU|vO50!#JbNP(ZS?NThS+gh2dv~~~hVRMxMJHj+innP8B zVDx#~KFk08H`8sDb5SBBbgYOjYuhf|qsUze$YO{mg+`CslqjIw;Z-(>sc3DiOTaN> z!ldHkpNa+@;XBzTgRU9P)-G{p*)qCFjgiR&GR+fEY<29Svz?v~g zBEI=v@!Hz#Zz@&kf`~G(qbcaoLGdLE0$XxV?Ev5z6`}M902vLG3lZ4}GKfU&wx*H@(nf6ixPhwn? z#>CT$lKe^uJBQUsi`!qkOB>Rdtg&}BuRB@QN#b^y@EoVN>>K&WPca*+!-!Ft0(T=? z_roHlCGpc+{^uB#?&m%1&^kjTcJVVqnKysGY6wR7`T1=N_s#Mfu6?u^+h56aB0(`E z+PMQSl~@8#!2a_j7ow_oi^9Rw}dusjZcCm>O(PM6YQCZSo@XRH zl-asx)fiq>RCM8VT(-8z@nL%=*^fpRx$wa7!1nF64zGAu@!$K?H~2jusE3Cne6mxz zMfq{l`M8{;f+{=kcjXq^F6P;wY_OhSd9S?9w#!5zy!A$EdM!cGE zUp@xy?&nh}-s>6i&L>tHF*W1Au8iy%V7UgPL-HLQi+i$6VO$y(+L1 zsXGpc&ULBf9b-*ucC*}&{UGauVdKKp47$M{%er7~TCHPSk`(N(%0X;vijkUGBKGLW zBU7gRzR&nbcg4%Jh#@71-O`R>l$4U@+Eu{|vdc+u3*t^<%fR}1)MoD=qB{H6sJ38+ z7x_KE{~#*kX=iP^?9XTX2tf`e=QAUTgBmyVSc2@Xe36C%Yr9-%&HE=Ku`0y=#Wk3( z>PYn?Ch^r{Sld@)ZC(fkk{xBszhU)N4qUn^bvk_1Vxnf@ZoOa3Y>hFWO*(QZonYwg z=~-8ovmXJzif*fg%$Uy_TUF!$!ZH7a)+e+i!nqmooF z;)DI2g|~9OIkiOrx=Q;qJ3BkKT2icprCR()c?{~qB#VtG=M244>Zur9?0+E>wdsAC z!7wZr-Db7AvZ7M|cH1xA=lP6ZzUCA!%6=^zxqA{mu{QLKYo7jsS38~mAI#jhI zfW7U-_Diu+Ul3%sw1}EwepM|l-jd`DgrYY9xXuDn=OS66rHRDw?AY|Sy{p4z6>x<=#%>8SkvQ}MrX4G%Moi|>GaRo#;}*loHp?hMrX-bgyZ8o}meQFWZ`8$gi;zU*$lvVH*7 zvt`2O>C!EA@8HCBY6y=_9C4`Eu+t}CB4w!8^DI6Zk#;x6&lH!6wdKTHK|B}HtB4P< zf6?OhjqFK9r;J5e^h)8Bm!hI#Ba`b6KsmwLTpEaJ48k{Va&C#XA#~_lR9haTVj)?* z^&VVs%6V^bY4W8vB$9F`$oH=1fAR?WQjC6wTSNDEPXiN0&qp17iNInDVN4ubHZ8D}vX@UCya! zs2tesVgp4$A#915_$zcsV7_^k8{w;lYejw#Jqy1!Z$+Y?^gt{W(Z)-dKqAYGoqPU%CxU^5#g+dty1t92fRYY{r0Ry#?% zcb;OW&W_VU$UYgN`lh2gJ{Iy0=Jm!##dm0v4Za+*%WGc0Y!*-nEG6})QViWtLuK=O zk=^$GUqn+7OW_0=9vgu|%jyOBK)t-8f(nSPjE{%J(jfc_h*>F-G6&W{q=hyb$R{VBI zbrD7jcMif&Du}MznjZ1YLVWTX(}xt%?qh(k=+@SzgI;vQkqH*GI~w0t`yl|Du*dCI z5ay6-n(s7m@``b*A2&t6H8lA=^FQ>r6(O_P5Yi`5+0}!iJcYO3QWIw@gA`39?dN{` zD(`V|a$wtI?N*cjC&5UV_0v*3x^ ztRRe9Q3iK1lY)(n2NcoIkr1qO?pL<27*_y0IV_L`6p$HD3ZZbafNP~ZKn0q7Qbzy$ zXtGiAMP(`>46F_KiE)9$Gq+DAv{j>5_#izyIT<2l*L#F6f>6gn|M7xR8Yp+I%14s4 zg!R;s>duyXM^M$hdr9@?083D04M-xbYR>D+?)q?l*5UQzih~WTBA=I)J$WCBdlK3t zlO8ih!7bCZkfWyst>OEqs;X*;YR_sq!u14x9Ke+zA9F4#(A_H7_#K;eM%Bn zVpN;%wqJDy<^G#vYoc(?RV;)T_vC#B8yD3U6&3i3;a#HJ&cI3?;qEN>bGWqzAdWVo zt|VHM=$sR}xE)_s27D=46Dv1t%BXa7DrqbA@*Rsq)-}7cu~UCu+`!k4Fdf+o*GIxn zogZ9r_&IBe$~qamNp#GKFXaVK?p0mFbThSYZoX)_Zlg(1=~#D7u|e+MC^!VBHsew) zr+unV3B<2{mCZ^wpUJ2og^Yh3{MDp(ZmY@sck1owO^nj5p6ua2(qaq;_gD4e5_5`B zYjXzc(krL+KB}FgCcC@4S2ENPj+daZf^x-mwio6^Qj2C)TB9U;Be&jz2@?%!8qzpN zeFf{6llX>*hvmx92aLfik;|`z(Qi#zv*PLfpAvtyIUIa_9s9k4bjtK-R(zleD~VM6 zKPkS``HkoUDqFc|zPWr;W=5r3A+)$LYpLEy6&QkkE^qKA9n74@&40IBENqwYVn+^? ze|83g!K9j^!0r2%nI~tQFtb~Q;sa%9EQsI|;u@m7#xpjJ^OwAoLvEeyQ24_QyRI;==%zpl8Hz(*wfUjvrnl$UL99l={4dw>1ZB z&L2Gg^Liza2JMh@#1q`pqT~I#lub|l<+GI$MIo>>l>%PhHexw)&zQmqB@tXm9!g&{XTfqGM{Gv_3%!vqK8a%=AYGp6eQQp6T z2c{P>IIaCc>E(MNI>WWXsX%smrqX|kC}oEDTkoStd{X-*8YA91=kYAj(}^vcME*{ zpBA{F**Psoj!t#@T&ly!ZHWi}bY^=--V!?!+2+7E_Zgkg7W_Zn+Z4`Fs&{K`}y%TTX$$xg$YHM#deePJ$BPyYo&Y9`x^Ig=%8Cp_- z2yM1TPk`D`N=i!KLr?1BaOZ&CA-q54^7Hox6rVqs^Sq#Uz)z>Q_rA{QPpo#p3@1p- zW=A0OC$)3Bfm}&vW+s;v%>Vq8jrl3ph_#@Rxmi;fUSyWI-A^t4yQ0it{3aD?B|zf7)uw?y=_RjxJ%kSElNuvL7GV^gjJKZ2+mW&P&rYdQvt9 zVCEu%9m2yaWCg&3mft7+8~4u>D&xI`(EPE_fbi;~HIqAi-NF7prrh3|0LI|gqw~Rs z@EZS94VbpyMh(%EBU>lWkiSjg81ClB^!(Tnc3)g&xkfC#|Vz?EA{ znp5hvi2S>XA|rX!N8am_(A|FwNMUUgh(x)NpDAI0IV$9ub{4}2Hn(m5pV~GW8hOZO zkT9;$PE#cHgw0<1%KhZfZlEC%7-pI4x|S4Hw)1((uA>?KkDRG!{p2VU85%4uDoB}m zXYdVncHFY)Xb$_Ir+|r^Ahh>gS8u??t+M)MB_^o4QyS{eG z@dNRX(SMxOi6JND!^z+a3q*Ur;;ed6vmhm{Ez@ib@22}we zZm07629IAGV4YxvZc_B9F7F`rvJEx?x$0OMmwTza$WTnS!FvwM;Z3=TxyS_Xi$IM%JU0= zC#*Zq53d7cfB2@n9~`jxJ$&n^6W?C@%^bw_>w%Z&HfP|DgOESUa|^dyNfcZrpEroN zx%yDZTypan$>8Fp-V?vENNq8$Z6sbBQp0u^ii+;hGhYC>T5gTFkz8`@uP82ZTuK^WCF>-L+ zvJ3gF2L`~+7tm+aSA^9l}m=Le!cCnIr%%G7}Y5RgH@Z-JTFp z=ZNjHP`<`17}S=lXm=Ti$0nH0`aC4?w;ebvYv04ano#mc^dyLm1sj~ml`*@Vo~myH zD)bY=IAUDW(npYWc{d&lec)6(3BYRzRP@HA3ooyNS|b>;cFb~f-s1zC#=0+XCuII` z-Hv6Dn%x5@ea-*UgLzX!K9=1Kkq7Z)6C>3_*T&##(795beAY#@AlL7R zS}?-jNDP8lrmtev4cY1U+jUDpx)T5S!_}7&U$MUO-`pZ`3j^wC&|Gv%k$)dv?R5@+ z?9-1WdZTFIx3UMxJF4xf4*zC4aw2r|SSx>L$ITE~m!E?;NEf1yn@k(qT~GSuu}?p7 z6jhC<+ymjlIZjJO@H0)Egf?4J&dqj2-LSo%+n5JBiL&@FkQy#u?qdCe_P7P+2{9Wx3)f5GA zi6+1Dy~*Dv(DC1YM1JO{dj;_4XFT3(f8iFp_1Nnfo+;x;g{oP0(*bJ_7O7I8iz7}a z`Hi;QULXW&LDL)(iS(VvgAN}ulC)*xb-#H>{JBcIWQg*au9@|-T?jSYOg{B=&nl}O zCn!NW%z*X{&@0@Ii>GQU;nM2YiJA59E6X3&5q22K8&f#YZd~}LWMXn_RR#YjOz6W4 z&igDx5z*#*r30FvcR4VJqX66Lab4rZyryK<_g>g9B8XMp{r(=7Kq}bzB)^6CljnGo z03w)n(ssSn+4&Dopdj{yw*X;azh7r&N|nB97p|P5c0^?$U8Ny5G85YUwip+NLG4{h zR<^S|_ogxKmkU54G+i%f2Y?f)qnhv+PZ@ftgE%q(^4XXQ=q3Y7@&p|uKCfNUoBzRc zFcC&RbmO1Nhq^)YK(QjHmD}j+9V3%lq_b;Es5~Y~Mm2d$-a+MjBP%$rtD`lU}+pj=}h>&TX>TERGmGICsu1*_qzmd>_vv9acG*pw7YpM z`3q>!#X{i+^po7dd)C*Soi8>9UIOPudWG@Qf5g__aze7B5@rvm0Yqtd#+fyCWX0uc zB4;B50M)m16gEtp$3u2{A7t-BeCfbIyz=y8W1o^iLFC5Ws#!UuIFf{Dx~cC=OrPcR zrN`B? zsd3|j4A-{dno8iSc?Xs%xlIoNTR#A-t2)G(=D?~thgY?5ySnW3FZ@qTL%kQ!(%7lW z9|m@Uy`Sf!4cDUESzul+RK}u)UHjd`g!Lj&Yci4*(`PL5;BllRk}L#$G4wLCFm+=B z2OjPS;u{TR<%;i9%CcpH3#6t+q3mcKhglPZ@S_+R@2JG?VgCwKQOqpOmI>F z^;q1bBZk)!8=glU3DmyC;!}MrXl3Pc&-QaTJ+>*-Bh$I6@f125x5j`AD)RqTLlGMy z-vYgFO(=uGRjJZP_vLYhC9u>f%N1Rp9-iX?z<~~Rf&wi1;JGm?(Ga(wpnnBm4v$iv zL)4_g4F+{dezllIb|jIY5)N8LeV~#Mh4dL*#W=JjLpH96mR-de3zg1RAk=I?*SEOL z|J7a4EHQ*Kq*?;Cuoss5F2A>c_Aa2K1II(?$10KjzJ)~*1zZUf>7>1R z?M&AVg{0*dB-fP>2^JO>iRjyTumQyXw5dmQtQk>v$oiH7|DJ`p`3O@`{X9*1(E(R~ zoE-n`x5STtmJ|svWtYKA`%KdO0MOlX0q?>s$4x5d!!)GtZESWf^*By^*88i+jXis0 zFvy6UcpNCA3-98#-~9?IfrFXJzNowsL~t+bLJ?%iuv8AKoQ!VfLOvWD+4nfjp?FCk ze%y|Byd2qvEIEDX@g&64kPl*7SGs34&X{rz3akQ4N43uf*fK6{LpELXIO2qYejcn3 zNwQ*8CuA8XaH!2xDZAK7g~fa-33M4<%KGb>7dUX2ui@B`uDO#65hFr^b1_C`ClKJeTkSfwPu(qt? zappuAY)Kq}R5(HouJw%)!8d{mPj1Nyx#S=xa9;(BX z3UuO;mG1*QNdNeRyQp=o4As|dMjc1GK9!o@_^y}O$^VMLMFv;~`!puuZ(`rWVB*Vkzuj5rPg!Z{bb z3;GD{-j#jdqrd;YBj_U9K&)(!-+T=($D$LEhF%3@^Jzym-6~~gvzO0!Me2;Of;W>C z2iIJ_2JDQ1(geB=y++97IgnsEFK=%-Z z3^g9x+rF}}LCHvGTC!%RujN-b3mPWlCSr1+m1#lfVZp^~Jfb zCtXAhWx<6y{OS!ud-a9zzB~Vn6K=N(ZkVrGXk7o4cMl7kl6q^p|U3W%^PI4{Da!W9GW2sRvr9a4xh7#uMwm&(3^m5bJ~ zjKNd<^07OCVwRI6R1g+Yo$L$nV>aux58yg~qE#mVRQ%f{x0nJ}_ zYFG?mB{!^-D=jz@QV3la3vxGgXz8g*|Cx%RiwSMAqgw2syYO()q5Z%AE~Cf@u#hX^ zJio-f@V6VRCq_0+NMu|9RaHYo?G6W-r)M?0tQ`9)v@!G8QL!r)13^4wosrG1E#jCT zXwftA+E{WWGy~$BW>hCY=_I^0Vj||P(y80T1-P+D4HHs0L5_xN%7I)SnA+8r_q4kE zF^bo5H$drDT|V-k9(IaF>^$ngZBD4O)61gW8SoLzq7KgA|H$KwsZ_)r#tC>|ftq^C zz}bw&y6KI2*!tb8k-!=L9$pS=TCM}ys1;1QhU-IkObFn|EBkz#%;a?Ef|7m@~{au1Xu$Dnepw|z$oPNPt_ zR8f)Gbq@>LMX+E0fR`0O$ziah;MocX$Zmq>+IE7@)V-e71s@CJ%RRN>iKjY_D0dJ= zI;Uo4;$vDTx$~$)5Gyt-`fHWuI}2PZU7L26;9v8gaWGX2$DKL=R@J1`R7{^+nl2PU ww}d@?P9R=lS$Pq~YNUbv|DXSt24wKNsu%-8D+%F1g}~>krarbv!}{_60UN9I^Z)<= diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/event.go b/vendor/github.com/lucas-clemente/quic-go/qlog/event.go deleted file mode 100644 index 8427b6e2..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/event.go +++ /dev/null @@ -1,529 +0,0 @@ -package qlog - -import ( - "errors" - "fmt" - "net" - "time" - - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/logging" - - "github.com/francoispqt/gojay" -) - -func milliseconds(dur time.Duration) float64 { return float64(dur.Nanoseconds()) / 1e6 } - -type eventDetails interface { - Category() category - Name() string - gojay.MarshalerJSONObject -} - -type event struct { - RelativeTime time.Duration - eventDetails -} - -var _ gojay.MarshalerJSONObject = event{} - -func (e event) IsNil() bool { return false } -func (e event) MarshalJSONObject(enc *gojay.Encoder) { - enc.Float64Key("time", milliseconds(e.RelativeTime)) - enc.StringKey("name", e.Category().String()+":"+e.Name()) - enc.ObjectKey("data", e.eventDetails) -} - -type versions []versionNumber - -func (v versions) IsNil() bool { return false } -func (v versions) MarshalJSONArray(enc *gojay.Encoder) { - for _, e := range v { - enc.AddString(e.String()) - } -} - -type rawInfo struct { - Length logging.ByteCount // full packet length, including header and AEAD authentication tag - PayloadLength logging.ByteCount // length of the packet payload, excluding AEAD tag -} - -func (i rawInfo) IsNil() bool { return false } -func (i rawInfo) MarshalJSONObject(enc *gojay.Encoder) { - enc.Uint64Key("length", uint64(i.Length)) - enc.Uint64KeyOmitEmpty("payload_length", uint64(i.PayloadLength)) -} - -type eventConnectionStarted struct { - SrcAddr *net.UDPAddr - DestAddr *net.UDPAddr - - SrcConnectionID protocol.ConnectionID - DestConnectionID protocol.ConnectionID -} - -var _ eventDetails = &eventConnectionStarted{} - -func (e eventConnectionStarted) Category() category { return categoryTransport } -func (e eventConnectionStarted) Name() string { return "connection_started" } -func (e eventConnectionStarted) IsNil() bool { return false } - -func (e eventConnectionStarted) MarshalJSONObject(enc *gojay.Encoder) { - if utils.IsIPv4(e.SrcAddr.IP) { - enc.StringKey("ip_version", "ipv4") - } else { - enc.StringKey("ip_version", "ipv6") - } - enc.StringKey("src_ip", e.SrcAddr.IP.String()) - enc.IntKey("src_port", e.SrcAddr.Port) - enc.StringKey("dst_ip", e.DestAddr.IP.String()) - enc.IntKey("dst_port", e.DestAddr.Port) - enc.StringKey("src_cid", connectionID(e.SrcConnectionID).String()) - enc.StringKey("dst_cid", connectionID(e.DestConnectionID).String()) -} - -type eventVersionNegotiated struct { - clientVersions, serverVersions []versionNumber - chosenVersion versionNumber -} - -func (e eventVersionNegotiated) Category() category { return categoryTransport } -func (e eventVersionNegotiated) Name() string { return "version_information" } -func (e eventVersionNegotiated) IsNil() bool { return false } - -func (e eventVersionNegotiated) MarshalJSONObject(enc *gojay.Encoder) { - if len(e.clientVersions) > 0 { - enc.ArrayKey("client_versions", versions(e.clientVersions)) - } - if len(e.serverVersions) > 0 { - enc.ArrayKey("server_versions", versions(e.serverVersions)) - } - enc.StringKey("chosen_version", e.chosenVersion.String()) -} - -type eventConnectionClosed struct { - e error -} - -func (e eventConnectionClosed) Category() category { return categoryTransport } -func (e eventConnectionClosed) Name() string { return "connection_closed" } -func (e eventConnectionClosed) IsNil() bool { return false } - -func (e eventConnectionClosed) MarshalJSONObject(enc *gojay.Encoder) { - var ( - statelessResetErr *quic.StatelessResetError - handshakeTimeoutErr *quic.HandshakeTimeoutError - idleTimeoutErr *quic.IdleTimeoutError - applicationErr *quic.ApplicationError - transportErr *quic.TransportError - versionNegotiationErr *quic.VersionNegotiationError - ) - switch { - case errors.As(e.e, &statelessResetErr): - enc.StringKey("owner", ownerRemote.String()) - enc.StringKey("trigger", "stateless_reset") - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", statelessResetErr.Token)) - case errors.As(e.e, &handshakeTimeoutErr): - enc.StringKey("owner", ownerLocal.String()) - enc.StringKey("trigger", "handshake_timeout") - case errors.As(e.e, &idleTimeoutErr): - enc.StringKey("owner", ownerLocal.String()) - enc.StringKey("trigger", "idle_timeout") - case errors.As(e.e, &applicationErr): - owner := ownerLocal - if applicationErr.Remote { - owner = ownerRemote - } - enc.StringKey("owner", owner.String()) - enc.Uint64Key("application_code", uint64(applicationErr.ErrorCode)) - enc.StringKey("reason", applicationErr.ErrorMessage) - case errors.As(e.e, &transportErr): - owner := ownerLocal - if transportErr.Remote { - owner = ownerRemote - } - enc.StringKey("owner", owner.String()) - enc.StringKey("connection_code", transportError(transportErr.ErrorCode).String()) - enc.StringKey("reason", transportErr.ErrorMessage) - case errors.As(e.e, &versionNegotiationErr): - enc.StringKey("owner", ownerRemote.String()) - enc.StringKey("trigger", "version_negotiation") - } -} - -type eventPacketSent struct { - Header packetHeader - Length logging.ByteCount - PayloadLength logging.ByteCount - Frames frames - IsCoalesced bool - Trigger string -} - -var _ eventDetails = eventPacketSent{} - -func (e eventPacketSent) Category() category { return categoryTransport } -func (e eventPacketSent) Name() string { return "packet_sent" } -func (e eventPacketSent) IsNil() bool { return false } - -func (e eventPacketSent) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) - enc.ArrayKeyOmitEmpty("frames", e.Frames) - enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) - enc.StringKeyOmitEmpty("trigger", e.Trigger) -} - -type eventPacketReceived struct { - Header packetHeader - Length logging.ByteCount - PayloadLength logging.ByteCount - Frames frames - IsCoalesced bool - Trigger string -} - -var _ eventDetails = eventPacketReceived{} - -func (e eventPacketReceived) Category() category { return categoryTransport } -func (e eventPacketReceived) Name() string { return "packet_received" } -func (e eventPacketReceived) IsNil() bool { return false } - -func (e eventPacketReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) - enc.ArrayKeyOmitEmpty("frames", e.Frames) - enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) - enc.StringKeyOmitEmpty("trigger", e.Trigger) -} - -type eventRetryReceived struct { - Header packetHeader -} - -func (e eventRetryReceived) Category() category { return categoryTransport } -func (e eventRetryReceived) Name() string { return "packet_received" } -func (e eventRetryReceived) IsNil() bool { return false } - -func (e eventRetryReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) -} - -type eventVersionNegotiationReceived struct { - Header packetHeader - SupportedVersions []versionNumber -} - -func (e eventVersionNegotiationReceived) Category() category { return categoryTransport } -func (e eventVersionNegotiationReceived) Name() string { return "packet_received" } -func (e eventVersionNegotiationReceived) IsNil() bool { return false } - -func (e eventVersionNegotiationReceived) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", e.Header) - enc.ArrayKey("supported_versions", versions(e.SupportedVersions)) -} - -type eventPacketBuffered struct { - PacketType logging.PacketType -} - -func (e eventPacketBuffered) Category() category { return categoryTransport } -func (e eventPacketBuffered) Name() string { return "packet_buffered" } -func (e eventPacketBuffered) IsNil() bool { return false } - -func (e eventPacketBuffered) MarshalJSONObject(enc *gojay.Encoder) { - //nolint:gosimple - enc.ObjectKey("header", packetHeaderWithType{PacketType: e.PacketType}) - enc.StringKey("trigger", "keys_unavailable") -} - -type eventPacketDropped struct { - PacketType logging.PacketType - PacketSize protocol.ByteCount - Trigger packetDropReason -} - -func (e eventPacketDropped) Category() category { return categoryTransport } -func (e eventPacketDropped) Name() string { return "packet_dropped" } -func (e eventPacketDropped) IsNil() bool { return false } - -func (e eventPacketDropped) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", packetHeaderWithType{PacketType: e.PacketType}) - enc.ObjectKey("raw", rawInfo{Length: e.PacketSize}) - enc.StringKey("trigger", e.Trigger.String()) -} - -type metrics struct { - MinRTT time.Duration - SmoothedRTT time.Duration - LatestRTT time.Duration - RTTVariance time.Duration - - CongestionWindow protocol.ByteCount - BytesInFlight protocol.ByteCount - PacketsInFlight int -} - -type eventMetricsUpdated struct { - Last *metrics - Current *metrics -} - -func (e eventMetricsUpdated) Category() category { return categoryRecovery } -func (e eventMetricsUpdated) Name() string { return "metrics_updated" } -func (e eventMetricsUpdated) IsNil() bool { return false } - -func (e eventMetricsUpdated) MarshalJSONObject(enc *gojay.Encoder) { - if e.Last == nil || e.Last.MinRTT != e.Current.MinRTT { - enc.FloatKey("min_rtt", milliseconds(e.Current.MinRTT)) - } - if e.Last == nil || e.Last.SmoothedRTT != e.Current.SmoothedRTT { - enc.FloatKey("smoothed_rtt", milliseconds(e.Current.SmoothedRTT)) - } - if e.Last == nil || e.Last.LatestRTT != e.Current.LatestRTT { - enc.FloatKey("latest_rtt", milliseconds(e.Current.LatestRTT)) - } - if e.Last == nil || e.Last.RTTVariance != e.Current.RTTVariance { - enc.FloatKey("rtt_variance", milliseconds(e.Current.RTTVariance)) - } - - if e.Last == nil || e.Last.CongestionWindow != e.Current.CongestionWindow { - enc.Uint64Key("congestion_window", uint64(e.Current.CongestionWindow)) - } - if e.Last == nil || e.Last.BytesInFlight != e.Current.BytesInFlight { - enc.Uint64Key("bytes_in_flight", uint64(e.Current.BytesInFlight)) - } - if e.Last == nil || e.Last.PacketsInFlight != e.Current.PacketsInFlight { - enc.Uint64KeyOmitEmpty("packets_in_flight", uint64(e.Current.PacketsInFlight)) - } -} - -type eventUpdatedPTO struct { - Value uint32 -} - -func (e eventUpdatedPTO) Category() category { return categoryRecovery } -func (e eventUpdatedPTO) Name() string { return "metrics_updated" } -func (e eventUpdatedPTO) IsNil() bool { return false } - -func (e eventUpdatedPTO) MarshalJSONObject(enc *gojay.Encoder) { - enc.Uint32Key("pto_count", e.Value) -} - -type eventPacketLost struct { - PacketType logging.PacketType - PacketNumber protocol.PacketNumber - Trigger packetLossReason -} - -func (e eventPacketLost) Category() category { return categoryRecovery } -func (e eventPacketLost) Name() string { return "packet_lost" } -func (e eventPacketLost) IsNil() bool { return false } - -func (e eventPacketLost) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("header", packetHeaderWithTypeAndPacketNumber{ - PacketType: e.PacketType, - PacketNumber: e.PacketNumber, - }) - enc.StringKey("trigger", e.Trigger.String()) -} - -type eventKeyUpdated struct { - Trigger keyUpdateTrigger - KeyType keyType - Generation protocol.KeyPhase - // we don't log the keys here, so we don't need `old` and `new`. -} - -func (e eventKeyUpdated) Category() category { return categorySecurity } -func (e eventKeyUpdated) Name() string { return "key_updated" } -func (e eventKeyUpdated) IsNil() bool { return false } - -func (e eventKeyUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("trigger", e.Trigger.String()) - enc.StringKey("key_type", e.KeyType.String()) - if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { - enc.Uint64Key("generation", uint64(e.Generation)) - } -} - -type eventKeyRetired struct { - KeyType keyType - Generation protocol.KeyPhase -} - -func (e eventKeyRetired) Category() category { return categorySecurity } -func (e eventKeyRetired) Name() string { return "key_retired" } -func (e eventKeyRetired) IsNil() bool { return false } - -func (e eventKeyRetired) MarshalJSONObject(enc *gojay.Encoder) { - if e.KeyType != keyTypeClient1RTT && e.KeyType != keyTypeServer1RTT { - enc.StringKey("trigger", "tls") - } - enc.StringKey("key_type", e.KeyType.String()) - if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { - enc.Uint64Key("generation", uint64(e.Generation)) - } -} - -type eventTransportParameters struct { - Restore bool - Owner owner - SentBy protocol.Perspective - - OriginalDestinationConnectionID protocol.ConnectionID - InitialSourceConnectionID protocol.ConnectionID - RetrySourceConnectionID *protocol.ConnectionID - - StatelessResetToken *protocol.StatelessResetToken - DisableActiveMigration bool - MaxIdleTimeout time.Duration - MaxUDPPayloadSize protocol.ByteCount - AckDelayExponent uint8 - MaxAckDelay time.Duration - ActiveConnectionIDLimit uint64 - - InitialMaxData protocol.ByteCount - InitialMaxStreamDataBidiLocal protocol.ByteCount - InitialMaxStreamDataBidiRemote protocol.ByteCount - InitialMaxStreamDataUni protocol.ByteCount - InitialMaxStreamsBidi int64 - InitialMaxStreamsUni int64 - - PreferredAddress *preferredAddress - - MaxDatagramFrameSize protocol.ByteCount -} - -func (e eventTransportParameters) Category() category { return categoryTransport } -func (e eventTransportParameters) Name() string { - if e.Restore { - return "parameters_restored" - } - return "parameters_set" -} -func (e eventTransportParameters) IsNil() bool { return false } - -func (e eventTransportParameters) MarshalJSONObject(enc *gojay.Encoder) { - if !e.Restore { - enc.StringKey("owner", e.Owner.String()) - if e.SentBy == protocol.PerspectiveServer { - enc.StringKey("original_destination_connection_id", connectionID(e.OriginalDestinationConnectionID).String()) - if e.StatelessResetToken != nil { - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", e.StatelessResetToken[:])) - } - if e.RetrySourceConnectionID != nil { - enc.StringKey("retry_source_connection_id", connectionID(*e.RetrySourceConnectionID).String()) - } - } - enc.StringKey("initial_source_connection_id", connectionID(e.InitialSourceConnectionID).String()) - } - enc.BoolKey("disable_active_migration", e.DisableActiveMigration) - enc.FloatKeyOmitEmpty("max_idle_timeout", milliseconds(e.MaxIdleTimeout)) - enc.Int64KeyNullEmpty("max_udp_payload_size", int64(e.MaxUDPPayloadSize)) - enc.Uint8KeyOmitEmpty("ack_delay_exponent", e.AckDelayExponent) - enc.FloatKeyOmitEmpty("max_ack_delay", milliseconds(e.MaxAckDelay)) - enc.Uint64KeyOmitEmpty("active_connection_id_limit", e.ActiveConnectionIDLimit) - - enc.Int64KeyOmitEmpty("initial_max_data", int64(e.InitialMaxData)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_local", int64(e.InitialMaxStreamDataBidiLocal)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_remote", int64(e.InitialMaxStreamDataBidiRemote)) - enc.Int64KeyOmitEmpty("initial_max_stream_data_uni", int64(e.InitialMaxStreamDataUni)) - enc.Int64KeyOmitEmpty("initial_max_streams_bidi", e.InitialMaxStreamsBidi) - enc.Int64KeyOmitEmpty("initial_max_streams_uni", e.InitialMaxStreamsUni) - - if e.PreferredAddress != nil { - enc.ObjectKey("preferred_address", e.PreferredAddress) - } - if e.MaxDatagramFrameSize != protocol.InvalidByteCount { - enc.Int64Key("max_datagram_frame_size", int64(e.MaxDatagramFrameSize)) - } -} - -type preferredAddress struct { - IPv4, IPv6 net.IP - PortV4, PortV6 uint16 - ConnectionID protocol.ConnectionID - StatelessResetToken protocol.StatelessResetToken -} - -var _ gojay.MarshalerJSONObject = &preferredAddress{} - -func (a preferredAddress) IsNil() bool { return false } -func (a preferredAddress) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("ip_v4", a.IPv4.String()) - enc.Uint16Key("port_v4", a.PortV4) - enc.StringKey("ip_v6", a.IPv6.String()) - enc.Uint16Key("port_v6", a.PortV6) - enc.StringKey("connection_id", connectionID(a.ConnectionID).String()) - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", a.StatelessResetToken)) -} - -type eventLossTimerSet struct { - TimerType timerType - EncLevel protocol.EncryptionLevel - Delta time.Duration -} - -func (e eventLossTimerSet) Category() category { return categoryRecovery } -func (e eventLossTimerSet) Name() string { return "loss_timer_updated" } -func (e eventLossTimerSet) IsNil() bool { return false } - -func (e eventLossTimerSet) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "set") - enc.StringKey("timer_type", e.TimerType.String()) - enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) - enc.Float64Key("delta", milliseconds(e.Delta)) -} - -type eventLossTimerExpired struct { - TimerType timerType - EncLevel protocol.EncryptionLevel -} - -func (e eventLossTimerExpired) Category() category { return categoryRecovery } -func (e eventLossTimerExpired) Name() string { return "loss_timer_updated" } -func (e eventLossTimerExpired) IsNil() bool { return false } - -func (e eventLossTimerExpired) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "expired") - enc.StringKey("timer_type", e.TimerType.String()) - enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) -} - -type eventLossTimerCanceled struct{} - -func (e eventLossTimerCanceled) Category() category { return categoryRecovery } -func (e eventLossTimerCanceled) Name() string { return "loss_timer_updated" } -func (e eventLossTimerCanceled) IsNil() bool { return false } - -func (e eventLossTimerCanceled) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("event_type", "cancelled") -} - -type eventCongestionStateUpdated struct { - state congestionState -} - -func (e eventCongestionStateUpdated) Category() category { return categoryRecovery } -func (e eventCongestionStateUpdated) Name() string { return "congestion_state_updated" } -func (e eventCongestionStateUpdated) IsNil() bool { return false } - -func (e eventCongestionStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("new", e.state.String()) -} - -type eventGeneric struct { - name string - msg string -} - -func (e eventGeneric) Category() category { return categoryTransport } -func (e eventGeneric) Name() string { return e.name } -func (e eventGeneric) IsNil() bool { return false } - -func (e eventGeneric) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("details", e.msg) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/frame.go b/vendor/github.com/lucas-clemente/quic-go/qlog/frame.go deleted file mode 100644 index 4530f0fb..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/frame.go +++ /dev/null @@ -1,227 +0,0 @@ -package qlog - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/wire" - "github.com/lucas-clemente/quic-go/logging" - - "github.com/francoispqt/gojay" -) - -type frame struct { - Frame logging.Frame -} - -var _ gojay.MarshalerJSONObject = frame{} - -var _ gojay.MarshalerJSONArray = frames{} - -func (f frame) MarshalJSONObject(enc *gojay.Encoder) { - switch frame := f.Frame.(type) { - case *logging.PingFrame: - marshalPingFrame(enc, frame) - case *logging.AckFrame: - marshalAckFrame(enc, frame) - case *logging.ResetStreamFrame: - marshalResetStreamFrame(enc, frame) - case *logging.StopSendingFrame: - marshalStopSendingFrame(enc, frame) - case *logging.CryptoFrame: - marshalCryptoFrame(enc, frame) - case *logging.NewTokenFrame: - marshalNewTokenFrame(enc, frame) - case *logging.StreamFrame: - marshalStreamFrame(enc, frame) - case *logging.MaxDataFrame: - marshalMaxDataFrame(enc, frame) - case *logging.MaxStreamDataFrame: - marshalMaxStreamDataFrame(enc, frame) - case *logging.MaxStreamsFrame: - marshalMaxStreamsFrame(enc, frame) - case *logging.DataBlockedFrame: - marshalDataBlockedFrame(enc, frame) - case *logging.StreamDataBlockedFrame: - marshalStreamDataBlockedFrame(enc, frame) - case *logging.StreamsBlockedFrame: - marshalStreamsBlockedFrame(enc, frame) - case *logging.NewConnectionIDFrame: - marshalNewConnectionIDFrame(enc, frame) - case *logging.RetireConnectionIDFrame: - marshalRetireConnectionIDFrame(enc, frame) - case *logging.PathChallengeFrame: - marshalPathChallengeFrame(enc, frame) - case *logging.PathResponseFrame: - marshalPathResponseFrame(enc, frame) - case *logging.ConnectionCloseFrame: - marshalConnectionCloseFrame(enc, frame) - case *logging.HandshakeDoneFrame: - marshalHandshakeDoneFrame(enc, frame) - case *logging.DatagramFrame: - marshalDatagramFrame(enc, frame) - default: - panic("unknown frame type") - } -} - -func (f frame) IsNil() bool { return false } - -type frames []frame - -func (fs frames) IsNil() bool { return fs == nil } -func (fs frames) MarshalJSONArray(enc *gojay.Encoder) { - for _, f := range fs { - enc.Object(f) - } -} - -func marshalPingFrame(enc *gojay.Encoder, _ *wire.PingFrame) { - enc.StringKey("frame_type", "ping") -} - -type ackRanges []wire.AckRange - -func (ars ackRanges) MarshalJSONArray(enc *gojay.Encoder) { - for _, r := range ars { - enc.Array(ackRange(r)) - } -} - -func (ars ackRanges) IsNil() bool { return false } - -type ackRange wire.AckRange - -func (ar ackRange) MarshalJSONArray(enc *gojay.Encoder) { - enc.AddInt64(int64(ar.Smallest)) - if ar.Smallest != ar.Largest { - enc.AddInt64(int64(ar.Largest)) - } -} - -func (ar ackRange) IsNil() bool { return false } - -func marshalAckFrame(enc *gojay.Encoder, f *logging.AckFrame) { - enc.StringKey("frame_type", "ack") - enc.FloatKeyOmitEmpty("ack_delay", milliseconds(f.DelayTime)) - enc.ArrayKey("acked_ranges", ackRanges(f.AckRanges)) - if hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0; hasECN { - enc.Uint64Key("ect0", f.ECT0) - enc.Uint64Key("ect1", f.ECT1) - enc.Uint64Key("ce", f.ECNCE) - } -} - -func marshalResetStreamFrame(enc *gojay.Encoder, f *logging.ResetStreamFrame) { - enc.StringKey("frame_type", "reset_stream") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("error_code", int64(f.ErrorCode)) - enc.Int64Key("final_size", int64(f.FinalSize)) -} - -func marshalStopSendingFrame(enc *gojay.Encoder, f *logging.StopSendingFrame) { - enc.StringKey("frame_type", "stop_sending") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("error_code", int64(f.ErrorCode)) -} - -func marshalCryptoFrame(enc *gojay.Encoder, f *logging.CryptoFrame) { - enc.StringKey("frame_type", "crypto") - enc.Int64Key("offset", int64(f.Offset)) - enc.Int64Key("length", int64(f.Length)) -} - -func marshalNewTokenFrame(enc *gojay.Encoder, f *logging.NewTokenFrame) { - enc.StringKey("frame_type", "new_token") - enc.ObjectKey("token", &token{Raw: f.Token}) -} - -func marshalStreamFrame(enc *gojay.Encoder, f *logging.StreamFrame) { - enc.StringKey("frame_type", "stream") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("offset", int64(f.Offset)) - enc.IntKey("length", int(f.Length)) - enc.BoolKeyOmitEmpty("fin", f.Fin) -} - -func marshalMaxDataFrame(enc *gojay.Encoder, f *logging.MaxDataFrame) { - enc.StringKey("frame_type", "max_data") - enc.Int64Key("maximum", int64(f.MaximumData)) -} - -func marshalMaxStreamDataFrame(enc *gojay.Encoder, f *logging.MaxStreamDataFrame) { - enc.StringKey("frame_type", "max_stream_data") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("maximum", int64(f.MaximumStreamData)) -} - -func marshalMaxStreamsFrame(enc *gojay.Encoder, f *logging.MaxStreamsFrame) { - enc.StringKey("frame_type", "max_streams") - enc.StringKey("stream_type", streamType(f.Type).String()) - enc.Int64Key("maximum", int64(f.MaxStreamNum)) -} - -func marshalDataBlockedFrame(enc *gojay.Encoder, f *logging.DataBlockedFrame) { - enc.StringKey("frame_type", "data_blocked") - enc.Int64Key("limit", int64(f.MaximumData)) -} - -func marshalStreamDataBlockedFrame(enc *gojay.Encoder, f *logging.StreamDataBlockedFrame) { - enc.StringKey("frame_type", "stream_data_blocked") - enc.Int64Key("stream_id", int64(f.StreamID)) - enc.Int64Key("limit", int64(f.MaximumStreamData)) -} - -func marshalStreamsBlockedFrame(enc *gojay.Encoder, f *logging.StreamsBlockedFrame) { - enc.StringKey("frame_type", "streams_blocked") - enc.StringKey("stream_type", streamType(f.Type).String()) - enc.Int64Key("limit", int64(f.StreamLimit)) -} - -func marshalNewConnectionIDFrame(enc *gojay.Encoder, f *logging.NewConnectionIDFrame) { - enc.StringKey("frame_type", "new_connection_id") - enc.Int64Key("sequence_number", int64(f.SequenceNumber)) - enc.Int64Key("retire_prior_to", int64(f.RetirePriorTo)) - enc.IntKey("length", f.ConnectionID.Len()) - enc.StringKey("connection_id", connectionID(f.ConnectionID).String()) - enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", f.StatelessResetToken)) -} - -func marshalRetireConnectionIDFrame(enc *gojay.Encoder, f *logging.RetireConnectionIDFrame) { - enc.StringKey("frame_type", "retire_connection_id") - enc.Int64Key("sequence_number", int64(f.SequenceNumber)) -} - -func marshalPathChallengeFrame(enc *gojay.Encoder, f *logging.PathChallengeFrame) { - enc.StringKey("frame_type", "path_challenge") - enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) -} - -func marshalPathResponseFrame(enc *gojay.Encoder, f *logging.PathResponseFrame) { - enc.StringKey("frame_type", "path_response") - enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) -} - -func marshalConnectionCloseFrame(enc *gojay.Encoder, f *logging.ConnectionCloseFrame) { - errorSpace := "transport" - if f.IsApplicationError { - errorSpace = "application" - } - enc.StringKey("frame_type", "connection_close") - enc.StringKey("error_space", errorSpace) - if errName := transportError(f.ErrorCode).String(); len(errName) > 0 { - enc.StringKey("error_code", errName) - } else { - enc.Uint64Key("error_code", f.ErrorCode) - } - enc.Uint64Key("raw_error_code", f.ErrorCode) - enc.StringKey("reason", f.ReasonPhrase) -} - -func marshalHandshakeDoneFrame(enc *gojay.Encoder, _ *logging.HandshakeDoneFrame) { - enc.StringKey("frame_type", "handshake_done") -} - -func marshalDatagramFrame(enc *gojay.Encoder, f *logging.DatagramFrame) { - enc.StringKey("frame_type", "datagram") - enc.Int64Key("length", int64(f.Length)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/packet_header.go b/vendor/github.com/lucas-clemente/quic-go/qlog/packet_header.go deleted file mode 100644 index cc270f2f..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/packet_header.go +++ /dev/null @@ -1,119 +0,0 @@ -package qlog - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" - "github.com/lucas-clemente/quic-go/logging" - - "github.com/francoispqt/gojay" -) - -func getPacketTypeFromEncryptionLevel(encLevel protocol.EncryptionLevel) logging.PacketType { - switch encLevel { - case protocol.EncryptionInitial: - return logging.PacketTypeInitial - case protocol.EncryptionHandshake: - return logging.PacketTypeHandshake - case protocol.Encryption0RTT: - return logging.PacketType0RTT - case protocol.Encryption1RTT: - return logging.PacketType1RTT - default: - panic("unknown encryption level") - } -} - -type token struct { - Raw []byte -} - -var _ gojay.MarshalerJSONObject = &token{} - -func (t token) IsNil() bool { return false } -func (t token) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("data", fmt.Sprintf("%x", t.Raw)) -} - -// PacketHeader is a QUIC packet header. -type packetHeader struct { - PacketType logging.PacketType - - KeyPhaseBit logging.KeyPhaseBit - PacketNumber logging.PacketNumber - - Version logging.VersionNumber - SrcConnectionID logging.ConnectionID - DestConnectionID logging.ConnectionID - - Token *token -} - -func transformHeader(hdr *wire.Header) *packetHeader { - h := &packetHeader{ - PacketType: logging.PacketTypeFromHeader(hdr), - SrcConnectionID: hdr.SrcConnectionID, - DestConnectionID: hdr.DestConnectionID, - Version: hdr.Version, - } - if len(hdr.Token) > 0 { - h.Token = &token{Raw: hdr.Token} - } - return h -} - -func transformExtendedHeader(hdr *wire.ExtendedHeader) *packetHeader { - h := transformHeader(&hdr.Header) - h.PacketNumber = hdr.PacketNumber - h.KeyPhaseBit = hdr.KeyPhase - return h -} - -func (h packetHeader) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) - if h.PacketType != logging.PacketTypeRetry && h.PacketType != logging.PacketTypeVersionNegotiation { - enc.Int64Key("packet_number", int64(h.PacketNumber)) - } - if h.Version != 0 { - enc.StringKey("version", versionNumber(h.Version).String()) - } - if h.PacketType != logging.PacketType1RTT { - enc.IntKey("scil", h.SrcConnectionID.Len()) - if h.SrcConnectionID.Len() > 0 { - enc.StringKey("scid", connectionID(h.SrcConnectionID).String()) - } - } - enc.IntKey("dcil", h.DestConnectionID.Len()) - if h.DestConnectionID.Len() > 0 { - enc.StringKey("dcid", connectionID(h.DestConnectionID).String()) - } - if h.KeyPhaseBit == logging.KeyPhaseZero || h.KeyPhaseBit == logging.KeyPhaseOne { - enc.StringKey("key_phase_bit", h.KeyPhaseBit.String()) - } - if h.Token != nil { - enc.ObjectKey("token", h.Token) - } -} - -// a minimal header that only outputs the packet type -type packetHeaderWithType struct { - PacketType logging.PacketType -} - -func (h packetHeaderWithType) IsNil() bool { return false } -func (h packetHeaderWithType) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) -} - -// a minimal header that only outputs the packet type -type packetHeaderWithTypeAndPacketNumber struct { - PacketType logging.PacketType - PacketNumber logging.PacketNumber -} - -func (h packetHeaderWithTypeAndPacketNumber) IsNil() bool { return false } -func (h packetHeaderWithTypeAndPacketNumber) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("packet_type", packetType(h.PacketType).String()) - enc.Int64Key("packet_number", int64(h.PacketNumber)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/qlog.go b/vendor/github.com/lucas-clemente/quic-go/qlog/qlog.go deleted file mode 100644 index 5b411742..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/qlog.go +++ /dev/null @@ -1,486 +0,0 @@ -package qlog - -import ( - "bytes" - "context" - "fmt" - "io" - "log" - "net" - "runtime/debug" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" - "github.com/lucas-clemente/quic-go/logging" - - "github.com/francoispqt/gojay" -) - -// Setting of this only works when quic-go is used as a library. -// When building a binary from this repository, the version can be set using the following go build flag: -// -ldflags="-X github.com/lucas-clemente/quic-go/qlog.quicGoVersion=foobar" -var quicGoVersion = "(devel)" - -func init() { - if quicGoVersion != "(devel)" { // variable set by ldflags - return - } - info, ok := debug.ReadBuildInfo() - if !ok { // no build info available. This happens when quic-go is not used as a library. - return - } - for _, d := range info.Deps { - if d.Path == "github.com/lucas-clemente/quic-go" { - quicGoVersion = d.Version - if d.Replace != nil { - if len(d.Replace.Version) > 0 { - quicGoVersion = d.Version - } else { - quicGoVersion += " (replaced)" - } - } - break - } - } -} - -const eventChanSize = 50 - -type tracer struct { - getLogWriter func(p logging.Perspective, connectionID []byte) io.WriteCloser -} - -var _ logging.Tracer = &tracer{} - -// NewTracer creates a new qlog tracer. -func NewTracer(getLogWriter func(p logging.Perspective, connectionID []byte) io.WriteCloser) logging.Tracer { - return &tracer{getLogWriter: getLogWriter} -} - -func (t *tracer) TracerForConnection(_ context.Context, p logging.Perspective, odcid protocol.ConnectionID) logging.ConnectionTracer { - if w := t.getLogWriter(p, odcid.Bytes()); w != nil { - return NewConnectionTracer(w, p, odcid) - } - return nil -} - -func (t *tracer) SentPacket(net.Addr, *logging.Header, protocol.ByteCount, []logging.Frame) {} -func (t *tracer) DroppedPacket(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { -} - -type connectionTracer struct { - mutex sync.Mutex - - w io.WriteCloser - odcid protocol.ConnectionID - perspective protocol.Perspective - referenceTime time.Time - - events chan event - encodeErr error - runStopped chan struct{} - - lastMetrics *metrics -} - -var _ logging.ConnectionTracer = &connectionTracer{} - -// NewConnectionTracer creates a new tracer to record a qlog for a connection. -func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) logging.ConnectionTracer { - t := &connectionTracer{ - w: w, - perspective: p, - odcid: odcid, - runStopped: make(chan struct{}), - events: make(chan event, eventChanSize), - referenceTime: time.Now(), - } - go t.run() - return t -} - -func (t *connectionTracer) run() { - defer close(t.runStopped) - buf := &bytes.Buffer{} - enc := gojay.NewEncoder(buf) - tl := &topLevel{ - trace: trace{ - VantagePoint: vantagePoint{Type: t.perspective}, - CommonFields: commonFields{ - ODCID: connectionID(t.odcid), - GroupID: connectionID(t.odcid), - ReferenceTime: t.referenceTime, - }, - }, - } - if err := enc.Encode(tl); err != nil { - panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) - } - if err := buf.WriteByte('\n'); err != nil { - panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) - } - if _, err := t.w.Write(buf.Bytes()); err != nil { - t.encodeErr = err - } - enc = gojay.NewEncoder(t.w) - for ev := range t.events { - if t.encodeErr != nil { // if encoding failed, just continue draining the event channel - continue - } - if err := enc.Encode(ev); err != nil { - t.encodeErr = err - continue - } - if _, err := t.w.Write([]byte{'\n'}); err != nil { - t.encodeErr = err - } - } -} - -func (t *connectionTracer) Close() { - if err := t.export(); err != nil { - log.Printf("exporting qlog failed: %s\n", err) - } -} - -// export writes a qlog. -func (t *connectionTracer) export() error { - close(t.events) - <-t.runStopped - if t.encodeErr != nil { - return t.encodeErr - } - return t.w.Close() -} - -func (t *connectionTracer) recordEvent(eventTime time.Time, details eventDetails) { - t.events <- event{ - RelativeTime: eventTime.Sub(t.referenceTime), - eventDetails: details, - } -} - -func (t *connectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID protocol.ConnectionID) { - // ignore this event if we're not dealing with UDP addresses here - localAddr, ok := local.(*net.UDPAddr) - if !ok { - return - } - remoteAddr, ok := remote.(*net.UDPAddr) - if !ok { - return - } - t.mutex.Lock() - t.recordEvent(time.Now(), &eventConnectionStarted{ - SrcAddr: localAddr, - DestAddr: remoteAddr, - SrcConnectionID: srcConnID, - DestConnectionID: destConnID, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) NegotiatedVersion(chosen logging.VersionNumber, client, server []logging.VersionNumber) { - var clientVersions, serverVersions []versionNumber - if len(client) > 0 { - clientVersions = make([]versionNumber, len(client)) - for i, v := range client { - clientVersions[i] = versionNumber(v) - } - } - if len(server) > 0 { - serverVersions = make([]versionNumber, len(server)) - for i, v := range server { - serverVersions[i] = versionNumber(v) - } - } - t.mutex.Lock() - t.recordEvent(time.Now(), &eventVersionNegotiated{ - clientVersions: clientVersions, - serverVersions: serverVersions, - chosenVersion: versionNumber(chosen), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) ClosedConnection(e error) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventConnectionClosed{e: e}) - t.mutex.Unlock() -} - -func (t *connectionTracer) SentTransportParameters(tp *wire.TransportParameters) { - t.recordTransportParameters(t.perspective, tp) -} - -func (t *connectionTracer) ReceivedTransportParameters(tp *wire.TransportParameters) { - t.recordTransportParameters(t.perspective.Opposite(), tp) -} - -func (t *connectionTracer) RestoredTransportParameters(tp *wire.TransportParameters) { - ev := t.toTransportParameters(tp) - ev.Restore = true - - t.mutex.Lock() - t.recordEvent(time.Now(), ev) - t.mutex.Unlock() -} - -func (t *connectionTracer) recordTransportParameters(sentBy protocol.Perspective, tp *wire.TransportParameters) { - ev := t.toTransportParameters(tp) - ev.Owner = ownerLocal - if sentBy != t.perspective { - ev.Owner = ownerRemote - } - ev.SentBy = sentBy - - t.mutex.Lock() - t.recordEvent(time.Now(), ev) - t.mutex.Unlock() -} - -func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) *eventTransportParameters { - var pa *preferredAddress - if tp.PreferredAddress != nil { - pa = &preferredAddress{ - IPv4: tp.PreferredAddress.IPv4, - PortV4: tp.PreferredAddress.IPv4Port, - IPv6: tp.PreferredAddress.IPv6, - PortV6: tp.PreferredAddress.IPv6Port, - ConnectionID: tp.PreferredAddress.ConnectionID, - StatelessResetToken: tp.PreferredAddress.StatelessResetToken, - } - } - return &eventTransportParameters{ - OriginalDestinationConnectionID: tp.OriginalDestinationConnectionID, - InitialSourceConnectionID: tp.InitialSourceConnectionID, - RetrySourceConnectionID: tp.RetrySourceConnectionID, - StatelessResetToken: tp.StatelessResetToken, - DisableActiveMigration: tp.DisableActiveMigration, - MaxIdleTimeout: tp.MaxIdleTimeout, - MaxUDPPayloadSize: tp.MaxUDPPayloadSize, - AckDelayExponent: tp.AckDelayExponent, - MaxAckDelay: tp.MaxAckDelay, - ActiveConnectionIDLimit: tp.ActiveConnectionIDLimit, - InitialMaxData: tp.InitialMaxData, - InitialMaxStreamDataBidiLocal: tp.InitialMaxStreamDataBidiLocal, - InitialMaxStreamDataBidiRemote: tp.InitialMaxStreamDataBidiRemote, - InitialMaxStreamDataUni: tp.InitialMaxStreamDataUni, - InitialMaxStreamsBidi: int64(tp.MaxBidiStreamNum), - InitialMaxStreamsUni: int64(tp.MaxUniStreamNum), - PreferredAddress: pa, - MaxDatagramFrameSize: tp.MaxDatagramFrameSize, - } -} - -func (t *connectionTracer) SentPacket(hdr *wire.ExtendedHeader, packetSize logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { - numFrames := len(frames) - if ack != nil { - numFrames++ - } - fs := make([]frame, 0, numFrames) - if ack != nil { - fs = append(fs, frame{Frame: ack}) - } - for _, f := range frames { - fs = append(fs, frame{Frame: f}) - } - header := *transformExtendedHeader(hdr) - t.mutex.Lock() - t.recordEvent(time.Now(), &eventPacketSent{ - Header: header, - Length: packetSize, - PayloadLength: hdr.Length, - Frames: fs, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) ReceivedPacket(hdr *wire.ExtendedHeader, packetSize logging.ByteCount, frames []logging.Frame) { - fs := make([]frame, len(frames)) - for i, f := range frames { - fs[i] = frame{Frame: f} - } - header := *transformExtendedHeader(hdr) - t.mutex.Lock() - t.recordEvent(time.Now(), &eventPacketReceived{ - Header: header, - Length: packetSize, - PayloadLength: hdr.Length, - Frames: fs, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) ReceivedRetry(hdr *wire.Header) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventRetryReceived{ - Header: *transformHeader(hdr), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) ReceivedVersionNegotiationPacket(hdr *wire.Header, versions []logging.VersionNumber) { - ver := make([]versionNumber, len(versions)) - for i, v := range versions { - ver[i] = versionNumber(v) - } - t.mutex.Lock() - t.recordEvent(time.Now(), &eventVersionNegotiationReceived{ - Header: *transformHeader(hdr), - SupportedVersions: ver, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) BufferedPacket(pt logging.PacketType) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventPacketBuffered{PacketType: pt}) - t.mutex.Unlock() -} - -func (t *connectionTracer) DroppedPacket(pt logging.PacketType, size protocol.ByteCount, reason logging.PacketDropReason) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventPacketDropped{ - PacketType: pt, - PacketSize: size, - Trigger: packetDropReason(reason), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) UpdatedMetrics(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { - m := &metrics{ - MinRTT: rttStats.MinRTT(), - SmoothedRTT: rttStats.SmoothedRTT(), - LatestRTT: rttStats.LatestRTT(), - RTTVariance: rttStats.MeanDeviation(), - CongestionWindow: cwnd, - BytesInFlight: bytesInFlight, - PacketsInFlight: packetsInFlight, - } - t.mutex.Lock() - t.recordEvent(time.Now(), &eventMetricsUpdated{ - Last: t.lastMetrics, - Current: m, - }) - t.lastMetrics = m - t.mutex.Unlock() -} - -func (t *connectionTracer) AcknowledgedPacket(protocol.EncryptionLevel, protocol.PacketNumber) {} - -func (t *connectionTracer) LostPacket(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventPacketLost{ - PacketType: getPacketTypeFromEncryptionLevel(encLevel), - PacketNumber: pn, - Trigger: packetLossReason(lossReason), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) UpdatedCongestionState(state logging.CongestionState) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventCongestionStateUpdated{state: congestionState(state)}) - t.mutex.Unlock() -} - -func (t *connectionTracer) UpdatedPTOCount(value uint32) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventUpdatedPTO{Value: value}) - t.mutex.Unlock() -} - -func (t *connectionTracer) UpdatedKeyFromTLS(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventKeyUpdated{ - Trigger: keyUpdateTLS, - KeyType: encLevelToKeyType(encLevel, pers), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) UpdatedKey(generation protocol.KeyPhase, remote bool) { - trigger := keyUpdateLocal - if remote { - trigger = keyUpdateRemote - } - t.mutex.Lock() - now := time.Now() - t.recordEvent(now, &eventKeyUpdated{ - Trigger: trigger, - KeyType: keyTypeClient1RTT, - Generation: generation, - }) - t.recordEvent(now, &eventKeyUpdated{ - Trigger: trigger, - KeyType: keyTypeServer1RTT, - Generation: generation, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) DroppedEncryptionLevel(encLevel protocol.EncryptionLevel) { - t.mutex.Lock() - now := time.Now() - if encLevel == protocol.Encryption0RTT { - t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, t.perspective)}) - } else { - t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveServer)}) - t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveClient)}) - } - t.mutex.Unlock() -} - -func (t *connectionTracer) DroppedKey(generation protocol.KeyPhase) { - t.mutex.Lock() - now := time.Now() - t.recordEvent(now, &eventKeyRetired{ - KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer), - Generation: generation, - }) - t.recordEvent(now, &eventKeyRetired{ - KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient), - Generation: generation, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) SetLossTimer(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { - t.mutex.Lock() - now := time.Now() - t.recordEvent(now, &eventLossTimerSet{ - TimerType: timerType(tt), - EncLevel: encLevel, - Delta: timeout.Sub(now), - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) LossTimerExpired(tt logging.TimerType, encLevel protocol.EncryptionLevel) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventLossTimerExpired{ - TimerType: timerType(tt), - EncLevel: encLevel, - }) - t.mutex.Unlock() -} - -func (t *connectionTracer) LossTimerCanceled() { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventLossTimerCanceled{}) - t.mutex.Unlock() -} - -func (t *connectionTracer) Debug(name, msg string) { - t.mutex.Lock() - t.recordEvent(time.Now(), &eventGeneric{ - name: name, - msg: msg, - }) - t.mutex.Unlock() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/trace.go b/vendor/github.com/lucas-clemente/quic-go/qlog/trace.go deleted file mode 100644 index 4f0b5e64..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/trace.go +++ /dev/null @@ -1,66 +0,0 @@ -package qlog - -import ( - "time" - - "github.com/francoispqt/gojay" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -type topLevel struct { - trace trace -} - -func (topLevel) IsNil() bool { return false } -func (l topLevel) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("qlog_format", "NDJSON") - enc.StringKey("qlog_version", "draft-02") - enc.StringKeyOmitEmpty("title", "quic-go qlog") - enc.StringKey("code_version", quicGoVersion) - enc.ObjectKey("trace", l.trace) -} - -type vantagePoint struct { - Name string - Type protocol.Perspective -} - -func (p vantagePoint) IsNil() bool { return false } -func (p vantagePoint) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKeyOmitEmpty("name", p.Name) - switch p.Type { - case protocol.PerspectiveClient: - enc.StringKey("type", "client") - case protocol.PerspectiveServer: - enc.StringKey("type", "server") - } -} - -type commonFields struct { - ODCID connectionID - GroupID connectionID - ProtocolType string - ReferenceTime time.Time -} - -func (f commonFields) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("ODCID", f.ODCID.String()) - enc.StringKey("group_id", f.ODCID.String()) - enc.StringKeyOmitEmpty("protocol_type", f.ProtocolType) - enc.Float64Key("reference_time", float64(f.ReferenceTime.UnixNano())/1e6) - enc.StringKey("time_format", "relative") -} - -func (f commonFields) IsNil() bool { return false } - -type trace struct { - VantagePoint vantagePoint - CommonFields commonFields -} - -func (trace) IsNil() bool { return false } -func (t trace) MarshalJSONObject(enc *gojay.Encoder) { - enc.ObjectKey("vantage_point", t.VantagePoint) - enc.ObjectKey("common_fields", t.CommonFields) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/qlog/types.go b/vendor/github.com/lucas-clemente/quic-go/qlog/types.go deleted file mode 100644 index b485e17d..00000000 --- a/vendor/github.com/lucas-clemente/quic-go/qlog/types.go +++ /dev/null @@ -1,320 +0,0 @@ -package qlog - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/logging" -) - -type owner uint8 - -const ( - ownerLocal owner = iota - ownerRemote -) - -func (o owner) String() string { - switch o { - case ownerLocal: - return "local" - case ownerRemote: - return "remote" - default: - return "unknown owner" - } -} - -type streamType protocol.StreamType - -func (s streamType) String() string { - switch protocol.StreamType(s) { - case protocol.StreamTypeUni: - return "unidirectional" - case protocol.StreamTypeBidi: - return "bidirectional" - default: - return "unknown stream type" - } -} - -type connectionID protocol.ConnectionID - -func (c connectionID) String() string { - return fmt.Sprintf("%x", []byte(c)) -} - -// category is the qlog event category. -type category uint8 - -const ( - categoryConnectivity category = iota - categoryTransport - categorySecurity - categoryRecovery -) - -func (c category) String() string { - switch c { - case categoryConnectivity: - return "connectivity" - case categoryTransport: - return "transport" - case categorySecurity: - return "security" - case categoryRecovery: - return "recovery" - default: - return "unknown category" - } -} - -type versionNumber protocol.VersionNumber - -func (v versionNumber) String() string { - return fmt.Sprintf("%x", uint32(v)) -} - -func (packetHeader) IsNil() bool { return false } - -func encLevelToPacketNumberSpace(encLevel protocol.EncryptionLevel) string { - switch encLevel { - case protocol.EncryptionInitial: - return "initial" - case protocol.EncryptionHandshake: - return "handshake" - case protocol.Encryption0RTT, protocol.Encryption1RTT: - return "application_data" - default: - return "unknown encryption level" - } -} - -type keyType uint8 - -const ( - keyTypeServerInitial keyType = 1 + iota - keyTypeClientInitial - keyTypeServerHandshake - keyTypeClientHandshake - keyTypeServer0RTT - keyTypeClient0RTT - keyTypeServer1RTT - keyTypeClient1RTT -) - -func encLevelToKeyType(encLevel protocol.EncryptionLevel, pers protocol.Perspective) keyType { - if pers == protocol.PerspectiveServer { - switch encLevel { - case protocol.EncryptionInitial: - return keyTypeServerInitial - case protocol.EncryptionHandshake: - return keyTypeServerHandshake - case protocol.Encryption0RTT: - return keyTypeServer0RTT - case protocol.Encryption1RTT: - return keyTypeServer1RTT - default: - return 0 - } - } - switch encLevel { - case protocol.EncryptionInitial: - return keyTypeClientInitial - case protocol.EncryptionHandshake: - return keyTypeClientHandshake - case protocol.Encryption0RTT: - return keyTypeClient0RTT - case protocol.Encryption1RTT: - return keyTypeClient1RTT - default: - return 0 - } -} - -func (t keyType) String() string { - switch t { - case keyTypeServerInitial: - return "server_initial_secret" - case keyTypeClientInitial: - return "client_initial_secret" - case keyTypeServerHandshake: - return "server_handshake_secret" - case keyTypeClientHandshake: - return "client_handshake_secret" - case keyTypeServer0RTT: - return "server_0rtt_secret" - case keyTypeClient0RTT: - return "client_0rtt_secret" - case keyTypeServer1RTT: - return "server_1rtt_secret" - case keyTypeClient1RTT: - return "client_1rtt_secret" - default: - return "unknown key type" - } -} - -type keyUpdateTrigger uint8 - -const ( - keyUpdateTLS keyUpdateTrigger = iota - keyUpdateRemote - keyUpdateLocal -) - -func (t keyUpdateTrigger) String() string { - switch t { - case keyUpdateTLS: - return "tls" - case keyUpdateRemote: - return "remote_update" - case keyUpdateLocal: - return "local_update" - default: - return "unknown key update trigger" - } -} - -type transportError uint64 - -func (e transportError) String() string { - switch qerr.TransportErrorCode(e) { - case qerr.NoError: - return "no_error" - case qerr.InternalError: - return "internal_error" - case qerr.ConnectionRefused: - return "connection_refused" - case qerr.FlowControlError: - return "flow_control_error" - case qerr.StreamLimitError: - return "stream_limit_error" - case qerr.StreamStateError: - return "stream_state_error" - case qerr.FinalSizeError: - return "final_size_error" - case qerr.FrameEncodingError: - return "frame_encoding_error" - case qerr.TransportParameterError: - return "transport_parameter_error" - case qerr.ConnectionIDLimitError: - return "connection_id_limit_error" - case qerr.ProtocolViolation: - return "protocol_violation" - case qerr.InvalidToken: - return "invalid_token" - case qerr.ApplicationErrorErrorCode: - return "application_error" - case qerr.CryptoBufferExceeded: - return "crypto_buffer_exceeded" - case qerr.KeyUpdateError: - return "key_update_error" - case qerr.AEADLimitReached: - return "aead_limit_reached" - case qerr.NoViablePathError: - return "no_viable_path" - default: - return "" - } -} - -type packetType logging.PacketType - -func (t packetType) String() string { - switch logging.PacketType(t) { - case logging.PacketTypeInitial: - return "initial" - case logging.PacketTypeHandshake: - return "handshake" - case logging.PacketTypeRetry: - return "retry" - case logging.PacketType0RTT: - return "0RTT" - case logging.PacketTypeVersionNegotiation: - return "version_negotiation" - case logging.PacketTypeStatelessReset: - return "stateless_reset" - case logging.PacketType1RTT: - return "1RTT" - case logging.PacketTypeNotDetermined: - return "" - default: - return "unknown packet type" - } -} - -type packetLossReason logging.PacketLossReason - -func (r packetLossReason) String() string { - switch logging.PacketLossReason(r) { - case logging.PacketLossReorderingThreshold: - return "reordering_threshold" - case logging.PacketLossTimeThreshold: - return "time_threshold" - default: - return "unknown loss reason" - } -} - -type packetDropReason logging.PacketDropReason - -func (r packetDropReason) String() string { - switch logging.PacketDropReason(r) { - case logging.PacketDropKeyUnavailable: - return "key_unavailable" - case logging.PacketDropUnknownConnectionID: - return "unknown_connection_id" - case logging.PacketDropHeaderParseError: - return "header_parse_error" - case logging.PacketDropPayloadDecryptError: - return "payload_decrypt_error" - case logging.PacketDropProtocolViolation: - return "protocol_violation" - case logging.PacketDropDOSPrevention: - return "dos_prevention" - case logging.PacketDropUnsupportedVersion: - return "unsupported_version" - case logging.PacketDropUnexpectedPacket: - return "unexpected_packet" - case logging.PacketDropUnexpectedSourceConnectionID: - return "unexpected_source_connection_id" - case logging.PacketDropUnexpectedVersion: - return "unexpected_version" - case logging.PacketDropDuplicate: - return "duplicate" - default: - return "unknown packet drop reason" - } -} - -type timerType logging.TimerType - -func (t timerType) String() string { - switch logging.TimerType(t) { - case logging.TimerTypeACK: - return "ack" - case logging.TimerTypePTO: - return "pto" - default: - return "unknown timer type" - } -} - -type congestionState logging.CongestionState - -func (s congestionState) String() string { - switch logging.CongestionState(s) { - case logging.CongestionStateSlowStart: - return "slow_start" - case logging.CongestionStateCongestionAvoidance: - return "congestion_avoidance" - case logging.CongestionStateRecovery: - return "recovery" - case logging.CongestionStateApplicationLimited: - return "application_limited" - default: - return "unknown congestion state" - } -} diff --git a/vendor/gopkg.in/yaml.v3/decode.go b/vendor/gopkg.in/yaml.v3/decode.go index df36e3a3..0173b698 100644 --- a/vendor/gopkg.in/yaml.v3/decode.go +++ b/vendor/gopkg.in/yaml.v3/decode.go @@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } - if !yaml_parser_parse(&p.parser, &p.event) { + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ @@ -320,6 +323,8 @@ type decoder struct { decodeCount int aliasCount int aliasDepth int + + mergedFields map[interface{}]bool } var ( @@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) @@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if mergedFields[ki] { + continue + } + mergedFields[ki] = true + } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() @@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + d.stringMapType = stringMapType d.generalMapType = generalMapType return true @@ -844,7 +867,8 @@ func isStringMap(n *Node) bool { } l := len(n.Content) for i := 0; i < l; i += 2 { - if n.Content[i].ShortTag() != strTag { + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { return false } } @@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } @@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.prepare(n, field) } + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node var doneFields []bool if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) @@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { for i := 0; i < l; i += 2 { ni := n.Content[i] if isMerge(ni) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } - if info, ok := sinfo.FieldsMap[name.String()]; ok { + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { if d.uniqueKeys { if doneFields[info.Id] { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) @@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } return true } @@ -918,19 +956,29 @@ func failWantMap() { failf("map merge requires map or sequence of maps as the value") } -func (d *decoder) merge(n *Node, out reflect.Value) { - switch n.Kind { +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.mergedFields[k.Interface()] = true + } + } + } + + switch merge.Kind { case MappingNode: - d.unmarshal(n, out) + d.unmarshal(merge, out) case AliasNode: - if n.Alias != nil && n.Alias.Kind != MappingNode { + if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } - d.unmarshal(n, out) + d.unmarshal(merge, out) case SequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.Content) - 1; i >= 0; i-- { - ni := n.Content[i] + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] if ni.Kind == AliasNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() @@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) { default: failWantMap() } + + d.mergedFields = mergedFields } func isMerge(n *Node) bool { diff --git a/vendor/gopkg.in/yaml.v3/parserc.go b/vendor/gopkg.in/yaml.v3/parserc.go index ac66fccc..268558a0 100644 --- a/vendor/gopkg.in/yaml.v3/parserc.go +++ b/vendor/gopkg.in/yaml.v3/parserc.go @@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } token := peek_token(parser) - if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { return } @@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 5951bced..cc9abba7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -85,9 +85,6 @@ github.com/facebookgo/grace/gracenet # github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 ## explicit github.com/flynn/go-shlex -# github.com/francoispqt/gojay v1.2.13 -## explicit; go 1.12 -github.com/francoispqt/gojay # github.com/fsnotify/fsnotify v1.4.9 ## explicit; go 1.13 github.com/fsnotify/fsnotify @@ -218,7 +215,6 @@ github.com/lucas-clemente/quic-go/internal/qtls github.com/lucas-clemente/quic-go/internal/utils github.com/lucas-clemente/quic-go/internal/wire github.com/lucas-clemente/quic-go/logging -github.com/lucas-clemente/quic-go/qlog github.com/lucas-clemente/quic-go/quicvarint # github.com/lucasb-eyer/go-colorful v1.0.3 ## explicit; go 1.12 @@ -596,7 +592,7 @@ gopkg.in/tomb.v1 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +# gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 # zombiezen.com/go/capnproto2 v2.18.0+incompatible From 73d948bc322e41dd80cc77c623dad99fde52b14c Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 31 May 2022 09:12:07 +0100 Subject: [PATCH 092/238] TUN-6210: Add cloudflared.repo to make it easy for yum installs This PR provides a cloudflared.repo template that can simply then be added to yum repos by running ``` sudo dnf config-manager --add-repo ``` removing the requirement for yum installers to handcraft this or run echo commands. --- release_pkgs.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index b2e75f6c..bcfb8376 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -120,6 +120,26 @@ class PkgCreator: self._sign_repomd() + """ + creates a .repo file with details like so + [cloudflared-stable] + name=cloudflared-stable + baseurl=https://pkg.cloudflare.com/cloudflared/rpm + enabled=1 + type=rpm + gpgcheck=1 + gpgkey=https://pkg.cloudflare.com/cloudflare-main.gpg + """ + def create_repo_file(self, file_path, binary_name, baseurl, gpgkey_url): + with open(file_path, "w+") as repo_file: + repo_file.write(f"[{binary_name}-stable]") + repo_file.write(f"{binary_name}-stable") + repo_file.write(f"baseurl={baseurl}/rpm") + repo_file.write("enabled=1") + repo_file.write("type=rpm") + repo_file.write("gpgcheck=1") + repo_file.write(f"gpgkey={gpgkey_url}") + def _sign_rpms(self, file_path): p = Popen(["rpm" , "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE) out, err = p.communicate() @@ -246,9 +266,19 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", release_version, binary_name) upload_from_directories(pkg_uploader, "pool", release_version, binary_name) -def create_rpm_packaging(pkg_creator, pkg_uploader, artifacts_path, release_version, binary_name, gpg_key_name): +def create_rpm_packaging( + pkg_creator, + pkg_uploader, + artifacts_path, + release_version, + binary_name, + gpg_key_name, + base_url, + gpg_key_url, +): print(f"creating rpm pkgs...") pkg_creator.create_rpm_pkgs(artifacts_path, gpg_key_name) + pkg_creator.create_repo_file(artifacts_path, binary_name, base_url, gpg_key_url) print("uploading latest to r2...") upload_from_directories(pkg_uploader, "rpm", None, binary_name) @@ -294,6 +324,15 @@ def parse_args(): signing packages" ) + parser.add_argument( + "--gpg-public-key-url", default=os.environ.get("GPG_PUBLIC_KEY_URL"), help="GPG public key url that\ + downloaders can use to verify signing" + ) + + parser.add_argument( + "--pkg-upload-url", default=os.environ.get("PKG_URL"), help="URL to be used by downloaders" + ) + parser.add_argument( "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic"], help="list of debian based releases that need to be packaged for" @@ -324,4 +363,13 @@ if __name__ == "__main__": create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs, "main", args.release_tag) - create_rpm_packaging(pkg_creator, pkg_uploader, "./built_artifacts", args.release_tag, args.binary, gpg_key_name) + create_rpm_packaging( + pkg_creator, + pkg_uploader, + "./built_artifacts", + args.release_tag, + args.binary, + gpg_key_name, + args.gpg_public_key_url, + args.pkg_upload_url, + ) From 056693c814250933a7832cc896ff23804a03a772 Mon Sep 17 00:00:00 2001 From: Nigel Armstrong Date: Tue, 31 May 2022 10:29:51 -0700 Subject: [PATCH 093/238] Add image source label to docker container. This label allows tools like [whitesource renovate](https://docs.renovatebot.com/modules/datasource/#docker-datasource) to find changelogs. --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 4f744f93..65224553 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ ENV GO111MODULE=on \ CGO_ENABLED=0 \ TARGET_GOOS=${TARGET_GOOS} \ TARGET_GOARCH=${TARGET_GOARCH} + +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" WORKDIR /go/src/github.com/cloudflare/cloudflared/ From 7d4afd4ae02dcc6f8f190258d2beed0a2d361886 Mon Sep 17 00:00:00 2001 From: Niklas Rehfeld Date: Wed, 1 Jun 2022 12:51:59 +1200 Subject: [PATCH 094/238] Add Http2Origin option to force HTTP/2 origin connections If `http2Origin` is set, it will set `ForceAttemptHTTP2` in the transport config of the `OriginService`. --- cmd/cloudflared/tunnel/cmd.go | 7 +++++++ config/configuration.go | 2 ++ config/configuration_test.go | 4 +++- ingress/config.go | 9 +++++++++ ingress/origin_service.go | 1 + 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index e9038403..0cae34a2 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -821,6 +821,13 @@ func configureProxyFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_NO_CHUNKED_ENCODING"}, Hidden: shouldHide, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: ingress.Http2OriginFlag, + Usage: "Enables HTTP/2 origin servers.", + EnvVars: []string{"TUNNEL_ORIGIN_ENABLE_HTTP2"}, + Hidden: shouldHide, + Value: false, + }), } return append(flags, sshFlags(shouldHide)...) } diff --git a/config/configuration.go b/config/configuration.go index 49395404..82491477 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -227,6 +227,8 @@ type OriginRequestConfig struct { ProxyType *string `yaml:"proxyType" json:"proxyType,omitempty"` // IP rules for the proxy service IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules,omitempty"` + // Attempt to connect to origin with HTTP/2 + Http2Origin *bool `yaml:"http2Origin" json:"http2Origin,omitempty"` } type IngressIPRule struct { diff --git a/config/configuration_test.go b/config/configuration_test.go index d870913d..2822b80b 100644 --- a/config/configuration_test.go +++ b/config/configuration_test.go @@ -139,7 +139,8 @@ var rawConfig = []byte(` "ports": [443, 4443], "allow": true } - ] + ], + "http2Origin": true } `) @@ -188,6 +189,7 @@ func assertConfig( assert.Equal(t, true, *config.NoTLSVerify) assert.Equal(t, uint(9000), *config.ProxyPort) assert.Equal(t, "socks", *config.ProxyType) + assert.Equal(t, true, *config.Http2Origin) privateV4 := "10.0.0.0/8" privateV6 := "fc00::/7" diff --git a/ingress/config.go b/ingress/config.go index bc2a9f6b..0d692fe9 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -35,6 +35,7 @@ const ( NoChunkedEncodingFlag = "no-chunked-encoding" ProxyAddressFlag = "proxy-address" ProxyPortFlag = "proxy-port" + Http2OriginFlag = "http2-origin" ) const ( @@ -93,6 +94,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { var proxyAddress = defaultProxyAddress var proxyPort uint var proxyType string + var http2Origin bool if flag := ProxyConnectTimeoutFlag; c.IsSet(flag) { connectTimeout = config.CustomDuration{Duration: c.Duration(flag)} } @@ -136,9 +138,13 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { // Note TUN-3758 , we use Int because UInt is not supported with altsrc proxyPort = uint(c.Int(flag)) } + if flag := Http2OriginFlag; c.IsSet(flag) { + http2Origin = c.Bool(flag) + } if c.IsSet(Socks5Flag) { proxyType = socksProxy } + return OriginRequestConfig{ ConnectTimeout: connectTimeout, TLSTimeout: tlsTimeout, @@ -155,6 +161,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { ProxyAddress: proxyAddress, ProxyPort: proxyPort, ProxyType: proxyType, + Http2Origin: http2Origin, } } @@ -263,6 +270,8 @@ type OriginRequestConfig struct { ProxyType string `yaml:"proxyType" json:"proxyType"` // IP rules for the proxy service IPRules []ipaccess.Rule `yaml:"ipRules" json:"ipRules"` + // Attempt to connect to origin with HTTP/2 + Http2Origin bool `yaml:"http2Origin" json:"http2Origin"` } func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) { diff --git a/ingress/origin_service.go b/ingress/origin_service.go index c96e4608..8877fdc7 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -291,6 +291,7 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol TLSHandshakeTimeout: cfg.TLSTimeout.Duration, ExpectContinueTimeout: 1 * time.Second, TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify}, + ForceAttemptHTTP2: cfg.Http2Origin, } if _, isHelloWorld := service.(*helloWorld); !isHelloWorld && cfg.OriginServerName != "" { httpTransport.TLSClientConfig.ServerName = cfg.OriginServerName From d714a62bd35a856f0f155f2f607c35405fee0cd5 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 1 Jun 2022 15:04:44 +0100 Subject: [PATCH 095/238] TUN-6323: Add Xenial and Trusty for Ubuntu pkging - Adds Xenial and Trusty Ubuntu releases to pkging --- release_pkgs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release_pkgs.py b/release_pkgs.py index bcfb8376..6f365b03 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -334,7 +334,8 @@ def parse_args(): ) parser.add_argument( - "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic"], + "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic", + "xenial", "trusty"], help="list of debian based releases that need to be packaged for" ) From 2345720b2b5a51a0e36fd519783708aa7900c429 Mon Sep 17 00:00:00 2001 From: Niklas Rehfeld Date: Thu, 2 Jun 2022 09:49:06 +1200 Subject: [PATCH 096/238] fix ingress rules unit test --- ingress/rule_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ingress/rule_test.go b/ingress/rule_test.go index 7561709a..279d7911 100644 --- a/ingress/rule_test.go +++ b/ingress/rule_test.go @@ -182,25 +182,25 @@ func TestMarshalJSON(t *testing.T) { { name: "Nil", path: nil, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, want: true, }, { name: "Nil regex", path: &Regexp{Regexp: nil}, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, want: true, }, { name: "Empty", path: &Regexp{Regexp: regexp.MustCompile("")}, - expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, want: true, }, { name: "Basic", path: &Regexp{Regexp: regexp.MustCompile("/echo")}, - expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null}}`, + expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, want: true, }, } From ee80e5583367a22441f42ea70c3835a595ba8d5f Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 2 Jun 2022 10:57:37 -0700 Subject: [PATCH 097/238] TUN-6339: Add config for IPv6 support --- cmd/cloudflared/tunnel/cmd.go | 6 +++ cmd/cloudflared/tunnel/configuration.go | 21 +++++++++ connection/errors.go | 9 ++++ connection/observer.go | 5 ++- connection/quic.go | 2 +- connection/rpc.go | 5 ++- edgediscovery/allregions/discovery.go | 52 +++++++++++++++++++--- edgediscovery/allregions/mocks_for_test.go | 2 +- supervisor/supervisor.go | 5 +-- supervisor/tunnel.go | 1 + 10 files changed, 94 insertions(+), 14 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index e9038403..6f013947 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -514,6 +514,12 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Usage: "Cloudflare Edge region to connect to. Omit or set to empty to connect to the global region.", EnvVars: []string{"TUNNEL_REGION"}, }), + altsrc.NewStringFlag(&cli.StringFlag{ + Name: "edge-ip-version", + Usage: "Cloudflare Edge ip address version to connect with. {4, 6, auto}", + EnvVars: []string{"TUNNEL_EDGE_IP_VERSION"}, + Hidden: true, + }), altsrc.NewStringFlag(&cli.StringFlag{ Name: tlsconfig.CaCertFlag, Usage: "Certificate Authority authenticating connections with Cloudflare's edge network.", diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 9a0728c5..9376d520 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -18,6 +18,7 @@ import ( "golang.org/x/crypto/ssh/terminal" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" + "github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" @@ -324,6 +325,10 @@ func prepareTunnelConfig( CompressionSetting: h2mux.CompressionSetting(uint64(c.Int("compression-quality"))), MetricsUpdateFreq: c.Duration("metrics-update-freq"), } + edgeIPVersion, err := parseConfigIPVersion(c.String("edge-ip-version")) + if err != nil { + return nil, nil, err + } tunnelConfig := &supervisor.TunnelConfig{ GracePeriod: gracePeriod, @@ -332,6 +337,7 @@ func prepareTunnelConfig( ClientID: clientID, EdgeAddrs: c.StringSlice("edge"), Region: c.String("region"), + EdgeIPVersion: edgeIPVersion, HAConnections: c.Int("ha-connections"), IncidentLookup: supervisor.NewIncidentLookup(), IsAutoupdated: c.Bool("is-autoupdated"), @@ -404,3 +410,18 @@ func dedup(slice []string) []string { } return keys } + +// ParseConfigIPVersion returns the IP version from possible expected values from config +func parseConfigIPVersion(version string) (v allregions.ConfigIPVersion, err error) { + switch version { + case "4": + v = allregions.IPv4Only + case "6": + v = allregions.IPv6Only + case "auto": + v = allregions.Auto + default: // unspecified or invalid + err = fmt.Errorf("invalid value for edge-ip-version: %s", version) + } + return +} diff --git a/connection/errors.go b/connection/errors.go index df3cfe97..98eececd 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -18,6 +18,15 @@ func (e DupConnRegisterTunnelError) Error() string { return "already connected to this server, trying another address" } +// Dial to edge server with quic failed +type EdgeQuicDialError struct { + Cause error +} + +func (e EdgeQuicDialError) Error() string { + return "failed to dial to edge with quic: " + e.Cause.Error() +} + // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/connection/observer.go b/connection/observer.go index 5c2bf06c..1e8b62b5 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -1,6 +1,7 @@ package connection import ( + "net" "strings" "github.com/rs/zerolog" @@ -8,6 +9,7 @@ import ( const ( LogFieldLocation = "location" + LogFieldIPAddress = "ip" observerChannelBufferSize = 16 ) @@ -41,11 +43,12 @@ func (o *Observer) RegisterSink(sink EventSink) { o.addSinkChan <- sink } -func (o *Observer) logServerInfo(connIndex uint8, location, msg string) { +func (o *Observer) logServerInfo(connIndex uint8, location string, address net.IP, msg string) { o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) o.log.Info(). Uint8(LogFieldConnIndex, connIndex). Str(LogFieldLocation, location). + IPAddr(LogFieldIPAddress, address). Msg(msg) o.metrics.registerServerLocation(uint8ToString(connIndex), location) } diff --git a/connection/quic.go b/connection/quic.go index 4e2f4681..607a17c7 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -55,7 +55,7 @@ func NewQUICConnection( ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { - return nil, fmt.Errorf("failed to dial to edge: %w", err) + return nil, EdgeQuicDialError{Cause: err} } datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) diff --git a/connection/rpc.go b/connection/rpc.go index 4565f396..a4444ea3 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "net" "time" "github.com/rs/zerolog" @@ -114,7 +115,7 @@ func (rsc *registrationServerClient) RegisterConnection( observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() - observer.logServerInfo(connIndex, conn.Location, fmt.Sprintf("Connection %s registered", conn.UUID)) + observer.logServerInfo(connIndex, conn.Location, options.OriginLocalIP, fmt.Sprintf("Connection %s registered", conn.UUID)) observer.sendConnectedEvent(connIndex, conn.Location) return conn, nil @@ -274,7 +275,7 @@ func (h *h2muxConnection) logServerInfo(ctx context.Context, rpcClient *tunnelSe h.observer.log.Err(err).Msg("Failed to retrieve server information") return err } - h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, "Connection established") + h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, net.IP{}, "Connection established") return nil } diff --git a/edgediscovery/allregions/discovery.go b/edgediscovery/allregions/discovery.go index 39a3b00b..e6a4103b 100644 --- a/edgediscovery/allregions/discovery.go +++ b/edgediscovery/allregions/discovery.go @@ -32,10 +32,40 @@ var ( netLookupIP = net.LookupIP ) +// ConfigIPVersion is the selection of IP versions from config +type ConfigIPVersion int8 + +const ( + Auto ConfigIPVersion = 2 + IPv4Only ConfigIPVersion = 4 + IPv6Only ConfigIPVersion = 6 +) + +// IPVersion is the IP version of an EdgeAddr +type EdgeIPVersion int8 + +const ( + V4 EdgeIPVersion = 4 + V6 EdgeIPVersion = 6 +) + +// String returns the enum's constant name. +func (c EdgeIPVersion) String() string { + switch c { + case V4: + return "4" + case V6: + return "6" + default: + return "" + } +} + // EdgeAddr is a representation of possible ways to refer an edge location. type EdgeAddr struct { - TCP *net.TCPAddr - UDP *net.UDPAddr + TCP *net.TCPAddr + UDP *net.UDPAddr + IPVersion EdgeIPVersion } // If the call to net.LookupSRV fails, try to fall back to DoT from Cloudflare directly. @@ -120,9 +150,14 @@ func resolveSRV(srv *net.SRV) ([]*EdgeAddr, error) { } addrs := make([]*EdgeAddr, len(ips)) for i, ip := range ips { + version := V6 + if ip.To4() != nil { + version = V4 + } addrs[i] = &EdgeAddr{ - TCP: &net.TCPAddr{IP: ip, Port: int(srv.Port)}, - UDP: &net.UDPAddr{IP: ip, Port: int(srv.Port)}, + TCP: &net.TCPAddr{IP: ip, Port: int(srv.Port)}, + UDP: &net.UDPAddr{IP: ip, Port: int(srv.Port)}, + IPVersion: version, } } return addrs, nil @@ -143,9 +178,14 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) { log.Error().Str(logFieldAddress, addr).Err(err).Msg("failed to resolve to UDP address") continue } + version := V6 + if udpAddr.IP.To4() != nil { + version = V4 + } resolved = append(resolved, &EdgeAddr{ - TCP: tcpAddr, - UDP: udpAddr, + TCP: tcpAddr, + UDP: udpAddr, + IPVersion: version, }) } diff --git a/edgediscovery/allregions/mocks_for_test.go b/edgediscovery/allregions/mocks_for_test.go index 36298be2..ab99c94f 100644 --- a/edgediscovery/allregions/mocks_for_test.go +++ b/edgediscovery/allregions/mocks_for_test.go @@ -36,7 +36,7 @@ func newMockAddrs(port uint16, numRegions uint8, numAddrsPerRegion uint8) mockAd IP: net.ParseIP(fmt.Sprintf("10.0.%v.%v", r, a)), Port: int(port), } - addrs = append(addrs, &EdgeAddr{tcpAddr, udpAddr}) + addrs = append(addrs, &EdgeAddr{tcpAddr, udpAddr, V4}) } addrMap[srv] = addrs numAddrs += len(addrs) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 8f33e279..62bf6ec3 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -63,7 +63,6 @@ var errEarlyShutdown = errors.New("shutdown started") type tunnelError struct { index int - addr *allregions.EdgeAddr err error } @@ -235,7 +234,7 @@ func (s *Supervisor) startFirstTunnel( ) const firstConnIndex = 0 defer func() { - s.tunnelErrors <- tunnelError{index: firstConnIndex, addr: addr, err: err} + s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err} }() addr, err = s.edgeIPs.GetAddr(firstConnIndex) @@ -306,7 +305,7 @@ func (s *Supervisor) startTunnel( err error ) defer func() { - s.tunnelErrors <- tunnelError{index: index, addr: addr, err: err} + s.tunnelErrors <- tunnelError{index: index, err: err} }() addr, err = s.edgeIPs.GetDifferentAddr(index) diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 8683c929..45a1ffba 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -43,6 +43,7 @@ type TunnelConfig struct { CloseConnOnce *sync.Once // Used to close connectedSignal no more than once EdgeAddrs []string Region string + EdgeIPVersion allregions.ConfigIPVersion HAConnections int IncidentLookup IncidentLookup IsAutoupdated bool From 2b0d704777fd6af8136376b4d5c38f8ea193146a Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 2 Jun 2022 20:12:15 -0700 Subject: [PATCH 098/238] TUN-6341: Fix default config value for edge-ip-version --- cmd/cloudflared/tunnel/cmd.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 6f013947..4cedff50 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -518,6 +518,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Name: "edge-ip-version", Usage: "Cloudflare Edge ip address version to connect with. {4, 6, auto}", EnvVars: []string{"TUNNEL_EDGE_IP_VERSION"}, + Value: "4", Hidden: true, }), altsrc.NewStringFlag(&cli.StringFlag{ From 4ccef23dbc8746dfdf25ac1053bf380a05b94d57 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Fri, 3 Jun 2022 10:15:52 +0100 Subject: [PATCH 099/238] TUN-6343: Fix QUIC->HTTP2 fallback --- connection/errors.go | 9 --------- connection/quic.go | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/connection/errors.go b/connection/errors.go index 98eececd..df3cfe97 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -18,15 +18,6 @@ func (e DupConnRegisterTunnelError) Error() string { return "already connected to this server, trying another address" } -// Dial to edge server with quic failed -type EdgeQuicDialError struct { - Cause error -} - -func (e EdgeQuicDialError) Error() string { - return "failed to dial to edge with quic: " + e.Cause.Error() -} - // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/connection/quic.go b/connection/quic.go index 607a17c7..4e2f4681 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -55,7 +55,7 @@ func NewQUICConnection( ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { - return nil, EdgeQuicDialError{Cause: err} + return nil, fmt.Errorf("failed to dial to edge: %w", err) } datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) From 475939a77fbb8132e2f11f1334ad46152404f9f2 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Mon, 6 Jun 2022 14:15:35 +0100 Subject: [PATCH 100/238] TUN-6191: Update quic-go to v0.27.1 and with custom patch to allow keep alive period to be configurable The idle period is set to 5sec. We now also ping every second since last activity. This makes the quic.Connection less prone to being closed with no network activity, since we send multiple pings per idle period, and thus a single packet loss cannot cause the problem. --- connection/quic.go | 2 +- connection/quic_test.go | 8 +- go.mod | 10 +- go.sum | 17 +- quic/datagram.go | 4 +- quic/datagram_test.go | 2 +- quic/quic_protocol.go | 3 +- quic/safe_stream_test.go | 3 +- supervisor/tunnel.go | 2 +- .../lucas-clemente/quic-go/.golangci.yml | 1 - .../lucas-clemente/quic-go/README.md | 8 +- .../lucas-clemente/quic-go/client.go | 66 ++--- .../{closed_session.go => closed_conn.go} | 48 +-- .../lucas-clemente/quic-go/config.go | 3 +- .../quic-go/{session.go => connection.go} | 277 +++++++++--------- .../lucas-clemente/quic-go/interface.go | 75 ++--- .../flowcontrol/base_flow_controller.go | 7 +- .../flowcontrol/connection_flow_controller.go | 11 +- .../quic-go/internal/protocol/params.go | 16 +- .../quic-go/internal/qtls/go119.go | 2 +- .../quic-go/internal/qtls/go_oldversion.go | 7 + .../lucas-clemente/quic-go/logging/types.go | 8 +- .../lucas-clemente/quic-go/mockgen.go | 4 +- .../lucas-clemente/quic-go/mtu_discoverer.go | 12 +- .../lucas-clemente/quic-go/multiplexer.go | 2 +- .../quic-go/packet_handler_map.go | 81 +++-- .../lucas-clemente/quic-go/send_conn.go | 8 +- .../lucas-clemente/quic-go/send_queue.go | 8 +- .../lucas-clemente/quic-go/server.go | 136 ++++----- .../quic-go/{conn.go => sys_conn.go} | 33 ++- .../lucas-clemente/quic-go/sys_conn_df.go | 16 + .../quic-go/sys_conn_df_linux.go | 41 +++ .../quic-go/sys_conn_df_windows.go | 46 +++ ...er_darwin.go => sys_conn_helper_darwin.go} | 5 +- ..._freebsd.go => sys_conn_helper_freebsd.go} | 3 +- ...lper_linux.go => sys_conn_helper_linux.go} | 5 +- .../{conn_generic.go => sys_conn_no_oob.go} | 4 +- .../quic-go/{conn_oob.go => sys_conn_oob.go} | 2 +- .../{conn_windows.go => sys_conn_windows.go} | 19 +- .../marten-seemann/qtls-go1-16/common.go | 14 - .../marten-seemann/qtls-go1-16/common_js.go | 12 + .../marten-seemann/qtls-go1-16/common_nojs.go | 20 ++ .../qtls-go1-17/cipher_suites.go | 14 - .../marten-seemann/qtls-go1-17/cpu.go | 22 ++ .../marten-seemann/qtls-go1-17/cpu_other.go | 12 + .../qtls-go1-18/cipher_suites.go | 14 - .../marten-seemann/qtls-go1-18/cpu.go | 22 ++ .../marten-seemann/qtls-go1-18/cpu_other.go | 12 + vendor/modules.txt | 10 +- 49 files changed, 671 insertions(+), 486 deletions(-) rename vendor/github.com/lucas-clemente/quic-go/{closed_session.go => closed_conn.go} (51%) rename vendor/github.com/lucas-clemente/quic-go/{session.go => connection.go} (87%) create mode 100644 vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go rename vendor/github.com/lucas-clemente/quic-go/{conn.go => sys_conn.go} (69%) create mode 100644 vendor/github.com/lucas-clemente/quic-go/sys_conn_df.go create mode 100644 vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go create mode 100644 vendor/github.com/lucas-clemente/quic-go/sys_conn_df_windows.go rename vendor/github.com/lucas-clemente/quic-go/{conn_helper_darwin.go => sys_conn_helper_darwin.go} (81%) rename vendor/github.com/lucas-clemente/quic-go/{conn_helper_freebsd.go => sys_conn_helper_freebsd.go} (75%) rename vendor/github.com/lucas-clemente/quic-go/{conn_helper_linux.go => sys_conn_helper_linux.go} (81%) rename vendor/github.com/lucas-clemente/quic-go/{conn_generic.go => sys_conn_no_oob.go} (75%) rename vendor/github.com/lucas-clemente/quic-go/{conn_oob.go => sys_conn_oob.go} (99%) rename vendor/github.com/lucas-clemente/quic-go/{conn_windows.go => sys_conn_windows.go} (56%) create mode 100644 vendor/github.com/marten-seemann/qtls-go1-16/common_js.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-16/common_nojs.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-17/cpu.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-17/cpu_other.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-18/cpu.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-18/cpu_other.go diff --git a/connection/quic.go b/connection/quic.go index 4e2f4681..f1524c67 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -35,7 +35,7 @@ const ( // QUICConnection represents the type that facilitates Proxying via QUIC streams. type QUICConnection struct { - session quic.Session + session quic.Connection logger *zerolog.Logger orchestrator Orchestrator sessionManager datagramsession.Manager diff --git a/connection/quic_test.go b/connection/quic_test.go index 05fd64e2..cbfaf714 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -31,7 +31,7 @@ import ( var ( testTLSServerConfig = quicpogs.GenerateTLSConfig() testQUICConfig = &quic.Config{ - KeepAlive: true, + KeepAlivePeriod: 5 * time.Second, EnableDatagrams: true, } ) @@ -502,7 +502,7 @@ func TestServeUDPSession(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) // Establish QUIC connection with edge - edgeQUICSessionChan := make(chan quic.Session) + edgeQUICSessionChan := make(chan quic.Connection) go func() { earlyListener, err := quic.Listen(udpListener, testTLSServerConfig, testQUICConfig) require.NoError(t, err) @@ -522,7 +522,7 @@ func TestServeUDPSession(t *testing.T) { cancel() } -func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Session, closeType closeReason, expectedReason string, t *testing.T) { +func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Connection, closeType closeReason, expectedReason string, t *testing.T) { var ( payload = []byte(t.Name()) ) @@ -583,7 +583,7 @@ const ( closedByTimeout ) -func runRPCServer(ctx context.Context, session quic.Session, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) { +func runRPCServer(ctx context.Context, session quic.Connection, sessionRPCServer tunnelpogs.SessionManager, configRPCServer tunnelpogs.ConfigurationManager, t *testing.T) { stream, err := session.AcceptStream(ctx) require.NoError(t, err) diff --git a/go.mod b/go.mod index 93e4945f..1793f367 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/json-iterator/go v1.1.12 - github.com/lucas-clemente/quic-go v0.24.0 + github.com/lucas-clemente/quic-go v0.27.1 github.com/mattn/go-colorable v0.1.8 github.com/miekg/dns v1.1.45 github.com/mitchellh/go-homedir v1.1.0 @@ -74,9 +74,9 @@ require ( github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.0.3 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect + github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect + github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.8 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect @@ -105,7 +105,7 @@ require ( replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 +replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da // Avoid 'CVE-2022-21698' replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 diff --git a/go.sum b/go.sum index c0a1e0d5..f6a15c92 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 h1:PLTB4iA6sOgAItzQY642tYdcGKfG/7i2gu93JQGgUcM= -github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg= +github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da h1:FmuwbQ8RU/ftTKnfz5diawqvQFH1KDB9wN2Q8S2wqds= +github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -405,13 +405,12 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco= -github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk= -github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= -github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM= -github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI= +github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= +github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= +github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= +github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= +github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= +github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= diff --git a/quic/datagram.go b/quic/datagram.go index e9b305af..d86d524b 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -14,11 +14,11 @@ const ( ) type DatagramMuxer struct { - session quic.Session + session quic.Connection logger *zerolog.Logger } -func NewDatagramMuxer(quicSession quic.Session, logger *zerolog.Logger) (*DatagramMuxer, error) { +func NewDatagramMuxer(quicSession quic.Connection, logger *zerolog.Logger) (*DatagramMuxer, error) { return &DatagramMuxer{ session: quicSession, logger: logger, diff --git a/quic/datagram_test.go b/quic/datagram_test.go index 3f3eef1a..ac32f410 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -56,7 +56,7 @@ func TestMaxDatagramPayload(t *testing.T) { payload := make([]byte, maxDatagramPayloadSize) quicConfig := &quic.Config{ - KeepAlive: true, + KeepAlivePeriod: 5 * time.Millisecond, EnableDatagrams: true, MaxDatagramFrameSize: MaxDatagramFrameSize, } diff --git a/quic/quic_protocol.go b/quic/quic_protocol.go index 1e0c6eef..776b2eec 100644 --- a/quic/quic_protocol.go +++ b/quic/quic_protocol.go @@ -37,7 +37,8 @@ const ( protocolVersionLength = 2 HandshakeIdleTimeout = 5 * time.Second - MaxIdleTimeout = 15 * time.Second + MaxIdleTimeout = 5 * time.Second + MaxIdlePingPeriod = 1 * time.Second ) // RequestServerStream is a stream to serve requests diff --git a/quic/safe_stream_test.go b/quic/safe_stream_test.go index 48ffb559..b4c096b2 100644 --- a/quic/safe_stream_test.go +++ b/quic/safe_stream_test.go @@ -7,6 +7,7 @@ import ( "net" "sync" "testing" + "time" "github.com/lucas-clemente/quic-go" "github.com/stretchr/testify/require" @@ -15,7 +16,7 @@ import ( var ( testTLSServerConfig = GenerateTLSConfig() testQUICConfig = &quic.Config{ - KeepAlive: true, + KeepAlivePeriod: 5 * time.Second, EnableDatagrams: true, } exchanges = 1000 diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 45a1ffba..a75fe7b7 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -550,9 +550,9 @@ func ServeQUIC( quicConfig := &quic.Config{ HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout, MaxIdleTimeout: quicpogs.MaxIdleTimeout, + KeepAlivePeriod: quicpogs.MaxIdlePingPeriod, MaxIncomingStreams: connection.MaxConcurrentStreams, MaxIncomingUniStreams: connection.MaxConcurrentStreams, - KeepAlive: true, EnableDatagrams: true, MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize, Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex), diff --git a/vendor/github.com/lucas-clemente/quic-go/.golangci.yml b/vendor/github.com/lucas-clemente/quic-go/.golangci.yml index 05ddb79a..2589c053 100644 --- a/vendor/github.com/lucas-clemente/quic-go/.golangci.yml +++ b/vendor/github.com/lucas-clemente/quic-go/.golangci.yml @@ -28,7 +28,6 @@ linters: - ineffassign - misspell - prealloc - - scopelint - staticcheck - stylecheck - structcheck diff --git a/vendor/github.com/lucas-clemente/quic-go/README.md b/vendor/github.com/lucas-clemente/quic-go/README.md index f047525e..b28062d5 100644 --- a/vendor/github.com/lucas-clemente/quic-go/README.md +++ b/vendor/github.com/lucas-clemente/quic-go/README.md @@ -3,9 +3,6 @@ [![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go) -[![Travis Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=Travis+build)](https://travis-ci.org/lucas-clemente/quic-go) -[![CircleCI Build Status](https://img.shields.io/circleci/project/github/lucas-clemente/quic-go.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/lucas-clemente/quic-go) -[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master) [![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/) quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go. @@ -13,7 +10,7 @@ In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https: ## Guides -*We currently support Go 1.16.x and Go 1.17.x.* +*We currently support Go 1.16.x, Go 1.17.x, and Go 1.18.x.* Running tests: @@ -51,10 +48,11 @@ http.Client{ | [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | | [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | | [go-ipfs](https://github.com/ipfs/go-ipfs) | IPFS implementation in go | ![GitHub Repo stars](https://img.shields.io/github/stars/ipfs/go-ipfs?style=flat-square) | -| [nextdns](https://github.com/nextdns/nextdns) | NextDNS CLI client (DoH Proxy) | ![GitHub Repo stars](https://img.shields.io/github/stars/nextdns/nextdns?style=flat-square) | | [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | | [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) | | [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) | +| [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | +| [OONI Probe](https://github.com/ooni/probe-cli) | The Open Observatory of Network Interference (OONI) aims to empower decentralized efforts in documenting Internet censorship around the world. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | ## Contributing diff --git a/vendor/github.com/lucas-clemente/quic-go/client.go b/vendor/github.com/lucas-clemente/quic-go/client.go index 9dbe4ac5..c29a1c29 100644 --- a/vendor/github.com/lucas-clemente/quic-go/client.go +++ b/vendor/github.com/lucas-clemente/quic-go/client.go @@ -14,7 +14,7 @@ import ( ) type client struct { - conn sendConn + sconn sendConn // If the client is created with DialAddr, we create a packet conn. // If it is started with Dial, we take a packet conn as a parameter. createdPacketConn bool @@ -35,7 +35,7 @@ type client struct { handshakeChan chan struct{} - session quicSession + conn quicConn tracer logging.ConnectionTracer tracingID uint64 @@ -49,26 +49,26 @@ var ( ) // DialAddr establishes a new QUIC connection to a server. -// It uses a new UDP connection and closes this connection when the QUIC session is closed. +// It uses a new UDP connection and closes this connection when the QUIC connection is closed. // The hostname for SNI is taken from the given address. // The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. func DialAddr( addr string, tlsConf *tls.Config, config *Config, -) (Session, error) { +) (Connection, error) { return DialAddrContext(context.Background(), addr, tlsConf, config) } // DialAddrEarly establishes a new 0-RTT QUIC connection to a server. -// It uses a new UDP connection and closes this connection when the QUIC session is closed. +// It uses a new UDP connection and closes this connection when the QUIC connection is closed. // The hostname for SNI is taken from the given address. // The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. func DialAddrEarly( addr string, tlsConf *tls.Config, config *Config, -) (EarlySession, error) { +) (EarlyConnection, error) { return DialAddrEarlyContext(context.Background(), addr, tlsConf, config) } @@ -79,13 +79,13 @@ func DialAddrEarlyContext( addr string, tlsConf *tls.Config, config *Config, -) (EarlySession, error) { - sess, err := dialAddrContext(ctx, addr, tlsConf, config, true) +) (EarlyConnection, error) { + conn, err := dialAddrContext(ctx, addr, tlsConf, config, true) if err != nil { return nil, err } - utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session") - return sess, nil + utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early connection") + return conn, nil } // DialAddrContext establishes a new QUIC connection to a server using the provided context. @@ -95,7 +95,7 @@ func DialAddrContext( addr string, tlsConf *tls.Config, config *Config, -) (Session, error) { +) (Connection, error) { return dialAddrContext(ctx, addr, tlsConf, config, false) } @@ -105,7 +105,7 @@ func dialAddrContext( tlsConf *tls.Config, config *Config, use0RTT bool, -) (quicSession, error) { +) (quicConn, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err @@ -131,7 +131,7 @@ func Dial( host string, tlsConf *tls.Config, config *Config, -) (Session, error) { +) (Connection, error) { return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false) } @@ -146,7 +146,7 @@ func DialEarly( host string, tlsConf *tls.Config, config *Config, -) (EarlySession, error) { +) (EarlyConnection, error) { return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config) } @@ -159,7 +159,7 @@ func DialEarlyContext( host string, tlsConf *tls.Config, config *Config, -) (EarlySession, error) { +) (EarlyConnection, error) { return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false) } @@ -172,7 +172,7 @@ func DialContext( host string, tlsConf *tls.Config, config *Config, -) (Session, error) { +) (Connection, error) { return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false) } @@ -185,7 +185,7 @@ func dialContext( config *Config, use0RTT bool, createdPacketConn bool, -) (quicSession, error) { +) (quicConn, error) { if tlsConf == nil { return nil, errors.New("quic: tls.Config not set") } @@ -203,21 +203,21 @@ func dialContext( } c.packetHandlers = packetHandlers - c.tracingID = nextSessionTracingID() + c.tracingID = nextConnTracingID() if c.config.Tracer != nil { c.tracer = c.config.Tracer.TracerForConnection( - context.WithValue(ctx, SessionTracingKey, c.tracingID), + context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID, ) } if c.tracer != nil { - c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID) + c.tracer.StartedConnection(c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID) } if err := c.dial(ctx); err != nil { return nil, err } - return c.session, nil + return c.conn, nil } func newClient( @@ -265,7 +265,7 @@ func newClient( c := &client{ srcConnID: srcConnID, destConnID: destConnID, - conn: newSendPconn(pconn, remoteAddr), + sconn: newSendPconn(pconn, remoteAddr), createdPacketConn: createdPacketConn, use0RTT: use0RTT, tlsConf: tlsConf, @@ -278,10 +278,10 @@ func newClient( } func (c *client) dial(ctx context.Context) error { - c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) + c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sconn.LocalAddr(), c.sconn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) - c.session = newClientSession( - c.conn, + c.conn = newClientConnection( + c.sconn, c.packetHandlers, c.destConnID, c.srcConnID, @@ -295,11 +295,11 @@ func (c *client) dial(ctx context.Context) error { c.logger, c.version, ) - c.packetHandlers.Add(c.srcConnID, c.session) + c.packetHandlers.Add(c.srcConnID, c.conn) errorChan := make(chan error, 1) go func() { - err := c.session.run() // returns as soon as the session is closed + err := c.conn.run() // returns as soon as the connection is closed if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn { c.packetHandlers.Destroy() @@ -308,15 +308,15 @@ func (c *client) dial(ctx context.Context) error { }() // only set when we're using 0-RTT - // Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever. - var earlySessionChan <-chan struct{} + // Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever. + var earlyConnChan <-chan struct{} if c.use0RTT { - earlySessionChan = c.session.earlySessionReady() + earlyConnChan = c.conn.earlyConnReady() } select { case <-ctx.Done(): - c.session.shutdown() + c.conn.shutdown() return ctx.Err() case err := <-errorChan: var recreateErr *errCloseForRecreating @@ -327,10 +327,10 @@ func (c *client) dial(ctx context.Context) error { return c.dial(ctx) } return err - case <-earlySessionChan: + case <-earlyConnChan: // ready to send 0-RTT data return nil - case <-c.session.HandshakeComplete().Done(): + case <-c.conn.HandshakeComplete().Done(): // handshake successfully completed return nil } diff --git a/vendor/github.com/lucas-clemente/quic-go/closed_session.go b/vendor/github.com/lucas-clemente/quic-go/closed_conn.go similarity index 51% rename from vendor/github.com/lucas-clemente/quic-go/closed_session.go rename to vendor/github.com/lucas-clemente/quic-go/closed_conn.go index 31279020..35c2d739 100644 --- a/vendor/github.com/lucas-clemente/quic-go/closed_session.go +++ b/vendor/github.com/lucas-clemente/quic-go/closed_conn.go @@ -7,15 +7,15 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" ) -// A closedLocalSession is a session that we closed locally. -// When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame, +// A closedLocalConn is a connection that we closed locally. +// When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame, // with an exponential backoff. -type closedLocalSession struct { +type closedLocalConn struct { conn sendConn connClosePacket []byte closeOnce sync.Once - closeChan chan struct{} // is closed when the session is closed or destroyed + closeChan chan struct{} // is closed when the connection is closed or destroyed receivedPackets chan *receivedPacket counter uint64 // number of packets received @@ -25,16 +25,16 @@ type closedLocalSession struct { logger utils.Logger } -var _ packetHandler = &closedLocalSession{} +var _ packetHandler = &closedLocalConn{} -// newClosedLocalSession creates a new closedLocalSession and runs it. -func newClosedLocalSession( +// newClosedLocalConn creates a new closedLocalConn and runs it. +func newClosedLocalConn( conn sendConn, connClosePacket []byte, perspective protocol.Perspective, logger utils.Logger, ) packetHandler { - s := &closedLocalSession{ + s := &closedLocalConn{ conn: conn, connClosePacket: connClosePacket, perspective: perspective, @@ -46,7 +46,7 @@ func newClosedLocalSession( return s } -func (s *closedLocalSession) run() { +func (s *closedLocalConn) run() { for { select { case p := <-s.receivedPackets: @@ -57,14 +57,14 @@ func (s *closedLocalSession) run() { } } -func (s *closedLocalSession) handlePacket(p *receivedPacket) { +func (s *closedLocalConn) handlePacket(p *receivedPacket) { select { case s.receivedPackets <- p: default: } } -func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) { +func (s *closedLocalConn) handlePacketImpl(_ *receivedPacket) { s.counter++ // exponential backoff // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving @@ -79,34 +79,34 @@ func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) { } } -func (s *closedLocalSession) shutdown() { +func (s *closedLocalConn) shutdown() { s.destroy(nil) } -func (s *closedLocalSession) destroy(error) { +func (s *closedLocalConn) destroy(error) { s.closeOnce.Do(func() { close(s.closeChan) }) } -func (s *closedLocalSession) getPerspective() protocol.Perspective { +func (s *closedLocalConn) getPerspective() protocol.Perspective { return s.perspective } -// A closedRemoteSession is a session that was closed remotely. -// For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE. +// A closedRemoteConn is a connection that was closed remotely. +// For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE. // We can just ignore those packets. -type closedRemoteSession struct { +type closedRemoteConn struct { perspective protocol.Perspective } -var _ packetHandler = &closedRemoteSession{} +var _ packetHandler = &closedRemoteConn{} -func newClosedRemoteSession(pers protocol.Perspective) packetHandler { - return &closedRemoteSession{perspective: pers} +func newClosedRemoteConn(pers protocol.Perspective) packetHandler { + return &closedRemoteConn{perspective: pers} } -func (s *closedRemoteSession) handlePacket(*receivedPacket) {} -func (s *closedRemoteSession) shutdown() {} -func (s *closedRemoteSession) destroy(error) {} -func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective } +func (s *closedRemoteConn) handlePacket(*receivedPacket) {} +func (s *closedRemoteConn) shutdown() {} +func (s *closedRemoteConn) destroy(error) {} +func (s *closedRemoteConn) getPerspective() protocol.Perspective { return s.perspective } diff --git a/vendor/github.com/lucas-clemente/quic-go/config.go b/vendor/github.com/lucas-clemente/quic-go/config.go index c5f2389e..ef7a2622 100644 --- a/vendor/github.com/lucas-clemente/quic-go/config.go +++ b/vendor/github.com/lucas-clemente/quic-go/config.go @@ -109,11 +109,12 @@ func populateConfig(config *Config) *Config { HandshakeIdleTimeout: handshakeIdleTimeout, MaxIdleTimeout: idleTimeout, AcceptToken: config.AcceptToken, - KeepAlive: config.KeepAlive, + KeepAlivePeriod: config.KeepAlivePeriod, InitialStreamReceiveWindow: initialStreamReceiveWindow, MaxStreamReceiveWindow: maxStreamReceiveWindow, InitialConnectionReceiveWindow: initialConnectionReceiveWindow, MaxConnectionReceiveWindow: maxConnectionReceiveWindow, + AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, MaxIncomingStreams: maxIncomingStreams, MaxIncomingUniStreams: maxIncomingUniStreams, ConnectionIDLength: config.ConnectionIDLength, diff --git a/vendor/github.com/lucas-clemente/quic-go/session.go b/vendor/github.com/lucas-clemente/quic-go/connection.go similarity index 87% rename from vendor/github.com/lucas-clemente/quic-go/session.go rename to vendor/github.com/lucas-clemente/quic-go/connection.go index 6231383b..2f17565a 100644 --- a/vendor/github.com/lucas-clemente/quic-go/session.go +++ b/vendor/github.com/lucas-clemente/quic-go/connection.go @@ -90,7 +90,7 @@ func (p *receivedPacket) Clone() *receivedPacket { } } -type sessionRunner interface { +type connRunner interface { Add(protocol.ConnectionID, packetHandler) bool GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken Retire(protocol.ConnectionID) @@ -124,18 +124,14 @@ type errCloseForRecreating struct { } func (e *errCloseForRecreating) Error() string { - return "closing session in order to recreate it" + return "closing connection in order to recreate it" } -var sessionTracingID uint64 // to be accessed atomically -func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) } +var connTracingID uint64 // to be accessed atomically +func nextConnTracingID() uint64 { return atomic.AddUint64(&connTracingID, 1) } -func pathMTUDiscoveryEnabled(config *Config) bool { - return !disablePathMTUDiscovery && !config.DisablePathMTUDiscovery -} - -// A Session is a QUIC session -type session struct { +// A Connection is a QUIC connection +type connection struct { // Destination connection ID used during the handshake. // Used to check source connection ID on incoming packets. handshakeDestConnID protocol.ConnectionID @@ -192,7 +188,7 @@ type session struct { undecryptablePacketsToProcess []*receivedPacket clientHelloWritten <-chan *wire.TransportParameters - earlySessionReadyChan chan struct{} + earlyConnReadyChan chan struct{} handshakeCompleteChan chan struct{} // is closed when the handshake completes handshakeComplete bool handshakeConfirmed bool @@ -201,8 +197,8 @@ type session struct { versionNegotiated bool receivedFirstPacket bool - idleTimeout time.Duration - sessionCreationTime time.Time + idleTimeout time.Duration + creationTime time.Time // The idle timeout is set based on the max of the time we received the last packet... lastPacketReceivedTime time.Time // ... and the time we sent a new ack-eliciting packet after receiving a packet. @@ -226,15 +222,15 @@ type session struct { } var ( - _ Session = &session{} - _ EarlySession = &session{} - _ streamSender = &session{} - deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine + _ Connection = &connection{} + _ EarlyConnection = &connection{} + _ streamSender = &connection{} + deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine ) -var newSession = func( +var newConnection = func( conn sendConn, - runner sessionRunner, + runner connRunner, origDestConnID protocol.ConnectionID, retrySrcConnID *protocol.ConnectionID, clientDestConnID protocol.ConnectionID, @@ -249,8 +245,8 @@ var newSession = func( tracingID uint64, logger utils.Logger, v protocol.VersionNumber, -) quicSession { - s := &session{ +) quicConn { + s := &connection{ conn: conn, config: conf, handshakeDestConnID: destConnID, @@ -286,7 +282,7 @@ var newSession = func( s.version, ) s.preSetup() - s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID)) + s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( 0, getMaxPacketSize(s.conn.RemoteAddr()), @@ -369,9 +365,9 @@ var newSession = func( } // declare this as a variable, such that we can it mock it in the tests -var newClientSession = func( +var newClientConnection = func( conn sendConn, - runner sessionRunner, + runner connRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, conf *Config, @@ -383,8 +379,8 @@ var newClientSession = func( tracingID uint64, logger utils.Logger, v protocol.VersionNumber, -) quicSession { - s := &session{ +) quicConn { + s := &connection{ conn: conn, config: conf, origDestConnID: destConnID, @@ -416,7 +412,7 @@ var newClientSession = func( s.version, ) s.preSetup() - s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID)) + s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( initialPacketNumber, getMaxPacketSize(s.conn.RemoteAddr()), @@ -500,7 +496,7 @@ var newClientSession = func( return s } -func (s *session) preSetup() { +func (s *connection) preSetup() { s.sendQueue = newSendQueue(s.conn) s.retransmissionQueue = newRetransmissionQueue(s.version) s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams, s.version) @@ -509,10 +505,16 @@ func (s *session) preSetup() { protocol.ByteCount(s.config.InitialConnectionReceiveWindow), protocol.ByteCount(s.config.MaxConnectionReceiveWindow), s.onHasConnectionWindowUpdate, + func(size protocol.ByteCount) bool { + if s.config.AllowConnectionWindowIncrease == nil { + return true + } + return s.config.AllowConnectionWindowIncrease(s, uint64(size)) + }, s.rttStats, s.logger, ) - s.earlySessionReadyChan = make(chan struct{}) + s.earlyConnReadyChan = make(chan struct{}) s.streamsMap = newStreamsMap( s, s.newFlowController, @@ -522,14 +524,14 @@ func (s *session) preSetup() { s.version, ) s.framer = newFramer(s.streamsMap, s.version) - s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets) + s.receivedPackets = make(chan *receivedPacket, protocol.MaxConnUnprocessedPackets) s.closeChan = make(chan closeError, 1) s.sendingScheduled = make(chan struct{}, 1) s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background()) now := time.Now() s.lastPacketReceivedTime = now - s.sessionCreationTime = now + s.creationTime = now s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame) if s.config.EnableDatagrams { @@ -537,8 +539,8 @@ func (s *session) preSetup() { } } -// run the session main loop -func (s *session) run() error { +// run the connection main loop +func (s *connection) run() error { defer s.ctxCancel() s.timer = utils.NewTimer() @@ -556,7 +558,7 @@ func (s *session) run() error { s.scheduleSending() if zeroRTTParams != nil { s.restoreTransportParameters(zeroRTTParams) - close(s.earlySessionReadyChan) + close(s.earlyConnReadyChan) } case closeErr := <-s.closeChan: // put the close error back into the channel, so that the run loop can receive it @@ -590,7 +592,7 @@ runLoop: if processed := s.handlePacketImpl(p); processed { processedUndecryptablePacket = true } - // Don't set timers and send packets if the packet made us close the session. + // Don't set timers and send packets if the packet made us close the connection. select { case closeErr = <-s.closeChan: break runLoop @@ -613,7 +615,7 @@ runLoop: case <-sendQueueAvailable: case firstPacket := <-s.receivedPackets: wasProcessed := s.handlePacketImpl(firstPacket) - // Don't set timers and send packets if the packet made us close the session. + // Don't set timers and send packets if the packet made us close the connection. select { case closeErr = <-s.closeChan: break runLoop @@ -662,11 +664,11 @@ runLoop: } if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) { - // send a PING frame since there is no activity in the session + // send a PING frame since there is no activity in the connection s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.") s.framer.QueueControlFrame(&wire.PingFrame{}) s.keepAlivePingSent = true - } else if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.handshakeTimeout() { + } else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() { s.destroyImpl(qerr.ErrHandshakeTimeout) continue } else { @@ -705,24 +707,24 @@ runLoop: return closeErr.err } -// blocks until the early session can be used -func (s *session) earlySessionReady() <-chan struct{} { - return s.earlySessionReadyChan +// blocks until the early connection can be used +func (s *connection) earlyConnReady() <-chan struct{} { + return s.earlyConnReadyChan } -func (s *session) HandshakeComplete() context.Context { +func (s *connection) HandshakeComplete() context.Context { return s.handshakeCtx } -func (s *session) Context() context.Context { +func (s *connection) Context() context.Context { return s.ctx } -func (s *session) supportsDatagrams() bool { +func (s *connection) supportsDatagrams() bool { return s.peerParams.MaxDatagramFrameSize != protocol.InvalidByteCount } -func (s *session) ConnectionState() ConnectionState { +func (s *connection) ConnectionState() ConnectionState { return ConnectionState{ TLS: s.cryptoStreamHandler.ConnectionState(), SupportsDatagrams: s.supportsDatagrams(), @@ -731,18 +733,18 @@ func (s *session) ConnectionState() ConnectionState { // Time when the next keep-alive packet should be sent. // It returns a zero time if no keep-alive should be sent. -func (s *session) nextKeepAliveTime() time.Time { - if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() { +func (s *connection) nextKeepAliveTime() time.Time { + if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() { return time.Time{} } return s.lastPacketReceivedTime.Add(s.keepAliveInterval) } -func (s *session) maybeResetTimer() { +func (s *connection) maybeResetTimer() { var deadline time.Time if !s.handshakeComplete { deadline = utils.MinTime( - s.sessionCreationTime.Add(s.config.handshakeTimeout()), + s.creationTime.Add(s.config.handshakeTimeout()), s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout), ) } else { @@ -752,11 +754,6 @@ func (s *session) maybeResetTimer() { deadline = s.idleTimeoutStartTime().Add(s.idleTimeout) } } - if s.handshakeConfirmed && pathMTUDiscoveryEnabled(s.config) { - if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() { - deadline = utils.MinTime(deadline, probeTime) - } - } if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() { deadline = utils.MinTime(deadline, ackAlarm) @@ -771,11 +768,11 @@ func (s *session) maybeResetTimer() { s.timer.Reset(deadline) } -func (s *session) idleTimeoutStartTime() time.Time { +func (s *connection) idleTimeoutStartTime() time.Time { return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime) } -func (s *session) handleHandshakeComplete() { +func (s *connection) handleHandshakeComplete() { s.handshakeComplete = true s.handshakeCompleteChan = nil // prevent this case from ever being selected again defer s.handshakeCtxCancel() @@ -811,12 +808,12 @@ func (s *session) handleHandshakeComplete() { s.queueControlFrame(&wire.HandshakeDoneFrame{}) } -func (s *session) handleHandshakeConfirmed() { +func (s *connection) handleHandshakeConfirmed() { s.handshakeConfirmed = true s.sentPacketHandler.SetHandshakeConfirmed() s.cryptoStreamHandler.SetHandshakeConfirmed() - if pathMTUDiscoveryEnabled(s.config) { + if !s.config.DisablePathMTUDiscovery { maxPacketSize := s.peerParams.MaxUDPPayloadSize if maxPacketSize == 0 { maxPacketSize = protocol.MaxByteCount @@ -834,7 +831,7 @@ func (s *session) handleHandshakeConfirmed() { } } -func (s *session) handlePacketImpl(rp *receivedPacket) bool { +func (s *connection) handlePacketImpl(rp *receivedPacket) bool { s.sentPacketHandler.ReceivedBytes(rp.Size()) if wire.IsVersionNegotiationPacket(rp.data) { @@ -902,7 +899,7 @@ func (s *session) handlePacketImpl(rp *receivedPacket) bool { return processed } -func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ { +func (s *connection) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ { var wasQueued bool defer func() { @@ -994,7 +991,7 @@ func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool / return true } -func (s *session) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ { +func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ { if s.perspective == protocol.PerspectiveServer { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) @@ -1056,7 +1053,7 @@ func (s *session) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was t return true } -func (s *session) handleVersionNegotiationPacket(p *receivedPacket) { +func (s *connection) handleVersionNegotiationPacket(p *receivedPacket) { if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets if s.tracer != nil { @@ -1110,7 +1107,7 @@ func (s *session) handleVersionNegotiationPacket(p *receivedPacket) { }) } -func (s *session) handleUnpackedPacket( +func (s *connection) handleUnpackedPacket( packet *unpackedPacket, ecn protocol.ECN, rcvTime time.Time, @@ -1142,10 +1139,10 @@ func (s *session) handleUnpackedPacket( s.handshakeDestConnID = cid s.connIDManager.ChangeInitialConnID(cid) } - // We create the session as soon as we receive the first packet from the client. + // We create the connection as soon as we receive the first packet from the client. // We do that before authenticating the packet. // That means that if the source connection ID was corrupted, - // we might have create a session with an incorrect source connection ID. + // we might have create a connection with an incorrect source connection ID. // Once we authenticate the first packet, we need to update it. if s.perspective == protocol.PerspectiveServer { if !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) { @@ -1210,7 +1207,7 @@ func (s *session) handleUnpackedPacket( return s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) } -func (s *session) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error { +func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error { var err error wire.LogFrame(s.logger, f, false) switch frame := f.(type) { @@ -1258,9 +1255,9 @@ func (s *session) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, d } // handlePacket is called by the server with a new packet -func (s *session) handlePacket(p *receivedPacket) { +func (s *connection) handlePacket(p *receivedPacket) { // Discard packets once the amount of queued packets is larger than - // the channel size, protocol.MaxSessionUnprocessedPackets + // the channel size, protocol.MaxConnUnprocessedPackets select { case s.receivedPackets <- p: default: @@ -1270,7 +1267,7 @@ func (s *session) handlePacket(p *receivedPacket) { } } -func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) { +func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) { if frame.IsApplicationError { s.closeRemote(&qerr.ApplicationError{ Remote: true, @@ -1287,7 +1284,7 @@ func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) { }) } -func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { +func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel) if err != nil { return err @@ -1300,7 +1297,7 @@ func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.E return nil } -func (s *session) handleStreamFrame(frame *wire.StreamFrame) error { +func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error { str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) if err != nil { return err @@ -1313,11 +1310,11 @@ func (s *session) handleStreamFrame(frame *wire.StreamFrame) error { return str.handleStreamFrame(frame) } -func (s *session) handleMaxDataFrame(frame *wire.MaxDataFrame) { +func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) { s.connFlowController.UpdateSendWindow(frame.MaximumData) } -func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { +func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) if err != nil { return err @@ -1330,11 +1327,11 @@ func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error return nil } -func (s *session) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { +func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { s.streamsMap.HandleMaxStreamsFrame(frame) } -func (s *session) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { +func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) if err != nil { return err @@ -1346,7 +1343,7 @@ func (s *session) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { return str.handleResetStreamFrame(frame) } -func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error { +func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error { str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) if err != nil { return err @@ -1359,11 +1356,11 @@ func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error { return nil } -func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { +func (s *connection) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data}) } -func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error { +func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error { if s.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, @@ -1376,15 +1373,15 @@ func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error { return nil } -func (s *session) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { +func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { return s.connIDManager.Add(f) } -func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { +func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { return s.connIDGenerator.Retire(f.SequenceNumber, destConnID) } -func (s *session) handleHandshakeDoneFrame() error { +func (s *connection) handleHandshakeDoneFrame() error { if s.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, @@ -1397,7 +1394,7 @@ func (s *session) handleHandshakeDoneFrame() error { return nil } -func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error { +func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error { acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime) if err != nil { return err @@ -1411,7 +1408,7 @@ func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encrypt return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) } -func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error { +func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error { if f.Length(s.version) > protocol.ByteCount(s.config.MaxDatagramFrameSize) { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, @@ -1422,50 +1419,50 @@ func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error { return nil } -// closeLocal closes the session and send a CONNECTION_CLOSE containing the error -func (s *session) closeLocal(e error) { +// closeLocal closes the connection and send a CONNECTION_CLOSE containing the error +func (s *connection) closeLocal(e error) { s.closeOnce.Do(func() { if e == nil { - s.logger.Infof("Closing session.") + s.logger.Infof("Closing connection.") } else { - s.logger.Errorf("Closing session with error: %s", e) + s.logger.Errorf("Closing connection with error: %s", e) } s.closeChan <- closeError{err: e, immediate: false, remote: false} }) } -// destroy closes the session without sending the error on the wire -func (s *session) destroy(e error) { +// destroy closes the connection without sending the error on the wire +func (s *connection) destroy(e error) { s.destroyImpl(e) <-s.ctx.Done() } -func (s *session) destroyImpl(e error) { +func (s *connection) destroyImpl(e error) { s.closeOnce.Do(func() { if nerr, ok := e.(net.Error); ok && nerr.Timeout() { - s.logger.Errorf("Destroying session: %s", e) + s.logger.Errorf("Destroying connection: %s", e) } else { - s.logger.Errorf("Destroying session with error: %s", e) + s.logger.Errorf("Destroying connection with error: %s", e) } s.closeChan <- closeError{err: e, immediate: true, remote: false} }) } -func (s *session) closeRemote(e error) { +func (s *connection) closeRemote(e error) { s.closeOnce.Do(func() { - s.logger.Errorf("Peer closed session with error: %s", e) + s.logger.Errorf("Peer closed connection with error: %s", e) s.closeChan <- closeError{err: e, immediate: true, remote: true} }) } // Close the connection. It sends a NO_ERROR application error. // It waits until the run loop has stopped before returning -func (s *session) shutdown() { +func (s *connection) shutdown() { s.closeLocal(nil) <-s.ctx.Done() } -func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error { +func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error { s.closeLocal(&qerr.ApplicationError{ ErrorCode: code, ErrorMessage: desc, @@ -1474,7 +1471,7 @@ func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error { return nil } -func (s *session) handleCloseError(closeErr *closeError) { +func (s *connection) handleCloseError(closeErr *closeError) { e := closeErr.err if e == nil { e = &qerr.ApplicationError{} @@ -1518,7 +1515,7 @@ func (s *session) handleCloseError(closeErr *closeError) { // If this is a remote close we're done here if closeErr.remote { - s.connIDGenerator.ReplaceWithClosed(newClosedRemoteSession(s.perspective)) + s.connIDGenerator.ReplaceWithClosed(newClosedRemoteConn(s.perspective)) return } if closeErr.immediate { @@ -1529,11 +1526,11 @@ func (s *session) handleCloseError(closeErr *closeError) { if err != nil { s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) } - cs := newClosedLocalSession(s.conn, connClosePacket, s.perspective, s.logger) + cs := newClosedLocalConn(s.conn, connClosePacket, s.perspective, s.logger) s.connIDGenerator.ReplaceWithClosed(cs) } -func (s *session) dropEncryptionLevel(encLevel protocol.EncryptionLevel) { +func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) { s.sentPacketHandler.DropPackets(encLevel) s.receivedPacketHandler.DropPackets(encLevel) if s.tracer != nil { @@ -1551,7 +1548,7 @@ func (s *session) dropEncryptionLevel(encLevel protocol.EncryptionLevel) { } // is called for the client, when restoring transport parameters saved for 0-RTT -func (s *session) restoreTransportParameters(params *wire.TransportParameters) { +func (s *connection) restoreTransportParameters(params *wire.TransportParameters) { if s.logger.Debug() { s.logger.Debugf("Restoring Transport Parameters: %s", params) } @@ -1562,7 +1559,7 @@ func (s *session) restoreTransportParameters(params *wire.TransportParameters) { s.streamsMap.UpdateLimits(params) } -func (s *session) handleTransportParameters(params *wire.TransportParameters) { +func (s *connection) handleTransportParameters(params *wire.TransportParameters) { if err := s.checkTransportParameters(params); err != nil { s.closeLocal(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, @@ -1574,13 +1571,13 @@ func (s *session) handleTransportParameters(params *wire.TransportParameters) { // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets. if s.perspective == protocol.PerspectiveServer { s.applyTransportParameters() - // On the server side, the early session is ready as soon as we processed + // On the server side, the early connection is ready as soon as we processed // the client's transport parameters. - close(s.earlySessionReadyChan) + close(s.earlyConnReadyChan) } } -func (s *session) checkTransportParameters(params *wire.TransportParameters) error { +func (s *connection) checkTransportParameters(params *wire.TransportParameters) error { if s.logger.Debug() { s.logger.Debugf("Processed Transport Parameters: %s", params) } @@ -1613,11 +1610,11 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err return nil } -func (s *session) applyTransportParameters() { +func (s *connection) applyTransportParameters() { params := s.peerParams // Our local idle timeout will always be > 0. s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout) - s.keepAliveInterval = utils.MinDuration(s.idleTimeout/2, protocol.MaxKeepAliveInterval) + s.keepAliveInterval = utils.MinDuration(s.config.KeepAlivePeriod, utils.MinDuration(s.idleTimeout/2, protocol.MaxKeepAliveInterval)) s.streamsMap.UpdateLimits(params) s.packer.HandleTransportParameters(params) s.frameParser.SetAckDelayExponent(params.AckDelayExponent) @@ -1634,7 +1631,7 @@ func (s *session) applyTransportParameters() { } } -func (s *session) sendPackets() error { +func (s *connection) sendPackets() error { s.pacingDeadline = time.Time{} var sentPacket bool // only used in for packets sent in send mode SendAny @@ -1700,7 +1697,7 @@ func (s *session) sendPackets() error { } } -func (s *session) maybeSendAckOnlyPacket() error { +func (s *connection) maybeSendAckOnlyPacket() error { packet, err := s.packer.MaybePackAckPacket(s.handshakeConfirmed) if err != nil { return err @@ -1712,7 +1709,7 @@ func (s *session) maybeSendAckOnlyPacket() error { return nil } -func (s *session) sendProbePacket(encLevel protocol.EncryptionLevel) error { +func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel) error { // Queue probe packets until we actually send out a packet, // or until there are no more packets to queue. var packet *packedPacket @@ -1748,13 +1745,13 @@ func (s *session) sendProbePacket(encLevel protocol.EncryptionLevel) error { } } if packet == nil || packet.packetContents == nil { - return fmt.Errorf("session BUG: couldn't pack %s probe packet", encLevel) + return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel) } s.sendPackedPacket(packet, time.Now()) return nil } -func (s *session) sendPacket() (bool, error) { +func (s *connection) sendPacket() (bool, error) { if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked { s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset}) } @@ -1777,7 +1774,7 @@ func (s *session) sendPacket() (bool, error) { s.sendQueue.Send(packet.buffer) return true, nil } - if pathMTUDiscoveryEnabled(s.config) && s.mtuDiscoverer.ShouldSendProbe(now) { + if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) { packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing()) if err != nil { return false, err @@ -1793,7 +1790,7 @@ func (s *session) sendPacket() (bool, error) { return true, nil } -func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) { +func (s *connection) sendPackedPacket(packet *packedPacket, now time.Time) { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && packet.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now } @@ -1803,7 +1800,7 @@ func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) { s.sendQueue.Send(packet.buffer) } -func (s *session) sendConnectionClose(e error) ([]byte, error) { +func (s *connection) sendConnectionClose(e error) ([]byte, error) { var packet *coalescedPacket var err error var transportErr *qerr.TransportError @@ -1815,7 +1812,7 @@ func (s *session) sendConnectionClose(e error) ([]byte, error) { } else { packet, err = s.packer.PackConnectionClose(&qerr.TransportError{ ErrorCode: qerr.InternalError, - ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()), + ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()), }) } if err != nil { @@ -1825,7 +1822,7 @@ func (s *session) sendConnectionClose(e error) ([]byte, error) { return packet.buffer.Data, s.conn.Write(packet.buffer.Data) } -func (s *session) logPacketContents(p *packetContents) { +func (s *connection) logPacketContents(p *packetContents) { // tracing if s.tracer != nil { frames := make([]logging.Frame, 0, len(p.frames)) @@ -1848,7 +1845,7 @@ func (s *session) logPacketContents(p *packetContents) { } } -func (s *session) logCoalescedPacket(packet *coalescedPacket) { +func (s *connection) logCoalescedPacket(packet *coalescedPacket) { if s.logger.Debug() { if len(packet.packets) > 1 { s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.packets), packet.buffer.Len(), s.logID) @@ -1861,7 +1858,7 @@ func (s *session) logCoalescedPacket(packet *coalescedPacket) { } } -func (s *session) logPacket(packet *packedPacket) { +func (s *connection) logPacket(packet *packedPacket) { if s.logger.Debug() { s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.header.PacketNumber, packet.buffer.Len(), s.logID, packet.EncryptionLevel()) } @@ -1869,32 +1866,32 @@ func (s *session) logPacket(packet *packedPacket) { } // AcceptStream returns the next stream openend by the peer -func (s *session) AcceptStream(ctx context.Context) (Stream, error) { +func (s *connection) AcceptStream(ctx context.Context) (Stream, error) { return s.streamsMap.AcceptStream(ctx) } -func (s *session) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { +func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { return s.streamsMap.AcceptUniStream(ctx) } // OpenStream opens a stream -func (s *session) OpenStream() (Stream, error) { +func (s *connection) OpenStream() (Stream, error) { return s.streamsMap.OpenStream() } -func (s *session) OpenStreamSync(ctx context.Context) (Stream, error) { +func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) { return s.streamsMap.OpenStreamSync(ctx) } -func (s *session) OpenUniStream() (SendStream, error) { +func (s *connection) OpenUniStream() (SendStream, error) { return s.streamsMap.OpenUniStream() } -func (s *session) OpenUniStreamSync(ctx context.Context) (SendStream, error) { +func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) { return s.streamsMap.OpenUniStreamSync(ctx) } -func (s *session) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { +func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { initialSendWindow := s.peerParams.InitialMaxStreamDataUni if id.Type() == protocol.StreamTypeBidi { if id.InitiatedBy() == s.perspective { @@ -1916,14 +1913,14 @@ func (s *session) newFlowController(id protocol.StreamID) flowcontrol.StreamFlow } // scheduleSending signals that we have data for sending -func (s *session) scheduleSending() { +func (s *connection) scheduleSending() { select { case s.sendingScheduled <- struct{}{}: default: } } -func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) { +func (s *connection) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) { if s.handshakeComplete { panic("shouldn't queue undecryptable packets after handshake completion") } @@ -1941,33 +1938,33 @@ func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.He s.undecryptablePackets = append(s.undecryptablePackets, p) } -func (s *session) queueControlFrame(f wire.Frame) { +func (s *connection) queueControlFrame(f wire.Frame) { s.framer.QueueControlFrame(f) s.scheduleSending() } -func (s *session) onHasStreamWindowUpdate(id protocol.StreamID) { +func (s *connection) onHasStreamWindowUpdate(id protocol.StreamID) { s.windowUpdateQueue.AddStream(id) s.scheduleSending() } -func (s *session) onHasConnectionWindowUpdate() { +func (s *connection) onHasConnectionWindowUpdate() { s.windowUpdateQueue.AddConnection() s.scheduleSending() } -func (s *session) onHasStreamData(id protocol.StreamID) { +func (s *connection) onHasStreamData(id protocol.StreamID) { s.framer.AddActiveStream(id) s.scheduleSending() } -func (s *session) onStreamCompleted(id protocol.StreamID) { +func (s *connection) onStreamCompleted(id protocol.StreamID) { if err := s.streamsMap.DeleteStream(id); err != nil { s.closeLocal(err) } } -func (s *session) SendMessage(p []byte) error { +func (s *connection) SendMessage(p []byte) error { f := &wire.DatagramFrame{DataLenPresent: true} if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) { return errors.New("message too large") @@ -1977,27 +1974,27 @@ func (s *session) SendMessage(p []byte) error { return s.datagramQueue.AddAndWait(f) } -func (s *session) ReceiveMessage() ([]byte, error) { +func (s *connection) ReceiveMessage() ([]byte, error) { return s.datagramQueue.Receive() } -func (s *session) LocalAddr() net.Addr { +func (s *connection) LocalAddr() net.Addr { return s.conn.LocalAddr() } -func (s *session) RemoteAddr() net.Addr { +func (s *connection) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } -func (s *session) getPerspective() protocol.Perspective { +func (s *connection) getPerspective() protocol.Perspective { return s.perspective } -func (s *session) GetVersion() protocol.VersionNumber { +func (s *connection) GetVersion() protocol.VersionNumber { return s.version } -func (s *session) NextSession() Session { +func (s *connection) NextConnection() Connection { <-s.HandshakeComplete().Done() s.streamsMap.UseResetMaps() return s diff --git a/vendor/github.com/lucas-clemente/quic-go/interface.go b/vendor/github.com/lucas-clemente/quic-go/interface.go index 5c012256..73154e4a 100644 --- a/vendor/github.com/lucas-clemente/quic-go/interface.go +++ b/vendor/github.com/lucas-clemente/quic-go/interface.go @@ -59,15 +59,15 @@ type TokenStore interface { // when the server rejects a 0-RTT connection attempt. var Err0RTTRejected = errors.New("0-RTT rejected") -// SessionTracingKey can be used to associate a ConnectionTracer with a Session. -// It is set on the Session.Context() context, +// ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection. +// It is set on the Connection.Context() context, // as well as on the context passed to logging.Tracer.NewConnectionTracer. -var SessionTracingKey = sessionTracingCtxKey{} +var ConnectionTracingKey = connTracingCtxKey{} -type sessionTracingCtxKey struct{} +type connTracingCtxKey struct{} // Stream is the interface implemented by QUIC streams -// In addition to the errors listed on the Session, +// In addition to the errors listed on the Connection, // calls to stream functions can return a StreamError if the stream is canceled. type Stream interface { ReceiveStream @@ -87,7 +87,7 @@ type ReceiveStream interface { // after a fixed time limit; see SetDeadline and SetReadDeadline. // If the stream was canceled by the peer, the error implements the StreamError // interface, and Canceled() == true. - // If the session was closed due to a timeout, the error satisfies + // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. io.Reader // CancelRead aborts receiving on this stream. @@ -111,7 +111,7 @@ type SendStream interface { // after a fixed time limit; see SetDeadline and SetWriteDeadline. // If the stream was canceled by the peer, the error implements the StreamError // interface, and Canceled() == true. - // If the session was closed due to a timeout, the error satisfies + // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. io.Writer // Close closes the write-direction of the stream. @@ -124,7 +124,7 @@ type SendStream interface { // Write will unblock immediately, and future calls to Write will fail. // When called multiple times or after closing the stream it is a no-op. CancelWrite(StreamErrorCode) - // The context is canceled as soon as the write-side of the stream is closed. + // The Context is canceled as soon as the write-side of the stream is closed. // This happens when Close() or CancelWrite() is called, or when the peer // cancels the read-side of their stream. // Warning: This API should not be considered stable and might change soon. @@ -132,26 +132,26 @@ type SendStream interface { // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. // Even if write times out, it may return n > 0, indicating that - // some of the data was successfully written. + // some data was successfully written. // A zero value for t means Write will not time out. SetWriteDeadline(t time.Time) error } -// A Session is a QUIC connection between two peers. -// Calls to the session (and to streams) can return the following types of errors: +// A Connection is a QUIC connection between two peers. +// Calls to the connection (and to streams) can return the following types of errors: // * ApplicationError: for errors triggered by the application running on top of QUIC // * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer) // * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error) // * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error) // * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error) // * VersionNegotiationError: returned by the client, when there's no version overlap between the peers -type Session interface { +type Connection interface { // AcceptStream returns the next stream opened by the peer, blocking until one is available. - // If the session was closed due to a timeout, the error satisfies + // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. AcceptStream(context.Context) (Stream, error) // AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available. - // If the session was closed due to a timeout, the error satisfies + // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. AcceptUniStream(context.Context) (ReceiveStream, error) // OpenStream opens a new bidirectional QUIC stream. @@ -159,22 +159,22 @@ type Session interface { // The peer can only accept the stream after data has been sent on the stream. // If the error is non-nil, it satisfies the net.Error interface. // When reaching the peer's stream limit, err.Temporary() will be true. - // If the session was closed due to a timeout, Timeout() will be true. + // If the connection was closed due to a timeout, Timeout() will be true. OpenStream() (Stream, error) // OpenStreamSync opens a new bidirectional QUIC stream. // It blocks until a new stream can be opened. // If the error is non-nil, it satisfies the net.Error interface. - // If the session was closed due to a timeout, Timeout() will be true. + // If the connection was closed due to a timeout, Timeout() will be true. OpenStreamSync(context.Context) (Stream, error) // OpenUniStream opens a new outgoing unidirectional QUIC stream. // If the error is non-nil, it satisfies the net.Error interface. // When reaching the peer's stream limit, Temporary() will be true. - // If the session was closed due to a timeout, Timeout() will be true. + // If the connection was closed due to a timeout, Timeout() will be true. OpenUniStream() (SendStream, error) // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. // It blocks until a new stream can be opened. // If the error is non-nil, it satisfies the net.Error interface. - // If the session was closed due to a timeout, Timeout() will be true. + // If the connection was closed due to a timeout, Timeout() will be true. OpenUniStreamSync(context.Context) (SendStream, error) // LocalAddr returns the local address. LocalAddr() net.Addr @@ -183,7 +183,7 @@ type Session interface { // CloseWithError closes the connection with an error. // The error string will be sent to the peer. CloseWithError(ApplicationErrorCode, string) error - // The context is cancelled when the session is closed. + // The context is cancelled when the connection is closed. // Warning: This API should not be considered stable and might change soon. Context() context.Context // ConnectionState returns basic details about the QUIC connection. @@ -199,19 +199,19 @@ type Session interface { ReceiveMessage() ([]byte, error) } -// An EarlySession is a session that is handshaking. +// An EarlyConnection is a connection that is handshaking. // Data sent during the handshake is encrypted using the forward secure keys. // When using client certificates, the client's identity is only verified // after completion of the handshake. -type EarlySession interface { - Session +type EarlyConnection interface { + Connection - // Blocks until the handshake completes (or fails). + // HandshakeComplete blocks until the handshake completes (or fails). // Data sent before completion of the handshake is encrypted with 1-RTT keys. // Note that the client's identity hasn't been verified yet. HandshakeComplete() context.Context - NextSession() Session + NextConnection() Connection } // Config contains all configuration data needed for a QUIC server or client. @@ -266,6 +266,13 @@ type Config struct { // MaxConnectionReceiveWindow is the connection-level flow control window for receiving data. // If this value is zero, it will default to 15 MB. MaxConnectionReceiveWindow uint64 + // AllowConnectionWindowIncrease is called every time the connection flow controller attempts + // to increase the connection flow control window. + // If set, the caller can prevent an increase of the window. Typically, it would do so to + // limit the memory usage. + // To avoid deadlocks, it is not valid to call other functions on the connection or on streams + // in this callback. + AllowConnectionWindowIncrease func(sess Connection, delta uint64) bool // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. // Values above 2^60 are invalid. // If not set, it will default to 100. @@ -279,11 +286,13 @@ type Config struct { // The StatelessResetKey is used to generate stateless reset tokens. // If no key is configured, sending of stateless resets is disabled. StatelessResetKey []byte - // KeepAlive defines whether this peer will periodically send a packet to keep the connection alive. - KeepAlive bool + // KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive. + // If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most + // every half of MaxIdleTimeout, whichever is smaller). + KeepAlivePeriod time.Duration // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). // Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size. - // Note that Path MTU discovery is always disabled on Windows, see https://github.com/lucas-clemente/quic-go/issues/3273. + // Note that if Path MTU discovery is causing issues on your system, please open a new issue DisablePathMTUDiscovery bool // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. // This can be useful if version information is exchanged out-of-band. @@ -304,21 +313,21 @@ type ConnectionState struct { // A Listener for incoming QUIC connections type Listener interface { - // Close the server. All active sessions will be closed. + // Close the server. All active connections will be closed. Close() error // Addr returns the local network addr that the server is listening on. Addr() net.Addr - // Accept returns new sessions. It should be called in a loop. - Accept(context.Context) (Session, error) + // Accept returns new connections. It should be called in a loop. + Accept(context.Context) (Connection, error) } // An EarlyListener listens for incoming QUIC connections, // and returns them before the handshake completes. type EarlyListener interface { - // Close the server. All active sessions will be closed. + // Close the server. All active connections will be closed. Close() error // Addr returns the local network addr that the server is listening on. Addr() net.Addr - // Accept returns new early sessions. It should be called in a loop. - Accept(context.Context) (EarlySession, error) + // Accept returns new early connections. It should be called in a loop. + Accept(context.Context) (EarlyConnection, error) } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go index da6a7125..2bf14fdc 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go @@ -23,6 +23,8 @@ type baseFlowController struct { receiveWindowSize protocol.ByteCount maxReceiveWindowSize protocol.ByteCount + allowWindowIncrease func(size protocol.ByteCount) bool + epochStartTime time.Time epochStartOffset protocol.ByteCount rttStats *utils.RTTStats @@ -105,7 +107,10 @@ func (c *baseFlowController) maybeAdjustWindowSize() { now := time.Now() if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) { // window is consumed too fast, try to increase the window size - c.receiveWindowSize = utils.MinByteCount(2*c.receiveWindowSize, c.maxReceiveWindowSize) + newSize := utils.MinByteCount(2*c.receiveWindowSize, c.maxReceiveWindowSize) + if newSize > c.receiveWindowSize && (c.allowWindowIncrease == nil || c.allowWindowIncrease(newSize-c.receiveWindowSize)) { + c.receiveWindowSize = newSize + } } c.startNewAutoTuningEpoch(now) } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go index 90e7ceab..6bf2241b 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go @@ -19,11 +19,12 @@ type connectionFlowController struct { var _ ConnectionFlowController = &connectionFlowController{} // NewConnectionFlowController gets a new flow controller for the connection -// It is created before we receive the peer's transport paramenters, thus it starts with a sendWindow of 0. +// It is created before we receive the peer's transport parameters, thus it starts with a sendWindow of 0. func NewConnectionFlowController( receiveWindow protocol.ByteCount, maxReceiveWindow protocol.ByteCount, queueWindowUpdate func(), + allowWindowIncrease func(size protocol.ByteCount) bool, rttStats *utils.RTTStats, logger utils.Logger, ) ConnectionFlowController { @@ -33,6 +34,7 @@ func NewConnectionFlowController( receiveWindow: receiveWindow, receiveWindowSize: receiveWindow, maxReceiveWindowSize: maxReceiveWindow, + allowWindowIncrease: allowWindowIncrease, logger: logger, }, queueWindowUpdate: queueWindowUpdate, @@ -85,13 +87,16 @@ func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCoun c.mutex.Lock() if inc > c.receiveWindowSize { c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10)) - c.receiveWindowSize = utils.MinByteCount(inc, c.maxReceiveWindowSize) + newSize := utils.MinByteCount(inc, c.maxReceiveWindowSize) + if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) { + c.receiveWindowSize = newSize + } c.startNewAutoTuningEpoch(time.Now()) } c.mutex.Unlock() } -// The flow controller is reset when 0-RTT is rejected. +// Reset rests the flow controller. This happens when 0-RTT is rejected. // All stream data is invalidated, it's if we had never opened a stream and never sent any data. // At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet. func (c *connectionFlowController) Reset() error { diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go index 4d3085fb..9c2a5e7f 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go @@ -14,7 +14,7 @@ const InitialPacketSizeIPv6 = 1232 // MaxCongestionWindowPackets is the maximum congestion window in packet. const MaxCongestionWindowPackets = 10000 -// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the session. +// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the connection. const MaxUndecryptablePackets = 32 // ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window @@ -45,8 +45,8 @@ const DefaultMaxIncomingUniStreams = 100 // MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed. const MaxServerUnprocessedPackets = 1024 -// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed. -const MaxSessionUnprocessedPackets = 256 +// MaxConnUnprocessedPackets is the max number of packets stored in each connection that are not yet processed. +const MaxConnUnprocessedPackets = 256 // SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack. // Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod. @@ -55,7 +55,7 @@ const SkipPacketInitialPeriod PacketNumber = 256 // SkipPacketMaxPeriod is the maximum period length used for packet number skipping. const SkipPacketMaxPeriod PacketNumber = 128 * 1024 -// MaxAcceptQueueSize is the maximum number of sessions that the server queues for accepting. +// MaxAcceptQueueSize is the maximum number of connections that the server queues for accepting. // If the queue is full, new connection attempts will be rejected. const MaxAcceptQueueSize = 32 @@ -112,7 +112,7 @@ const DefaultHandshakeTimeout = 10 * time.Second // It should be shorter than the time that NATs clear their mapping. const MaxKeepAliveInterval = 20 * time.Second -// RetiredConnectionIDDeleteTimeout is the time we keep closed sessions around in order to retransmit the CONNECTION_CLOSE. +// RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE. // after this time all information about the old connection will be deleted const RetiredConnectionIDDeleteTimeout = 5 * time.Second @@ -189,7 +189,7 @@ const Max0RTTQueueingDuration = 100 * time.Millisecond const Max0RTTQueues = 32 // Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection. -// When a new session is created, all buffered packets are passed to the session immediately. -// To avoid blocking, this value has to be smaller than MaxSessionUnprocessedPackets. -// To avoid packets being dropped as undecryptable by the session, this value has to be smaller than MaxUndecryptablePackets. +// When a new connection is created, all buffered packets are passed to the connection immediately. +// To avoid blocking, this value has to be smaller than MaxConnUnprocessedPackets. +// To avoid packets being dropped as undecryptable by the connection, this value has to be smaller than MaxUndecryptablePackets. const Max0RTTQueueLen = 31 diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go index 2c648639..87e7132e 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go @@ -3,4 +3,4 @@ package qtls -var _ int = "quic-go doesn't build on Go 1.19 yet." +var _ int = "The version of quic-go you're using can't be built on Go 1.19 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions." diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go new file mode 100644 index 00000000..384d719c --- /dev/null +++ b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go_oldversion.go @@ -0,0 +1,7 @@ +//go:build (go1.9 || go1.10 || go1.11 || go1.12 || go1.13 || go1.14 || go1.15) && !go1.16 +// +build go1.9 go1.10 go1.11 go1.12 go1.13 go1.14 go1.15 +// +build !go1.16 + +package qtls + +var _ int = "The version of quic-go you're using can't be built using outdated Go versions. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions." diff --git a/vendor/github.com/lucas-clemente/quic-go/logging/types.go b/vendor/github.com/lucas-clemente/quic-go/logging/types.go index e1886503..ad800692 100644 --- a/vendor/github.com/lucas-clemente/quic-go/logging/types.go +++ b/vendor/github.com/lucas-clemente/quic-go/logging/types.go @@ -68,14 +68,14 @@ const ( TimerTypePTO ) -// TimeoutReason is the reason why a session is closed +// TimeoutReason is the reason why a connection is closed type TimeoutReason uint8 const ( - // TimeoutReasonHandshake is used when the session is closed due to a handshake timeout + // TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout // This reason is not defined in the qlog draft, but very useful for debugging. TimeoutReasonHandshake TimeoutReason = iota - // TimeoutReasonIdle is used when the session is closed due to an idle timeout + // TimeoutReasonIdle is used when the connection is closed due to an idle timeout // This reason is not defined in the qlog draft, but very useful for debugging. TimeoutReasonIdle ) @@ -87,7 +87,7 @@ const ( CongestionStateSlowStart CongestionState = iota // CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic CongestionStateCongestionAvoidance - // CongestionStateCongestionAvoidance is the recovery phase of Reno / Cubic + // CongestionStateRecovery is the recovery phase of Reno / Cubic CongestionStateRecovery // CongestionStateApplicationLimited means that the congestion controller is application limited CongestionStateApplicationLimited diff --git a/vendor/github.com/lucas-clemente/quic-go/mockgen.go b/vendor/github.com/lucas-clemente/quic-go/mockgen.go index 053cfa9a..22c2c0e7 100644 --- a/vendor/github.com/lucas-clemente/quic-go/mockgen.go +++ b/vendor/github.com/lucas-clemente/quic-go/mockgen.go @@ -16,8 +16,8 @@ package quic //go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker" //go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer" //go:generate sh -c "./mockgen_private.sh quic mock_mtu_discoverer_test.go github.com/lucas-clemente/quic-go mtuDiscoverer" -//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner" -//go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession" +//go:generate sh -c "./mockgen_private.sh quic mock_conn_runner_test.go github.com/lucas-clemente/quic-go connRunner" +//go:generate sh -c "./mockgen_private.sh quic mock_quic_conn_test.go github.com/lucas-clemente/quic-go quicConn" //go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/lucas-clemente/quic-go packetHandler" //go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler" //go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager" diff --git a/vendor/github.com/lucas-clemente/quic-go/mtu_discoverer.go b/vendor/github.com/lucas-clemente/quic-go/mtu_discoverer.go index a5a83021..bf38eaac 100644 --- a/vendor/github.com/lucas-clemente/quic-go/mtu_discoverer.go +++ b/vendor/github.com/lucas-clemente/quic-go/mtu_discoverer.go @@ -11,7 +11,6 @@ import ( type mtuDiscoverer interface { ShouldSendProbe(now time.Time) bool - NextProbeTime() time.Time GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount) } @@ -53,16 +52,7 @@ func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { if f.probeInFlight || f.done() { return false } - return !now.Before(f.NextProbeTime()) -} - -// NextProbeTime returns the time when the next probe packet should be sent. -// It returns the zero value if no probe packet should be sent. -func (f *mtuFinder) NextProbeTime() time.Time { - if f.probeInFlight || f.done() { - return time.Time{} - } - return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()) + return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())) } func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { diff --git a/vendor/github.com/lucas-clemente/quic-go/multiplexer.go b/vendor/github.com/lucas-clemente/quic-go/multiplexer.go index 006305af..2271b551 100644 --- a/vendor/github.com/lucas-clemente/quic-go/multiplexer.go +++ b/vendor/github.com/lucas-clemente/quic-go/multiplexer.go @@ -32,7 +32,7 @@ type connManager struct { } // The connMultiplexer listens on multiple net.PacketConns and dispatches -// incoming packets to the session handler. +// incoming packets to the connection handler. type connMultiplexer struct { mutex sync.Mutex diff --git a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go b/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go index 5b4659d2..6975d5a2 100644 --- a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go +++ b/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go @@ -7,8 +7,12 @@ import ( "errors" "fmt" "hash" + "io" "log" "net" + "os" + "strconv" + "strings" "sync" "time" @@ -45,6 +49,14 @@ func (h *zeroRTTQueue) Clear() { } } +// rawConn is a connection that allow reading of a receivedPacket. +type rawConn interface { + ReadPacket() (*receivedPacket, error) + WritePacket(b []byte, addr net.Addr, oob []byte) (int, error) + LocalAddr() net.Addr + io.Closer +} + type packetHandlerMapEntry struct { packetHandler packetHandler is0RTTQueue bool @@ -52,12 +64,12 @@ type packetHandlerMapEntry struct { // The packetHandlerMap stores packetHandlers, identified by connection ID. // It is used: -// * by the server to store sessions +// * by the server to store connections // * when multiplexing outgoing connections to store clients type packetHandlerMap struct { mutex sync.Mutex - conn connection + conn rawConn connIDLen int handlers map[string] /* string(ConnectionID)*/ packetHandlerMapEntry @@ -68,8 +80,8 @@ type packetHandlerMap struct { listening chan struct{} // is closed when listen returns closed bool - deleteRetiredSessionsAfter time.Duration - zeroRTTQueueDuration time.Duration + deleteRetiredConnsAfter time.Duration + zeroRTTQueueDuration time.Duration statelessResetEnabled bool statelessResetMutex sync.Mutex @@ -110,7 +122,7 @@ func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error { return nil } -// only print warnings about the UPD receive buffer size once +// only print warnings about the UDP receive buffer size once var receiveBufferWarningOnce sync.Once func newPacketHandlerMap( @@ -121,26 +133,31 @@ func newPacketHandlerMap( logger utils.Logger, ) (packetHandlerManager, error) { if err := setReceiveBuffer(c, logger); err != nil { - receiveBufferWarningOnce.Do(func() { - log.Printf("%s. See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) - }) + if !strings.Contains(err.Error(), "use of closed network connection") { + receiveBufferWarningOnce.Do(func() { + if disable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING")); disable { + return + } + log.Printf("%s. See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) + }) + } } conn, err := wrapConn(c) if err != nil { return nil, err } m := &packetHandlerMap{ - conn: conn, - connIDLen: connIDLen, - listening: make(chan struct{}), - handlers: make(map[string]packetHandlerMapEntry), - resetTokens: make(map[protocol.StatelessResetToken]packetHandler), - deleteRetiredSessionsAfter: protocol.RetiredConnectionIDDeleteTimeout, - zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration, - statelessResetEnabled: len(statelessResetKey) > 0, - statelessResetHasher: hmac.New(sha256.New, statelessResetKey), - tracer: tracer, - logger: logger, + conn: conn, + connIDLen: connIDLen, + listening: make(chan struct{}), + handlers: make(map[string]packetHandlerMapEntry), + resetTokens: make(map[protocol.StatelessResetToken]packetHandler), + deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, + zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration, + statelessResetEnabled: len(statelessResetKey) > 0, + statelessResetHasher: hmac.New(sha256.New, statelessResetKey), + tracer: tracer, + logger: logger, } go m.listen() @@ -196,7 +213,7 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co var q *zeroRTTQueue if entry, ok := h.handlers[string(clientDestConnID)]; ok { if !entry.is0RTTQueue { - h.logger.Debugf("Not adding connection ID %s for a new session, as it already exists.", clientDestConnID) + h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) return false } q = entry.packetHandler.(*zeroRTTQueue) @@ -212,7 +229,7 @@ func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.Co } h.handlers[string(clientDestConnID)] = packetHandlerMapEntry{packetHandler: sess} h.handlers[string(newConnID)] = packetHandlerMapEntry{packetHandler: sess} - h.logger.Debugf("Adding connection IDs %s and %s for a new session.", clientDestConnID, newConnID) + h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) return true } @@ -224,8 +241,8 @@ func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { } func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { - h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredSessionsAfter) - time.AfterFunc(h.deleteRetiredSessionsAfter, func() { + h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) + time.AfterFunc(h.deleteRetiredConnsAfter, func() { h.mutex.Lock() delete(h.handlers, string(id)) h.mutex.Unlock() @@ -237,14 +254,14 @@ func (h *packetHandlerMap) ReplaceWithClosed(id protocol.ConnectionID, handler p h.mutex.Lock() h.handlers[string(id)] = packetHandlerMapEntry{packetHandler: handler} h.mutex.Unlock() - h.logger.Debugf("Replacing session for connection ID %s with a closed session.", id) + h.logger.Debugf("Replacing connection for connection ID %s with a closed connection.", id) - time.AfterFunc(h.deleteRetiredSessionsAfter, func() { + time.AfterFunc(h.deleteRetiredConnsAfter, func() { h.mutex.Lock() handler.shutdown() delete(h.handlers, string(id)) h.mutex.Unlock() - h.logger.Debugf("Removing connection ID %s for a closed session after it has been retired.", id) + h.logger.Debugf("Removing connection ID %s for a closed connection after it has been retired.", id) }) } @@ -289,7 +306,7 @@ func (h *packetHandlerMap) CloseServer() { } // Destroy closes the underlying connection and waits until listen() has returned. -// It does not close active sessions. +// It does not close active connections. func (h *packetHandlerMap) Destroy() error { if err := h.conn.Close(); err != nil { return err @@ -327,6 +344,10 @@ func (h *packetHandlerMap) listen() { defer close(h.listening) for { p, err := h.conn.ReadPacket() + //nolint:staticcheck // SA1019 ignore this! + // TODO: This code is used to ignore wsa errors on Windows. + // Since net.Error.Temporary is deprecated as of Go 1.18, we should find a better solution. + // See https://github.com/lucas-clemente/quic-go/issues/1737 for details. if nerr, ok := err.(net.Error); ok && nerr.Temporary() { h.logger.Debugf("Temporary error reading from conn: %w", err) continue @@ -363,7 +384,7 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) { entry.packetHandler.handlePacket(p) return } - } else { // existing session + } else { // existing connection entry.packetHandler.handlePacket(p) return } @@ -389,7 +410,7 @@ func (h *packetHandlerMap) handlePacket(p *receivedPacket) { queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() { h.mutex.Lock() defer h.mutex.Unlock() - // The entry might have been replaced by an actual session. + // The entry might have been replaced by an actual connection. // Only delete it if it's still a 0-RTT queue. if entry, ok := h.handlers[string(connID)]; ok && entry.is0RTTQueue { delete(h.handlers, string(connID)) @@ -421,7 +442,7 @@ func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool { var token protocol.StatelessResetToken copy(token[:], data[len(data)-16:]) if sess, ok := h.resetTokens[token]; ok { - h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token) + h.logger.Debugf("Received a stateless reset with token %#x. Closing connection.", token) go sess.destroy(&StatelessResetError{Token: token}) return true } diff --git a/vendor/github.com/lucas-clemente/quic-go/send_conn.go b/vendor/github.com/lucas-clemente/quic-go/send_conn.go index b276af11..c53ebdfa 100644 --- a/vendor/github.com/lucas-clemente/quic-go/send_conn.go +++ b/vendor/github.com/lucas-clemente/quic-go/send_conn.go @@ -13,7 +13,7 @@ type sendConn interface { } type sconn struct { - connection + rawConn remoteAddr net.Addr info *packetInfo @@ -22,9 +22,9 @@ type sconn struct { var _ sendConn = &sconn{} -func newSendConn(c connection, remote net.Addr, info *packetInfo) sendConn { +func newSendConn(c rawConn, remote net.Addr, info *packetInfo) sendConn { return &sconn{ - connection: c, + rawConn: c, remoteAddr: remote, info: info, oob: info.OOB(), @@ -41,7 +41,7 @@ func (c *sconn) RemoteAddr() net.Addr { } func (c *sconn) LocalAddr() net.Addr { - addr := c.connection.LocalAddr() + addr := c.rawConn.LocalAddr() if c.info != nil { if udpAddr, ok := addr.(*net.UDPAddr); ok { addrCopy := *udpAddr diff --git a/vendor/github.com/lucas-clemente/quic-go/send_queue.go b/vendor/github.com/lucas-clemente/quic-go/send_queue.go index bf25dded..1fc8c1bf 100644 --- a/vendor/github.com/lucas-clemente/quic-go/send_queue.go +++ b/vendor/github.com/lucas-clemente/quic-go/send_queue.go @@ -64,7 +64,13 @@ func (h *sendQueue) Run() error { shouldClose = true case p := <-h.queue: if err := h.conn.Write(p.Data); err != nil { - return err + // This additional check enables: + // 1. Checking for "datagram too large" message from the kernel, as such, + // 2. Path MTU discovery,and + // 3. Eventual detection of loss PingFrame. + if !isMsgSizeErr(err) { + return err + } } p.Release() select { diff --git a/vendor/github.com/lucas-clemente/quic-go/server.go b/vendor/github.com/lucas-clemente/quic-go/server.go index 6ab869c0..5bf568a6 100644 --- a/vendor/github.com/lucas-clemente/quic-go/server.go +++ b/vendor/github.com/lucas-clemente/quic-go/server.go @@ -36,14 +36,14 @@ type unknownPacketHandler interface { type packetHandlerManager interface { AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool Destroy() error - sessionRunner + connRunner SetServer(unknownPacketHandler) CloseServer() } -type quicSession interface { - EarlySession - earlySessionReady() <-chan struct{} +type quicConn interface { + EarlyConnection + earlyConnReady() <-chan struct{} handlePacket(*receivedPacket) GetVersion() protocol.VersionNumber getPerspective() protocol.Perspective @@ -56,26 +56,26 @@ type quicSession interface { type baseServer struct { mutex sync.Mutex - acceptEarlySessions bool + acceptEarlyConns bool tlsConf *tls.Config config *Config - conn connection + conn rawConn // If the server is started with ListenAddr, we create a packet conn. // If it is started with Listen, we take a packet conn as a parameter. createdPacketConn bool tokenGenerator *handshake.TokenGenerator - sessionHandler packetHandlerManager + connHandler packetHandlerManager receivedPackets chan *receivedPacket // set as a member, so they can be set in the tests - newSession func( + newConn func( sendConn, - sessionRunner, + connRunner, protocol.ConnectionID, /* original dest connection ID */ *protocol.ConnectionID, /* retry src connection ID */ protocol.ConnectionID, /* client dest connection ID */ @@ -90,15 +90,15 @@ type baseServer struct { uint64, utils.Logger, protocol.VersionNumber, - ) quicSession + ) quicConn serverError error errorChan chan struct{} closed bool running chan struct{} // closed as soon as run() returns - sessionQueue chan quicSession - sessionQueueLen int32 // to be used as an atomic + connQueue chan quicConn + connQueueLen int32 // to be used as an atomic logger utils.Logger } @@ -112,7 +112,7 @@ type earlyServer struct{ *baseServer } var _ EarlyListener = &earlyServer{} -func (s *earlyServer) Accept(ctx context.Context) (EarlySession, error) { +func (s *earlyServer) Accept(ctx context.Context) (EarlyConnection, error) { return s.baseServer.accept(ctx) } @@ -123,7 +123,7 @@ func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, err return listenAddr(addr, tlsConf, config, false) } -// ListenAddrEarly works like ListenAddr, but it returns sessions before the handshake completes. +// ListenAddrEarly works like ListenAddr, but it returns connections before the handshake completes. func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) { s, err := listenAddr(addr, tlsConf, config, true) if err != nil { @@ -164,7 +164,7 @@ func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, return listen(conn, tlsConf, config, false) } -// ListenEarly works like Listen, but it returns sessions before the handshake completes. +// ListenEarly works like Listen, but it returns connections before the handshake completes. func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) { s, err := listen(conn, tlsConf, config, true) if err != nil { @@ -187,7 +187,7 @@ func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarl } } - sessionHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) + connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) if err != nil { return nil, err } @@ -200,21 +200,21 @@ func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarl return nil, err } s := &baseServer{ - conn: c, - tlsConf: tlsConf, - config: config, - tokenGenerator: tokenGenerator, - sessionHandler: sessionHandler, - sessionQueue: make(chan quicSession), - errorChan: make(chan struct{}), - running: make(chan struct{}), - receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), - newSession: newSession, - logger: utils.DefaultLogger.WithPrefix("server"), - acceptEarlySessions: acceptEarly, + conn: c, + tlsConf: tlsConf, + config: config, + tokenGenerator: tokenGenerator, + connHandler: connHandler, + connQueue: make(chan quicConn), + errorChan: make(chan struct{}), + running: make(chan struct{}), + receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), + newConn: newConnection, + logger: utils.DefaultLogger.WithPrefix("server"), + acceptEarlyConns: acceptEarly, } go s.run() - sessionHandler.SetServer(s) + connHandler.SetServer(s) s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) return s, nil } @@ -258,19 +258,19 @@ var defaultAcceptToken = func(clientAddr net.Addr, token *Token) bool { return sourceAddr == token.RemoteAddr } -// Accept returns sessions that already completed the handshake. -// It is only valid if acceptEarlySessions is false. -func (s *baseServer) Accept(ctx context.Context) (Session, error) { +// Accept returns connections that already completed the handshake. +// It is only valid if acceptEarlyConns is false. +func (s *baseServer) Accept(ctx context.Context) (Connection, error) { return s.accept(ctx) } -func (s *baseServer) accept(ctx context.Context) (quicSession, error) { +func (s *baseServer) accept(ctx context.Context) (quicConn, error) { select { case <-ctx.Done(): return nil, ctx.Err() - case sess := <-s.sessionQueue: - atomic.AddInt32(&s.sessionQueueLen, -1) - return sess, nil + case conn := <-s.connQueue: + atomic.AddInt32(&s.connQueueLen, -1) + return conn, nil case <-s.errorChan: return nil, s.serverError } @@ -294,9 +294,9 @@ func (s *baseServer) Close() error { s.mutex.Unlock() <-s.running - s.sessionHandler.CloseServer() + s.connHandler.CloseServer() if createdPacketConn { - return s.sessionHandler.Destroy() + return s.connHandler.Destroy() } return nil } @@ -336,7 +336,7 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s } return false } - // If we're creating a new session, the packet will be passed to the session. + // If we're creating a new connection, the packet will be passed to the connection. // The header will then be parsed again. hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength) if err != nil && err != wire.ErrUnsupportedVersion { @@ -436,7 +436,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro return nil } - if queueLen := atomic.LoadInt32(&s.sessionQueueLen); queueLen >= protocol.MaxAcceptQueueSize { + if queueLen := atomic.LoadInt32(&s.connQueueLen); queueLen >= protocol.MaxAcceptQueueSize { s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize) go func() { defer p.buffer.Release() @@ -452,9 +452,9 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro return err } s.logger.Debugf("Changing connection ID to %s.", connID) - var sess quicSession - tracingID := nextSessionTracingID() - if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { + var conn quicConn + tracingID := nextConnTracingID() + if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { var tracer logging.ConnectionTracer if s.config.Tracer != nil { // Use the same connection ID that is passed to the client's GetLogWriter callback. @@ -463,74 +463,74 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro connID = origDestConnID } tracer = s.config.Tracer.TracerForConnection( - context.WithValue(context.Background(), SessionTracingKey, tracingID), + context.WithValue(context.Background(), ConnectionTracingKey, tracingID), protocol.PerspectiveServer, connID, ) } - sess = s.newSession( + conn = s.newConn( newSendConn(s.conn, p.remoteAddr, p.info), - s.sessionHandler, + s.connHandler, origDestConnID, retrySrcConnID, hdr.DestConnectionID, hdr.SrcConnectionID, connID, - s.sessionHandler.GetStatelessResetToken(connID), + s.connHandler.GetStatelessResetToken(connID), s.config, s.tlsConf, s.tokenGenerator, - s.acceptEarlySessions, + s.acceptEarlyConns, tracer, tracingID, s.logger, hdr.Version, ) - sess.handlePacket(p) - return sess + conn.handlePacket(p) + return conn }); !added { return nil } - go sess.run() - go s.handleNewSession(sess) - if sess == nil { + go conn.run() + go s.handleNewConn(conn) + if conn == nil { p.buffer.Release() return nil } return nil } -func (s *baseServer) handleNewSession(sess quicSession) { - sessCtx := sess.Context() - if s.acceptEarlySessions { - // wait until the early session is ready (or the handshake fails) +func (s *baseServer) handleNewConn(conn quicConn) { + connCtx := conn.Context() + if s.acceptEarlyConns { + // wait until the early connection is ready (or the handshake fails) select { - case <-sess.earlySessionReady(): - case <-sessCtx.Done(): + case <-conn.earlyConnReady(): + case <-connCtx.Done(): return } } else { // wait until the handshake is complete (or fails) select { - case <-sess.HandshakeComplete().Done(): - case <-sessCtx.Done(): + case <-conn.HandshakeComplete().Done(): + case <-connCtx.Done(): return } } - atomic.AddInt32(&s.sessionQueueLen, 1) + atomic.AddInt32(&s.connQueueLen, 1) select { - case s.sessionQueue <- sess: - // blocks until the session is accepted - case <-sessCtx.Done(): - atomic.AddInt32(&s.sessionQueueLen, -1) - // don't pass sessions that were already closed to Accept() + case s.connQueue <- conn: + // blocks until the connection is accepted + case <-connCtx.Done(): + atomic.AddInt32(&s.connQueueLen, -1) + // don't pass connections that were already closed to Accept() } } func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { // Log the Initial packet now. - // If no Retry is sent, the packet will be logged by the session. + // If no Retry is sent, the packet will be logged by the connection. (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) if err != nil { diff --git a/vendor/github.com/lucas-clemente/quic-go/conn.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn.go similarity index 69% rename from vendor/github.com/lucas-clemente/quic-go/conn.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn.go index 2f4e3a23..d73b01d2 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn.go @@ -1,7 +1,6 @@ package quic import ( - "io" "net" "syscall" "time" @@ -10,14 +9,8 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" ) -type connection interface { - ReadPacket() (*receivedPacket, error) - WritePacket(b []byte, addr net.Addr, oob []byte) (int, error) - LocalAddr() net.Addr - io.Closer -} - -// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will read the ECN bits from the IP header. +// OOBCapablePacketConn is a connection that allows the reading of ECN bits from the IP header. +// If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will use it. // In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets. type OOBCapablePacketConn interface { net.PacketConn @@ -28,7 +21,20 @@ type OOBCapablePacketConn interface { var _ OOBCapablePacketConn = &net.UDPConn{} -func wrapConn(pc net.PacketConn) (connection, error) { +func wrapConn(pc net.PacketConn) (rawConn, error) { + conn, ok := pc.(interface { + SyscallConn() (syscall.RawConn, error) + }) + if ok { + rawConn, err := conn.SyscallConn() + if err != nil { + return nil, err + } + err = setDF(rawConn) + if err != nil { + return nil, err + } + } c, ok := pc.(OOBCapablePacketConn) if !ok { utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.") @@ -37,11 +43,16 @@ func wrapConn(pc net.PacketConn) (connection, error) { return newConn(c) } +// The basicConn is the most trivial implementation of a connection. +// It reads a single packet from the underlying net.PacketConn. +// It is used when +// * the net.PacketConn is not a OOBCapablePacketConn, and +// * when the OS doesn't support OOB. type basicConn struct { net.PacketConn } -var _ connection = &basicConn{} +var _ rawConn = &basicConn{} func (c *basicConn) ReadPacket() (*receivedPacket, error) { buffer := getPacketBuffer() diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df.go new file mode 100644 index 00000000..ae9274d9 --- /dev/null +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df.go @@ -0,0 +1,16 @@ +//go:build !linux && !windows +// +build !linux,!windows + +package quic + +import "syscall" + +func setDF(rawConn syscall.RawConn) error { + // no-op on unsupported platforms + return nil +} + +func isMsgSizeErr(err error) bool { + // to be implemented for more specific platforms + return false +} diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go new file mode 100644 index 00000000..69f8fc93 --- /dev/null +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go @@ -0,0 +1,41 @@ +//go:build linux +// +build linux + +package quic + +import ( + "errors" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/lucas-clemente/quic-go/internal/utils" +) + +func setDF(rawConn syscall.RawConn) error { + // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" + // and the datagram will not be fragmented + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO) + errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO) + }); err != nil { + return err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + utils.DefaultLogger.Errorf("setting DF failed for both IPv4 and IPv6") + } + return nil +} + +func isMsgSizeErr(err error) bool { + // https://man7.org/linux/man-pages/man7/udp.7.html + return errors.Is(err, unix.EMSGSIZE) +} diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_windows.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_windows.go new file mode 100644 index 00000000..4649f646 --- /dev/null +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_windows.go @@ -0,0 +1,46 @@ +//go:build windows +// +build windows + +package quic + +import ( + "errors" + "syscall" + + "github.com/lucas-clemente/quic-go/internal/utils" + "golang.org/x/sys/windows" +) + +const ( + // same for both IPv4 and IPv6 on Windows + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html + IP_DONTFRAGMENT = 14 + IPV6_DONTFRAG = 14 +) + +func setDF(rawConn syscall.RawConn) error { + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) + errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_DONTFRAG, 1) + }); err != nil { + return err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + return errors.New("setting DF failed for both IPv4 and IPv6") + } + return nil +} + +func isMsgSizeErr(err error) bool { + // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + return errors.Is(err, windows.WSAEMSGSIZE) +} diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_helper_darwin.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_darwin.go similarity index 81% rename from vendor/github.com/lucas-clemente/quic-go/conn_helper_darwin.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_darwin.go index fdab73b6..eabf489f 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_helper_darwin.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_darwin.go @@ -5,10 +5,7 @@ package quic import "golang.org/x/sys/unix" -const ( - msgTypeIPTOS = unix.IP_RECVTOS - disablePathMTUDiscovery = false -) +const msgTypeIPTOS = unix.IP_RECVTOS const ( ipv4RECVPKTINFO = unix.IP_RECVPKTINFO diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_helper_freebsd.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_freebsd.go similarity index 75% rename from vendor/github.com/lucas-clemente/quic-go/conn_helper_freebsd.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_freebsd.go index e22f9861..0b3e8434 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_helper_freebsd.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_freebsd.go @@ -6,8 +6,7 @@ package quic import "golang.org/x/sys/unix" const ( - msgTypeIPTOS = unix.IP_RECVTOS - disablePathMTUDiscovery = false + msgTypeIPTOS = unix.IP_RECVTOS ) const ( diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_helper_linux.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_linux.go similarity index 81% rename from vendor/github.com/lucas-clemente/quic-go/conn_helper_linux.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_linux.go index 4aa04dc9..51bec900 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_helper_linux.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_helper_linux.go @@ -5,10 +5,7 @@ package quic import "golang.org/x/sys/unix" -const ( - msgTypeIPTOS = unix.IP_TOS - disablePathMTUDiscovery = false -) +const msgTypeIPTOS = unix.IP_TOS const ( ipv4RECVPKTINFO = unix.IP_PKTINFO diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_generic.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_no_oob.go similarity index 75% rename from vendor/github.com/lucas-clemente/quic-go/conn_generic.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_no_oob.go index 526778c1..e3b0d11f 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_generic.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_no_oob.go @@ -5,9 +5,7 @@ package quic import "net" -const disablePathMTUDiscovery = false - -func newConn(c net.PacketConn) (connection, error) { +func newConn(c net.PacketConn) (rawConn, error) { return &basicConn{PacketConn: c}, nil } diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_oob.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_oob.go similarity index 99% rename from vendor/github.com/lucas-clemente/quic-go/conn_oob.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_oob.go index b4678137..acd74d02 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_oob.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_oob.go @@ -64,7 +64,7 @@ type oobConn struct { buffers [batchSize]*packetBuffer } -var _ connection = &oobConn{} +var _ rawConn = &oobConn{} func newConn(c OOBCapablePacketConn) (*oobConn, error) { rawConn, err := c.SyscallConn() diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_windows.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_windows.go similarity index 56% rename from vendor/github.com/lucas-clemente/quic-go/conn_windows.go rename to vendor/github.com/lucas-clemente/quic-go/sys_conn_windows.go index a6e591b6..f2cc22ab 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_windows.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_windows.go @@ -12,24 +12,7 @@ import ( "golang.org/x/sys/windows" ) -const ( - disablePathMTUDiscovery = true - IP_DONTFRAGMENT = 14 -) - -func newConn(c OOBCapablePacketConn) (connection, error) { - rawConn, err := c.SyscallConn() - if err != nil { - return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err) - } - if err := rawConn.Control(func(fd uintptr) { - // This should succeed if the connection is a IPv4 or a dual-stack connection. - // It will fail for IPv6 connections. - // TODO: properly handle error. - _ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) - }); err != nil { - return nil, err - } +func newConn(c OOBCapablePacketConn) (rawConn, error) { return &basicConn{PacketConn: c}, nil } diff --git a/vendor/github.com/marten-seemann/qtls-go1-16/common.go b/vendor/github.com/marten-seemann/qtls-go1-16/common.go index 9d07e175..266f93fe 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-16/common.go +++ b/vendor/github.com/marten-seemann/qtls-go1-16/common.go @@ -20,13 +20,10 @@ import ( "fmt" "io" "net" - "runtime" "sort" "strings" "sync" "time" - - "golang.org/x/sys/cpu" ) const ( @@ -1463,17 +1460,6 @@ func defaultCipherSuitesTLS13() []uint16 { return varDefaultCipherSuitesTLS13 } -var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X -) - func initDefaultCipherSuites() { var topCipherSuites []uint16 diff --git a/vendor/github.com/marten-seemann/qtls-go1-16/common_js.go b/vendor/github.com/marten-seemann/qtls-go1-16/common_js.go new file mode 100644 index 00000000..97e6ecef --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-16/common_js.go @@ -0,0 +1,12 @@ +// +build js + +package qtls + +var ( + hasGCMAsmAMD64 = false + hasGCMAsmARM64 = false + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = false + + hasAESGCMHardwareSupport = false +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-16/common_nojs.go b/vendor/github.com/marten-seemann/qtls-go1-16/common_nojs.go new file mode 100644 index 00000000..5e56e0fb --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-16/common_nojs.go @@ -0,0 +1,20 @@ +// +build !js + +package qtls + +import ( + "runtime" + + "golang.org/x/sys/cpu" +) + +var ( + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || + runtime.GOARCH == "arm64" && hasGCMAsmARM64 || + runtime.GOARCH == "s390x" && hasGCMAsmS390X +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/cipher_suites.go b/vendor/github.com/marten-seemann/qtls-go1-17/cipher_suites.go index f2846642..53a3956a 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-17/cipher_suites.go +++ b/vendor/github.com/marten-seemann/qtls-go1-17/cipher_suites.go @@ -15,10 +15,8 @@ import ( "crypto/sha256" "fmt" "hash" - "runtime" "golang.org/x/crypto/chacha20poly1305" - "golang.org/x/sys/cpu" ) // CipherSuite is a TLS cipher suite. Note that most functions in this package @@ -365,18 +363,6 @@ var defaultCipherSuitesTLS13NoAES = []uint16{ TLS_AES_256_GCM_SHA384, } -var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && - (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X -) - var aesgcmCiphers = map[uint16]bool{ // TLS 1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true, diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/cpu.go b/vendor/github.com/marten-seemann/qtls-go1-17/cpu.go new file mode 100644 index 00000000..12194508 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-17/cpu.go @@ -0,0 +1,22 @@ +//go:build !js +// +build !js + +package qtls + +import ( + "runtime" + + "golang.org/x/sys/cpu" +) + +var ( + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && + (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || + runtime.GOARCH == "arm64" && hasGCMAsmARM64 || + runtime.GOARCH == "s390x" && hasGCMAsmS390X +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/cpu_other.go b/vendor/github.com/marten-seemann/qtls-go1-17/cpu_other.go new file mode 100644 index 00000000..33f7d219 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-17/cpu_other.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package qtls + +var ( + hasGCMAsmAMD64 = false + hasGCMAsmARM64 = false + hasGCMAsmS390X = false + + hasAESGCMHardwareSupport = false +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/cipher_suites.go b/vendor/github.com/marten-seemann/qtls-go1-18/cipher_suites.go index 2071cebf..e0be5147 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/cipher_suites.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/cipher_suites.go @@ -15,10 +15,8 @@ import ( "crypto/sha256" "fmt" "hash" - "runtime" "golang.org/x/crypto/chacha20poly1305" - "golang.org/x/sys/cpu" ) // CipherSuite is a TLS cipher suite. Note that most functions in this package @@ -365,18 +363,6 @@ var defaultCipherSuitesTLS13NoAES = []uint16{ TLS_AES_256_GCM_SHA384, } -var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && - (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X -) - var aesgcmCiphers = map[uint16]bool{ // TLS 1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true, diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/cpu.go b/vendor/github.com/marten-seemann/qtls-go1-18/cpu.go new file mode 100644 index 00000000..12194508 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-18/cpu.go @@ -0,0 +1,22 @@ +//go:build !js +// +build !js + +package qtls + +import ( + "runtime" + + "golang.org/x/sys/cpu" +) + +var ( + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && + (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || + runtime.GOARCH == "arm64" && hasGCMAsmARM64 || + runtime.GOARCH == "s390x" && hasGCMAsmS390X +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/cpu_other.go b/vendor/github.com/marten-seemann/qtls-go1-18/cpu_other.go new file mode 100644 index 00000000..33f7d219 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-18/cpu_other.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package qtls + +var ( + hasGCMAsmAMD64 = false + hasGCMAsmARM64 = false + hasGCMAsmS390X = false + + hasAESGCMHardwareSupport = false +) diff --git a/vendor/modules.txt b/vendor/modules.txt index cc9abba7..d18395cb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -201,7 +201,7 @@ github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc github.com/json-iterator/go # github.com/kylelemons/godebug v1.1.0 ## explicit; go 1.11 -# github.com/lucas-clemente/quic-go v0.24.0 => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 +# github.com/lucas-clemente/quic-go v0.27.1 => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da ## explicit; go 1.16 github.com/lucas-clemente/quic-go github.com/lucas-clemente/quic-go/internal/ackhandler @@ -219,13 +219,13 @@ github.com/lucas-clemente/quic-go/quicvarint # github.com/lucasb-eyer/go-colorful v1.0.3 ## explicit; go 1.12 github.com/lucasb-eyer/go-colorful -# github.com/marten-seemann/qtls-go1-16 v0.1.4 +# github.com/marten-seemann/qtls-go1-16 v0.1.5 ## explicit; go 1.16 github.com/marten-seemann/qtls-go1-16 -# github.com/marten-seemann/qtls-go1-17 v0.1.0 +# github.com/marten-seemann/qtls-go1-17 v0.1.1 ## explicit; go 1.17 github.com/marten-seemann/qtls-go1-17 -# github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 +# github.com/marten-seemann/qtls-go1-18 v0.1.1 ## explicit; go 1.18 github.com/marten-seemann/qtls-go1-18 # github.com/mattn/go-colorable v0.1.8 @@ -612,5 +612,5 @@ zombiezen.com/go/capnproto2/schemas zombiezen.com/go/capnproto2/server zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.24.1-0.20220110095058-981dc498cb62 +# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 From cc8aa0efb58bff1162d4a52a3fa0e687734ce1b8 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 8 Jun 2022 15:59:36 +0100 Subject: [PATCH 101/238] TUN-6361: Add cloudflared arm builds to pkging as well --- release_pkgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release_pkgs.py b/release_pkgs.py index 6f365b03..36f672ff 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -340,7 +340,7 @@ def parse_args(): ) parser.add_argument( - "--archs", default=["amd64", "386", "arm64"], help="list of architectures we want to package for. Note that\ + "--archs", default=["amd64", "386", "arm64", "arm"], help="list of architectures we want to package for. Note that\ it is the caller's responsiblity to ensure that these debs are already present in a directory. This script\ will not build binaries or create their debs." ) From e3aad7799eecdd7249d13b72b6f967e2d57e09b9 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 7 Jun 2022 23:09:26 -0700 Subject: [PATCH 102/238] TUN-6357: Add connector id to ready check endpoint --- cmd/cloudflared/tunnel/cmd.go | 11 ++++++++++- metrics/readiness.go | 19 ++++++++++++------- metrics/readiness_test.go | 6 +++--- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 4cedff50..ae1e5f55 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -15,6 +15,7 @@ import ( "github.com/coreos/go-systemd/daemon" "github.com/facebookgo/grace/gracenet" "github.com/getsentry/raven-go" + "github.com/google/uuid" homedir "github.com/mitchellh/go-homedir" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -348,6 +349,14 @@ func StartServer( log.Err(err).Msg("Couldn't start tunnel") return err } + var clientID uuid.UUID + if tunnelConfig.NamedTunnel != nil { + clientID, err = uuid.FromBytes(tunnelConfig.NamedTunnel.Client.ClientID) + if err != nil { + // set to nil for classic tunnels + clientID = uuid.Nil + } + } orchestrator, err := orchestration.NewOrchestrator(ctx, orchestratorConfig, tunnelConfig.Tags, tunnelConfig.Log) if err != nil { @@ -363,7 +372,7 @@ func StartServer( wg.Add(1) go func() { defer wg.Done() - readinessServer := metrics.NewReadyServer(log) + readinessServer := metrics.NewReadyServer(log, clientID) observer.RegisterSink(readinessServer) errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, orchestrator, log) }() diff --git a/metrics/readiness.go b/metrics/readiness.go index 8524bae8..b4e2025d 100644 --- a/metrics/readiness.go +++ b/metrics/readiness.go @@ -5,21 +5,24 @@ import ( "fmt" "net/http" + "github.com/google/uuid" + "github.com/rs/zerolog" + conn "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/tunnelstate" - - "github.com/rs/zerolog" ) // ReadyServer serves HTTP 200 if the tunnel can serve traffic. Intended for k8s readiness checks. type ReadyServer struct { - tracker *tunnelstate.ConnTracker + clientID uuid.UUID + tracker *tunnelstate.ConnTracker } // NewReadyServer initializes a ReadyServer and starts listening for dis/connection events. -func NewReadyServer(log *zerolog.Logger) *ReadyServer { +func NewReadyServer(log *zerolog.Logger, clientID uuid.UUID) *ReadyServer { return &ReadyServer{ - tracker: tunnelstate.NewConnTracker(log), + clientID: clientID, + tracker: tunnelstate.NewConnTracker(log), } } @@ -28,8 +31,9 @@ func (rs *ReadyServer) OnTunnelEvent(c conn.Event) { } type body struct { - Status int `json:"status"` - ReadyConnections uint `json:"readyConnections"` + Status int `json:"status"` + ReadyConnections uint `json:"readyConnections"` + ConnectorID uuid.UUID `json:"connectorId"` } // ServeHTTP responds with HTTP 200 if the tunnel is connected to the edge. @@ -39,6 +43,7 @@ func (rs *ReadyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { body := body{ Status: statusCode, ReadyConnections: readyConnections, + ConnectorID: rs.clientID, } msg, err := json.Marshal(body) if err != nil { diff --git a/metrics/readiness_test.go b/metrics/readiness_test.go index e6578962..62e42f63 100644 --- a/metrics/readiness_test.go +++ b/metrics/readiness_test.go @@ -4,12 +4,12 @@ import ( "net/http" "testing" + "github.com/google/uuid" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" - "github.com/cloudflare/cloudflared/tunnelstate" - "github.com/cloudflare/cloudflared/connection" + "github.com/cloudflare/cloudflared/tunnelstate" ) func TestReadyServer_makeResponse(t *testing.T) { @@ -66,7 +66,7 @@ func TestReadyServer_makeResponse(t *testing.T) { func TestReadinessEventHandling(t *testing.T) { nopLogger := zerolog.Nop() - rs := NewReadyServer(&nopLogger) + rs := NewReadyServer(&nopLogger, uuid.Nil) // start not ok code, ready := rs.makeResponse() From 4f468b8a5da8f89f40df9ae99927417331cf9763 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 20 May 2022 14:51:36 -0700 Subject: [PATCH 103/238] TUN-6007: Implement new edge discovery algorithm --- connection/errors.go | 9 + connection/quic.go | 2 +- edgediscovery/allregions/address.go | 64 +++ edgediscovery/allregions/address_test.go | 247 +++++++++ edgediscovery/allregions/discovery.go | 6 +- edgediscovery/allregions/mocks_for_test.go | 109 ++++ edgediscovery/allregions/region.go | 166 ++++-- edgediscovery/allregions/region_test.go | 583 ++++++++++++--------- edgediscovery/allregions/regions.go | 26 +- edgediscovery/allregions/regions_test.go | 269 ++++++---- edgediscovery/edgediscovery.go | 45 +- edgediscovery/edgediscovery_test.go | 102 +++- supervisor/supervisor.go | 124 ++--- supervisor/tunnel.go | 213 +++++--- 14 files changed, 1378 insertions(+), 587 deletions(-) create mode 100644 edgediscovery/allregions/address.go create mode 100644 edgediscovery/allregions/address_test.go diff --git a/connection/errors.go b/connection/errors.go index df3cfe97..10e3b5d6 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -18,6 +18,15 @@ func (e DupConnRegisterTunnelError) Error() string { return "already connected to this server, trying another address" } +// Dial to edge server with quic failed +type EdgeQuicDialError struct { + Cause error +} + +func (e *EdgeQuicDialError) Error() string { + return "failed to dial to edge with quic: " + e.Cause.Error() +} + // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/connection/quic.go b/connection/quic.go index f1524c67..ef46a658 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -55,7 +55,7 @@ func NewQUICConnection( ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { - return nil, fmt.Errorf("failed to dial to edge: %w", err) + return nil, &EdgeQuicDialError{Cause: err} } datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) diff --git a/edgediscovery/allregions/address.go b/edgediscovery/allregions/address.go new file mode 100644 index 00000000..36bf2d48 --- /dev/null +++ b/edgediscovery/allregions/address.go @@ -0,0 +1,64 @@ +package allregions + +// Region contains cloudflared edge addresses. The edge is partitioned into several regions for +// redundancy purposes. +type AddrSet map[*EdgeAddr]UsedBy + +// AddrUsedBy finds the address used by the given connection in this region. +// Returns nil if the connection isn't using any IP. +func (a AddrSet) AddrUsedBy(connID int) *EdgeAddr { + for addr, used := range a { + if used.Used && used.ConnID == connID { + return addr + } + } + return nil +} + +// AvailableAddrs counts how many unused addresses this region contains. +func (a AddrSet) AvailableAddrs() int { + n := 0 + for _, usedby := range a { + if !usedby.Used { + n++ + } + } + return n +} + +// GetUnusedIP returns a random unused address in this region. +// Returns nil if all addresses are in use. +func (a AddrSet) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { + for addr, usedby := range a { + if !usedby.Used && addr != excluding { + return addr + } + } + return nil +} + +// Use the address, assigning it to a proxy connection. +func (a AddrSet) Use(addr *EdgeAddr, connID int) { + if addr == nil { + return + } + a[addr] = InUse(connID) +} + +// GetAnyAddress returns an arbitrary address from the region. +func (a AddrSet) GetAnyAddress() *EdgeAddr { + for addr := range a { + return addr + } + return nil +} + +// GiveBack the address, ensuring it is no longer assigned to an IP. +// Returns true if the address is in this region. +func (a AddrSet) GiveBack(addr *EdgeAddr) (ok bool) { + if _, ok := a[addr]; !ok { + return false + } + a[addr] = Unused() + return true +} diff --git a/edgediscovery/allregions/address_test.go b/edgediscovery/allregions/address_test.go new file mode 100644 index 00000000..9c8d9289 --- /dev/null +++ b/edgediscovery/allregions/address_test.go @@ -0,0 +1,247 @@ +package allregions + +import ( + "reflect" + "testing" +) + +func TestAddrSet_AddrUsedBy(t *testing.T) { + type args struct { + connID int + } + tests := []struct { + name string + addrSet AddrSet + args args + want *EdgeAddr + }{ + { + name: "happy trivial test", + addrSet: AddrSet{ + &addr0: InUse(0), + }, + args: args{connID: 0}, + want: &addr0, + }, + { + name: "sad trivial test", + addrSet: AddrSet{ + &addr0: InUse(0), + }, + args: args{connID: 1}, + want: nil, + }, + { + name: "sad test", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{connID: 3}, + want: nil, + }, + { + name: "happy test", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{connID: 1}, + want: &addr1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_AvailableAddrs(t *testing.T) { + tests := []struct { + name string + addrSet AddrSet + want int + }{ + { + name: "contains addresses", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }, + want: 1, + }, + { + name: "all free", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: Unused(), + }, + want: 3, + }, + { + name: "all used", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + want: 0, + }, + { + name: "empty", + addrSet: AddrSet{}, + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.AvailableAddrs(); got != tt.want { + t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_GetUnusedIP(t *testing.T) { + type args struct { + excluding *EdgeAddr + } + tests := []struct { + name string + addrSet AddrSet + args args + want *EdgeAddr + }{ + { + name: "happy test with excluding set", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: InUse(2), + }, + args: args{excluding: &addr0}, + want: &addr1, + }, + { + name: "happy test with no excluding", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }, + args: args{excluding: nil}, + want: &addr1, + }, + { + name: "sad test with no excluding", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{excluding: nil}, + want: nil, + }, + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{excluding: &addr0}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_GiveBack(t *testing.T) { + type args struct { + addr *EdgeAddr + } + tests := []struct { + name string + addrSet AddrSet + args args + wantOk bool + availableAfter int + }{ + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr1: InUse(1), + }, + args: args{addr: &addr1}, + wantOk: true, + availableAfter: 1, + }, + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr1: InUse(1), + }, + args: args{addr: &addr2}, + wantOk: false, + availableAfter: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotOk := tt.addrSet.GiveBack(tt.args.addr); gotOk != tt.wantOk { + t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) + } + if tt.availableAfter != tt.addrSet.AvailableAddrs() { + t.Errorf("Region.AvailableAddrs() = %v, want %v", tt.addrSet.AvailableAddrs(), tt.availableAfter) + } + }) + } +} + +func TestAddrSet_GetAnyAddress(t *testing.T) { + tests := []struct { + name string + addrSet AddrSet + wantNil bool + }{ + { + name: "Sad test -- GetAnyAddress should only fail if the region is empty", + addrSet: AddrSet{}, + wantNil: true, + }, + { + name: "Happy test (all addresses unused)", + addrSet: AddrSet{ + &addr0: Unused(), + }, + wantNil: false, + }, + { + name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", + addrSet: AddrSet{ + &addr0: InUse(2), + }, + wantNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.GetAnyAddress(); tt.wantNil != (got == nil) { + t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) + } + }) + } +} diff --git a/edgediscovery/allregions/discovery.go b/edgediscovery/allregions/discovery.go index e6a4103b..35f464f8 100644 --- a/edgediscovery/allregions/discovery.go +++ b/edgediscovery/allregions/discovery.go @@ -13,7 +13,7 @@ import ( const ( // Used to discover HA origintunneld servers - srvService = "origintunneld" + srvService = "v2-origintunneld" srvProto = "tcp" srvName = "argotunnel.com" @@ -115,6 +115,9 @@ func edgeDiscovery(log *zerolog.Logger, srvService string) ([][]*EdgeAddr, error if err != nil { return nil, err } + for _, e := range edgeAddrs { + log.Debug().Msgf("Edge Address: %+v", *e) + } resolvedAddrPerCNAME = append(resolvedAddrPerCNAME, edgeAddrs) } @@ -187,7 +190,6 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) { UDP: udpAddr, IPVersion: version, }) - } return } diff --git a/edgediscovery/allregions/mocks_for_test.go b/edgediscovery/allregions/mocks_for_test.go index ab99c94f..51ff746c 100644 --- a/edgediscovery/allregions/mocks_for_test.go +++ b/edgediscovery/allregions/mocks_for_test.go @@ -9,6 +9,115 @@ import ( "testing/quick" ) +var ( + v4Addrs = []*EdgeAddr{&addr0, &addr1, &addr2, &addr3} + v6Addrs = []*EdgeAddr{&addr4, &addr5, &addr6, &addr7} + addr0 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr1 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr2 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr3 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr4 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr5 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr6 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr7 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } +) + type mockAddrs struct { // a set of synthetic SRV records addrMap map[net.SRV][]*EdgeAddr diff --git a/edgediscovery/allregions/region.go b/edgediscovery/allregions/region.go index 4d268c77..e0ad3ab4 100644 --- a/edgediscovery/allregions/region.go +++ b/edgediscovery/allregions/region.go @@ -1,79 +1,155 @@ package allregions +import "time" + +const ( + timeoutDuration = 10 * time.Minute +) + // Region contains cloudflared edge addresses. The edge is partitioned into several regions for // redundancy purposes. type Region struct { - connFor map[*EdgeAddr]UsedBy + primaryIsActive bool + active AddrSet + primary AddrSet + secondary AddrSet + primaryTimeout time.Time + timeoutDuration time.Duration } // NewRegion creates a region with the given addresses, which are all unused. -func NewRegion(addrs []*EdgeAddr) Region { +func NewRegion(addrs []*EdgeAddr, overrideIPVersion ConfigIPVersion) Region { // The zero value of UsedBy is Unused(), so we can just initialize the map's values with their // zero values. - connFor := make(map[*EdgeAddr]UsedBy) - for _, addr := range addrs { - connFor[addr] = Unused() + connForv4 := make(AddrSet) + connForv6 := make(AddrSet) + systemPreference := V6 + for i, addr := range addrs { + if i == 0 { + // First family of IPs returned is system preference of IP + systemPreference = addr.IPVersion + } + switch addr.IPVersion { + case V4: + connForv4[addr] = Unused() + case V6: + connForv6[addr] = Unused() + } } + + // Process as system preference + var primary AddrSet + var secondary AddrSet + switch systemPreference { + case V4: + primary = connForv4 + secondary = connForv6 + case V6: + primary = connForv6 + secondary = connForv4 + } + + // Override with provided preference + switch overrideIPVersion { + case IPv4Only: + primary = connForv4 + secondary = make(AddrSet) // empty + case IPv6Only: + primary = connForv6 + secondary = make(AddrSet) // empty + case Auto: + // no change + default: + // no change + } + return Region{ - connFor: connFor, + primaryIsActive: true, + active: primary, + primary: primary, + secondary: secondary, + timeoutDuration: timeoutDuration, } } // AddrUsedBy finds the address used by the given connection in this region. // Returns nil if the connection isn't using any IP. func (r *Region) AddrUsedBy(connID int) *EdgeAddr { - for addr, used := range r.connFor { - if used.Used && used.ConnID == connID { - return addr - } + edgeAddr := r.primary.AddrUsedBy(connID) + if edgeAddr == nil { + edgeAddr = r.secondary.AddrUsedBy(connID) } - return nil + return edgeAddr } // AvailableAddrs counts how many unused addresses this region contains. func (r Region) AvailableAddrs() int { - n := 0 - for _, usedby := range r.connFor { - if !usedby.Used { - n++ - } - } - return n + return r.active.AvailableAddrs() } -// GetUnusedIP returns a random unused address in this region. -// Returns nil if all addresses are in use. -func (r Region) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { - for addr, usedby := range r.connFor { - if !usedby.Used && addr != excluding { - return addr - } - } - return nil -} - -// Use the address, assigning it to a proxy connection. -func (r Region) Use(addr *EdgeAddr, connID int) { - if addr == nil { - return - } - r.connFor[addr] = InUse(connID) -} - -// GetAnyAddress returns an arbitrary address from the region. -func (r Region) GetAnyAddress() *EdgeAddr { - for addr := range r.connFor { +// AssignAnyAddress returns a random unused address in this region now +// assigned to the connID excluding the provided EdgeAddr. +// Returns nil if all addresses are in use for the region. +func (r Region) AssignAnyAddress(connID int, excluding *EdgeAddr) *EdgeAddr { + if addr := r.active.GetUnusedIP(excluding); addr != nil { + r.active.Use(addr, connID) return addr } return nil } +// GetAnyAddress returns an arbitrary address from the region. +func (r Region) GetAnyAddress() *EdgeAddr { + return r.active.GetAnyAddress() +} + // GiveBack the address, ensuring it is no longer assigned to an IP. // Returns true if the address is in this region. -func (r Region) GiveBack(addr *EdgeAddr) (ok bool) { - if _, ok := r.connFor[addr]; !ok { - return false +func (r *Region) GiveBack(addr *EdgeAddr, hasConnectivityError bool) (ok bool) { + if ok = r.primary.GiveBack(addr); !ok { + // Attempt to give back the address in the secondary set + if ok = r.secondary.GiveBack(addr); !ok { + // Address is not in this region + return + } } - r.connFor[addr] = Unused() - return true + + // No connectivity error: no worry + if !hasConnectivityError { + return + } + + // If using primary and returned address is IPv6 and secondary is available + if r.primaryIsActive && addr.IPVersion == V6 && len(r.secondary) > 0 { + r.active = r.secondary + r.primaryIsActive = false + r.primaryTimeout = time.Now().Add(r.timeoutDuration) + return + } + + // Do nothing for IPv4 or if secondary is empty + if r.primaryIsActive { + return + } + + // Immediately return to primary pool, regardless of current primary timeout + if addr.IPVersion == V4 { + activatePrimary(r) + return + } + + // Timeout exceeded and can be reset to primary pool + if r.primaryTimeout.Before(time.Now()) { + activatePrimary(r) + return + } + + return +} + +// activatePrimary sets the primary set to the active set and resets the timeout. +func activatePrimary(r *Region) { + r.active = r.primary + r.primaryIsActive = true + r.primaryTimeout = time.Now() // reset timeout } diff --git a/edgediscovery/allregions/region_test.go b/edgediscovery/allregions/region_test.go index d83dea61..e8d230c4 100644 --- a/edgediscovery/allregions/region_test.go +++ b/edgediscovery/allregions/region_test.go @@ -1,284 +1,357 @@ package allregions import ( - "reflect" + "net" "testing" + "time" + + "github.com/stretchr/testify/assert" ) +func makeAddrSet(addrs []*EdgeAddr) AddrSet { + addrSet := make(AddrSet, len(addrs)) + for _, addr := range addrs { + addrSet[addr] = Unused() + } + return addrSet +} + func TestRegion_New(t *testing.T) { - r := NewRegion([]*EdgeAddr{&addr0, &addr1, &addr2}) - if r.AvailableAddrs() != 3 { - t.Errorf("r.AvailableAddrs() == %v but want 3", r.AvailableAddrs()) - } -} - -func TestRegion_AddrUsedBy(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - connID int - } tests := []struct { - name string - fields fields - args args - want *EdgeAddr + name string + addrs []*EdgeAddr + mode ConfigIPVersion + expectedAddrs int + primary AddrSet + secondary AddrSet }{ { - name: "happy trivial test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - }}, - args: args{connID: 0}, - want: &addr0, + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, }, { - name: "sad trivial test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - }}, - args: args{connID: 1}, - want: nil, + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + expectedAddrs: 0, + primary: AddrSet{}, + secondary: AddrSet{}, }, { - name: "sad test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{connID: 3}, - want: nil, + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, }, { - name: "happy test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{connID: 1}, - want: &addr1, + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + expectedAddrs: 0, + primary: AddrSet{}, + secondary: AddrSet{}, + }, + { + name: "IPv4 (first) and IPv6 addresses with Auto", + addrs: append(v4Addrs, v6Addrs...), + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: makeAddrSet(v6Addrs), + }, + { + name: "IPv6 (first) and IPv4 addresses with Auto", + addrs: append(v6Addrs, v4Addrs...), + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: makeAddrSet(v4Addrs), + }, + { + name: "IPv4 addresses with Auto", + addrs: v4Addrs, + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + }, + { + name: "IPv6 addresses with Auto", + addrs: v6Addrs, + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := &Region{ - connFor: tt.fields.connFor, + r := NewRegion(tt.addrs, tt.mode) + assert.Equal(t, tt.expectedAddrs, r.AvailableAddrs()) + assert.Equal(t, tt.primary, r.primary) + assert.Equal(t, tt.secondary, r.secondary) + }) + } +} + +func TestRegion_AnyAddress_EmptyActiveSet(t *testing.T) { + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + }, + { + name: "IPv4 addresses with IPv6Only", + addrs: v4Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.GetAnyAddress() + assert.Nil(t, addr) + addr = r.AssignAnyAddress(0, nil) + assert.Nil(t, addr) + }) + } +} + +func TestRegion_AssignAnyAddress_FullyUsedActiveSet(t *testing.T) { + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + total := r.active.AvailableAddrs() + for i := 0; i < total; i++ { + addr := r.AssignAnyAddress(i, nil) + assert.NotNil(t, addr) } - if got := r.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) + addr := r.AssignAnyAddress(9, nil) + assert.Nil(t, addr) + }) + } +} + +var giveBackTests = []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + expectedAddrs int + primary AddrSet + secondary AddrSet + primarySwap bool +}{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv4 (first) and IPv6 addresses with Auto", + addrs: append(v4Addrs, v6Addrs...), + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: makeAddrSet(v6Addrs), + primarySwap: false, + }, + { + name: "IPv6 (first) and IPv4 addresses with Auto", + addrs: append(v6Addrs, v4Addrs...), + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: makeAddrSet(v4Addrs), + primarySwap: true, + }, + { + name: "IPv4 addresses with Auto", + addrs: v4Addrs, + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv6 addresses with Auto", + addrs: v6Addrs, + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, +} + +func TestRegion_GiveBack_NoConnectivityError(t *testing.T) { + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.AssignAnyAddress(0, nil) + assert.NotNil(t, addr) + assert.True(t, r.GiveBack(addr, false)) + }) + } +} + +func TestRegion_GiveBack_ForeignAddr(t *testing.T) { + invalid := EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + assert.False(t, r.GiveBack(&invalid, false)) + assert.False(t, r.GiveBack(&invalid, true)) + }) + } +} + +func TestRegion_GiveBack_SwapPrimary(t *testing.T) { + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.AssignAnyAddress(0, nil) + assert.NotNil(t, addr) + assert.True(t, r.GiveBack(addr, true)) + assert.Equal(t, tt.primarySwap, !r.primaryIsActive) + if tt.primarySwap { + assert.Equal(t, r.secondary, r.active) + assert.False(t, r.primaryTimeout.IsZero()) + } else { + assert.Equal(t, r.primary, r.active) + assert.True(t, r.primaryTimeout.IsZero()) } }) } } -func TestRegion_AvailableAddrs(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - tests := []struct { - name string - fields fields - want int - }{ - { - name: "contains addresses", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }}, - want: 1, - }, - { - name: "all free", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: Unused(), - }}, - want: 3, - }, - { - name: "all used", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - want: 0, - }, - { - name: "empty", - fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, - want: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.AvailableAddrs(); got != tt.want { - t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) - } - }) - } +func TestRegion_GiveBack_IPv4_ResetPrimary(t *testing.T) { + r := NewRegion(append(v6Addrs, v4Addrs...), Auto) + // Exhaust all IPv6 addresses + a0 := r.AssignAnyAddress(0, nil) + a1 := r.AssignAnyAddress(1, nil) + a2 := r.AssignAnyAddress(2, nil) + a3 := r.AssignAnyAddress(3, nil) + assert.NotNil(t, a0) + assert.NotNil(t, a1) + assert.NotNil(t, a2) + assert.NotNil(t, a3) + // Give back the first IPv6 address to fallback to secondary IPv4 address set + assert.True(t, r.GiveBack(a0, true)) + assert.False(t, r.primaryIsActive) + // Give back another IPv6 address + assert.True(t, r.GiveBack(a1, true)) + // Primary shouldn't change + assert.False(t, r.primaryIsActive) + // Request an address (should be IPv4 from secondary) + a4_v4 := r.AssignAnyAddress(4, nil) + assert.NotNil(t, a4_v4) + assert.Equal(t, V4, a4_v4.IPVersion) + a5_v4 := r.AssignAnyAddress(5, nil) + assert.NotNil(t, a5_v4) + assert.Equal(t, V4, a5_v4.IPVersion) + a6_v4 := r.AssignAnyAddress(6, nil) + assert.NotNil(t, a6_v4) + assert.Equal(t, V4, a6_v4.IPVersion) + // Return IPv4 address (without failure) + // Primary shouldn't change because it is not a connectivity failure + assert.True(t, r.GiveBack(a4_v4, false)) + assert.False(t, r.primaryIsActive) + // Return IPv4 address (with failure) + // Primary should change because it is a connectivity failure + assert.True(t, r.GiveBack(a5_v4, true)) + assert.True(t, r.primaryIsActive) + // Return IPv4 address (with failure) + // Primary shouldn't change because the address is returned to the inactive + // secondary address set + assert.True(t, r.GiveBack(a6_v4, true)) + assert.True(t, r.primaryIsActive) + // Return IPv6 address (without failure) + // Primary shoudn't change because it is not a connectivity failure + assert.True(t, r.GiveBack(a2, false)) + assert.True(t, r.primaryIsActive) } -func TestRegion_GetUnusedIP(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - excluding *EdgeAddr - } - tests := []struct { - name string - fields fields - args args - want *EdgeAddr - }{ - { - name: "happy test with excluding set", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: InUse(2), - }}, - args: args{excluding: &addr0}, - want: &addr1, - }, - { - name: "happy test with no excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }}, - args: args{excluding: nil}, - want: &addr1, - }, - { - name: "sad test with no excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{excluding: nil}, - want: nil, - }, - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{excluding: &addr0}, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestRegion_GiveBack(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - addr *EdgeAddr - } - tests := []struct { - name string - fields fields - args args - wantOk bool - availableAfter int - }{ - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr1: InUse(1), - }}, - args: args{addr: &addr1}, - wantOk: true, - availableAfter: 1, - }, - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr1: InUse(1), - }}, - args: args{addr: &addr2}, - wantOk: false, - availableAfter: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if gotOk := r.GiveBack(tt.args.addr); gotOk != tt.wantOk { - t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) - } - if tt.availableAfter != r.AvailableAddrs() { - t.Errorf("Region.AvailableAddrs() = %v, want %v", r.AvailableAddrs(), tt.availableAfter) - } - }) - } -} - -func TestRegion_GetAnyAddress(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - tests := []struct { - name string - fields fields - wantNil bool - }{ - { - name: "Sad test -- GetAnyAddress should only fail if the region is empty", - fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, - wantNil: true, - }, - { - name: "Happy test (all addresses unused)", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - }}, - wantNil: false, - }, - { - name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(2), - }}, - wantNil: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.GetAnyAddress(); tt.wantNil != (got == nil) { - t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) - } - }) - } +func TestRegion_GiveBack_Timeout(t *testing.T) { + r := NewRegion(append(v6Addrs, v4Addrs...), Auto) + a0 := r.AssignAnyAddress(0, nil) + a1 := r.AssignAnyAddress(1, nil) + a2 := r.AssignAnyAddress(2, nil) + assert.NotNil(t, a0) + assert.NotNil(t, a1) + assert.NotNil(t, a2) + // Give back IPv6 address to set timeout + assert.True(t, r.GiveBack(a0, true)) + assert.False(t, r.primaryIsActive) + assert.False(t, r.primaryTimeout.IsZero()) + // Request an address (should be IPv4 from secondary) + a3_v4 := r.AssignAnyAddress(3, nil) + assert.NotNil(t, a3_v4) + assert.Equal(t, V4, a3_v4.IPVersion) + assert.False(t, r.primaryIsActive) + // Give back IPv6 address inside timeout (no change) + assert.True(t, r.GiveBack(a2, true)) + assert.False(t, r.primaryIsActive) + assert.False(t, r.primaryTimeout.IsZero()) + // Accelerate timeout + r.primaryTimeout = time.Now().Add(-time.Minute) + // Return IPv6 address + assert.True(t, r.GiveBack(a1, true)) + assert.True(t, r.primaryIsActive) + // Returning an IPv4 address after primary is active shouldn't change primary + // even with a connectivity error + assert.True(t, r.GiveBack(a3_v4, true)) + assert.True(t, r.primaryIsActive) } diff --git a/edgediscovery/allregions/regions.go b/edgediscovery/allregions/regions.go index cec79240..4b2a841b 100644 --- a/edgediscovery/allregions/regions.go +++ b/edgediscovery/allregions/regions.go @@ -18,7 +18,7 @@ type Regions struct { // ------------------------------------ // ResolveEdge resolves the Cloudflare edge, returning all regions discovered. -func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) { +func ResolveEdge(log *zerolog.Logger, region string, overrideIPVersion ConfigIPVersion) (*Regions, error) { edgeAddrs, err := edgeDiscovery(log, getRegionalServiceName(region)) if err != nil { return nil, err @@ -27,8 +27,8 @@ func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) { return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(edgeAddrs)) } return &Regions{ - region1: NewRegion(edgeAddrs[0]), - region2: NewRegion(edgeAddrs[1]), + region1: NewRegion(edgeAddrs[0], overrideIPVersion), + region2: NewRegion(edgeAddrs[1], overrideIPVersion), }, nil } @@ -56,8 +56,8 @@ func NewNoResolve(addrs []*EdgeAddr) *Regions { } return &Regions{ - region1: NewRegion(region1), - region2: NewRegion(region2), + region1: NewRegion(region1, Auto), + region2: NewRegion(region2, Auto), } } @@ -95,14 +95,12 @@ func (rs *Regions) GetUnusedAddr(excluding *EdgeAddr, connID int) *EdgeAddr { // getAddrs tries to grab address form `first` region, then `second` region // this is an unrolled loop over 2 element array func getAddrs(excluding *EdgeAddr, connID int, first *Region, second *Region) *EdgeAddr { - addr := first.GetUnusedIP(excluding) + addr := first.AssignAnyAddress(connID, excluding) if addr != nil { - first.Use(addr, connID) return addr } - addr = second.GetUnusedIP(excluding) + addr = second.AssignAnyAddress(connID, excluding) if addr != nil { - second.Use(addr, connID) return addr } @@ -116,18 +114,18 @@ func (rs *Regions) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (rs *Regions) GiveBack(addr *EdgeAddr) bool { - if found := rs.region1.GiveBack(addr); found { +func (rs *Regions) GiveBack(addr *EdgeAddr, hasConnectivityError bool) bool { + if found := rs.region1.GiveBack(addr, hasConnectivityError); found { return found } - return rs.region2.GiveBack(addr) + return rs.region2.GiveBack(addr, hasConnectivityError) } // Return regionalized service name if `region` isn't empty, otherwise return the global service name for origintunneld func getRegionalServiceName(region string) string { if region != "" { - return region + "-" + srvService // Example: `us-origintunneld` + return region + "-" + srvService // Example: `us-v2-origintunneld` } - return srvService // Global service is just `origintunneld` + return srvService // Global service is just `v2-origintunneld` } diff --git a/edgediscovery/allregions/regions_test.go b/edgediscovery/allregions/regions_test.go index d6446df2..e399c4ee 100644 --- a/edgediscovery/allregions/regions_test.go +++ b/edgediscovery/allregions/regions_test.go @@ -1,134 +1,215 @@ package allregions import ( - "net" "testing" "github.com/stretchr/testify/assert" ) -var ( - addr0 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, +func makeRegions(addrs []*EdgeAddr, mode ConfigIPVersion) Regions { + r1addrs := make([]*EdgeAddr, 0) + r2addrs := make([]*EdgeAddr, 0) + for i, addr := range addrs { + if i%2 == 0 { + r1addrs = append(r1addrs, addr) + } else { + r2addrs = append(r2addrs, addr) + } } - addr1 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - } - addr2 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - } - addr3 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - } -) - -func makeRegions() Regions { - r1 := NewRegion([]*EdgeAddr{&addr0, &addr1}) - r2 := NewRegion([]*EdgeAddr{&addr2, &addr3}) + r1 := NewRegion(r1addrs, mode) + r2 := NewRegion(r2addrs, mode) return Regions{region1: r1, region2: r2} } func TestRegions_AddrUsedBy(t *testing.T) { - rs := makeRegions() - addr1 := rs.GetUnusedAddr(nil, 1) - assert.Equal(t, addr1, rs.AddrUsedBy(1)) - addr2 := rs.GetUnusedAddr(nil, 2) - assert.Equal(t, addr2, rs.AddrUsedBy(2)) - addr3 := rs.GetUnusedAddr(nil, 3) - assert.Equal(t, addr3, rs.AddrUsedBy(3)) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + addr1 := rs.GetUnusedAddr(nil, 1) + assert.Equal(t, addr1, rs.AddrUsedBy(1)) + addr2 := rs.GetUnusedAddr(nil, 2) + assert.Equal(t, addr2, rs.AddrUsedBy(2)) + addr3 := rs.GetUnusedAddr(nil, 3) + assert.Equal(t, addr3, rs.AddrUsedBy(3)) + }) + } } func TestRegions_Giveback_Region1(t *testing.T) { - rs := makeRegions() - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - rs.region2.Use(&addr3, 3) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + addr := rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + rs.region2.AssignAnyAddress(2, nil) + rs.region2.AssignAnyAddress(3, nil) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(&addr0) - assert.Equal(t, &addr0, rs.GetUnusedAddr(nil, 3)) + rs.GiveBack(addr, false) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 0)) + }) + } } func TestRegions_Giveback_Region2(t *testing.T) { - rs := makeRegions() - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - rs.region2.Use(&addr3, 3) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + addr := rs.region2.AssignAnyAddress(2, nil) + rs.region2.AssignAnyAddress(3, nil) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(&addr2) - assert.Equal(t, &addr2, rs.GetUnusedAddr(nil, 2)) + rs.GiveBack(addr, false) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 2)) + }) + } } func TestRegions_GetUnusedAddr_OneAddrLeft(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + rs.region2.AssignAnyAddress(2, nil) + addr := rs.region2.active.GetUnusedIP(nil) - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - - assert.Equal(t, 1, rs.AvailableAddrs()) - assert.Equal(t, &addr3, rs.GetUnusedAddr(nil, 3)) + assert.Equal(t, 1, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 3)) + }) + } } func TestRegions_GetUnusedAddr_Excluding_Region1(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + addr := rs.region2.active.GetUnusedIP(nil) + a2 := rs.region2.active.GetUnusedIP(addr) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, &addr3, rs.GetUnusedAddr(&addr2, 3)) + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(a2, 3)) + }) + } } func TestRegions_GetUnusedAddr_Excluding_Region2(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) - rs.region2.Use(&addr2, 0) - rs.region2.Use(&addr3, 1) + rs.region2.AssignAnyAddress(0, nil) + rs.region2.AssignAnyAddress(1, nil) + addr := rs.region1.active.GetUnusedIP(nil) + a2 := rs.region1.active.GetUnusedIP(addr) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, &addr1, rs.GetUnusedAddr(&addr0, 1)) + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(a2, 1)) + }) + } } func TestNewNoResolveBalancesRegions(t *testing.T) { diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index df9c42cc..b5f338e8 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -11,9 +11,10 @@ import ( const ( LogFieldConnIndex = "connIndex" + LogFieldIPAddress = "ip" ) -var errNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") +var ErrNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") // Edge finds addresses on the Cloudflare edge and hands them out to connections. type Edge struct { @@ -28,8 +29,8 @@ type Edge struct { // ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated // to connections. -func ResolveEdge(log *zerolog.Logger, region string) (*Edge, error) { - regions, err := allregions.ResolveEdge(log, region) +func ResolveEdge(log *zerolog.Logger, region string, edgeIpVersion allregions.ConfigIPVersion) (*Edge, error) { + regions, err := allregions.ResolveEdge(log, region, edgeIpVersion) if err != nil { return new(Edge), err } @@ -51,15 +52,6 @@ func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) { }, nil } -// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. -func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { - regions := allregions.NewNoResolve(addrs) - return &Edge{ - log: log, - regions: regions, - } -} - // ------------------------------------ // Methods // ------------------------------------ @@ -70,7 +62,7 @@ func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) { defer ed.Unlock() addr := ed.regions.GetAnyAddress() if addr == nil { - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } return addr, nil } @@ -91,14 +83,17 @@ func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) { addr := ed.regions.GetUnusedAddr(nil, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetAddr: No addresses left to give proxy connection") - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } - log.Debug().Msg("edgediscovery - GetAddr: Giving connection its new address") + log = ed.log.With(). + Int(LogFieldConnIndex, connIndex). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GetAddr: Giving connection its new address") return addr, nil } // GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one. -func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { +func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*allregions.EdgeAddr, error) { log := ed.log.With().Int(LogFieldConnIndex, connIndex).Logger() ed.Lock() @@ -106,16 +101,18 @@ func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { oldAddr := ed.regions.AddrUsedBy(connIndex) if oldAddr != nil { - ed.regions.GiveBack(oldAddr) + ed.regions.GiveBack(oldAddr, hasConnectivityError) } addr := ed.regions.GetUnusedAddr(oldAddr, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection") // note: if oldAddr were not nil, it will become available on the next iteration - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } - log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address: %v from the address list: %v", - addr, ed.regions.AvailableAddrs()) + log = ed.log.With(). + Int(LogFieldConnIndex, connIndex). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address from the address list: %v", ed.regions.AvailableAddrs()) return addr, nil } @@ -128,9 +125,11 @@ func (ed *Edge) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (ed *Edge) GiveBack(addr *allregions.EdgeAddr) bool { +func (ed *Edge) GiveBack(addr *allregions.EdgeAddr, hasConnectivityError bool) bool { ed.Lock() defer ed.Unlock() - ed.log.Debug().Msg("edgediscovery - GiveBack: Address now unused") - return ed.regions.GiveBack(addr) + log := ed.log.With(). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GiveBack: Address now unused") + return ed.regions.GiveBack(addr, hasConnectivityError) } diff --git a/edgediscovery/edgediscovery_test.go b/edgediscovery/edgediscovery_test.go index 9cc93807..55288ce5 100644 --- a/edgediscovery/edgediscovery_test.go +++ b/edgediscovery/edgediscovery_test.go @@ -11,56 +11,113 @@ import ( ) var ( - addr0 = allregions.EdgeAddr{ + testLogger = zerolog.Nop() + v4Addrs = []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3} + v6Addrs = []*allregions.EdgeAddr{&addr4, &addr5, &addr6, &addr7} + addr0 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.0"), + IP: net.ParseIP("123.4.5.0"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.0"), + IP: net.ParseIP("123.4.5.0"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr1 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.1"), + IP: net.ParseIP("123.4.5.1"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.1"), + IP: net.ParseIP("123.4.5.1"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr2 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.2"), + IP: net.ParseIP("123.4.5.2"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.2"), + IP: net.ParseIP("123.4.5.2"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr3 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.3"), + IP: net.ParseIP("123.4.5.3"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.3"), + IP: net.ParseIP("123.4.5.3"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, + } + addr4 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr5 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr6 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr7 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, } - - testLogger = zerolog.Nop() ) func TestGiveBack(t *testing.T) { @@ -75,7 +132,7 @@ func TestGiveBack(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr) + edge.GiveBack(addr, false) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -107,7 +164,7 @@ func TestGetAddrForRPC(t *testing.T) { assert.Equal(t, 4, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr) + edge.GiveBack(addr, false) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -122,13 +179,13 @@ func TestOnePerRegion(t *testing.T) { assert.NotNil(t, a1) // if the first address is bad, get the second one - a2, err := edge.GetDifferentAddr(connID) + a2, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotNil(t, a2) assert.NotEqual(t, a1, a2) // now that second one is bad, get the first one again - a3, err := edge.GetDifferentAddr(connID) + a3, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.Equal(t, a1, a3) } @@ -144,11 +201,11 @@ func TestOnlyOneAddrLeft(t *testing.T) { assert.NotNil(t, addr) // If that edge address is "bad", there's no alternative address. - _, err = edge.GetDifferentAddr(connID) + _, err = edge.GetDifferentAddr(connID, false) assert.Error(t, err) // previously bad address should become available again on next iteration. - addr, err = edge.GetDifferentAddr(connID) + addr, err = edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotNil(t, addr) } @@ -190,8 +247,17 @@ func TestGetDifferentAddr(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // If the same connection requests another address, it should get the same one. - addr2, err := edge.GetDifferentAddr(connID) + addr2, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotEqual(t, addr, addr2) assert.Equal(t, 3, edge.AvailableAddrs()) } + +// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. +func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { + regions := allregions.NewNoResolve(addrs) + return &Edge{ + log: log, + regions: regions, + } +} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 62bf6ec3..4bec3f56 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -7,7 +7,6 @@ import ( "time" "github.com/google/uuid" - "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/connection" @@ -42,6 +41,7 @@ type Supervisor struct { config *TunnelConfig orchestrator *orchestration.Orchestrator edgeIPs *edgediscovery.Edge + edgeTunnelServer EdgeTunnelServer tunnelErrors chan tunnelError tunnelsConnecting map[int]chan struct{} // nextConnectedIndex and nextConnectedSignal are used to wait for all @@ -76,12 +76,34 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato if len(config.EdgeAddrs) > 0 { edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs) } else { - edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region) + edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region, config.EdgeIPVersion) } if err != nil { return nil, err } + reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) + log := NewConnAwareLogger(config.Log, config.Observer) + + var edgeAddrHandler EdgeAddrHandler + if config.EdgeIPVersion == allregions.IPv6Only || config.EdgeIPVersion == allregions.Auto { + edgeAddrHandler = &IPAddrFallback{} + } else { // IPv4Only + edgeAddrHandler = &DefaultAddrFallback{} + } + + edgeTunnelServer := EdgeTunnelServer{ + config: config, + cloudflaredUUID: cloudflaredUUID, + orchestrator: orchestrator, + credentialManager: reconnectCredentialManager, + edgeAddrs: edgeIPs, + edgeAddrHandler: edgeAddrHandler, + reconnectCh: reconnectCh, + gracefulShutdownC: gracefulShutdownC, + connAwareLogger: log, + } + useReconnectToken := false if config.ClassicTunnel != nil { useReconnectToken = config.ClassicTunnel.UseReconnectToken @@ -92,11 +114,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato config: config, orchestrator: orchestrator, edgeIPs: edgeIPs, + edgeTunnelServer: edgeTunnelServer, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, - log: NewConnAwareLogger(config.Log, config.Observer), + log: log, logTransport: config.LogTransport, - reconnectCredentialManager: newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections), + reconnectCredentialManager: reconnectCredentialManager, useReconnectToken: useReconnectToken, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, @@ -143,11 +166,18 @@ func (s *Supervisor) Run( tunnelsActive-- } return nil - // startTunnel returned with error + // startTunnel completed with a response // (note that this may also be caused by context cancellation) case tunnelError := <-s.tunnelErrors: tunnelsActive-- if tunnelError.err != nil && !shuttingDown { + switch tunnelError.err.(type) { + case ReconnectSignal: + // For tunnels that closed with reconnect signal, we reconnect immediately + go s.startTunnel(ctx, tunnelError.index, s.newConnectedTunnelSignal(tunnelError.index)) + tunnelsActive++ + continue + } s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated") tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) s.waitForNextTunnel(tunnelError.index) @@ -155,10 +185,9 @@ func (s *Supervisor) Run( if backoffTimer == nil { backoffTimer = backoff.BackoffTimer() } - - // Previously we'd mark the edge address as bad here, but now we'll just silently use another. } else if tunnelsActive == 0 { - // all connected tunnels exited gracefully, no more work to do + s.log.ConnAwareLogger().Msg("no more connections active and exiting") + // All connected tunnels exited gracefully, no more work to do return nil } // Backoff was set and its timer expired @@ -192,6 +221,8 @@ func (s *Supervisor) Run( } // Returns nil if initialization succeeded, else the initialization error. +// Attempts here will be made to connect one tunnel, if successful, it will +// connect the available tunnels up to config.HAConnections. func (s *Supervisor) initialize( ctx context.Context, connectedSignal *signal.Signal, @@ -203,6 +234,8 @@ func (s *Supervisor) initialize( } go s.startFirstTunnel(ctx, connectedSignal) + + // Wait for response from first tunnel before proceeding to attempt other HA edge tunnels select { case <-ctx.Done(): <-s.tunnelErrors @@ -213,6 +246,7 @@ func (s *Supervisor) initialize( return errEarlyShutdown case <-connectedSignal.Wait(): } + // At least one successful connection, so start the rest for i := 1; i < s.config.HAConnections; i++ { ch := signal.New(make(chan struct{})) @@ -229,102 +263,42 @@ func (s *Supervisor) startFirstTunnel( connectedSignal *signal.Signal, ) { var ( - addr *allregions.EdgeAddr - err error + err error ) const firstConnIndex = 0 defer func() { s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err} }() - addr, err = s.edgeIPs.GetAddr(firstConnIndex) - if err != nil { - return - } + err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - firstConnIndex, - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) // If the first tunnel disconnects, keep restarting it. - edgeErrors := 0 for s.unusedIPs() { if ctx.Err() != nil { return } - switch err.(type) { - case nil: - return - // try the next address if it was a quic.IdleTimeoutError, dialError(network problem) or - // dupConnRegisterTunnelError - case *quic.IdleTimeoutError, edgediscovery.DialError, connection.DupConnRegisterTunnelError: - edgeErrors++ - default: + if err == nil { return } - if edgeErrors >= 2 { - addr, err = s.edgeIPs.GetDifferentAddr(firstConnIndex) - if err != nil { - return - } - } - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - firstConnIndex, - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) + err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) } } // startTunnel starts a new tunnel connection. The resulting error will be sent on -// s.tunnelErrors. +// s.tunnelError as this is expected to run in a goroutine. func (s *Supervisor) startTunnel( ctx context.Context, index int, connectedSignal *signal.Signal, ) { var ( - addr *allregions.EdgeAddr - err error + err error ) defer func() { s.tunnelErrors <- tunnelError{index: index, err: err} }() - addr, err = s.edgeIPs.GetDifferentAddr(index) - if err != nil { - return - } - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - uint8(index), - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) + err = s.edgeTunnelServer.Serve(ctx, uint8(index), connectedSignal) } func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal { diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index a75fe7b7..8d3fe9f0 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -122,28 +122,84 @@ func StartTunnelDaemon( return s.Run(ctx, connectedSignal) } -func ServeTunnelLoop( - ctx context.Context, - credentialManager *reconnectCredentialManager, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, - addr *allregions.EdgeAddr, - connAwareLogger *ConnAwareLogger, - connIndex uint8, - connectedSignal *signal.Signal, - cloudflaredUUID uuid.UUID, - reconnectCh chan ReconnectSignal, - gracefulShutdownC <-chan struct{}, -) error { +// EdgeAddrHandler provides a mechanism switch between behaviors in ServeTunnel +// for handling the errors when attempting to make edge connections. +type EdgeAddrHandler interface { + // ShouldGetNewAddress will check the edge connection error and determine if + // the edge address should be replaced with a new one. Also, will return if the + // error should be recognized as a connectivity error, or otherwise, a general + // application error. + ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) +} + +// DefaultAddrFallback will always return false for isConnectivityError since this +// handler is a way to provide the legacy behavior in the new edge discovery algorithm. +type DefaultAddrFallback struct { + edgeErrors int +} + +func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { + switch err.(type) { + case nil: // maintain current IP address + // Try the next address if it was a quic.IdleTimeoutError or + // dupConnRegisterTunnelError + case *quic.IdleTimeoutError, + connection.DupConnRegisterTunnelError, + edgediscovery.DialError, + *connection.EdgeQuicDialError: + // Wait for two failures before falling back to a new address + f.edgeErrors++ + if f.edgeErrors >= 2 { + f.edgeErrors = 0 + return true, false + } + default: // maintain current IP address + } + return false, false +} + +// IPAddrFallback will have more conditions to fall back to a new address for certain +// edge connection errors. This means that this handler will return true for isConnectivityError +// for more cases like duplicate connection register and edge quic dial errors. +type IPAddrFallback struct{} + +func (f IPAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { + switch err.(type) { + case nil: // maintain current IP address + // Try the next address if it was a quic.IdleTimeoutError + // DupConnRegisterTunnelError needs to also receive a new ip address + case connection.DupConnRegisterTunnelError, + *quic.IdleTimeoutError: + return true, false + // Network problems should be retried with new address immediately and report + // as connectivity error + case edgediscovery.DialError, *connection.EdgeQuicDialError: + return true, true + default: // maintain current IP address + } + return false, false +} + +type EdgeTunnelServer struct { + config *TunnelConfig + cloudflaredUUID uuid.UUID + orchestrator *orchestration.Orchestrator + credentialManager *reconnectCredentialManager + edgeAddrHandler EdgeAddrHandler + edgeAddrs *edgediscovery.Edge + reconnectCh chan ReconnectSignal + gracefulShutdownC <-chan struct{} + + connAwareLogger *ConnAwareLogger +} + +func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedSignal *signal.Signal) error { haConnections.Inc() defer haConnections.Dec() - logger := config.Log.With().Uint8(connection.LogFieldConnIndex, connIndex).Logger() - connLog := connAwareLogger.ReplaceLogger(&logger) - protocolFallback := &protocolFallback{ - retry.BackoffHandler{MaxRetries: config.Retries}, - config.ProtocolSelector.Current(), + retry.BackoffHandler{MaxRetries: e.config.Retries}, + e.config.ProtocolSelector.Current(), false, } connectedFuse := h2mux.NewBooleanFuse() @@ -154,54 +210,81 @@ func ServeTunnelLoop( }() // Ensure the above goroutine will terminate if we return without connecting defer connectedFuse.Fuse(false) + + // Fetch IP address to associated connection index + addr, err := e.edgeAddrs.GetAddr(int(connIndex)) + switch err { + case nil: // no error + case edgediscovery.ErrNoAddressesLeft: + return err + default: + return err + } + + logger := e.config.Log.With(). + IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). + Uint8(connection.LogFieldConnIndex, connIndex). + Logger() + connLog := e.connAwareLogger.ReplaceLogger(&logger) // Each connection to keep its own copy of protocol, because individual connections might fallback // to another protocol when a particular metal doesn't support new protocol - for { - err, recoverable := ServeTunnel( - ctx, - connLog, - credentialManager, - config, - orchestrator, - addr, - connIndex, - connectedFuse, - protocolFallback, - cloudflaredUUID, - reconnectCh, - protocolFallback.protocol, - gracefulShutdownC, - ) + // Each connection can also have it's own IP version because individual connections might fallback + // to another IP version. + err, recoverable := ServeTunnel( + ctx, + connLog, + e.credentialManager, + e.config, + e.orchestrator, + addr, + connIndex, + connectedFuse, + protocolFallback, + e.cloudflaredUUID, + e.reconnectCh, + protocolFallback.protocol, + e.gracefulShutdownC, + ) - if recoverable { - duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) - if !ok { - return err - } - config.Observer.SendReconnect(connIndex) - connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) + // If the connection is recoverable, we want to maintain the same IP + // but backoff a reconnect with some duration. + if recoverable { + duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) + if !ok { + return err + } + e.config.Observer.SendReconnect(connIndex) + connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) + } + + // Check if the connection error was from an IP issue with the host or + // establishing a connection to the edge and if so, rotate the IP address. + yes, hasConnectivityError := e.edgeAddrHandler.ShouldGetNewAddress(err) + if yes { + e.edgeAddrs.GetDifferentAddr(int(connIndex), hasConnectivityError) + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-e.gracefulShutdownC: + return nil + case <-protocolFallback.BackoffTimer(): + if !recoverable { + return err } - select { - case <-ctx.Done(): - return ctx.Err() - case <-gracefulShutdownC: - return nil - case <-protocolFallback.BackoffTimer(): - if !recoverable { - return err - } - - if !selectNextProtocol( - connLog.Logger(), - protocolFallback, - config.ProtocolSelector, - err, - ) { - return err - } + if !selectNextProtocol( + connLog.Logger(), + protocolFallback, + e.config.ProtocolSelector, + err, + ) { + return err } } + + return err } // protocolFallback is a wrapper around backoffHandler that will try fallback option when backoff reaches @@ -233,6 +316,10 @@ func selectNextProtocol( ) bool { var idleTimeoutError *quic.IdleTimeoutError isNetworkActivityTimeout := errors.As(cause, &idleTimeoutError) + edgeQuicDialError, ok := cause.(*connection.EdgeQuicDialError) + if !isNetworkActivityTimeout && ok { + isNetworkActivityTimeout = errors.As(edgeQuicDialError.Cause, &idleTimeoutError) + } _, hasFallback := selector.Fallback() if protocolBackoff.ReachedMaxRetries() || (hasFallback && isNetworkActivityTimeout) { @@ -241,7 +328,7 @@ func selectNextProtocol( "Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " + "UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips/\n" + - "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work" + + "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work " + "unless your cloudflared can connect with Cloudflare Network with `quic`.") } @@ -326,8 +413,12 @@ func ServeTunnel( connLog.ConnAwareLogger().Msg(activeIncidentsMsg(incidents)) } return err.Cause, !err.Permanent + case *connection.EdgeQuicDialError: + // Don't retry connection for a dial error + return err, false case ReconnectSignal: connLog.Logger().Info(). + IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). Uint8(connection.LogFieldConnIndex, connIndex). Msgf("Restarting connection due to reconnect signal in %s", err.Delay) err.DelayBeforeReconnect() @@ -526,6 +617,7 @@ func ServeHTTP2( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + connLog.Logger().Debug().Msg("Forcefully breaking http2 connection") _ = tlsServerConn.Close() } return err @@ -584,6 +676,7 @@ func ServeQUIC( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + connLogger.Logger().Debug().Msg("Forcefully breaking quic connection") quicConn.Close() } return err From 5ed3d4e29a7f04c8b7abc3dcff45a55b90aefa2f Mon Sep 17 00:00:00 2001 From: Niklas Rehfeld Date: Thu, 9 Jun 2022 16:44:40 +1200 Subject: [PATCH 104/238] Update remaining OriginRequestConfig functions for Http2Origins --- ingress/config.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ingress/config.go b/ingress/config.go index 0d692fe9..8b0804b1 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -227,6 +227,9 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { } } } + if c.Http2Origin != nil { + out.Http2Origin = *c.Http2Origin + } return out } @@ -377,6 +380,12 @@ func (defaults *OriginRequestConfig) setIPRules(overrides config.OriginRequestCo } } +func (defaults *OriginRequestConfig) setHttp2Origin(overrides config.OriginRequestConfig) { + if val := overrides.Http2Origin; val != nil { + defaults.Http2Origin = *val + } +} + // SetConfig gets config for the requests that cloudflared sends to origins. // Each field has a setter method which sets a value for the field by trying to find: // 1. The user config for this rule @@ -402,6 +411,7 @@ func setConfig(defaults OriginRequestConfig, overrides config.OriginRequestConfi cfg.setProxyAddress(overrides) cfg.setProxyType(overrides) cfg.setIPRules(overrides) + cfg.setHttp2Origin(overrides) return cfg } @@ -449,6 +459,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig ProxyPort: zeroUIntToNil(c.ProxyPort), ProxyType: emptyStringToNil(c.ProxyType), IPRules: convertToRawIPRules(c.IPRules), + Http2Origin: defaultBoolToNil(c.Http2Origin), } } From 69b28e358c0280ff72b9ce94e0970c2d39fbf800 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 9 Jun 2022 13:55:26 +0100 Subject: [PATCH 105/238] TUN-6347: Add TCP stream logs with FlowID --- connection/connection.go | 1 + connection/quic.go | 7 ++++++- proxy/proxy.go | 17 +++++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index aa66e494..bcc24b64 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -132,6 +132,7 @@ type TCPRequest struct { Dest string CFRay string LBProbe bool + FlowID string } // ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has diff --git a/connection/quic.go b/connection/quic.go index ef46a658..2d0684bc 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -31,6 +31,8 @@ const ( HTTPMethodKey = "HttpMethod" // HTTPHostKey is used to get or set http Method in QUIC ALPN if the underlying proxy connection type is HTTP. HTTPHostKey = "HttpHost" + + QUICMetadataFlowID = "FlowID" ) // QUICConnection represents the type that facilitates Proxying via QUIC streams. @@ -180,6 +182,7 @@ func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) if err != nil { return err } + switch connectRequest.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: tracedReq, err := buildHTTPRequest(connectRequest, stream) @@ -191,7 +194,9 @@ func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) return originProxy.ProxyHTTP(w, tracedReq, connectRequest.Type == quicpogs.ConnectionTypeWebsocket) case quicpogs.ConnectionTypeTCP: rwa := &streamReadWriteAcker{stream} - return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest}) + metadata := connectRequest.MetadataMap() + return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest, + FlowID: metadata[QUICMetadataFlowID]}) } return nil } diff --git a/proxy/proxy.go b/proxy/proxy.go index 661ab363..5db9ab4c 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -28,6 +28,7 @@ const ( LogFieldCFRay = "cfRay" LogFieldRule = "ingressRule" LogFieldOriginService = "originService" + LogFieldFlowID = "flowID" ) // Proxy represents a means to Proxy between cloudflared and the origin services. @@ -96,7 +97,7 @@ func (p *Proxy) ProxyHTTP( logFields, ); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) - p.logRequestError(err, cfRay, rule, srv) + p.logRequestError(err, cfRay, "", rule, srv) return err } return nil @@ -109,7 +110,7 @@ func (p *Proxy) ProxyHTTP( rws := connection.NewHTTPResponseReadWriterAcker(w, req) if err := p.proxyStream(req.Context(), rws, dest, originProxy, logFields); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) - p.logRequestError(err, cfRay, rule, srv) + p.logRequestError(err, cfRay, "", rule, srv) return err } return nil @@ -140,13 +141,17 @@ func (p *Proxy) ProxyTCP( cfRay: req.CFRay, lbProbe: req.LBProbe, rule: ingress.ServiceWarpRouting, + flowID: req.FlowID, } + p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started") if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy, logFields); err != nil { - p.logRequestError(err, req.CFRay, "", ingress.ServiceWarpRouting) + p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting) return err } + p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream finished successfully") + return nil } @@ -317,6 +322,7 @@ type logFields struct { cfRay string lbProbe bool rule interface{} + flowID string } func (p *Proxy) logRequest(r *http.Request, fields logFields) { @@ -360,12 +366,15 @@ func (p *Proxy) logOriginResponse(resp *http.Response, fields logFields) { } } -func (p *Proxy) logRequestError(err error, cfRay string, rule, service string) { +func (p *Proxy) logRequestError(err error, cfRay string, flowID string, rule, service string) { requestErrors.Inc() log := p.log.Error().Err(err) if cfRay != "" { log = log.Str(LogFieldCFRay, cfRay) } + if flowID != "" { + log = log.Str(LogFieldFlowID, flowID) + } if rule != "" { log = log.Str(LogFieldRule, rule) } From cc1c6d9abc865533e1ebb5a6a387fd3973ff7bc6 Mon Sep 17 00:00:00 2001 From: abe <39502846+abelinkinbio@users.noreply.github.com> Date: Sat, 11 Jun 2022 15:29:53 -0500 Subject: [PATCH 106/238] Update issue templates Updating existing issue templates with new labels --- .github/ISSUE_TEMPLATE/---bug-report.md | 31 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/---documentation.md | 17 +++++++++++ .github/ISSUE_TEMPLATE/---feature-request.md | 17 +++++++++++ 3 files changed, 65 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/---bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/---documentation.md create mode 100644 .github/ISSUE_TEMPLATE/---feature-request.md diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 00000000..9aaa512c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,31 @@ +--- +name: "\U0001F41B Bug report" +about: Create a report to help us improve cloudflared +title: "\U0001F41B" +labels: 'Priority: Normal, Type: Bug' +assignees: abelinkinbio + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Configure '...' +2. Run '....' +3. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Environment and versions** + - OS: [e.g. MacOS] + - Architecture: [e.g. AMD, ARM] + - Version: [e.g. 2022.02.0] + +**Logs and errors** +If applicable, add logs or errors to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/---documentation.md b/.github/ISSUE_TEMPLATE/---documentation.md new file mode 100644 index 00000000..4231ddf4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---documentation.md @@ -0,0 +1,17 @@ +--- +name: "\U0001F4DD Documentation" +about: Request new or updated documentation for cloudflared +title: "\U0001F4DD" +labels: 'Priority: Normal, Type: Documentation' +assignees: abelinkinbio, ranbel + +--- + +**Available Documentation** +A link to the documentation that is available today and the areas which could be improved. + +**Suggested Documentation** +A clear and concise description of the documentation, tutorial, or guide that should be added. + +**Additional context** +Add any other context or screenshots about the documentation request here. diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md new file mode 100644 index 00000000..869b7794 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -0,0 +1,17 @@ +--- +name: "\U0001F4A1 Feature request" +about: Suggest a feature or enhancement for cloudflared +title: "\U0001F4A1" +labels: 'Priority: Normal, Type: Feature Request' +assignees: sssilver, abelinkinbio + +--- + +**Describe the feature you'd like** +A clear and concise description of the feature. What problem does it solve for you? + +**Describe alternatives you've considered** +Are there any alternatives to solving this problem? If so, what was your experience with them? + +**Additional context** +Add any other context or screenshots about the feature request here. From 29d809535e2fb932ee3f0e3a8fd070be6058a270 Mon Sep 17 00:00:00 2001 From: abe <39502846+abelinkinbio@users.noreply.github.com> Date: Sat, 11 Jun 2022 15:32:11 -0500 Subject: [PATCH 107/238] Amendment to previous PR Previous PR created two new templates instead of modifying current templates. This removes the unnecessary created issue templates --- .github/ISSUE_TEMPLATE/---feature-request.md | 2 +- .github/ISSUE_TEMPLATE/bug-report---.md | 31 -------------------- .github/ISSUE_TEMPLATE/feature-request---.md | 17 ----------- 3 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report---.md delete mode 100644 .github/ISSUE_TEMPLATE/feature-request---.md diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md index 869b7794..2862e063 100644 --- a/.github/ISSUE_TEMPLATE/---feature-request.md +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -3,7 +3,7 @@ name: "\U0001F4A1 Feature request" about: Suggest a feature or enhancement for cloudflared title: "\U0001F4A1" labels: 'Priority: Normal, Type: Feature Request' -assignees: sssilver, abelinkinbio +assignees: abelinkinbio, sssilver --- diff --git a/.github/ISSUE_TEMPLATE/bug-report---.md b/.github/ISSUE_TEMPLATE/bug-report---.md deleted file mode 100644 index bb54d9f8..00000000 --- a/.github/ISSUE_TEMPLATE/bug-report---.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: "Bug report \U0001F41B" -about: Create a report to help us improve cloudflared -title: '' -labels: awaiting reply, bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Configure '...' -2. Run '....' -3. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Environment and versions** - - OS: [e.g. MacOS] - - Architecture: [e.g. AMD, ARM] - - Version: [e.g. 2022.02.0] - -**Logs and errors** -If applicable, add logs or errors to help explain your problem. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature-request---.md b/.github/ISSUE_TEMPLATE/feature-request---.md deleted file mode 100644 index 62fdbe20..00000000 --- a/.github/ISSUE_TEMPLATE/feature-request---.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: "Feature request \U0001F4A1" -about: Suggest a feature or enhancement for cloudflared -title: '' -labels: awaiting reply, feature-request -assignees: '' - ---- - -**Describe the feature you'd like** -A clear and concise description of the feature. What problem does it solve for you? - -**Describe alternatives you've considered** -Are there any alternatives to solving this problem? If so, what was your experience with them? - -**Additional context** -Add any other context or screenshots about the feature request here. From e8407848ecb04493070f45f1b02a1c11ebb83ff4 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Mon, 13 Jun 2022 12:45:40 -0500 Subject: [PATCH 108/238] TUN-6385: Don't share err between acceptStream loop and per-stream goroutines --- connection/quic.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 2d0684bc..819d9ec7 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -122,6 +122,11 @@ func (q *QUICConnection) serveControlStream(ctx context.Context, controlStream q return nil } +// Close closes the session with no errors specified. +func (q *QUICConnection) Close() { + q.session.CloseWithError(0, "") +} + func (q *QUICConnection) acceptStream(ctx context.Context) error { defer q.Close() for { @@ -133,20 +138,17 @@ func (q *QUICConnection) acceptStream(ctx context.Context) error { } return fmt.Errorf("failed to accept QUIC stream: %w", err) } - go func() { - stream := quicpogs.NewSafeStreamCloser(quicStream) - defer stream.Close() - - if err = q.handleStream(stream); err != nil { - q.logger.Err(err).Msg("Failed to handle QUIC stream") - } - }() + go q.runStream(quicStream) } } -// Close closes the session with no errors specified. -func (q *QUICConnection) Close() { - q.session.CloseWithError(0, "") +func (q *QUICConnection) runStream(quicStream quic.Stream) { + stream := quicpogs.NewSafeStreamCloser(quicStream) + defer stream.Close() + + if err := q.handleStream(stream); err != nil { + q.logger.Err(err).Msg("Failed to handle QUIC stream") + } } func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { From 76add5ca77a5d9fd1e474b325952ae7c678e44f1 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 13 Jun 2022 10:36:40 -0700 Subject: [PATCH 109/238] TUN-6384: Correct duplicate connection error to fetch new IP first --- supervisor/tunnel.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 8d3fe9f0..fcbace54 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -141,10 +141,11 @@ type DefaultAddrFallback struct { func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { switch err.(type) { case nil: // maintain current IP address - // Try the next address if it was a quic.IdleTimeoutError or - // dupConnRegisterTunnelError + // DupConnRegisterTunnelError should indicate to get a new address immediately + case connection.DupConnRegisterTunnelError: + return true, false + // Try the next address if it was a quic.IdleTimeoutError case *quic.IdleTimeoutError, - connection.DupConnRegisterTunnelError, edgediscovery.DialError, *connection.EdgeQuicDialError: // Wait for two failures before falling back to a new address From 8e9091cc483e83a14845148faa6353ef77638334 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 13 Jun 2022 10:40:21 -0700 Subject: [PATCH 110/238] TUN-6373: Add edge-ip-version to remotely pushed configuration --- cmd/cloudflared/tunnel/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 9376d520..6bce68f6 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -45,7 +45,7 @@ var ( secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} - configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq"} + configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq", "edge-ip-version"} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories From 978e01f77eedf4f098f71423b42e639d12355015 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 7 Jun 2022 12:32:29 -0700 Subject: [PATCH 111/238] TUN-6010: Add component tests for --edge-ip-version --- component-tests/cli.py | 95 ++++++++++++++ component-tests/test_edge_discovery.py | 165 +++++++++++++++++++++++++ component-tests/test_service.py | 8 +- component-tests/util.py | 18 +++ 4 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 component-tests/cli.py create mode 100644 component-tests/test_edge_discovery.py diff --git a/component-tests/cli.py b/component-tests/cli.py new file mode 100644 index 00000000..ae4a1b12 --- /dev/null +++ b/component-tests/cli.py @@ -0,0 +1,95 @@ +import json +import subprocess +from time import sleep + +from setup import get_config_from_file + +SINGLE_CASE_TIMEOUT = 600 + +class CloudflaredCli: + def __init__(self, config, config_path, logger): + self.basecmd = [config.cloudflared_binary, "tunnel"] + if config_path is not None: + self.basecmd += ["--config", str(config_path)] + origincert = get_config_from_file()["origincert"] + if origincert: + self.basecmd += ["--origincert", origincert] + self.logger = logger + + def _run_command(self, subcmd, subcmd_name, needs_to_pass=True): + cmd = self.basecmd + subcmd + # timeout limits the time a subprocess can run. This is useful to guard against running a tunnel when + # command/args are in wrong order. + result = run_subprocess(cmd, subcmd_name, self.logger, check=needs_to_pass, capture_output=True, timeout=15) + return result + + def list_tunnels(self): + cmd_args = ["list", "--output", "json"] + listed = self._run_command(cmd_args, "list") + return json.loads(listed.stdout) + + def get_tunnel_info(self, tunnel_id): + info = self._run_command(["info", "--output", "json", tunnel_id], "info") + return json.loads(info.stdout) + + def __enter__(self): + self.basecmd += ["run"] + self.process = subprocess.Popen(self.basecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.logger.info(f"Run cmd {self.basecmd}") + return self.process + + def __exit__(self, exc_type, exc_value, exc_traceback): + terminate_gracefully(self.process, self.logger, self.basecmd) + self.logger.debug(f"{self.basecmd} logs: {self.process.stderr.read()}") + + +def terminate_gracefully(process, logger, cmd): + process.terminate() + process_terminated = wait_for_terminate(process) + if not process_terminated: + process.kill() + logger.warning(f"{cmd}: cloudflared did not terminate within wait period. Killing process. logs: \ + stdout: {process.stdout.read()}, stderr: {process.stderr.read()}") + + +def wait_for_terminate(opened_subprocess, attempts=10, poll_interval=1): + """ + wait_for_terminate polls the opened_subprocess every x seconds for a given number of attempts. + It returns true if the subprocess was terminated and false if it didn't. + """ + for _ in range(attempts): + if _is_process_stopped(opened_subprocess): + return True + sleep(poll_interval) + return False + + +def _is_process_stopped(process): + return process.poll() is not None + + +def cert_path(): + return get_config_from_file()["origincert"] + + +class SubprocessError(Exception): + def __init__(self, program, exit_code, cause): + self.program = program + self.exit_code = exit_code + self.cause = cause + + +def run_subprocess(cmd, cmd_name, logger, timeout=SINGLE_CASE_TIMEOUT, **kargs): + kargs["timeout"] = timeout + try: + result = subprocess.run(cmd, **kargs) + logger.debug(f"{cmd} log: {result.stdout}", extra={"cmd": cmd_name}) + return result + except subprocess.CalledProcessError as e: + err = f"{cmd} return exit code {e.returncode}, stderr" + e.stderr.decode("utf-8") + logger.error(err, extra={"cmd": cmd_name, "return_code": e.returncode}) + raise SubprocessError(cmd[0], e.returncode, e) + except subprocess.TimeoutExpired as e: + err = f"{cmd} timeout after {e.timeout} seconds, stdout: {e.stdout}, stderr: {e.stderr}" + logger.error(err, extra={"cmd": cmd_name, "return_code": "timeout"}) + raise e \ No newline at end of file diff --git a/component-tests/test_edge_discovery.py b/component-tests/test_edge_discovery.py new file mode 100644 index 00000000..61e036a3 --- /dev/null +++ b/component-tests/test_edge_discovery.py @@ -0,0 +1,165 @@ +import ipaddress +import socket + +import pytest + +from constants import protocols +from cli import CloudflaredCli +from util import get_tunnel_connector_id, LOGGER, wait_tunnel_ready, write_config + + +class TestEdgeDiscovery: + def _extra_config(self, protocol, edge_ip_version): + config = { + "protocol": protocol, + } + if edge_ip_version: + config["edge-ip-version"] = edge_ip_version + return config + + @pytest.mark.parametrize("protocol", protocols()) + def test_default_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv4-only edge addresses (default is unset "--edge-ip-version 4") + """ + if self.has_ipv6_only(): + pytest.skip("Host has IPv6 only support and current default is IPv4 only") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, None, self.expect_ipv4_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_ipv4_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv4-only edge addresses + """ + if self.has_ipv6_only(): + pytest.skip("Host has IPv6 only support") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "4", self.expect_ipv4_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_ipv6_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv6-only edge addresses + """ + if self.has_ipv4_only(): + pytest.skip("Host has IPv4 only support") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "6", self.expect_ipv6_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_auto_ip64(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via auto with a preference of IPv6 then IPv4 addresses for a dual stack host + + This test also assumes that the host has IPv6 preference. + """ + if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET6): + pytest.skip("Host does not support dual stack with IPv6 preference") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "auto", self.expect_ipv6_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_auto_ip46(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via auto with a preference of IPv4 then IPv6 addresses for a dual stack host + + This test also assumes that the host has IPv4 preference. + """ + if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET): + pytest.skip("Host does not support dual stack with IPv4 preference") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "auto", self.expect_ipv4_address) + + def expect_address_connections(self, tmp_path, component_tests_config, protocol, edge_ip_version, assert_address_type): + config = component_tests_config( + self._extra_config(protocol, edge_ip_version)) + config_path = write_config(tmp_path, config.full_config) + LOGGER.debug(config) + with CloudflaredCli(config, config_path, LOGGER): + wait_tunnel_ready(tunnel_url=config.get_url(), + require_min_connections=4) + cfd_cli = CloudflaredCli(config, config_path, LOGGER) + tunnel_id = config.get_tunnel_id() + info = cfd_cli.get_tunnel_info(tunnel_id) + connector_id = get_tunnel_connector_id() + connector = next( + (c for c in info["conns"] if c["id"] == connector_id), None) + assert connector, f"Expected connection info from get tunnel info for the connected instance: {info}" + conns = connector["conns"] + assert conns == None or len( + conns) == 4, f"There should be 4 connections registered: {conns}" + for conn in conns: + origin_ip = conn["origin_ip"] + assert origin_ip, f"No available origin_ip for this connection: {conn}" + assert_address_type(origin_ip) + + def expect_ipv4_address(self, address): + assert type(ipaddress.ip_address( + address)) is ipaddress.IPv4Address, f"Expected connection from origin to be a valid IPv4 address: {address}" + + def expect_ipv6_address(self, address): + assert type(ipaddress.ip_address( + address)) is ipaddress.IPv6Address, f"Expected connection from origin to be a valid IPv6 address: {address}" + + def get_addresses(self): + """ + Returns a list of addresses for the host. + """ + host_addresses = socket.getaddrinfo( + "region1.v2.argotunnel.com", 7844, socket.AF_UNSPEC, socket.SOCK_STREAM) + assert len( + host_addresses) > 0, "No addresses returned from getaddrinfo" + return host_addresses + + def has_dual_stack(self, address_family_preference=None): + """ + Returns true if the host has dual stack support and can optionally check + the provided IP family preference. + """ + dual_stack = not self.has_ipv6_only() and not self.has_ipv4_only() + if address_family_preference: + address = self.get_addresses()[0] + return dual_stack and address[0] == address_family_preference + + return dual_stack + + def has_ipv6_only(self): + """ + Returns True if the host has only IPv6 address support. + """ + return self.attempt_connection(socket.AddressFamily.AF_INET6) and not self.attempt_connection(socket.AddressFamily.AF_INET) + + def has_ipv4_only(self): + """ + Returns True if the host has only IPv4 address support. + """ + return self.attempt_connection(socket.AddressFamily.AF_INET) and not self.attempt_connection(socket.AddressFamily.AF_INET6) + + def attempt_connection(self, address_family): + """ + Returns True if a successful socket connection can be made to the + remote host with the provided address family to validate host support + for the provided address family. + """ + address = None + for a in self.get_addresses(): + if a[0] == address_family: + address = a + break + if address is None: + # Couldn't even lookup the address family so we can't connect + return False + af, socktype, proto, canonname, sockaddr = address + s = None + try: + s = socket.socket(af, socktype, proto) + except OSError: + return False + try: + s.connect(sockaddr) + except OSError: + s.close() + return False + s.close() + return True diff --git a/component-tests/test_service.py b/component-tests/test_service.py index f8ace641..e1c155e6 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import os import pathlib -import platform import subprocess from contextlib import contextmanager from pathlib import Path @@ -10,12 +9,7 @@ import pytest import test_logging from conftest import CfdModes -from util import start_cloudflared, wait_tunnel_ready, write_config - - -def select_platform(plat): - return pytest.mark.skipif( - platform.system() != plat, reason=f"Only runs on {plat}") +from util import select_platform, start_cloudflared, wait_tunnel_ready, write_config def default_config_dir(): diff --git a/component-tests/util.py b/component-tests/util.py index db8f925d..aae9b85f 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -1,9 +1,12 @@ import logging import os +import platform import subprocess from contextlib import contextmanager from time import sleep +import pytest + import requests import yaml from retrying import retry @@ -12,6 +15,10 @@ from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS LOGGER = logging.getLogger(__name__) +def select_platform(plat): + return pytest.mark.skipif( + platform.system() != plat, reason=f"Only runs on {plat}") + def write_config(directory, config): config_path = directory / "config.yml" @@ -111,6 +118,17 @@ def check_tunnel_not_connected(): LOGGER.warning(f"Failed to connect to {url}, error: {e}") +def get_tunnel_connector_id(): + url = f'http://localhost:{METRICS_PORT}/ready' + + try: + resp = requests.get(url, timeout=1) + return resp.json()["connectorId"] + # cloudflared might already terminated + except requests.exceptions.ConnectionError as e: + LOGGER.warning(f"Failed to connect to {url}, error: {e}") + + # In some cases we don't need to check response status, such as when sending batch requests to generate logs def send_requests(url, count, require_ok=True): errors = 0 From f2339a72445ec1e0b5e5c49d3e0b42a6b6930a33 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Mon, 13 Jun 2022 11:44:27 -0500 Subject: [PATCH 112/238] TUN-6380: Enforce connect and keep-alive timeouts for TCP connections in both WARP routing and websocket based TCP proxy. For WARP routing the defaults for these new settings are 5 seconds for connect timeout and 30 seconds for keep-alive timeout. These values can be configured either remotely or locally. Local config lives under "warp-routing" section in config.yaml. For websocket-based proxy, the defaults come from originConfig settings (either global or per-service) and use the same defaults as HTTP proxying. --- cmd/cloudflared/tunnel/configuration.go | 2 +- config/configuration.go | 4 +- config/configuration_test.go | 7 ++- ingress/config.go | 61 +++++++++++++++++++------ ingress/config_test.go | 4 +- ingress/ingress.go | 12 ++++- ingress/origin_proxy.go | 22 ++++----- ingress/origin_proxy_test.go | 6 +-- ingress/origin_service.go | 6 ++- orchestration/config.go | 6 +-- orchestration/config_test.go | 25 +++++++--- orchestration/orchestrator.go | 14 +++--- orchestration/orchestrator_test.go | 23 ++++------ proxy/proxy.go | 21 +++------ proxy/proxy_test.go | 19 +++++--- 15 files changed, 144 insertions(+), 88 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 6bce68f6..36257cc7 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -358,7 +358,7 @@ func prepareTunnelConfig( } orchestratorConfig := &orchestration.Config{ Ingress: &ingressRules, - WarpRoutingEnabled: warpRoutingEnabled, + WarpRouting: ingress.NewWarpRoutingConfig(&cfg.WarpRouting), ConfigurationFlags: parseConfigFlags(c), } return tunnelConfig, orchestratorConfig, nil diff --git a/config/configuration.go b/config/configuration.go index 49395404..fdbf0aef 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -244,7 +244,9 @@ type Configuration struct { } type WarpRoutingConfig struct { - Enabled bool `yaml:"enabled" json:"enabled"` + Enabled bool `yaml:"enabled" json:"enabled"` + ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"` + TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"` } type configFileSettings struct { diff --git a/config/configuration_test.go b/config/configuration_test.go index dff785c8..96adb74b 100644 --- a/config/configuration_test.go +++ b/config/configuration_test.go @@ -23,7 +23,9 @@ func TestConfigFileSettings(t *testing.T) { Service: "https://localhost:8001", } warpRouting = WarpRoutingConfig{ - Enabled: true, + Enabled: true, + ConnectTimeout: &CustomDuration{Duration: 2 * time.Second}, + TCPKeepAlive: &CustomDuration{Duration: 10 * time.Second}, } ) rawYAML := ` @@ -48,6 +50,9 @@ ingress: service: https://localhost:8001 warp-routing: enabled: true + connectTimeout: 2s + tcpKeepAlive: 10s + retries: 5 grace-period: 30s percentage: 3.14 diff --git a/ingress/config.go b/ingress/config.go index bc2a9f6b..350edb0f 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -12,10 +12,11 @@ import ( ) var ( - defaultConnectTimeout = config.CustomDuration{Duration: 30 * time.Second} - defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second} - defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second} - defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second} + defaultHTTPConnectTimeout = config.CustomDuration{Duration: 30 * time.Second} + defaultWarpRoutingConnectTimeout = config.CustomDuration{Duration: 5 * time.Second} + defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second} + defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second} + defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second} ) const ( @@ -41,10 +42,44 @@ const ( socksProxy = "socks" ) +type WarpRoutingConfig struct { + Enabled bool `yaml:"enabled" json:"enabled"` + ConnectTimeout config.CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"` + TCPKeepAlive config.CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"` +} + +func NewWarpRoutingConfig(raw *config.WarpRoutingConfig) WarpRoutingConfig { + cfg := WarpRoutingConfig{ + Enabled: raw.Enabled, + ConnectTimeout: defaultWarpRoutingConnectTimeout, + TCPKeepAlive: defaultTCPKeepAlive, + } + if raw.ConnectTimeout != nil { + cfg.ConnectTimeout = *raw.ConnectTimeout + } + if raw.TCPKeepAlive != nil { + cfg.TCPKeepAlive = *raw.TCPKeepAlive + } + return cfg +} + +func (c *WarpRoutingConfig) RawConfig() config.WarpRoutingConfig { + raw := config.WarpRoutingConfig{ + Enabled: c.Enabled, + } + if c.ConnectTimeout.Duration != defaultWarpRoutingConnectTimeout.Duration { + raw.ConnectTimeout = &c.ConnectTimeout + } + if c.TCPKeepAlive.Duration != defaultTCPKeepAlive.Duration { + raw.TCPKeepAlive = &c.TCPKeepAlive + } + return raw +} + // RemoteConfig models ingress settings that can be managed remotely, for example through the dashboard. type RemoteConfig struct { Ingress Ingress - WarpRouting config.WarpRoutingConfig + WarpRouting WarpRoutingConfig } type RemoteConfigJSON struct { @@ -72,18 +107,18 @@ func (rc *RemoteConfig) UnmarshalJSON(b []byte) error { } rc.Ingress = ingress - rc.WarpRouting = rawConfig.WarpRouting + rc.WarpRouting = NewWarpRoutingConfig(&rawConfig.WarpRouting) return nil } func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { - var connectTimeout config.CustomDuration = defaultConnectTimeout - var tlsTimeout config.CustomDuration = defaultTLSTimeout - var tcpKeepAlive config.CustomDuration = defaultTCPKeepAlive + var connectTimeout = defaultHTTPConnectTimeout + var tlsTimeout = defaultTLSTimeout + var tcpKeepAlive = defaultTCPKeepAlive var noHappyEyeballs bool - var keepAliveConnections int = defaultKeepAliveConnections - var keepAliveTimeout config.CustomDuration = defaultKeepAliveTimeout + var keepAliveConnections = defaultKeepAliveConnections + var keepAliveTimeout = defaultKeepAliveTimeout var httpHostHeader string var originServerName string var caPool string @@ -160,7 +195,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig { func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { out := OriginRequestConfig{ - ConnectTimeout: defaultConnectTimeout, + ConnectTimeout: defaultHTTPConnectTimeout, TLSTimeout: defaultTLSTimeout, TCPKeepAlive: defaultTCPKeepAlive, KeepAliveConnections: defaultKeepAliveConnections, @@ -404,7 +439,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig var keepAliveTimeout *config.CustomDuration var proxyAddress *string - if c.ConnectTimeout != defaultConnectTimeout { + if c.ConnectTimeout != defaultHTTPConnectTimeout { connectTimeout = &c.ConnectTimeout } if c.TLSTimeout != defaultTLSTimeout { diff --git a/ingress/config_test.go b/ingress/config_test.go index 93c971fc..2b9f8a3b 100644 --- a/ingress/config_test.go +++ b/ingress/config_test.go @@ -274,7 +274,7 @@ func TestOriginRequestConfigDefaults(t *testing.T) { // Rule 0 didn't override anything, so it inherits the cloudflared defaults actual0 := ing.Rules[0].Config expected0 := OriginRequestConfig{ - ConnectTimeout: defaultConnectTimeout, + ConnectTimeout: defaultHTTPConnectTimeout, TLSTimeout: defaultTLSTimeout, TCPKeepAlive: defaultTCPKeepAlive, KeepAliveConnections: defaultKeepAliveConnections, @@ -404,7 +404,7 @@ func TestDefaultConfigFromCLI(t *testing.T) { c := cli.NewContext(nil, set, nil) expected := OriginRequestConfig{ - ConnectTimeout: defaultConnectTimeout, + ConnectTimeout: defaultHTTPConnectTimeout, TLSTimeout: defaultTLSTimeout, TCPKeepAlive: defaultTCPKeepAlive, KeepAliveConnections: defaultKeepAliveConnections, diff --git a/ingress/ingress.go b/ingress/ingress.go index 8d2ec5ba..05a90a8b 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -97,8 +97,16 @@ type WarpRoutingService struct { Proxy StreamBasedOriginProxy } -func NewWarpRoutingService() *WarpRoutingService { - return &WarpRoutingService{Proxy: &rawTCPService{name: ServiceWarpRouting}} +func NewWarpRoutingService(config WarpRoutingConfig) *WarpRoutingService { + svc := &rawTCPService{ + name: ServiceWarpRouting, + dialer: net.Dialer{ + Timeout: config.ConnectTimeout.Duration, + KeepAlive: config.TCPKeepAlive.Duration, + }, + } + + return &WarpRoutingService{Proxy: svc} } // Get a single origin service from the CLI/config. diff --git a/ingress/origin_proxy.go b/ingress/origin_proxy.go index 90371dfa..83bcd4fc 100644 --- a/ingress/origin_proxy.go +++ b/ingress/origin_proxy.go @@ -1,26 +1,20 @@ package ingress import ( + "context" "fmt" - "net" "net/http" - - "github.com/pkg/errors" -) - -var ( - errUnsupportedConnectionType = errors.New("internal error: unsupported connection type") ) // HTTPOriginProxy can be implemented by origin services that want to proxy http requests. type HTTPOriginProxy interface { - // RoundTrip is how cloudflared proxies eyeball requests to the actual origin services + // RoundTripper is how cloudflared proxies eyeball requests to the actual origin services http.RoundTripper } // StreamBasedOriginProxy can be implemented by origin services that want to proxy ws/TCP. type StreamBasedOriginProxy interface { - EstablishConnection(dest string) (OriginConnection, error) + EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) } func (o *unixSocketPath) RoundTrip(req *http.Request) (*http.Response, error) { @@ -59,8 +53,8 @@ func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) { return resp, nil } -func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, error) { - conn, err := net.Dial("tcp", dest) +func (o *rawTCPService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) { + conn, err := o.dialer.DialContext(ctx, "tcp", dest) if err != nil { return nil, err } @@ -71,13 +65,13 @@ func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, erro return originConn, nil } -func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, error) { +func (o *tcpOverWSService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) { var err error if !o.isBastion { dest = o.dest } - conn, err := net.Dial("tcp", dest) + conn, err := o.dialer.DialContext(ctx, "tcp", dest) if err != nil { return nil, err } @@ -89,6 +83,6 @@ func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, e } -func (o *socksProxyOverWSService) EstablishConnection(dest string) (OriginConnection, error) { +func (o *socksProxyOverWSService) EstablishConnection(_ctx context.Context, _dest string) (OriginConnection, error) { return o.conn, nil } diff --git a/ingress/origin_proxy_test.go b/ingress/origin_proxy_test.go index cc244aee..427b2a65 100644 --- a/ingress/origin_proxy_test.go +++ b/ingress/origin_proxy_test.go @@ -36,7 +36,7 @@ func TestRawTCPServiceEstablishConnection(t *testing.T) { require.NoError(t, err) // Origin not listening for new connection, should return an error - _, err = rawTCPService.EstablishConnection(req.URL.String()) + _, err = rawTCPService.EstablishConnection(context.Background(), req.URL.String()) require.Error(t, err) } @@ -87,7 +87,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) { t.Run(test.testCase, func(t *testing.T) { if test.expectErr { bastionHost, _ := carrier.ResolveBastionDest(test.req) - _, err := test.service.EstablishConnection(bastionHost) + _, err := test.service.EstablishConnection(context.Background(), bastionHost) assert.Error(t, err) } }) @@ -99,7 +99,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) { for _, service := range []*tcpOverWSService{newTCPOverWSService(originURL), newBastionService()} { // Origin not listening for new connection, should return an error bastionHost, _ := carrier.ResolveBastionDest(bastionReq) - _, err := service.EstablishConnection(bastionHost) + _, err := service.EstablishConnection(context.Background(), bastionHost) assert.Error(t, err) } } diff --git a/ingress/origin_service.go b/ingress/origin_service.go index 38ceeda5..6f348bee 100644 --- a/ingress/origin_service.go +++ b/ingress/origin_service.go @@ -91,7 +91,8 @@ func (o httpService) MarshalJSON() ([]byte, error) { // rawTCPService dials TCP to the destination specified by the client // It's used by warp routing type rawTCPService struct { - name string + name string + dialer net.Dialer } func (o *rawTCPService) String() string { @@ -113,6 +114,7 @@ type tcpOverWSService struct { dest string isBastion bool streamHandler streamHandlerFunc + dialer net.Dialer } type socksProxyOverWSService struct { @@ -176,6 +178,8 @@ func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg Ori } else { o.streamHandler = DefaultStreamHandler } + o.dialer.Timeout = cfg.ConnectTimeout.Duration + o.dialer.KeepAlive = cfg.TCPKeepAlive.Duration return nil } diff --git a/orchestration/config.go b/orchestration/config.go index cfdbd939..26904b57 100644 --- a/orchestration/config.go +++ b/orchestration/config.go @@ -19,8 +19,8 @@ type newLocalConfig struct { // Config is the original config as read and parsed by cloudflared. type Config struct { - Ingress *ingress.Ingress - WarpRoutingEnabled bool + Ingress *ingress.Ingress + WarpRouting ingress.WarpRoutingConfig // Extra settings used to configure this instance but that are not eligible for remotely management // ie. (--protocol, --loglevel, ...) @@ -37,7 +37,7 @@ func (rc *newLocalConfig) MarshalJSON() ([]byte, error) { // UI doesn't support top level configs, so we reconcile to individual ingress configs. GlobalOriginRequest: nil, IngressRules: convertToUnvalidatedIngressRules(rc.RemoteConfig.Ingress), - WarpRouting: rc.RemoteConfig.WarpRouting, + WarpRouting: rc.RemoteConfig.WarpRouting.RawConfig(), }, } diff --git a/orchestration/config_test.go b/orchestration/config_test.go index 53d9a23f..04c15359 100644 --- a/orchestration/config_test.go +++ b/orchestration/config_test.go @@ -3,16 +3,17 @@ package orchestration import ( "encoding/json" "testing" + "time" "github.com/stretchr/testify/require" + "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/ingress" ) // TestNewLocalConfig_MarshalJSON tests that we are able to converte a compiled and validated config back // into an "unvalidated" format which is compatible with Remote Managed configurations. func TestNewLocalConfig_MarshalJSON(t *testing.T) { - rawConfig := []byte(` { "originRequest": { @@ -57,7 +58,11 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) { ] } } - ] + ], + "warp-routing": { + "enabled": true, + "connectTimeout": 1 + } } `) @@ -73,10 +78,18 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) { jsonSerde, err := json.Marshal(c) require.NoError(t, err) - var config ingress.RemoteConfig - err = json.Unmarshal(jsonSerde, &config) + var remoteConfig ingress.RemoteConfig + err = json.Unmarshal(jsonSerde, &remoteConfig) require.NoError(t, err) - require.Equal(t, config.WarpRouting.Enabled, false) - require.Equal(t, config.Ingress.Rules, expectedConfig.Ingress.Rules) + require.Equal(t, remoteConfig.WarpRouting, ingress.WarpRoutingConfig{ + Enabled: true, + ConnectTimeout: config.CustomDuration{ + Duration: time.Second, + }, + TCPKeepAlive: config.CustomDuration{ + Duration: 30 * time.Second, // default value is 30 seconds + }, + }) + require.Equal(t, remoteConfig.Ingress.Rules, expectedConfig.Ingress.Rules) } diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index 60cd1f10..0659411a 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -47,7 +47,7 @@ func NewOrchestrator(ctx context.Context, config *Config, tags []tunnelpogs.Tag, log: log, shutdownC: ctx.Done(), } - if err := o.updateIngress(*config.Ingress, config.WarpRoutingEnabled); err != nil { + if err := o.updateIngress(*config.Ingress, config.WarpRouting); err != nil { return nil, err } go o.waitToCloseLastProxy() @@ -80,7 +80,7 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up } } - if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting.Enabled); err != nil { + if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting); err != nil { o.log.Err(err). Int32("version", version). Str("config", string(config)). @@ -103,7 +103,7 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *tunnelpogs.Up } // The caller is responsible to make sure there is no concurrent access -func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEnabled bool) error { +func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting ingress.WarpRoutingConfig) error { select { case <-o.shutdownC: return fmt.Errorf("cloudflared already shutdown") @@ -118,10 +118,10 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRoutingEn if err := ingressRules.StartOrigins(o.log, proxyShutdownC); err != nil { return errors.Wrap(err, "failed to start origin") } - newProxy := proxy.NewOriginProxy(ingressRules, warpRoutingEnabled, o.tags, o.log) + newProxy := proxy.NewOriginProxy(ingressRules, warpRouting, o.tags, o.log) o.proxy.Store(newProxy) o.config.Ingress = &ingressRules - o.config.WarpRoutingEnabled = warpRoutingEnabled + o.config.WarpRouting = warpRouting // If proxyShutdownC is nil, there is no previous running proxy if o.proxyShutdownC != nil { @@ -139,7 +139,7 @@ func (o *Orchestrator) GetConfigJSON() ([]byte, error) { c := &newLocalConfig{ RemoteConfig: ingress.RemoteConfig{ Ingress: *o.config.Ingress, - WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled}, + WarpRouting: o.config.WarpRouting, }, ConfigurationFlags: o.config.ConfigurationFlags, } @@ -166,7 +166,7 @@ func (o *Orchestrator) GetVersionedConfigJSON() ([]byte, error) { OriginRequest ingress.OriginRequestConfig `json:"originRequest"` }{ Ingress: o.config.Ingress.Rules, - WarpRouting: config.WarpRoutingConfig{Enabled: o.config.WarpRoutingEnabled}, + WarpRouting: o.config.WarpRouting.RawConfig(), OriginRequest: o.config.Ingress.Defaults, }, } diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 85f0f83a..dfac28a0 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -48,8 +48,7 @@ var ( // - receiving an old version is noop func TestUpdateConfiguration(t *testing.T) { initConfig := &Config{ - Ingress: &ingress.Ingress{}, - WarpRoutingEnabled: false, + Ingress: &ingress.Ingress{}, } orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, &testLogger) require.NoError(t, err) @@ -87,7 +86,8 @@ func TestUpdateConfiguration(t *testing.T) { } ], "warp-routing": { - "enabled": true + "enabled": true, + "connectTimeout": 10 } } `) @@ -121,7 +121,8 @@ func TestUpdateConfiguration(t *testing.T) { require.Equal(t, config.CustomDuration{Duration: time.Second * 90}, configV2.Ingress.Rules[2].Config.ConnectTimeout) require.Equal(t, false, configV2.Ingress.Rules[2].Config.NoTLSVerify) require.Equal(t, true, configV2.Ingress.Rules[2].Config.NoHappyEyeballs) - require.True(t, configV2.WarpRoutingEnabled) + require.True(t, configV2.WarpRouting.Enabled) + require.Equal(t, configV2.WarpRouting.ConnectTimeout.Duration, 10*time.Second) originProxyV2, err := orchestrator.GetOriginProxy() require.NoError(t, err) @@ -164,7 +165,7 @@ func TestUpdateConfiguration(t *testing.T) { require.Len(t, configV10.Ingress.Rules, 1) require.True(t, configV10.Ingress.Rules[0].Matches("blogs.tunnel.io", "/2022/02/10")) require.Equal(t, ingress.HelloWorldService, configV10.Ingress.Rules[0].Service.String()) - require.False(t, configV10.WarpRoutingEnabled) + require.False(t, configV10.WarpRouting.Enabled) originProxyV10, err := orchestrator.GetOriginProxy() require.NoError(t, err) @@ -246,8 +247,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) { appliedV2 = make(chan struct{}) initConfig = &Config{ - Ingress: &ingress.Ingress{}, - WarpRoutingEnabled: false, + Ingress: &ingress.Ingress{}, } ) @@ -476,8 +476,7 @@ func TestClosePreviousProxies(t *testing.T) { } `) initConfig = &Config{ - Ingress: &ingress.Ingress{}, - WarpRoutingEnabled: false, + Ingress: &ingress.Ingress{}, } ) @@ -534,8 +533,7 @@ func TestPersistentConnection(t *testing.T) { ) msg := t.Name() initConfig := &Config{ - Ingress: &ingress.Ingress{}, - WarpRoutingEnabled: false, + Ingress: &ingress.Ingress{}, } orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, &testLogger) require.NoError(t, err) @@ -645,8 +643,7 @@ func TestPersistentConnection(t *testing.T) { func TestSerializeLocalConfig(t *testing.T) { c := &newLocalConfig{ RemoteConfig: ingress.RemoteConfig{ - Ingress: ingress.Ingress{}, - WarpRouting: config.WarpRoutingConfig{}, + Ingress: ingress.Ingress{}, }, ConfigurationFlags: map[string]string{"a": "b"}, } diff --git a/proxy/proxy.go b/proxy/proxy.go index 5db9ab4c..e31dc75b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -42,7 +42,7 @@ type Proxy struct { // NewOriginProxy returns a new instance of the Proxy struct. func NewOriginProxy( ingressRules ingress.Ingress, - warpRoutingEnabled bool, + warpRouting ingress.WarpRoutingConfig, tags []tunnelpogs.Tag, log *zerolog.Logger, ) *Proxy { @@ -51,8 +51,8 @@ func NewOriginProxy( tags: tags, log: log, } - if warpRoutingEnabled { - proxy.warpRouting = ingress.NewWarpRoutingService() + if warpRouting.Enabled { + proxy.warpRouting = ingress.NewWarpRoutingService(warpRouting) log.Info().Msgf("Warp-routing is enabled") } @@ -108,7 +108,7 @@ func (p *Proxy) ProxyHTTP( } rws := connection.NewHTTPResponseReadWriterAcker(w, req) - if err := p.proxyStream(req.Context(), rws, dest, originProxy, logFields); err != nil { + if err := p.proxyStream(req.Context(), rws, dest, originProxy); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) p.logRequestError(err, cfRay, "", rule, srv) return err @@ -137,15 +137,9 @@ func (p *Proxy) ProxyTCP( serveCtx, cancel := context.WithCancel(ctx) defer cancel() - logFields := logFields{ - cfRay: req.CFRay, - lbProbe: req.LBProbe, - rule: ingress.ServiceWarpRouting, - flowID: req.FlowID, - } - p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started") - if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy, logFields); err != nil { + + if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting) return err } @@ -255,9 +249,8 @@ func (p *Proxy) proxyStream( rwa connection.ReadWriteAcker, dest string, connectionProxy ingress.StreamBasedOriginProxy, - fields logFields, ) error { - originConn, err := connectionProxy.EstablishConnection(dest) + originConn, err := connectionProxy.EstablishConnection(ctx, dest) if err != nil { return err } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 9f0461ce..0e0019f1 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -32,7 +32,12 @@ import ( ) var ( - testTags = []tunnelpogs.Tag{tunnelpogs.Tag{Name: "Name", Value: "value"}} + testTags = []tunnelpogs.Tag{tunnelpogs.Tag{Name: "Name", Value: "value"}} + noWarpRouting = ingress.WarpRoutingConfig{} + testWarpRouting = ingress.WarpRoutingConfig{ + Enabled: true, + ConnectTimeout: config.CustomDuration{Duration: time.Second}, + } ) type mockHTTPRespWriter struct { @@ -138,7 +143,7 @@ func TestProxySingleOrigin(t *testing.T) { require.NoError(t, ingressRule.StartOrigins(&log, ctx.Done())) - proxy := NewOriginProxy(ingressRule, false, testTags, &log) + proxy := NewOriginProxy(ingressRule, noWarpRouting, testTags, &log) t.Run("testProxyHTTP", testProxyHTTP(proxy)) t.Run("testProxyWebsocket", testProxyWebsocket(proxy)) t.Run("testProxySSE", testProxySSE(proxy)) @@ -345,7 +350,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat ctx, cancel := context.WithCancel(context.Background()) require.NoError(t, ingress.StartOrigins(&log, ctx.Done())) - proxy := NewOriginProxy(ingress, false, testTags, &log) + proxy := NewOriginProxy(ingress, noWarpRouting, testTags, &log) for _, test := range tests { responseWriter := newMockHTTPRespWriter() @@ -393,7 +398,7 @@ func TestProxyError(t *testing.T) { log := zerolog.Nop() - proxy := NewOriginProxy(ing, false, testTags, &log) + proxy := NewOriginProxy(ing, noWarpRouting, testTags, &log) responseWriter := newMockHTTPRespWriter() req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil) @@ -509,7 +514,7 @@ func TestConnections(t *testing.T) { originService: runEchoTCPService, eyeballResponseWriter: newTCPRespWriter(replayer), eyeballRequestBody: newTCPRequestBody([]byte("test2")), - warpRoutingService: ingress.NewWarpRoutingService(), + warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting), connectionType: connection.TypeTCP, requestHeaders: map[string][]string{ "Cf-Cloudflared-Proxy-Src": {"non-blank-value"}, @@ -526,7 +531,7 @@ func TestConnections(t *testing.T) { originService: runEchoWSService, // eyeballResponseWriter gets set after roundtrip dial. eyeballRequestBody: newPipedWSRequestBody([]byte("test3")), - warpRoutingService: ingress.NewWarpRoutingService(), + warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting), requestHeaders: map[string][]string{ "Cf-Cloudflared-Proxy-Src": {"non-blank-value"}, }, @@ -652,7 +657,7 @@ func TestConnections(t *testing.T) { ingressRule := createSingleIngressConfig(t, test.args.ingressServiceScheme+ln.Addr().String()) ingressRule.StartOrigins(logger, ctx.Done()) - proxy := NewOriginProxy(ingressRule, true, testTags, logger) + proxy := NewOriginProxy(ingressRule, testWarpRouting, testTags, logger) proxy.warpRouting = test.args.warpRoutingService dest := ln.Addr().String() From ab81ff8bfb05dc9f7ec592cfa95ebcc6e7ce5473 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 14 Jun 2022 16:07:27 -0700 Subject: [PATCH 113/238] Revert "TUN-6010: Add component tests for --edge-ip-version" This reverts commit 978e01f77eedf4f098f71423b42e639d12355015. --- component-tests/cli.py | 95 -------------- component-tests/test_edge_discovery.py | 165 ------------------------- component-tests/test_service.py | 8 +- component-tests/util.py | 18 --- 4 files changed, 7 insertions(+), 279 deletions(-) delete mode 100644 component-tests/cli.py delete mode 100644 component-tests/test_edge_discovery.py diff --git a/component-tests/cli.py b/component-tests/cli.py deleted file mode 100644 index ae4a1b12..00000000 --- a/component-tests/cli.py +++ /dev/null @@ -1,95 +0,0 @@ -import json -import subprocess -from time import sleep - -from setup import get_config_from_file - -SINGLE_CASE_TIMEOUT = 600 - -class CloudflaredCli: - def __init__(self, config, config_path, logger): - self.basecmd = [config.cloudflared_binary, "tunnel"] - if config_path is not None: - self.basecmd += ["--config", str(config_path)] - origincert = get_config_from_file()["origincert"] - if origincert: - self.basecmd += ["--origincert", origincert] - self.logger = logger - - def _run_command(self, subcmd, subcmd_name, needs_to_pass=True): - cmd = self.basecmd + subcmd - # timeout limits the time a subprocess can run. This is useful to guard against running a tunnel when - # command/args are in wrong order. - result = run_subprocess(cmd, subcmd_name, self.logger, check=needs_to_pass, capture_output=True, timeout=15) - return result - - def list_tunnels(self): - cmd_args = ["list", "--output", "json"] - listed = self._run_command(cmd_args, "list") - return json.loads(listed.stdout) - - def get_tunnel_info(self, tunnel_id): - info = self._run_command(["info", "--output", "json", tunnel_id], "info") - return json.loads(info.stdout) - - def __enter__(self): - self.basecmd += ["run"] - self.process = subprocess.Popen(self.basecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - self.logger.info(f"Run cmd {self.basecmd}") - return self.process - - def __exit__(self, exc_type, exc_value, exc_traceback): - terminate_gracefully(self.process, self.logger, self.basecmd) - self.logger.debug(f"{self.basecmd} logs: {self.process.stderr.read()}") - - -def terminate_gracefully(process, logger, cmd): - process.terminate() - process_terminated = wait_for_terminate(process) - if not process_terminated: - process.kill() - logger.warning(f"{cmd}: cloudflared did not terminate within wait period. Killing process. logs: \ - stdout: {process.stdout.read()}, stderr: {process.stderr.read()}") - - -def wait_for_terminate(opened_subprocess, attempts=10, poll_interval=1): - """ - wait_for_terminate polls the opened_subprocess every x seconds for a given number of attempts. - It returns true if the subprocess was terminated and false if it didn't. - """ - for _ in range(attempts): - if _is_process_stopped(opened_subprocess): - return True - sleep(poll_interval) - return False - - -def _is_process_stopped(process): - return process.poll() is not None - - -def cert_path(): - return get_config_from_file()["origincert"] - - -class SubprocessError(Exception): - def __init__(self, program, exit_code, cause): - self.program = program - self.exit_code = exit_code - self.cause = cause - - -def run_subprocess(cmd, cmd_name, logger, timeout=SINGLE_CASE_TIMEOUT, **kargs): - kargs["timeout"] = timeout - try: - result = subprocess.run(cmd, **kargs) - logger.debug(f"{cmd} log: {result.stdout}", extra={"cmd": cmd_name}) - return result - except subprocess.CalledProcessError as e: - err = f"{cmd} return exit code {e.returncode}, stderr" + e.stderr.decode("utf-8") - logger.error(err, extra={"cmd": cmd_name, "return_code": e.returncode}) - raise SubprocessError(cmd[0], e.returncode, e) - except subprocess.TimeoutExpired as e: - err = f"{cmd} timeout after {e.timeout} seconds, stdout: {e.stdout}, stderr: {e.stderr}" - logger.error(err, extra={"cmd": cmd_name, "return_code": "timeout"}) - raise e \ No newline at end of file diff --git a/component-tests/test_edge_discovery.py b/component-tests/test_edge_discovery.py deleted file mode 100644 index 61e036a3..00000000 --- a/component-tests/test_edge_discovery.py +++ /dev/null @@ -1,165 +0,0 @@ -import ipaddress -import socket - -import pytest - -from constants import protocols -from cli import CloudflaredCli -from util import get_tunnel_connector_id, LOGGER, wait_tunnel_ready, write_config - - -class TestEdgeDiscovery: - def _extra_config(self, protocol, edge_ip_version): - config = { - "protocol": protocol, - } - if edge_ip_version: - config["edge-ip-version"] = edge_ip_version - return config - - @pytest.mark.parametrize("protocol", protocols()) - def test_default_only(self, tmp_path, component_tests_config, protocol): - """ - This test runs a tunnel to connect via IPv4-only edge addresses (default is unset "--edge-ip-version 4") - """ - if self.has_ipv6_only(): - pytest.skip("Host has IPv6 only support and current default is IPv4 only") - self.expect_address_connections( - tmp_path, component_tests_config, protocol, None, self.expect_ipv4_address) - - @pytest.mark.parametrize("protocol", protocols()) - def test_ipv4_only(self, tmp_path, component_tests_config, protocol): - """ - This test runs a tunnel to connect via IPv4-only edge addresses - """ - if self.has_ipv6_only(): - pytest.skip("Host has IPv6 only support") - self.expect_address_connections( - tmp_path, component_tests_config, protocol, "4", self.expect_ipv4_address) - - @pytest.mark.parametrize("protocol", protocols()) - def test_ipv6_only(self, tmp_path, component_tests_config, protocol): - """ - This test runs a tunnel to connect via IPv6-only edge addresses - """ - if self.has_ipv4_only(): - pytest.skip("Host has IPv4 only support") - self.expect_address_connections( - tmp_path, component_tests_config, protocol, "6", self.expect_ipv6_address) - - @pytest.mark.parametrize("protocol", protocols()) - def test_auto_ip64(self, tmp_path, component_tests_config, protocol): - """ - This test runs a tunnel to connect via auto with a preference of IPv6 then IPv4 addresses for a dual stack host - - This test also assumes that the host has IPv6 preference. - """ - if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET6): - pytest.skip("Host does not support dual stack with IPv6 preference") - self.expect_address_connections( - tmp_path, component_tests_config, protocol, "auto", self.expect_ipv6_address) - - @pytest.mark.parametrize("protocol", protocols()) - def test_auto_ip46(self, tmp_path, component_tests_config, protocol): - """ - This test runs a tunnel to connect via auto with a preference of IPv4 then IPv6 addresses for a dual stack host - - This test also assumes that the host has IPv4 preference. - """ - if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET): - pytest.skip("Host does not support dual stack with IPv4 preference") - self.expect_address_connections( - tmp_path, component_tests_config, protocol, "auto", self.expect_ipv4_address) - - def expect_address_connections(self, tmp_path, component_tests_config, protocol, edge_ip_version, assert_address_type): - config = component_tests_config( - self._extra_config(protocol, edge_ip_version)) - config_path = write_config(tmp_path, config.full_config) - LOGGER.debug(config) - with CloudflaredCli(config, config_path, LOGGER): - wait_tunnel_ready(tunnel_url=config.get_url(), - require_min_connections=4) - cfd_cli = CloudflaredCli(config, config_path, LOGGER) - tunnel_id = config.get_tunnel_id() - info = cfd_cli.get_tunnel_info(tunnel_id) - connector_id = get_tunnel_connector_id() - connector = next( - (c for c in info["conns"] if c["id"] == connector_id), None) - assert connector, f"Expected connection info from get tunnel info for the connected instance: {info}" - conns = connector["conns"] - assert conns == None or len( - conns) == 4, f"There should be 4 connections registered: {conns}" - for conn in conns: - origin_ip = conn["origin_ip"] - assert origin_ip, f"No available origin_ip for this connection: {conn}" - assert_address_type(origin_ip) - - def expect_ipv4_address(self, address): - assert type(ipaddress.ip_address( - address)) is ipaddress.IPv4Address, f"Expected connection from origin to be a valid IPv4 address: {address}" - - def expect_ipv6_address(self, address): - assert type(ipaddress.ip_address( - address)) is ipaddress.IPv6Address, f"Expected connection from origin to be a valid IPv6 address: {address}" - - def get_addresses(self): - """ - Returns a list of addresses for the host. - """ - host_addresses = socket.getaddrinfo( - "region1.v2.argotunnel.com", 7844, socket.AF_UNSPEC, socket.SOCK_STREAM) - assert len( - host_addresses) > 0, "No addresses returned from getaddrinfo" - return host_addresses - - def has_dual_stack(self, address_family_preference=None): - """ - Returns true if the host has dual stack support and can optionally check - the provided IP family preference. - """ - dual_stack = not self.has_ipv6_only() and not self.has_ipv4_only() - if address_family_preference: - address = self.get_addresses()[0] - return dual_stack and address[0] == address_family_preference - - return dual_stack - - def has_ipv6_only(self): - """ - Returns True if the host has only IPv6 address support. - """ - return self.attempt_connection(socket.AddressFamily.AF_INET6) and not self.attempt_connection(socket.AddressFamily.AF_INET) - - def has_ipv4_only(self): - """ - Returns True if the host has only IPv4 address support. - """ - return self.attempt_connection(socket.AddressFamily.AF_INET) and not self.attempt_connection(socket.AddressFamily.AF_INET6) - - def attempt_connection(self, address_family): - """ - Returns True if a successful socket connection can be made to the - remote host with the provided address family to validate host support - for the provided address family. - """ - address = None - for a in self.get_addresses(): - if a[0] == address_family: - address = a - break - if address is None: - # Couldn't even lookup the address family so we can't connect - return False - af, socktype, proto, canonname, sockaddr = address - s = None - try: - s = socket.socket(af, socktype, proto) - except OSError: - return False - try: - s.connect(sockaddr) - except OSError: - s.close() - return False - s.close() - return True diff --git a/component-tests/test_service.py b/component-tests/test_service.py index e1c155e6..f8ace641 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import os import pathlib +import platform import subprocess from contextlib import contextmanager from pathlib import Path @@ -9,7 +10,12 @@ import pytest import test_logging from conftest import CfdModes -from util import select_platform, start_cloudflared, wait_tunnel_ready, write_config +from util import start_cloudflared, wait_tunnel_ready, write_config + + +def select_platform(plat): + return pytest.mark.skipif( + platform.system() != plat, reason=f"Only runs on {plat}") def default_config_dir(): diff --git a/component-tests/util.py b/component-tests/util.py index aae9b85f..db8f925d 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -1,12 +1,9 @@ import logging import os -import platform import subprocess from contextlib import contextmanager from time import sleep -import pytest - import requests import yaml from retrying import retry @@ -15,10 +12,6 @@ from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS LOGGER = logging.getLogger(__name__) -def select_platform(plat): - return pytest.mark.skipif( - platform.system() != plat, reason=f"Only runs on {plat}") - def write_config(directory, config): config_path = directory / "config.yml" @@ -118,17 +111,6 @@ def check_tunnel_not_connected(): LOGGER.warning(f"Failed to connect to {url}, error: {e}") -def get_tunnel_connector_id(): - url = f'http://localhost:{METRICS_PORT}/ready' - - try: - resp = requests.get(url, timeout=1) - return resp.json()["connectorId"] - # cloudflared might already terminated - except requests.exceptions.ConnectionError as e: - LOGGER.warning(f"Failed to connect to {url}, error: {e}") - - # In some cases we don't need to check response status, such as when sending batch requests to generate logs def send_requests(url, count, require_ok=True): errors = 0 From b9453b84bb15c9c78dd8f405a771d474036e61ec Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 14 Jun 2022 16:07:37 -0700 Subject: [PATCH 114/238] Revert "TUN-6373: Add edge-ip-version to remotely pushed configuration" This reverts commit 8e9091cc483e83a14845148faa6353ef77638334. --- cmd/cloudflared/tunnel/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 36257cc7..1e46b487 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -45,7 +45,7 @@ var ( secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} - configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq", "edge-ip-version"} + configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq"} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories From 0458ad41ddcb54e01d03a1ae7d52825c89a4d7e1 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 14 Jun 2022 16:07:47 -0700 Subject: [PATCH 115/238] Revert "TUN-6384: Correct duplicate connection error to fetch new IP first" This reverts commit 76add5ca77a5d9fd1e474b325952ae7c678e44f1. --- supervisor/tunnel.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index fcbace54..8d3fe9f0 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -141,11 +141,10 @@ type DefaultAddrFallback struct { func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { switch err.(type) { case nil: // maintain current IP address - // DupConnRegisterTunnelError should indicate to get a new address immediately - case connection.DupConnRegisterTunnelError: - return true, false - // Try the next address if it was a quic.IdleTimeoutError + // Try the next address if it was a quic.IdleTimeoutError or + // dupConnRegisterTunnelError case *quic.IdleTimeoutError, + connection.DupConnRegisterTunnelError, edgediscovery.DialError, *connection.EdgeQuicDialError: // Wait for two failures before falling back to a new address From 1d79831651b348aa8c0d28f6e431a5ec649fb0ef Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 14 Jun 2022 16:08:03 -0700 Subject: [PATCH 116/238] Revert "TUN-6007: Implement new edge discovery algorithm" This reverts commit 4f468b8a5da8f89f40df9ae99927417331cf9763. --- connection/errors.go | 9 - connection/quic.go | 2 +- edgediscovery/allregions/address.go | 64 --- edgediscovery/allregions/address_test.go | 247 --------- edgediscovery/allregions/discovery.go | 6 +- edgediscovery/allregions/mocks_for_test.go | 109 ---- edgediscovery/allregions/region.go | 166 ++---- edgediscovery/allregions/region_test.go | 585 +++++++++------------ edgediscovery/allregions/regions.go | 26 +- edgediscovery/allregions/regions_test.go | 269 ++++------ edgediscovery/edgediscovery.go | 45 +- edgediscovery/edgediscovery_test.go | 102 +--- supervisor/supervisor.go | 124 +++-- supervisor/tunnel.go | 217 +++----- 14 files changed, 590 insertions(+), 1381 deletions(-) delete mode 100644 edgediscovery/allregions/address.go delete mode 100644 edgediscovery/allregions/address_test.go diff --git a/connection/errors.go b/connection/errors.go index 10e3b5d6..df3cfe97 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -18,15 +18,6 @@ func (e DupConnRegisterTunnelError) Error() string { return "already connected to this server, trying another address" } -// Dial to edge server with quic failed -type EdgeQuicDialError struct { - Cause error -} - -func (e *EdgeQuicDialError) Error() string { - return "failed to dial to edge with quic: " + e.Cause.Error() -} - // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/connection/quic.go b/connection/quic.go index 819d9ec7..00765651 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -57,7 +57,7 @@ func NewQUICConnection( ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { - return nil, &EdgeQuicDialError{Cause: err} + return nil, fmt.Errorf("failed to dial to edge: %w", err) } datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) diff --git a/edgediscovery/allregions/address.go b/edgediscovery/allregions/address.go deleted file mode 100644 index 36bf2d48..00000000 --- a/edgediscovery/allregions/address.go +++ /dev/null @@ -1,64 +0,0 @@ -package allregions - -// Region contains cloudflared edge addresses. The edge is partitioned into several regions for -// redundancy purposes. -type AddrSet map[*EdgeAddr]UsedBy - -// AddrUsedBy finds the address used by the given connection in this region. -// Returns nil if the connection isn't using any IP. -func (a AddrSet) AddrUsedBy(connID int) *EdgeAddr { - for addr, used := range a { - if used.Used && used.ConnID == connID { - return addr - } - } - return nil -} - -// AvailableAddrs counts how many unused addresses this region contains. -func (a AddrSet) AvailableAddrs() int { - n := 0 - for _, usedby := range a { - if !usedby.Used { - n++ - } - } - return n -} - -// GetUnusedIP returns a random unused address in this region. -// Returns nil if all addresses are in use. -func (a AddrSet) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { - for addr, usedby := range a { - if !usedby.Used && addr != excluding { - return addr - } - } - return nil -} - -// Use the address, assigning it to a proxy connection. -func (a AddrSet) Use(addr *EdgeAddr, connID int) { - if addr == nil { - return - } - a[addr] = InUse(connID) -} - -// GetAnyAddress returns an arbitrary address from the region. -func (a AddrSet) GetAnyAddress() *EdgeAddr { - for addr := range a { - return addr - } - return nil -} - -// GiveBack the address, ensuring it is no longer assigned to an IP. -// Returns true if the address is in this region. -func (a AddrSet) GiveBack(addr *EdgeAddr) (ok bool) { - if _, ok := a[addr]; !ok { - return false - } - a[addr] = Unused() - return true -} diff --git a/edgediscovery/allregions/address_test.go b/edgediscovery/allregions/address_test.go deleted file mode 100644 index 9c8d9289..00000000 --- a/edgediscovery/allregions/address_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package allregions - -import ( - "reflect" - "testing" -) - -func TestAddrSet_AddrUsedBy(t *testing.T) { - type args struct { - connID int - } - tests := []struct { - name string - addrSet AddrSet - args args - want *EdgeAddr - }{ - { - name: "happy trivial test", - addrSet: AddrSet{ - &addr0: InUse(0), - }, - args: args{connID: 0}, - want: &addr0, - }, - { - name: "sad trivial test", - addrSet: AddrSet{ - &addr0: InUse(0), - }, - args: args{connID: 1}, - want: nil, - }, - { - name: "sad test", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }, - args: args{connID: 3}, - want: nil, - }, - { - name: "happy test", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }, - args: args{connID: 1}, - want: &addr1, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.addrSet.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestAddrSet_AvailableAddrs(t *testing.T) { - tests := []struct { - name string - addrSet AddrSet - want int - }{ - { - name: "contains addresses", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }, - want: 1, - }, - { - name: "all free", - addrSet: AddrSet{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: Unused(), - }, - want: 3, - }, - { - name: "all used", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }, - want: 0, - }, - { - name: "empty", - addrSet: AddrSet{}, - want: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.addrSet.AvailableAddrs(); got != tt.want { - t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestAddrSet_GetUnusedIP(t *testing.T) { - type args struct { - excluding *EdgeAddr - } - tests := []struct { - name string - addrSet AddrSet - args args - want *EdgeAddr - }{ - { - name: "happy test with excluding set", - addrSet: AddrSet{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: InUse(2), - }, - args: args{excluding: &addr0}, - want: &addr1, - }, - { - name: "happy test with no excluding", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }, - args: args{excluding: nil}, - want: &addr1, - }, - { - name: "sad test with no excluding", - addrSet: AddrSet{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }, - args: args{excluding: nil}, - want: nil, - }, - { - name: "sad test with excluding", - addrSet: AddrSet{ - &addr0: Unused(), - &addr1: InUse(1), - &addr2: InUse(2), - }, - args: args{excluding: &addr0}, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.addrSet.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestAddrSet_GiveBack(t *testing.T) { - type args struct { - addr *EdgeAddr - } - tests := []struct { - name string - addrSet AddrSet - args args - wantOk bool - availableAfter int - }{ - { - name: "sad test with excluding", - addrSet: AddrSet{ - &addr1: InUse(1), - }, - args: args{addr: &addr1}, - wantOk: true, - availableAfter: 1, - }, - { - name: "sad test with excluding", - addrSet: AddrSet{ - &addr1: InUse(1), - }, - args: args{addr: &addr2}, - wantOk: false, - availableAfter: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if gotOk := tt.addrSet.GiveBack(tt.args.addr); gotOk != tt.wantOk { - t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) - } - if tt.availableAfter != tt.addrSet.AvailableAddrs() { - t.Errorf("Region.AvailableAddrs() = %v, want %v", tt.addrSet.AvailableAddrs(), tt.availableAfter) - } - }) - } -} - -func TestAddrSet_GetAnyAddress(t *testing.T) { - tests := []struct { - name string - addrSet AddrSet - wantNil bool - }{ - { - name: "Sad test -- GetAnyAddress should only fail if the region is empty", - addrSet: AddrSet{}, - wantNil: true, - }, - { - name: "Happy test (all addresses unused)", - addrSet: AddrSet{ - &addr0: Unused(), - }, - wantNil: false, - }, - { - name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", - addrSet: AddrSet{ - &addr0: InUse(2), - }, - wantNil: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.addrSet.GetAnyAddress(); tt.wantNil != (got == nil) { - t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) - } - }) - } -} diff --git a/edgediscovery/allregions/discovery.go b/edgediscovery/allregions/discovery.go index 35f464f8..e6a4103b 100644 --- a/edgediscovery/allregions/discovery.go +++ b/edgediscovery/allregions/discovery.go @@ -13,7 +13,7 @@ import ( const ( // Used to discover HA origintunneld servers - srvService = "v2-origintunneld" + srvService = "origintunneld" srvProto = "tcp" srvName = "argotunnel.com" @@ -115,9 +115,6 @@ func edgeDiscovery(log *zerolog.Logger, srvService string) ([][]*EdgeAddr, error if err != nil { return nil, err } - for _, e := range edgeAddrs { - log.Debug().Msgf("Edge Address: %+v", *e) - } resolvedAddrPerCNAME = append(resolvedAddrPerCNAME, edgeAddrs) } @@ -190,6 +187,7 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) { UDP: udpAddr, IPVersion: version, }) + } return } diff --git a/edgediscovery/allregions/mocks_for_test.go b/edgediscovery/allregions/mocks_for_test.go index 51ff746c..ab99c94f 100644 --- a/edgediscovery/allregions/mocks_for_test.go +++ b/edgediscovery/allregions/mocks_for_test.go @@ -9,115 +9,6 @@ import ( "testing/quick" ) -var ( - v4Addrs = []*EdgeAddr{&addr0, &addr1, &addr2, &addr3} - v6Addrs = []*EdgeAddr{&addr4, &addr5, &addr6, &addr7} - addr0 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - IPVersion: V4, - } - addr1 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - IPVersion: V4, - } - addr2 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - IPVersion: V4, - } - addr3 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - IPVersion: V4, - } - addr4 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::1"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::1"), - Port: 8000, - Zone: "", - }, - IPVersion: V6, - } - addr5 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::2"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::2"), - Port: 8000, - Zone: "", - }, - IPVersion: V6, - } - addr6 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::3"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::3"), - Port: 8000, - Zone: "", - }, - IPVersion: V6, - } - addr7 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::4"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::4"), - Port: 8000, - Zone: "", - }, - IPVersion: V6, - } -) - type mockAddrs struct { // a set of synthetic SRV records addrMap map[net.SRV][]*EdgeAddr diff --git a/edgediscovery/allregions/region.go b/edgediscovery/allregions/region.go index e0ad3ab4..4d268c77 100644 --- a/edgediscovery/allregions/region.go +++ b/edgediscovery/allregions/region.go @@ -1,155 +1,79 @@ package allregions -import "time" - -const ( - timeoutDuration = 10 * time.Minute -) - // Region contains cloudflared edge addresses. The edge is partitioned into several regions for // redundancy purposes. type Region struct { - primaryIsActive bool - active AddrSet - primary AddrSet - secondary AddrSet - primaryTimeout time.Time - timeoutDuration time.Duration + connFor map[*EdgeAddr]UsedBy } // NewRegion creates a region with the given addresses, which are all unused. -func NewRegion(addrs []*EdgeAddr, overrideIPVersion ConfigIPVersion) Region { +func NewRegion(addrs []*EdgeAddr) Region { // The zero value of UsedBy is Unused(), so we can just initialize the map's values with their // zero values. - connForv4 := make(AddrSet) - connForv6 := make(AddrSet) - systemPreference := V6 - for i, addr := range addrs { - if i == 0 { - // First family of IPs returned is system preference of IP - systemPreference = addr.IPVersion - } - switch addr.IPVersion { - case V4: - connForv4[addr] = Unused() - case V6: - connForv6[addr] = Unused() - } + connFor := make(map[*EdgeAddr]UsedBy) + for _, addr := range addrs { + connFor[addr] = Unused() } - - // Process as system preference - var primary AddrSet - var secondary AddrSet - switch systemPreference { - case V4: - primary = connForv4 - secondary = connForv6 - case V6: - primary = connForv6 - secondary = connForv4 - } - - // Override with provided preference - switch overrideIPVersion { - case IPv4Only: - primary = connForv4 - secondary = make(AddrSet) // empty - case IPv6Only: - primary = connForv6 - secondary = make(AddrSet) // empty - case Auto: - // no change - default: - // no change - } - return Region{ - primaryIsActive: true, - active: primary, - primary: primary, - secondary: secondary, - timeoutDuration: timeoutDuration, + connFor: connFor, } } // AddrUsedBy finds the address used by the given connection in this region. // Returns nil if the connection isn't using any IP. func (r *Region) AddrUsedBy(connID int) *EdgeAddr { - edgeAddr := r.primary.AddrUsedBy(connID) - if edgeAddr == nil { - edgeAddr = r.secondary.AddrUsedBy(connID) + for addr, used := range r.connFor { + if used.Used && used.ConnID == connID { + return addr + } } - return edgeAddr + return nil } // AvailableAddrs counts how many unused addresses this region contains. func (r Region) AvailableAddrs() int { - return r.active.AvailableAddrs() + n := 0 + for _, usedby := range r.connFor { + if !usedby.Used { + n++ + } + } + return n } -// AssignAnyAddress returns a random unused address in this region now -// assigned to the connID excluding the provided EdgeAddr. -// Returns nil if all addresses are in use for the region. -func (r Region) AssignAnyAddress(connID int, excluding *EdgeAddr) *EdgeAddr { - if addr := r.active.GetUnusedIP(excluding); addr != nil { - r.active.Use(addr, connID) +// GetUnusedIP returns a random unused address in this region. +// Returns nil if all addresses are in use. +func (r Region) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { + for addr, usedby := range r.connFor { + if !usedby.Used && addr != excluding { + return addr + } + } + return nil +} + +// Use the address, assigning it to a proxy connection. +func (r Region) Use(addr *EdgeAddr, connID int) { + if addr == nil { + return + } + r.connFor[addr] = InUse(connID) +} + +// GetAnyAddress returns an arbitrary address from the region. +func (r Region) GetAnyAddress() *EdgeAddr { + for addr := range r.connFor { return addr } return nil } -// GetAnyAddress returns an arbitrary address from the region. -func (r Region) GetAnyAddress() *EdgeAddr { - return r.active.GetAnyAddress() -} - // GiveBack the address, ensuring it is no longer assigned to an IP. // Returns true if the address is in this region. -func (r *Region) GiveBack(addr *EdgeAddr, hasConnectivityError bool) (ok bool) { - if ok = r.primary.GiveBack(addr); !ok { - // Attempt to give back the address in the secondary set - if ok = r.secondary.GiveBack(addr); !ok { - // Address is not in this region - return - } +func (r Region) GiveBack(addr *EdgeAddr) (ok bool) { + if _, ok := r.connFor[addr]; !ok { + return false } - - // No connectivity error: no worry - if !hasConnectivityError { - return - } - - // If using primary and returned address is IPv6 and secondary is available - if r.primaryIsActive && addr.IPVersion == V6 && len(r.secondary) > 0 { - r.active = r.secondary - r.primaryIsActive = false - r.primaryTimeout = time.Now().Add(r.timeoutDuration) - return - } - - // Do nothing for IPv4 or if secondary is empty - if r.primaryIsActive { - return - } - - // Immediately return to primary pool, regardless of current primary timeout - if addr.IPVersion == V4 { - activatePrimary(r) - return - } - - // Timeout exceeded and can be reset to primary pool - if r.primaryTimeout.Before(time.Now()) { - activatePrimary(r) - return - } - - return -} - -// activatePrimary sets the primary set to the active set and resets the timeout. -func activatePrimary(r *Region) { - r.active = r.primary - r.primaryIsActive = true - r.primaryTimeout = time.Now() // reset timeout + r.connFor[addr] = Unused() + return true } diff --git a/edgediscovery/allregions/region_test.go b/edgediscovery/allregions/region_test.go index e8d230c4..d83dea61 100644 --- a/edgediscovery/allregions/region_test.go +++ b/edgediscovery/allregions/region_test.go @@ -1,357 +1,284 @@ package allregions import ( - "net" + "reflect" "testing" - "time" - - "github.com/stretchr/testify/assert" ) -func makeAddrSet(addrs []*EdgeAddr) AddrSet { - addrSet := make(AddrSet, len(addrs)) - for _, addr := range addrs { - addrSet[addr] = Unused() - } - return addrSet -} - func TestRegion_New(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - expectedAddrs int - primary AddrSet - secondary AddrSet - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: AddrSet{}, - }, - { - name: "IPv6 addresses with IPv4Only", - addrs: v6Addrs, - mode: IPv4Only, - expectedAddrs: 0, - primary: AddrSet{}, - secondary: AddrSet{}, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: AddrSet{}, - }, - { - name: "IPv6 addresses with IPv4Only", - addrs: v6Addrs, - mode: IPv4Only, - expectedAddrs: 0, - primary: AddrSet{}, - secondary: AddrSet{}, - }, - { - name: "IPv4 (first) and IPv6 addresses with Auto", - addrs: append(v4Addrs, v6Addrs...), - mode: Auto, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: makeAddrSet(v6Addrs), - }, - { - name: "IPv6 (first) and IPv4 addresses with Auto", - addrs: append(v6Addrs, v4Addrs...), - mode: Auto, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: makeAddrSet(v4Addrs), - }, - { - name: "IPv4 addresses with Auto", - addrs: v4Addrs, - mode: Auto, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: AddrSet{}, - }, - { - name: "IPv6 addresses with Auto", - addrs: v6Addrs, - mode: Auto, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: AddrSet{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - assert.Equal(t, tt.expectedAddrs, r.AvailableAddrs()) - assert.Equal(t, tt.primary, r.primary) - assert.Equal(t, tt.secondary, r.secondary) - }) + r := NewRegion([]*EdgeAddr{&addr0, &addr1, &addr2}) + if r.AvailableAddrs() != 3 { + t.Errorf("r.AvailableAddrs() == %v but want 3", r.AvailableAddrs()) } } -func TestRegion_AnyAddress_EmptyActiveSet(t *testing.T) { +func TestRegion_AddrUsedBy(t *testing.T) { + type fields struct { + connFor map[*EdgeAddr]UsedBy + } + type args struct { + connID int + } tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion + name string + fields fields + args args + want *EdgeAddr }{ { - name: "IPv6 addresses with IPv4Only", - addrs: v6Addrs, - mode: IPv4Only, + name: "happy trivial test", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + }}, + args: args{connID: 0}, + want: &addr0, }, { - name: "IPv4 addresses with IPv6Only", - addrs: v4Addrs, - mode: IPv6Only, + name: "sad trivial test", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + }}, + args: args{connID: 1}, + want: nil, + }, + { + name: "sad test", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }}, + args: args{connID: 3}, + want: nil, + }, + { + name: "happy test", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }}, + args: args{connID: 1}, + want: &addr1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - addr := r.GetAnyAddress() - assert.Nil(t, addr) - addr = r.AssignAnyAddress(0, nil) - assert.Nil(t, addr) - }) - } -} - -func TestRegion_AssignAnyAddress_FullyUsedActiveSet(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - total := r.active.AvailableAddrs() - for i := 0; i < total; i++ { - addr := r.AssignAnyAddress(i, nil) - assert.NotNil(t, addr) + r := &Region{ + connFor: tt.fields.connFor, } - addr := r.AssignAnyAddress(9, nil) - assert.Nil(t, addr) - }) - } -} - -var giveBackTests = []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - expectedAddrs int - primary AddrSet - secondary AddrSet - primarySwap bool -}{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: AddrSet{}, - primarySwap: false, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: AddrSet{}, - primarySwap: false, - }, - { - name: "IPv4 (first) and IPv6 addresses with Auto", - addrs: append(v4Addrs, v6Addrs...), - mode: Auto, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: makeAddrSet(v6Addrs), - primarySwap: false, - }, - { - name: "IPv6 (first) and IPv4 addresses with Auto", - addrs: append(v6Addrs, v4Addrs...), - mode: Auto, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: makeAddrSet(v4Addrs), - primarySwap: true, - }, - { - name: "IPv4 addresses with Auto", - addrs: v4Addrs, - mode: Auto, - expectedAddrs: len(v4Addrs), - primary: makeAddrSet(v4Addrs), - secondary: AddrSet{}, - primarySwap: false, - }, - { - name: "IPv6 addresses with Auto", - addrs: v6Addrs, - mode: Auto, - expectedAddrs: len(v6Addrs), - primary: makeAddrSet(v6Addrs), - secondary: AddrSet{}, - primarySwap: false, - }, -} - -func TestRegion_GiveBack_NoConnectivityError(t *testing.T) { - for _, tt := range giveBackTests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - addr := r.AssignAnyAddress(0, nil) - assert.NotNil(t, addr) - assert.True(t, r.GiveBack(addr, false)) - }) - } -} - -func TestRegion_GiveBack_ForeignAddr(t *testing.T) { - invalid := EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - IPVersion: V4, - } - for _, tt := range giveBackTests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - assert.False(t, r.GiveBack(&invalid, false)) - assert.False(t, r.GiveBack(&invalid, true)) - }) - } -} - -func TestRegion_GiveBack_SwapPrimary(t *testing.T) { - for _, tt := range giveBackTests { - t.Run(tt.name, func(t *testing.T) { - r := NewRegion(tt.addrs, tt.mode) - addr := r.AssignAnyAddress(0, nil) - assert.NotNil(t, addr) - assert.True(t, r.GiveBack(addr, true)) - assert.Equal(t, tt.primarySwap, !r.primaryIsActive) - if tt.primarySwap { - assert.Equal(t, r.secondary, r.active) - assert.False(t, r.primaryTimeout.IsZero()) - } else { - assert.Equal(t, r.primary, r.active) - assert.True(t, r.primaryTimeout.IsZero()) + if got := r.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) } }) } } -func TestRegion_GiveBack_IPv4_ResetPrimary(t *testing.T) { - r := NewRegion(append(v6Addrs, v4Addrs...), Auto) - // Exhaust all IPv6 addresses - a0 := r.AssignAnyAddress(0, nil) - a1 := r.AssignAnyAddress(1, nil) - a2 := r.AssignAnyAddress(2, nil) - a3 := r.AssignAnyAddress(3, nil) - assert.NotNil(t, a0) - assert.NotNil(t, a1) - assert.NotNil(t, a2) - assert.NotNil(t, a3) - // Give back the first IPv6 address to fallback to secondary IPv4 address set - assert.True(t, r.GiveBack(a0, true)) - assert.False(t, r.primaryIsActive) - // Give back another IPv6 address - assert.True(t, r.GiveBack(a1, true)) - // Primary shouldn't change - assert.False(t, r.primaryIsActive) - // Request an address (should be IPv4 from secondary) - a4_v4 := r.AssignAnyAddress(4, nil) - assert.NotNil(t, a4_v4) - assert.Equal(t, V4, a4_v4.IPVersion) - a5_v4 := r.AssignAnyAddress(5, nil) - assert.NotNil(t, a5_v4) - assert.Equal(t, V4, a5_v4.IPVersion) - a6_v4 := r.AssignAnyAddress(6, nil) - assert.NotNil(t, a6_v4) - assert.Equal(t, V4, a6_v4.IPVersion) - // Return IPv4 address (without failure) - // Primary shouldn't change because it is not a connectivity failure - assert.True(t, r.GiveBack(a4_v4, false)) - assert.False(t, r.primaryIsActive) - // Return IPv4 address (with failure) - // Primary should change because it is a connectivity failure - assert.True(t, r.GiveBack(a5_v4, true)) - assert.True(t, r.primaryIsActive) - // Return IPv4 address (with failure) - // Primary shouldn't change because the address is returned to the inactive - // secondary address set - assert.True(t, r.GiveBack(a6_v4, true)) - assert.True(t, r.primaryIsActive) - // Return IPv6 address (without failure) - // Primary shoudn't change because it is not a connectivity failure - assert.True(t, r.GiveBack(a2, false)) - assert.True(t, r.primaryIsActive) +func TestRegion_AvailableAddrs(t *testing.T) { + type fields struct { + connFor map[*EdgeAddr]UsedBy + } + tests := []struct { + name string + fields fields + want int + }{ + { + name: "contains addresses", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }}, + want: 1, + }, + { + name: "all free", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: Unused(), + }}, + want: 3, + }, + { + name: "all used", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }}, + want: 0, + }, + { + name: "empty", + fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Region{ + connFor: tt.fields.connFor, + } + if got := r.AvailableAddrs(); got != tt.want { + t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) + } + }) + } } -func TestRegion_GiveBack_Timeout(t *testing.T) { - r := NewRegion(append(v6Addrs, v4Addrs...), Auto) - a0 := r.AssignAnyAddress(0, nil) - a1 := r.AssignAnyAddress(1, nil) - a2 := r.AssignAnyAddress(2, nil) - assert.NotNil(t, a0) - assert.NotNil(t, a1) - assert.NotNil(t, a2) - // Give back IPv6 address to set timeout - assert.True(t, r.GiveBack(a0, true)) - assert.False(t, r.primaryIsActive) - assert.False(t, r.primaryTimeout.IsZero()) - // Request an address (should be IPv4 from secondary) - a3_v4 := r.AssignAnyAddress(3, nil) - assert.NotNil(t, a3_v4) - assert.Equal(t, V4, a3_v4.IPVersion) - assert.False(t, r.primaryIsActive) - // Give back IPv6 address inside timeout (no change) - assert.True(t, r.GiveBack(a2, true)) - assert.False(t, r.primaryIsActive) - assert.False(t, r.primaryTimeout.IsZero()) - // Accelerate timeout - r.primaryTimeout = time.Now().Add(-time.Minute) - // Return IPv6 address - assert.True(t, r.GiveBack(a1, true)) - assert.True(t, r.primaryIsActive) - // Returning an IPv4 address after primary is active shouldn't change primary - // even with a connectivity error - assert.True(t, r.GiveBack(a3_v4, true)) - assert.True(t, r.primaryIsActive) +func TestRegion_GetUnusedIP(t *testing.T) { + type fields struct { + connFor map[*EdgeAddr]UsedBy + } + type args struct { + excluding *EdgeAddr + } + tests := []struct { + name string + fields fields + args args + want *EdgeAddr + }{ + { + name: "happy test with excluding set", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: InUse(2), + }}, + args: args{excluding: &addr0}, + want: &addr1, + }, + { + name: "happy test with no excluding", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }}, + args: args{excluding: nil}, + want: &addr1, + }, + { + name: "sad test with no excluding", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }}, + args: args{excluding: nil}, + want: nil, + }, + { + name: "sad test with excluding", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: Unused(), + &addr1: InUse(1), + &addr2: InUse(2), + }}, + args: args{excluding: &addr0}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Region{ + connFor: tt.fields.connFor, + } + if got := r.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRegion_GiveBack(t *testing.T) { + type fields struct { + connFor map[*EdgeAddr]UsedBy + } + type args struct { + addr *EdgeAddr + } + tests := []struct { + name string + fields fields + args args + wantOk bool + availableAfter int + }{ + { + name: "sad test with excluding", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr1: InUse(1), + }}, + args: args{addr: &addr1}, + wantOk: true, + availableAfter: 1, + }, + { + name: "sad test with excluding", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr1: InUse(1), + }}, + args: args{addr: &addr2}, + wantOk: false, + availableAfter: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Region{ + connFor: tt.fields.connFor, + } + if gotOk := r.GiveBack(tt.args.addr); gotOk != tt.wantOk { + t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) + } + if tt.availableAfter != r.AvailableAddrs() { + t.Errorf("Region.AvailableAddrs() = %v, want %v", r.AvailableAddrs(), tt.availableAfter) + } + }) + } +} + +func TestRegion_GetAnyAddress(t *testing.T) { + type fields struct { + connFor map[*EdgeAddr]UsedBy + } + tests := []struct { + name string + fields fields + wantNil bool + }{ + { + name: "Sad test -- GetAnyAddress should only fail if the region is empty", + fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, + wantNil: true, + }, + { + name: "Happy test (all addresses unused)", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: Unused(), + }}, + wantNil: false, + }, + { + name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", + fields: fields{connFor: map[*EdgeAddr]UsedBy{ + &addr0: InUse(2), + }}, + wantNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Region{ + connFor: tt.fields.connFor, + } + if got := r.GetAnyAddress(); tt.wantNil != (got == nil) { + t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) + } + }) + } } diff --git a/edgediscovery/allregions/regions.go b/edgediscovery/allregions/regions.go index 4b2a841b..cec79240 100644 --- a/edgediscovery/allregions/regions.go +++ b/edgediscovery/allregions/regions.go @@ -18,7 +18,7 @@ type Regions struct { // ------------------------------------ // ResolveEdge resolves the Cloudflare edge, returning all regions discovered. -func ResolveEdge(log *zerolog.Logger, region string, overrideIPVersion ConfigIPVersion) (*Regions, error) { +func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) { edgeAddrs, err := edgeDiscovery(log, getRegionalServiceName(region)) if err != nil { return nil, err @@ -27,8 +27,8 @@ func ResolveEdge(log *zerolog.Logger, region string, overrideIPVersion ConfigIPV return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(edgeAddrs)) } return &Regions{ - region1: NewRegion(edgeAddrs[0], overrideIPVersion), - region2: NewRegion(edgeAddrs[1], overrideIPVersion), + region1: NewRegion(edgeAddrs[0]), + region2: NewRegion(edgeAddrs[1]), }, nil } @@ -56,8 +56,8 @@ func NewNoResolve(addrs []*EdgeAddr) *Regions { } return &Regions{ - region1: NewRegion(region1, Auto), - region2: NewRegion(region2, Auto), + region1: NewRegion(region1), + region2: NewRegion(region2), } } @@ -95,12 +95,14 @@ func (rs *Regions) GetUnusedAddr(excluding *EdgeAddr, connID int) *EdgeAddr { // getAddrs tries to grab address form `first` region, then `second` region // this is an unrolled loop over 2 element array func getAddrs(excluding *EdgeAddr, connID int, first *Region, second *Region) *EdgeAddr { - addr := first.AssignAnyAddress(connID, excluding) + addr := first.GetUnusedIP(excluding) if addr != nil { + first.Use(addr, connID) return addr } - addr = second.AssignAnyAddress(connID, excluding) + addr = second.GetUnusedIP(excluding) if addr != nil { + second.Use(addr, connID) return addr } @@ -114,18 +116,18 @@ func (rs *Regions) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (rs *Regions) GiveBack(addr *EdgeAddr, hasConnectivityError bool) bool { - if found := rs.region1.GiveBack(addr, hasConnectivityError); found { +func (rs *Regions) GiveBack(addr *EdgeAddr) bool { + if found := rs.region1.GiveBack(addr); found { return found } - return rs.region2.GiveBack(addr, hasConnectivityError) + return rs.region2.GiveBack(addr) } // Return regionalized service name if `region` isn't empty, otherwise return the global service name for origintunneld func getRegionalServiceName(region string) string { if region != "" { - return region + "-" + srvService // Example: `us-v2-origintunneld` + return region + "-" + srvService // Example: `us-origintunneld` } - return srvService // Global service is just `v2-origintunneld` + return srvService // Global service is just `origintunneld` } diff --git a/edgediscovery/allregions/regions_test.go b/edgediscovery/allregions/regions_test.go index e399c4ee..d6446df2 100644 --- a/edgediscovery/allregions/regions_test.go +++ b/edgediscovery/allregions/regions_test.go @@ -1,215 +1,134 @@ package allregions import ( + "net" "testing" "github.com/stretchr/testify/assert" ) -func makeRegions(addrs []*EdgeAddr, mode ConfigIPVersion) Regions { - r1addrs := make([]*EdgeAddr, 0) - r2addrs := make([]*EdgeAddr, 0) - for i, addr := range addrs { - if i%2 == 0 { - r1addrs = append(r1addrs, addr) - } else { - r2addrs = append(r2addrs, addr) - } +var ( + addr0 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, } - r1 := NewRegion(r1addrs, mode) - r2 := NewRegion(r2addrs, mode) + addr1 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + } + addr2 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + } + addr3 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + } +) + +func makeRegions() Regions { + r1 := NewRegion([]*EdgeAddr{&addr0, &addr1}) + r2 := NewRegion([]*EdgeAddr{&addr2, &addr3}) return Regions{region1: r1, region2: r2} } func TestRegions_AddrUsedBy(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) - addr1 := rs.GetUnusedAddr(nil, 1) - assert.Equal(t, addr1, rs.AddrUsedBy(1)) - addr2 := rs.GetUnusedAddr(nil, 2) - assert.Equal(t, addr2, rs.AddrUsedBy(2)) - addr3 := rs.GetUnusedAddr(nil, 3) - assert.Equal(t, addr3, rs.AddrUsedBy(3)) - }) - } + rs := makeRegions() + addr1 := rs.GetUnusedAddr(nil, 1) + assert.Equal(t, addr1, rs.AddrUsedBy(1)) + addr2 := rs.GetUnusedAddr(nil, 2) + assert.Equal(t, addr2, rs.AddrUsedBy(2)) + addr3 := rs.GetUnusedAddr(nil, 3) + assert.Equal(t, addr3, rs.AddrUsedBy(3)) } func TestRegions_Giveback_Region1(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) - addr := rs.region1.AssignAnyAddress(0, nil) - rs.region1.AssignAnyAddress(1, nil) - rs.region2.AssignAnyAddress(2, nil) - rs.region2.AssignAnyAddress(3, nil) + rs := makeRegions() + rs.region1.Use(&addr0, 0) + rs.region1.Use(&addr1, 1) + rs.region2.Use(&addr2, 2) + rs.region2.Use(&addr3, 3) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(addr, false) - assert.Equal(t, addr, rs.GetUnusedAddr(nil, 0)) - }) - } + rs.GiveBack(&addr0) + assert.Equal(t, &addr0, rs.GetUnusedAddr(nil, 3)) } func TestRegions_Giveback_Region2(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) - rs.region1.AssignAnyAddress(0, nil) - rs.region1.AssignAnyAddress(1, nil) - addr := rs.region2.AssignAnyAddress(2, nil) - rs.region2.AssignAnyAddress(3, nil) + rs := makeRegions() + rs.region1.Use(&addr0, 0) + rs.region1.Use(&addr1, 1) + rs.region2.Use(&addr2, 2) + rs.region2.Use(&addr3, 3) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(addr, false) - assert.Equal(t, addr, rs.GetUnusedAddr(nil, 2)) - }) - } + rs.GiveBack(&addr2) + assert.Equal(t, &addr2, rs.GetUnusedAddr(nil, 2)) } func TestRegions_GetUnusedAddr_OneAddrLeft(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) - rs.region1.AssignAnyAddress(0, nil) - rs.region1.AssignAnyAddress(1, nil) - rs.region2.AssignAnyAddress(2, nil) - addr := rs.region2.active.GetUnusedIP(nil) + rs := makeRegions() - assert.Equal(t, 1, rs.AvailableAddrs()) - assert.Equal(t, addr, rs.GetUnusedAddr(nil, 3)) - }) - } + rs.region1.Use(&addr0, 0) + rs.region1.Use(&addr1, 1) + rs.region2.Use(&addr2, 2) + + assert.Equal(t, 1, rs.AvailableAddrs()) + assert.Equal(t, &addr3, rs.GetUnusedAddr(nil, 3)) } func TestRegions_GetUnusedAddr_Excluding_Region1(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) + rs := makeRegions() - rs.region1.AssignAnyAddress(0, nil) - rs.region1.AssignAnyAddress(1, nil) - addr := rs.region2.active.GetUnusedIP(nil) - a2 := rs.region2.active.GetUnusedIP(addr) + rs.region1.Use(&addr0, 0) + rs.region1.Use(&addr1, 1) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, addr, rs.GetUnusedAddr(a2, 3)) - }) - } + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, &addr3, rs.GetUnusedAddr(&addr2, 3)) } func TestRegions_GetUnusedAddr_Excluding_Region2(t *testing.T) { - tests := []struct { - name string - addrs []*EdgeAddr - mode ConfigIPVersion - }{ - { - name: "IPv4 addresses with IPv4Only", - addrs: v4Addrs, - mode: IPv4Only, - }, - { - name: "IPv6 addresses with IPv6Only", - addrs: v6Addrs, - mode: IPv6Only, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rs := makeRegions(tt.addrs, tt.mode) + rs := makeRegions() - rs.region2.AssignAnyAddress(0, nil) - rs.region2.AssignAnyAddress(1, nil) - addr := rs.region1.active.GetUnusedIP(nil) - a2 := rs.region1.active.GetUnusedIP(addr) + rs.region2.Use(&addr2, 0) + rs.region2.Use(&addr3, 1) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, addr, rs.GetUnusedAddr(a2, 1)) - }) - } + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, &addr1, rs.GetUnusedAddr(&addr0, 1)) } func TestNewNoResolveBalancesRegions(t *testing.T) { diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index b5f338e8..df9c42cc 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -11,10 +11,9 @@ import ( const ( LogFieldConnIndex = "connIndex" - LogFieldIPAddress = "ip" ) -var ErrNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") +var errNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") // Edge finds addresses on the Cloudflare edge and hands them out to connections. type Edge struct { @@ -29,8 +28,8 @@ type Edge struct { // ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated // to connections. -func ResolveEdge(log *zerolog.Logger, region string, edgeIpVersion allregions.ConfigIPVersion) (*Edge, error) { - regions, err := allregions.ResolveEdge(log, region, edgeIpVersion) +func ResolveEdge(log *zerolog.Logger, region string) (*Edge, error) { + regions, err := allregions.ResolveEdge(log, region) if err != nil { return new(Edge), err } @@ -52,6 +51,15 @@ func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) { }, nil } +// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. +func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { + regions := allregions.NewNoResolve(addrs) + return &Edge{ + log: log, + regions: regions, + } +} + // ------------------------------------ // Methods // ------------------------------------ @@ -62,7 +70,7 @@ func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) { defer ed.Unlock() addr := ed.regions.GetAnyAddress() if addr == nil { - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } return addr, nil } @@ -83,17 +91,14 @@ func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) { addr := ed.regions.GetUnusedAddr(nil, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetAddr: No addresses left to give proxy connection") - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } - log = ed.log.With(). - Int(LogFieldConnIndex, connIndex). - IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() - log.Debug().Msgf("edgediscovery - GetAddr: Giving connection its new address") + log.Debug().Msg("edgediscovery - GetAddr: Giving connection its new address") return addr, nil } // GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one. -func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*allregions.EdgeAddr, error) { +func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { log := ed.log.With().Int(LogFieldConnIndex, connIndex).Logger() ed.Lock() @@ -101,18 +106,16 @@ func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*all oldAddr := ed.regions.AddrUsedBy(connIndex) if oldAddr != nil { - ed.regions.GiveBack(oldAddr, hasConnectivityError) + ed.regions.GiveBack(oldAddr) } addr := ed.regions.GetUnusedAddr(oldAddr, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection") // note: if oldAddr were not nil, it will become available on the next iteration - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } - log = ed.log.With(). - Int(LogFieldConnIndex, connIndex). - IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() - log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address from the address list: %v", ed.regions.AvailableAddrs()) + log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address: %v from the address list: %v", + addr, ed.regions.AvailableAddrs()) return addr, nil } @@ -125,11 +128,9 @@ func (ed *Edge) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (ed *Edge) GiveBack(addr *allregions.EdgeAddr, hasConnectivityError bool) bool { +func (ed *Edge) GiveBack(addr *allregions.EdgeAddr) bool { ed.Lock() defer ed.Unlock() - log := ed.log.With(). - IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() - log.Debug().Msgf("edgediscovery - GiveBack: Address now unused") - return ed.regions.GiveBack(addr, hasConnectivityError) + ed.log.Debug().Msg("edgediscovery - GiveBack: Address now unused") + return ed.regions.GiveBack(addr) } diff --git a/edgediscovery/edgediscovery_test.go b/edgediscovery/edgediscovery_test.go index 55288ce5..9cc93807 100644 --- a/edgediscovery/edgediscovery_test.go +++ b/edgediscovery/edgediscovery_test.go @@ -11,113 +11,56 @@ import ( ) var ( - testLogger = zerolog.Nop() - v4Addrs = []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3} - v6Addrs = []*allregions.EdgeAddr{&addr4, &addr5, &addr6, &addr7} - addr0 = allregions.EdgeAddr{ + addr0 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.0"), + IP: net.ParseIP("123.0.0.0"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.0"), + IP: net.ParseIP("123.0.0.0"), Port: 8000, Zone: "", }, - IPVersion: allregions.V4, } addr1 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.1"), + IP: net.ParseIP("123.0.0.1"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.1"), + IP: net.ParseIP("123.0.0.1"), Port: 8000, Zone: "", }, - IPVersion: allregions.V4, } addr2 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.2"), + IP: net.ParseIP("123.0.0.2"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.2"), + IP: net.ParseIP("123.0.0.2"), Port: 8000, Zone: "", }, - IPVersion: allregions.V4, } addr3 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.3"), + IP: net.ParseIP("123.0.0.3"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.3"), + IP: net.ParseIP("123.0.0.3"), Port: 8000, Zone: "", }, - IPVersion: allregions.V4, - } - addr4 = allregions.EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::1"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::1"), - Port: 8000, - Zone: "", - }, - IPVersion: allregions.V6, - } - addr5 = allregions.EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::2"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::2"), - Port: 8000, - Zone: "", - }, - IPVersion: allregions.V6, - } - addr6 = allregions.EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::3"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::3"), - Port: 8000, - Zone: "", - }, - IPVersion: allregions.V6, - } - addr7 = allregions.EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("2606:4700:a0::4"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("2606:4700:a0::4"), - Port: 8000, - Zone: "", - }, - IPVersion: allregions.V6, } + + testLogger = zerolog.Nop() ) func TestGiveBack(t *testing.T) { @@ -132,7 +75,7 @@ func TestGiveBack(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr, false) + edge.GiveBack(addr) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -164,7 +107,7 @@ func TestGetAddrForRPC(t *testing.T) { assert.Equal(t, 4, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr, false) + edge.GiveBack(addr) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -179,13 +122,13 @@ func TestOnePerRegion(t *testing.T) { assert.NotNil(t, a1) // if the first address is bad, get the second one - a2, err := edge.GetDifferentAddr(connID, false) + a2, err := edge.GetDifferentAddr(connID) assert.NoError(t, err) assert.NotNil(t, a2) assert.NotEqual(t, a1, a2) // now that second one is bad, get the first one again - a3, err := edge.GetDifferentAddr(connID, false) + a3, err := edge.GetDifferentAddr(connID) assert.NoError(t, err) assert.Equal(t, a1, a3) } @@ -201,11 +144,11 @@ func TestOnlyOneAddrLeft(t *testing.T) { assert.NotNil(t, addr) // If that edge address is "bad", there's no alternative address. - _, err = edge.GetDifferentAddr(connID, false) + _, err = edge.GetDifferentAddr(connID) assert.Error(t, err) // previously bad address should become available again on next iteration. - addr, err = edge.GetDifferentAddr(connID, false) + addr, err = edge.GetDifferentAddr(connID) assert.NoError(t, err) assert.NotNil(t, addr) } @@ -247,17 +190,8 @@ func TestGetDifferentAddr(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // If the same connection requests another address, it should get the same one. - addr2, err := edge.GetDifferentAddr(connID, false) + addr2, err := edge.GetDifferentAddr(connID) assert.NoError(t, err) assert.NotEqual(t, addr, addr2) assert.Equal(t, 3, edge.AvailableAddrs()) } - -// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. -func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { - regions := allregions.NewNoResolve(addrs) - return &Edge{ - log: log, - regions: regions, - } -} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 4bec3f56..62bf6ec3 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -7,6 +7,7 @@ import ( "time" "github.com/google/uuid" + "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/connection" @@ -41,7 +42,6 @@ type Supervisor struct { config *TunnelConfig orchestrator *orchestration.Orchestrator edgeIPs *edgediscovery.Edge - edgeTunnelServer EdgeTunnelServer tunnelErrors chan tunnelError tunnelsConnecting map[int]chan struct{} // nextConnectedIndex and nextConnectedSignal are used to wait for all @@ -76,34 +76,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato if len(config.EdgeAddrs) > 0 { edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs) } else { - edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region, config.EdgeIPVersion) + edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region) } if err != nil { return nil, err } - reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) - log := NewConnAwareLogger(config.Log, config.Observer) - - var edgeAddrHandler EdgeAddrHandler - if config.EdgeIPVersion == allregions.IPv6Only || config.EdgeIPVersion == allregions.Auto { - edgeAddrHandler = &IPAddrFallback{} - } else { // IPv4Only - edgeAddrHandler = &DefaultAddrFallback{} - } - - edgeTunnelServer := EdgeTunnelServer{ - config: config, - cloudflaredUUID: cloudflaredUUID, - orchestrator: orchestrator, - credentialManager: reconnectCredentialManager, - edgeAddrs: edgeIPs, - edgeAddrHandler: edgeAddrHandler, - reconnectCh: reconnectCh, - gracefulShutdownC: gracefulShutdownC, - connAwareLogger: log, - } - useReconnectToken := false if config.ClassicTunnel != nil { useReconnectToken = config.ClassicTunnel.UseReconnectToken @@ -114,12 +92,11 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato config: config, orchestrator: orchestrator, edgeIPs: edgeIPs, - edgeTunnelServer: edgeTunnelServer, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, - log: log, + log: NewConnAwareLogger(config.Log, config.Observer), logTransport: config.LogTransport, - reconnectCredentialManager: reconnectCredentialManager, + reconnectCredentialManager: newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections), useReconnectToken: useReconnectToken, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, @@ -166,18 +143,11 @@ func (s *Supervisor) Run( tunnelsActive-- } return nil - // startTunnel completed with a response + // startTunnel returned with error // (note that this may also be caused by context cancellation) case tunnelError := <-s.tunnelErrors: tunnelsActive-- if tunnelError.err != nil && !shuttingDown { - switch tunnelError.err.(type) { - case ReconnectSignal: - // For tunnels that closed with reconnect signal, we reconnect immediately - go s.startTunnel(ctx, tunnelError.index, s.newConnectedTunnelSignal(tunnelError.index)) - tunnelsActive++ - continue - } s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated") tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) s.waitForNextTunnel(tunnelError.index) @@ -185,9 +155,10 @@ func (s *Supervisor) Run( if backoffTimer == nil { backoffTimer = backoff.BackoffTimer() } + + // Previously we'd mark the edge address as bad here, but now we'll just silently use another. } else if tunnelsActive == 0 { - s.log.ConnAwareLogger().Msg("no more connections active and exiting") - // All connected tunnels exited gracefully, no more work to do + // all connected tunnels exited gracefully, no more work to do return nil } // Backoff was set and its timer expired @@ -221,8 +192,6 @@ func (s *Supervisor) Run( } // Returns nil if initialization succeeded, else the initialization error. -// Attempts here will be made to connect one tunnel, if successful, it will -// connect the available tunnels up to config.HAConnections. func (s *Supervisor) initialize( ctx context.Context, connectedSignal *signal.Signal, @@ -234,8 +203,6 @@ func (s *Supervisor) initialize( } go s.startFirstTunnel(ctx, connectedSignal) - - // Wait for response from first tunnel before proceeding to attempt other HA edge tunnels select { case <-ctx.Done(): <-s.tunnelErrors @@ -246,7 +213,6 @@ func (s *Supervisor) initialize( return errEarlyShutdown case <-connectedSignal.Wait(): } - // At least one successful connection, so start the rest for i := 1; i < s.config.HAConnections; i++ { ch := signal.New(make(chan struct{})) @@ -263,42 +229,102 @@ func (s *Supervisor) startFirstTunnel( connectedSignal *signal.Signal, ) { var ( - err error + addr *allregions.EdgeAddr + err error ) const firstConnIndex = 0 defer func() { s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err} }() - err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) + addr, err = s.edgeIPs.GetAddr(firstConnIndex) + if err != nil { + return + } + err = ServeTunnelLoop( + ctx, + s.reconnectCredentialManager, + s.config, + s.orchestrator, + addr, + s.log, + firstConnIndex, + connectedSignal, + s.cloudflaredUUID, + s.reconnectCh, + s.gracefulShutdownC, + ) // If the first tunnel disconnects, keep restarting it. + edgeErrors := 0 for s.unusedIPs() { if ctx.Err() != nil { return } - if err == nil { + switch err.(type) { + case nil: + return + // try the next address if it was a quic.IdleTimeoutError, dialError(network problem) or + // dupConnRegisterTunnelError + case *quic.IdleTimeoutError, edgediscovery.DialError, connection.DupConnRegisterTunnelError: + edgeErrors++ + default: return } - err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) + if edgeErrors >= 2 { + addr, err = s.edgeIPs.GetDifferentAddr(firstConnIndex) + if err != nil { + return + } + } + err = ServeTunnelLoop( + ctx, + s.reconnectCredentialManager, + s.config, + s.orchestrator, + addr, + s.log, + firstConnIndex, + connectedSignal, + s.cloudflaredUUID, + s.reconnectCh, + s.gracefulShutdownC, + ) } } // startTunnel starts a new tunnel connection. The resulting error will be sent on -// s.tunnelError as this is expected to run in a goroutine. +// s.tunnelErrors. func (s *Supervisor) startTunnel( ctx context.Context, index int, connectedSignal *signal.Signal, ) { var ( - err error + addr *allregions.EdgeAddr + err error ) defer func() { s.tunnelErrors <- tunnelError{index: index, err: err} }() - err = s.edgeTunnelServer.Serve(ctx, uint8(index), connectedSignal) + addr, err = s.edgeIPs.GetDifferentAddr(index) + if err != nil { + return + } + err = ServeTunnelLoop( + ctx, + s.reconnectCredentialManager, + s.config, + s.orchestrator, + addr, + s.log, + uint8(index), + connectedSignal, + s.cloudflaredUUID, + s.reconnectCh, + s.gracefulShutdownC, + ) } func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal { diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 8d3fe9f0..a75fe7b7 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -122,84 +122,28 @@ func StartTunnelDaemon( return s.Run(ctx, connectedSignal) } -// EdgeAddrHandler provides a mechanism switch between behaviors in ServeTunnel -// for handling the errors when attempting to make edge connections. -type EdgeAddrHandler interface { - // ShouldGetNewAddress will check the edge connection error and determine if - // the edge address should be replaced with a new one. Also, will return if the - // error should be recognized as a connectivity error, or otherwise, a general - // application error. - ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) -} - -// DefaultAddrFallback will always return false for isConnectivityError since this -// handler is a way to provide the legacy behavior in the new edge discovery algorithm. -type DefaultAddrFallback struct { - edgeErrors int -} - -func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { - switch err.(type) { - case nil: // maintain current IP address - // Try the next address if it was a quic.IdleTimeoutError or - // dupConnRegisterTunnelError - case *quic.IdleTimeoutError, - connection.DupConnRegisterTunnelError, - edgediscovery.DialError, - *connection.EdgeQuicDialError: - // Wait for two failures before falling back to a new address - f.edgeErrors++ - if f.edgeErrors >= 2 { - f.edgeErrors = 0 - return true, false - } - default: // maintain current IP address - } - return false, false -} - -// IPAddrFallback will have more conditions to fall back to a new address for certain -// edge connection errors. This means that this handler will return true for isConnectivityError -// for more cases like duplicate connection register and edge quic dial errors. -type IPAddrFallback struct{} - -func (f IPAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { - switch err.(type) { - case nil: // maintain current IP address - // Try the next address if it was a quic.IdleTimeoutError - // DupConnRegisterTunnelError needs to also receive a new ip address - case connection.DupConnRegisterTunnelError, - *quic.IdleTimeoutError: - return true, false - // Network problems should be retried with new address immediately and report - // as connectivity error - case edgediscovery.DialError, *connection.EdgeQuicDialError: - return true, true - default: // maintain current IP address - } - return false, false -} - -type EdgeTunnelServer struct { - config *TunnelConfig - cloudflaredUUID uuid.UUID - orchestrator *orchestration.Orchestrator - credentialManager *reconnectCredentialManager - edgeAddrHandler EdgeAddrHandler - edgeAddrs *edgediscovery.Edge - reconnectCh chan ReconnectSignal - gracefulShutdownC <-chan struct{} - - connAwareLogger *ConnAwareLogger -} - -func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedSignal *signal.Signal) error { +func ServeTunnelLoop( + ctx context.Context, + credentialManager *reconnectCredentialManager, + config *TunnelConfig, + orchestrator *orchestration.Orchestrator, + addr *allregions.EdgeAddr, + connAwareLogger *ConnAwareLogger, + connIndex uint8, + connectedSignal *signal.Signal, + cloudflaredUUID uuid.UUID, + reconnectCh chan ReconnectSignal, + gracefulShutdownC <-chan struct{}, +) error { haConnections.Inc() defer haConnections.Dec() + logger := config.Log.With().Uint8(connection.LogFieldConnIndex, connIndex).Logger() + connLog := connAwareLogger.ReplaceLogger(&logger) + protocolFallback := &protocolFallback{ - retry.BackoffHandler{MaxRetries: e.config.Retries}, - e.config.ProtocolSelector.Current(), + retry.BackoffHandler{MaxRetries: config.Retries}, + config.ProtocolSelector.Current(), false, } connectedFuse := h2mux.NewBooleanFuse() @@ -210,81 +154,54 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedS }() // Ensure the above goroutine will terminate if we return without connecting defer connectedFuse.Fuse(false) - - // Fetch IP address to associated connection index - addr, err := e.edgeAddrs.GetAddr(int(connIndex)) - switch err { - case nil: // no error - case edgediscovery.ErrNoAddressesLeft: - return err - default: - return err - } - - logger := e.config.Log.With(). - IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). - Uint8(connection.LogFieldConnIndex, connIndex). - Logger() - connLog := e.connAwareLogger.ReplaceLogger(&logger) // Each connection to keep its own copy of protocol, because individual connections might fallback // to another protocol when a particular metal doesn't support new protocol - // Each connection can also have it's own IP version because individual connections might fallback - // to another IP version. - err, recoverable := ServeTunnel( - ctx, - connLog, - e.credentialManager, - e.config, - e.orchestrator, - addr, - connIndex, - connectedFuse, - protocolFallback, - e.cloudflaredUUID, - e.reconnectCh, - protocolFallback.protocol, - e.gracefulShutdownC, - ) - - // If the connection is recoverable, we want to maintain the same IP - // but backoff a reconnect with some duration. - if recoverable { - duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) - if !ok { - return err - } - e.config.Observer.SendReconnect(connIndex) - connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) - } - - // Check if the connection error was from an IP issue with the host or - // establishing a connection to the edge and if so, rotate the IP address. - yes, hasConnectivityError := e.edgeAddrHandler.ShouldGetNewAddress(err) - if yes { - e.edgeAddrs.GetDifferentAddr(int(connIndex), hasConnectivityError) - } - - select { - case <-ctx.Done(): - return ctx.Err() - case <-e.gracefulShutdownC: - return nil - case <-protocolFallback.BackoffTimer(): - if !recoverable { - return err - } - - if !selectNextProtocol( - connLog.Logger(), + for { + err, recoverable := ServeTunnel( + ctx, + connLog, + credentialManager, + config, + orchestrator, + addr, + connIndex, + connectedFuse, protocolFallback, - e.config.ProtocolSelector, - err, - ) { - return err + cloudflaredUUID, + reconnectCh, + protocolFallback.protocol, + gracefulShutdownC, + ) + + if recoverable { + duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) + if !ok { + return err + } + config.Observer.SendReconnect(connIndex) + connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-gracefulShutdownC: + return nil + case <-protocolFallback.BackoffTimer(): + if !recoverable { + return err + } + + if !selectNextProtocol( + connLog.Logger(), + protocolFallback, + config.ProtocolSelector, + err, + ) { + return err + } } } - - return err } // protocolFallback is a wrapper around backoffHandler that will try fallback option when backoff reaches @@ -316,10 +233,6 @@ func selectNextProtocol( ) bool { var idleTimeoutError *quic.IdleTimeoutError isNetworkActivityTimeout := errors.As(cause, &idleTimeoutError) - edgeQuicDialError, ok := cause.(*connection.EdgeQuicDialError) - if !isNetworkActivityTimeout && ok { - isNetworkActivityTimeout = errors.As(edgeQuicDialError.Cause, &idleTimeoutError) - } _, hasFallback := selector.Fallback() if protocolBackoff.ReachedMaxRetries() || (hasFallback && isNetworkActivityTimeout) { @@ -328,7 +241,7 @@ func selectNextProtocol( "Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " + "UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips/\n" + - "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work " + + "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work" + "unless your cloudflared can connect with Cloudflare Network with `quic`.") } @@ -413,12 +326,8 @@ func ServeTunnel( connLog.ConnAwareLogger().Msg(activeIncidentsMsg(incidents)) } return err.Cause, !err.Permanent - case *connection.EdgeQuicDialError: - // Don't retry connection for a dial error - return err, false case ReconnectSignal: connLog.Logger().Info(). - IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). Uint8(connection.LogFieldConnIndex, connIndex). Msgf("Restarting connection due to reconnect signal in %s", err.Delay) err.DelayBeforeReconnect() @@ -617,7 +526,6 @@ func ServeHTTP2( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) - connLog.Logger().Debug().Msg("Forcefully breaking http2 connection") _ = tlsServerConn.Close() } return err @@ -676,7 +584,6 @@ func ServeQUIC( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) - connLogger.Logger().Debug().Msg("Forcefully breaking quic connection") quicConn.Close() } return err From 6c3d2fc33977e4de6309d478c704d008751efc36 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Tue, 14 Jun 2022 20:48:07 -0500 Subject: [PATCH 117/238] Release 2022.6.0 --- RELEASE_NOTES | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 4610c3d2..d8e6b98d 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,26 @@ +2022.6.0 +- 2022-06-14 Revert "TUN-6010: Add component tests for --edge-ip-version" +- 2022-06-14 Revert "TUN-6373: Add edge-ip-version to remotely pushed configuration" +- 2022-06-14 Revert "TUN-6384: Correct duplicate connection error to fetch new IP first" +- 2022-06-14 Revert "TUN-6007: Implement new edge discovery algorithm" +- 2022-06-13 TUN-6385: Don't share err between acceptStream loop and per-stream goroutines +- 2022-06-13 TUN-6384: Correct duplicate connection error to fetch new IP first +- 2022-06-13 TUN-6373: Add edge-ip-version to remotely pushed configuration +- 2022-06-13 TUN-6380: Enforce connect and keep-alive timeouts for TCP connections in both WARP routing and websocket based TCP proxy. +- 2022-06-11 Update issue templates +- 2022-06-11 Amendment to previous PR +- 2022-06-09 TUN-6347: Add TCP stream logs with FlowID +- 2022-06-08 TUN-6361: Add cloudflared arm builds to pkging as well +- 2022-06-07 TUN-6357: Add connector id to ready check endpoint +- 2022-06-07 TUN-6010: Add component tests for --edge-ip-version +- 2022-06-06 TUN-6191: Update quic-go to v0.27.1 and with custom patch to allow keep alive period to be configurable +- 2022-06-03 TUN-6343: Fix QUIC->HTTP2 fallback +- 2022-06-02 TUN-6339: Add config for IPv6 support +- 2022-06-02 TUN-6341: Fix default config value for edge-ip-version +- 2022-06-01 TUN-6323: Add Xenial and Trusty for Ubuntu pkging +- 2022-05-31 TUN-6210: Add cloudflared.repo to make it easy for yum installs +- 2022-05-30 TUN-6293: Update yaml v3 to latest hotfix +- 2022-05-20 TUN-6007: Implement new edge discovery algorithm 2022.5.3 - 2022-05-30 TUN-6308: Add debug logs to see if packets are sent/received from edge - 2022-05-30 TUN-6301: Allow to update logger used by UDP session manager From 102631d98d9eb0a5d71b6d7459ceaead439404c2 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Tue, 14 Jun 2022 21:53:02 -0500 Subject: [PATCH 118/238] TUN-6395: Fix writing RPM repo data --- release_pkgs.py | 141 ++++++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index 36f672ff..c5a26a76 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -8,23 +8,24 @@ them to be in an uploadable state. 2. Upload these packages to a storage in a format that apt and yum expect. """ -from subprocess import Popen, PIPE -import os import argparse import base64 -from pathlib import Path import logging +import os import shutil from hashlib import sha256 +from pathlib import Path +from subprocess import Popen, PIPE -import gnupg import boto3 +import gnupg from botocore.client import Config from botocore.exceptions import ClientError # The front facing R2 URL to access assets from. R2_ASSET_URL = 'https://demo-r2-worker.cloudflare-tunnel.workers.dev/' + class PkgUploader: def __init__(self, account_id, bucket_name, client_id, client_secret): self.account_id = account_id @@ -35,14 +36,14 @@ class PkgUploader: def upload_pkg_to_r2(self, filename, upload_file_path): endpoint_url = f"https://{self.account_id}.r2.cloudflarestorage.com" token_secret_hash = sha256(self.client_secret.encode()).hexdigest() - + config = Config( - region_name = 'auto', + region_name='auto', s3={ "addressing_style": "path", } ) - + r2 = boto3.client( "s3", endpoint_url=endpoint_url, @@ -57,7 +58,7 @@ class PkgUploader: except ClientError as e: raise e - + class PkgCreator: """ The distribution conf is what dictates to reprepro, the debian packaging building @@ -72,15 +73,16 @@ class PkgCreator: description - (String) gpg_key_id - gpg key id of what you want to use to sign the packages.(String) """ - def create_distribution_conf(self, - file_path, - origin, - label, - releases, - archs, - components, - description, - gpg_key_id ): + + def create_distribution_conf(self, + file_path, + origin, + label, + releases, + archs, + components, + description, + gpg_key_id): with open(file_path, "w+") as distributions_file: for release in releases: distributions_file.write(f"Origin: {origin}\n") @@ -102,6 +104,7 @@ class PkgCreator: db and pool contain information and metadata about builds. We can ignore these. dist: contains all the pkgs and signed releases that are necessary for an apt download. """ + def create_deb_pkgs(self, release, deb_file): print(f"creating deb pkgs: {release} : {deb_file}") p = Popen(["reprepro", "includedeb", release, deb_file], stdout=PIPE, stderr=PIPE) @@ -130,8 +133,9 @@ class PkgCreator: gpgcheck=1 gpgkey=https://pkg.cloudflare.com/cloudflare-main.gpg """ + def create_repo_file(self, file_path, binary_name, baseurl, gpgkey_url): - with open(file_path, "w+") as repo_file: + with open(os.path.join(file_path, binary_name + '.repo'), "w+") as repo_file: repo_file.write(f"[{binary_name}-stable]") repo_file.write(f"{binary_name}-stable") repo_file.write(f"baseurl={baseurl}/rpm") @@ -141,19 +145,19 @@ class PkgCreator: repo_file.write(f"gpgkey={gpgkey_url}") def _sign_rpms(self, file_path): - p = Popen(["rpm" , "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE) + p = Popen(["rpm", "--define", f"_gpg_name {gpg_key_name}", "--addsign", file_path], stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: print(f"rpm sign result result => {out}, {err}") raise def _sign_repomd(self): - p = Popen(["gpg", "--batch", "--detach-sign", "--armor", "./rpm/repodata/repomd.xml"], stdout=PIPE, stderr=PIPE) + p = Popen(["gpg", "--batch", "--detach-sign", "--armor", "./rpm/repodata/repomd.xml"], stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: print(f"sign repomd result => {out}, {err}") raise - + """ sets up and signs the RPM directories in the following format: - rpm @@ -163,9 +167,10 @@ class PkgCreator: this assumes the assets are in the format -.rpm """ + def _setup_rpm_pkg_directories(self, artifacts_path, gpg_key_name, archs=["aarch64", "x86_64", "386"]): for arch in archs: - for root, _ , files in os.walk(artifacts_path): + for root, _, files in os.walk(artifacts_path): for file in files: if file.endswith(f"{arch}.rpm"): new_dir = f"./rpm/{arch}" @@ -174,11 +179,12 @@ class PkgCreator: new_path = os.path.join(new_dir, file) shutil.copyfile(old_path, new_path) self._sign_rpms(new_path) - + """ imports gpg keys into the system so reprepro and createrepo can use it to sign packages. it returns the GPG ID after a successful import """ + def import_gpg_keys(self, private_key, public_key): gpg = gnupg.GPG() private_key = base64.b64decode(private_key) @@ -192,12 +198,13 @@ class PkgCreator: basically rpm --import This enables us to sign rpms. """ + def import_rpm_key(self, public_key): file_name = "pb.key" with open(file_name, "wb") as f: public_key = base64.b64decode(public_key) f.write(public_key) - + p = Popen(["rpm", "--import", file_name], stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: @@ -212,18 +219,21 @@ class PkgCreator: and the release will be uploaded to the default path. binary: name of the binary to upload """ + + def upload_from_directories(pkg_uploader, directory, release, binary): - for root, _ , files in os.walk(directory): + for root, _, files in os.walk(directory): for file in files: upload_file_name = os.path.join(binary, root, file) if release: upload_file_name = os.path.join(release, upload_file_name) - filename = os.path.join(root,file) - try: + filename = os.path.join(root, file) + try: pkg_uploader.upload_pkg_to_r2(filename, upload_file_name) except ClientError as e: logging.error(e) - return + return + """ 1. looks into a built_artifacts folder for cloudflared debs @@ -237,19 +247,22 @@ def upload_from_directories(pkg_uploader, directory, release, binary): release_version: is the cloudflared release version. Only provide this if you want a permanent backup. """ -def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, release_version): + + +def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary_name, archs, package_component, + release_version): # set configuration for package creation. print(f"initialising configuration for {binary_name} , {archs}") Path("./conf").mkdir(parents=True, exist_ok=True) pkg_creator.create_distribution_conf( - "./conf/distributions", - binary_name, - binary_name, - releases, - archs, - package_component, - f"apt repository for {binary_name}", - gpg_key_id) + "./conf/distributions", + binary_name, + binary_name, + releases, + archs, + package_component, + f"apt repository for {binary_name}", + gpg_key_id) # create deb pkgs for release in releases: @@ -266,6 +279,7 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, gpg_key_id, binary upload_from_directories(pkg_uploader, "dists", release_version, binary_name) upload_from_directories(pkg_uploader, "pool", release_version, binary_name) + def create_rpm_packaging( pkg_creator, pkg_uploader, @@ -294,60 +308,61 @@ def parse_args(): ) parser.add_argument( - "--bucket", default=os.environ.get("R2_BUCKET"), help="R2 Bucket name" + "--bucket", default=os.environ.get("R2_BUCKET"), help="R2 Bucket name" ) parser.add_argument( - "--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID" + "--id", default=os.environ.get("R2_CLIENT_ID"), help="R2 Client ID" ) parser.add_argument( - "--secret", default=os.environ.get("R2_CLIENT_SECRET"), help="R2 Client Secret" + "--secret", default=os.environ.get("R2_CLIENT_SECRET"), help="R2 Client Secret" ) parser.add_argument( - "--account", default=os.environ.get("R2_ACCOUNT_ID"), help="R2 Account Tag" + "--account", default=os.environ.get("R2_ACCOUNT_ID"), help="R2 Account Tag" ) parser.add_argument( - "--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\ + "--release-tag", default=os.environ.get("RELEASE_VERSION"), help="Release version you want your pkgs to be\ prefixed with. Leave empty if you don't want tagged release versions backed up to R2." ) parser.add_argument( - "--binary", default=os.environ.get("BINARY_NAME"), help="The name of the binary the packages are for" + "--binary", default=os.environ.get("BINARY_NAME"), help="The name of the binary the packages are for" ) parser.add_argument( - "--gpg-private-key", default=os.environ.get("LINUX_SIGNING_PRIVATE_KEY"), help="GPG private key to sign the\ + "--gpg-private-key", default=os.environ.get("LINUX_SIGNING_PRIVATE_KEY"), help="GPG private key to sign the\ packages" ) parser.add_argument( - "--gpg-public-key", default=os.environ.get("LINUX_SIGNING_PUBLIC_KEY"), help="GPG public key used for\ + "--gpg-public-key", default=os.environ.get("LINUX_SIGNING_PUBLIC_KEY"), help="GPG public key used for\ signing packages" ) parser.add_argument( - "--gpg-public-key-url", default=os.environ.get("GPG_PUBLIC_KEY_URL"), help="GPG public key url that\ + "--gpg-public-key-url", default=os.environ.get("GPG_PUBLIC_KEY_URL"), help="GPG public key url that\ downloaders can use to verify signing" ) parser.add_argument( - "--pkg-upload-url", default=os.environ.get("PKG_URL"), help="URL to be used by downloaders" + "--pkg-upload-url", default=os.environ.get("PKG_URL"), help="URL to be used by downloaders" ) parser.add_argument( - "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic", - "xenial", "trusty"], - help="list of debian based releases that need to be packaged for" + "--deb-based-releases", default=["bookworm", "bullseye", "buster", "jammy", "impish", "focal", "bionic", + "xenial", "trusty"], + help="list of debian based releases that need to be packaged for" ) parser.add_argument( - "--archs", default=["amd64", "386", "arm64", "arm"], help="list of architectures we want to package for. Note that\ + "--archs", default=["amd64", "386", "arm64", "arm"], help="list of architectures we want to package for. Note that\ it is the caller's responsiblity to ensure that these debs are already present in a directory. This script\ will not build binaries or create their debs." - ) + ) args = parser.parse_args() return args + if __name__ == "__main__": try: args = parse_args() @@ -362,15 +377,15 @@ if __name__ == "__main__": pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) print(f"signing with gpg_key: {gpg_key_id}") create_deb_packaging(pkg_creator, pkg_uploader, args.deb_based_releases, gpg_key_id, args.binary, args.archs, - "main", args.release_tag) - + "main", args.release_tag) + create_rpm_packaging( - pkg_creator, - pkg_uploader, - "./built_artifacts", - args.release_tag, - args.binary, - gpg_key_name, - args.gpg_public_key_url, - args.pkg_upload_url, - ) + pkg_creator, + pkg_uploader, + "./built_artifacts", + args.release_tag, + args.binary, + gpg_key_name, + args.gpg_public_key_url, + args.pkg_upload_url, + ) From 7d0a2710003588a58b2535270624d0db571ea9df Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 15 Jun 2022 10:00:02 +0100 Subject: [PATCH 119/238] Release 2022.6.1 --- RELEASE_NOTES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index d8e6b98d..0a7f52d6 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +2022.6.1 +- 2022-06-14 TUN-6395: Fix writing RPM repo data + 2022.6.0 - 2022-06-14 Revert "TUN-6010: Add component tests for --edge-ip-version" - 2022-06-14 Revert "TUN-6373: Add edge-ip-version to remotely pushed configuration" From 3da1c25471df3148b12dc766b3ee9c2440b9b0c5 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Mon, 13 Jun 2022 12:46:52 -0500 Subject: [PATCH 120/238] TUN-6381: Write error data on QUIC stream when we fail to talk to the origin; separate logging for protocol errors vs. origin errors. --- connection/quic.go | 29 ++++++++++++++++++++--------- quic/pogs.go | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 00765651..fc138e90 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -160,7 +160,7 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { case quicpogs.DataStreamProtocolSignature: reqServerStream, err := quicpogs.NewRequestServerStream(stream, signature) if err != nil { - return nil + return err } return q.handleDataStream(reqServerStream) case quicpogs.RPCStreamProtocolSignature: @@ -175,30 +175,41 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { } func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) error { - connectRequest, err := stream.ReadConnectRequestData() + request, err := stream.ReadConnectRequestData() if err != nil { return err } + if err := q.dispatchRequest(stream, err, request); err != nil { + _ = stream.WriteConnectResponseData(err) + q.logger.Err(err).Str("type", request.Type.String()).Str("dest", request.Dest).Msg("Request failed") + } + + return nil +} + +func (q *QUICConnection) dispatchRequest(stream *quicpogs.RequestServerStream, err error, request *quicpogs.ConnectRequest) error { originProxy, err := q.orchestrator.GetOriginProxy() if err != nil { return err } - switch connectRequest.Type { + switch request.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - tracedReq, err := buildHTTPRequest(connectRequest, stream) + tracedReq, err := buildHTTPRequest(request, stream) if err != nil { return err } - w := newHTTPResponseAdapter(stream) - return originProxy.ProxyHTTP(w, tracedReq, connectRequest.Type == quicpogs.ConnectionTypeWebsocket) + return originProxy.ProxyHTTP(w, tracedReq, request.Type == quicpogs.ConnectionTypeWebsocket) + case quicpogs.ConnectionTypeTCP: rwa := &streamReadWriteAcker{stream} - metadata := connectRequest.MetadataMap() - return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest, - FlowID: metadata[QUICMetadataFlowID]}) + metadata := request.MetadataMap() + return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{ + Dest: request.Dest, + FlowID: metadata[QUICMetadataFlowID], + }) } return nil } diff --git a/quic/pogs.go b/quic/pogs.go index e82fbe47..262e6561 100644 --- a/quic/pogs.go +++ b/quic/pogs.go @@ -1,6 +1,8 @@ package quic import ( + "fmt" + capnp "zombiezen.com/go/capnproto2" "zombiezen.com/go/capnproto2/pogs" @@ -16,6 +18,18 @@ const ( ConnectionTypeTCP ) +func (c ConnectionType) String() string { + switch c { + case ConnectionTypeHTTP: + return "http" + case ConnectionTypeWebsocket: + return "ws" + case ConnectionTypeTCP: + return "tcp" + } + panic(fmt.Sprintf("invalid ConnectionType: %d", c)) +} + // ConnectRequest is the representation of metadata sent at the start of a QUIC application handshake. type ConnectRequest struct { Dest string `capnp:"dest"` From 337591b2bb9d2301414388633b687c3b0c0dcb8d Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 17 Jun 2022 09:23:44 +0100 Subject: [PATCH 121/238] TUN-6414: Remove go-sumtype from cloudflared build process This PR removes go-sumtype from cloudflared's build processes. The value we see from analysing exhaustive match patterns in go is minimal (we can do this in code reviews) compared to using a tool that is unmaintained and (now broken) in Go 1.18. We'd already been using the patched version here: https://github.com/sudarshan-reddy/go-sumtype because the original is broken for go tools > 1.16 --- .github/workflows/check.yaml | 2 -- Makefile | 5 ----- cfsetup.yaml | 1 - 3 files changed, 8 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 6a3423c7..aa61d4d6 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -12,8 +12,6 @@ jobs: uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - - name: Install go-sumtype - run: go get github.com/sudarshan-reddy/go-sumtype - name: Checkout code uses: actions/checkout@v2 - name: Test diff --git a/Makefile b/Makefile index bfd64cf5..0b103775 100644 --- a/Makefile +++ b/Makefile @@ -293,11 +293,6 @@ quic-deps: .PHONY: vet vet: go vet -mod=vendor ./... - # go get github.com/sudarshan-reddy/go-sumtype (don't do this in build directory or this will cause vendor issues) - # Note: If you have github.com/BurntSushi/go-sumtype then you might have to use the repo above instead - # for now because it uses an older version of golang.org/x/tools. - which go-sumtype - go-sumtype $$(go list -mod=vendor ./...) .PHONY: goimports goimports: diff --git a/cfsetup.yaml b/cfsetup.yaml index 3f546135..130f6bbe 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -153,7 +153,6 @@ stretch: &stretch - gotest-to-teamcity pre-cache: &test_pre_cache - go get golang.org/x/tools/cmd/goimports - - go get github.com/sudarshan-reddy/go-sumtype@v0.0.0-20210827105221-82eca7e5abb1 post-cache: - export GOOS=linux - export GOARCH=amd64 From 420e80ea501d532365955e8383d4bfffd1078602 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 17 Jun 2022 10:07:49 +0100 Subject: [PATCH 122/238] Release 2022.6.2 --- RELEASE_NOTES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 0a7f52d6..7feabfc9 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,12 @@ +2022.6.2 +- 2022-06-13 TUN-6381: Write error data on QUIC stream when we fail to talk to the origin; separate logging for protocol errors vs. origin errors. +- 2022-06-17 TUN-6414: Remove go-sumtype from cloudflared build process +- 2022-06-01 Add Http2Origin option to force HTTP/2 origin connections +- 2022-06-02 fix ingress rules unit test +- 2022-06-09 Update remaining OriginRequestConfig functions for Http2Origins +- 2022-05-31 Add image source label to docker container. +- 2022-05-10 Warp Private Network link updated + 2022.6.1 - 2022-06-14 TUN-6395: Fix writing RPM repo data From 135c8e6d13663d2aa2d3c9169cde0cfc1e6e2062 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 20 Jun 2022 12:05:03 +0100 Subject: [PATCH 123/238] TUN-6362: Add armhf support to cloudflare packaging We now will have `armhf` based debs on our github pages This will also sync to our R2 Release process allowing legacy rpi users to eventually be able to apt-get install cloudflared. --- Makefile | 14 ++++++++++++-- build-packages.sh | 10 +++++++++- release_pkgs.py | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 0b103775..0fc54935 100644 --- a/Makefile +++ b/Makefile @@ -98,6 +98,16 @@ else TARGET_PUBLIC_REPO ?= $(FLAVOR) endif +ifneq ($(TARGET_ARM), ) + ARM_COMMAND := GOARM=$(TARGET_ARM) +endif + +ifeq ($(TARGET_ARM), 7) + PACKAGE_ARCH := armhf +else + PACKAGE_ARCH := $(TARGET_ARCH) +endif + .PHONY: all all: cloudflared test @@ -111,7 +121,7 @@ ifeq ($(FIPS), true) $(info Building cloudflared with go-fips) cp -f fips/fips.go.linux-amd64 cmd/cloudflared/fips.go endif - GOOS=$(TARGET_OS) GOARCH=$(TARGET_ARCH) go build -v -mod=vendor $(GO_BUILD_TAGS) $(LDFLAGS) $(IMPORT_PATH)/cmd/cloudflared + GOOS=$(TARGET_OS) GOARCH=$(TARGET_ARCH) $(ARM_COMMAND) go build -v -mod=vendor $(GO_BUILD_TAGS) $(LDFLAGS) $(IMPORT_PATH)/cmd/cloudflared ifeq ($(FIPS), true) rm -f cmd/cloudflared/fips.go ./check-fips.sh cloudflared @@ -171,7 +181,7 @@ define build_package --license 'Apache License Version 2.0' \ --url 'https://github.com/cloudflare/cloudflared' \ -m 'Cloudflare ' \ - -a $(TARGET_ARCH) -v $(VERSION) -n $(DEB_PACKAGE_NAME) $(NIGHTLY_FLAGS) --after-install postinst.sh --after-remove postrm.sh \ + -a $(PACKAGE_ARCH) -v $(VERSION) -n $(DEB_PACKAGE_NAME) $(NIGHTLY_FLAGS) --after-install postinst.sh --after-remove postrm.sh \ cloudflared=$(INSTALL_BINDIR) cloudflared.1=$(INSTALL_MANDIR) endef diff --git a/build-packages.sh b/build-packages.sh index 2c01bf9b..97c5b681 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -17,10 +17,18 @@ for arch in ${windowsArchs[@]}; do done -linuxArchs=("386" "amd64" "arm" "arm64") +linuxArchs=("386" "amd64" "arm" "armhf" "arm64") export TARGET_OS=linux for arch in ${linuxArchs[@]}; do + unset TARGET_ARM export TARGET_ARCH=$arch + + ## Support for armhf builds + if [[ $arch == armhf ]] ; then + export TARGET_ARCH=arm + export TARGET_ARM=7 + fi + make cloudflared-deb mv cloudflared\_$VERSION\_$arch.deb $ARTIFACT_DIR/cloudflared-linux-$arch.deb diff --git a/release_pkgs.py b/release_pkgs.py index c5a26a76..e916e249 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -354,7 +354,7 @@ def parse_args(): ) parser.add_argument( - "--archs", default=["amd64", "386", "arm64", "arm"], help="list of architectures we want to package for. Note that\ + "--archs", default=["amd64", "386", "arm64", "arm", "armhf"], help="list of architectures we want to package for. Note that\ it is the caller's responsiblity to ensure that these debs are already present in a directory. This script\ will not build binaries or create their debs." ) From 6a6ba704f1d5b83e5fa6e8f6e80c6243b3018c2a Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 20 Jun 2022 13:52:31 +0100 Subject: [PATCH 124/238] Release 2022.6.3 --- RELEASE_NOTES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 7feabfc9..3e50405c 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +2022.6.3 +- 2022-06-20 TUN-6362: Add armhf support to cloudflare packaging + 2022.6.2 - 2022-06-13 TUN-6381: Write error data on QUIC stream when we fail to talk to the origin; separate logging for protocol errors vs. origin errors. - 2022-06-17 TUN-6414: Remove go-sumtype from cloudflared build process From f4667c6345b662420a9562be13333a1b348bfa33 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 17 Jun 2022 14:39:38 -0700 Subject: [PATCH 125/238] TUN-6427: Differentiate between upstream request closed/canceled and failed origin requests --- proxy/proxy.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index e31dc75b..19f14778 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -196,6 +196,9 @@ func (p *Proxy) proxyHTTPRequest( resp, err := httpService.RoundTrip(roundTripReq) if err != nil { tracing.EndWithErrorStatus(ttfbSpan, err) + if err := roundTripReq.Context().Err(); err != nil { + return errors.Wrap(err, "Incoming request ended abruptly") + } return errors.Wrap(err, "Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared") } From c7a6304d32a3704da28cae20c172290bb92daf42 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 20 May 2022 14:51:36 -0700 Subject: [PATCH 126/238] TUN-6007: Implement new edge discovery algorithm (cherry picked from commit 4f468b8a5da8f89f40df9ae99927417331cf9763) --- connection/errors.go | 9 + connection/quic.go | 2 +- edgediscovery/allregions/address.go | 64 +++ edgediscovery/allregions/address_test.go | 247 +++++++++ edgediscovery/allregions/discovery.go | 6 +- edgediscovery/allregions/mocks_for_test.go | 109 ++++ edgediscovery/allregions/region.go | 166 ++++-- edgediscovery/allregions/region_test.go | 583 ++++++++++++--------- edgediscovery/allregions/regions.go | 26 +- edgediscovery/allregions/regions_test.go | 269 ++++++---- edgediscovery/edgediscovery.go | 45 +- edgediscovery/edgediscovery_test.go | 102 +++- supervisor/supervisor.go | 124 ++--- supervisor/tunnel.go | 213 +++++--- 14 files changed, 1378 insertions(+), 587 deletions(-) create mode 100644 edgediscovery/allregions/address.go create mode 100644 edgediscovery/allregions/address_test.go diff --git a/connection/errors.go b/connection/errors.go index df3cfe97..10e3b5d6 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -18,6 +18,15 @@ func (e DupConnRegisterTunnelError) Error() string { return "already connected to this server, trying another address" } +// Dial to edge server with quic failed +type EdgeQuicDialError struct { + Cause error +} + +func (e *EdgeQuicDialError) Error() string { + return "failed to dial to edge with quic: " + e.Cause.Error() +} + // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/connection/quic.go b/connection/quic.go index fc138e90..4c530b19 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -57,7 +57,7 @@ func NewQUICConnection( ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { - return nil, fmt.Errorf("failed to dial to edge: %w", err) + return nil, &EdgeQuicDialError{Cause: err} } datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) diff --git a/edgediscovery/allregions/address.go b/edgediscovery/allregions/address.go new file mode 100644 index 00000000..36bf2d48 --- /dev/null +++ b/edgediscovery/allregions/address.go @@ -0,0 +1,64 @@ +package allregions + +// Region contains cloudflared edge addresses. The edge is partitioned into several regions for +// redundancy purposes. +type AddrSet map[*EdgeAddr]UsedBy + +// AddrUsedBy finds the address used by the given connection in this region. +// Returns nil if the connection isn't using any IP. +func (a AddrSet) AddrUsedBy(connID int) *EdgeAddr { + for addr, used := range a { + if used.Used && used.ConnID == connID { + return addr + } + } + return nil +} + +// AvailableAddrs counts how many unused addresses this region contains. +func (a AddrSet) AvailableAddrs() int { + n := 0 + for _, usedby := range a { + if !usedby.Used { + n++ + } + } + return n +} + +// GetUnusedIP returns a random unused address in this region. +// Returns nil if all addresses are in use. +func (a AddrSet) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { + for addr, usedby := range a { + if !usedby.Used && addr != excluding { + return addr + } + } + return nil +} + +// Use the address, assigning it to a proxy connection. +func (a AddrSet) Use(addr *EdgeAddr, connID int) { + if addr == nil { + return + } + a[addr] = InUse(connID) +} + +// GetAnyAddress returns an arbitrary address from the region. +func (a AddrSet) GetAnyAddress() *EdgeAddr { + for addr := range a { + return addr + } + return nil +} + +// GiveBack the address, ensuring it is no longer assigned to an IP. +// Returns true if the address is in this region. +func (a AddrSet) GiveBack(addr *EdgeAddr) (ok bool) { + if _, ok := a[addr]; !ok { + return false + } + a[addr] = Unused() + return true +} diff --git a/edgediscovery/allregions/address_test.go b/edgediscovery/allregions/address_test.go new file mode 100644 index 00000000..9c8d9289 --- /dev/null +++ b/edgediscovery/allregions/address_test.go @@ -0,0 +1,247 @@ +package allregions + +import ( + "reflect" + "testing" +) + +func TestAddrSet_AddrUsedBy(t *testing.T) { + type args struct { + connID int + } + tests := []struct { + name string + addrSet AddrSet + args args + want *EdgeAddr + }{ + { + name: "happy trivial test", + addrSet: AddrSet{ + &addr0: InUse(0), + }, + args: args{connID: 0}, + want: &addr0, + }, + { + name: "sad trivial test", + addrSet: AddrSet{ + &addr0: InUse(0), + }, + args: args{connID: 1}, + want: nil, + }, + { + name: "sad test", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{connID: 3}, + want: nil, + }, + { + name: "happy test", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{connID: 1}, + want: &addr1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_AvailableAddrs(t *testing.T) { + tests := []struct { + name string + addrSet AddrSet + want int + }{ + { + name: "contains addresses", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }, + want: 1, + }, + { + name: "all free", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: Unused(), + }, + want: 3, + }, + { + name: "all used", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + want: 0, + }, + { + name: "empty", + addrSet: AddrSet{}, + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.AvailableAddrs(); got != tt.want { + t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_GetUnusedIP(t *testing.T) { + type args struct { + excluding *EdgeAddr + } + tests := []struct { + name string + addrSet AddrSet + args args + want *EdgeAddr + }{ + { + name: "happy test with excluding set", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: Unused(), + &addr2: InUse(2), + }, + args: args{excluding: &addr0}, + want: &addr1, + }, + { + name: "happy test with no excluding", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: Unused(), + &addr2: InUse(2), + }, + args: args{excluding: nil}, + want: &addr1, + }, + { + name: "sad test with no excluding", + addrSet: AddrSet{ + &addr0: InUse(0), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{excluding: nil}, + want: nil, + }, + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr0: Unused(), + &addr1: InUse(1), + &addr2: InUse(2), + }, + args: args{excluding: &addr0}, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAddrSet_GiveBack(t *testing.T) { + type args struct { + addr *EdgeAddr + } + tests := []struct { + name string + addrSet AddrSet + args args + wantOk bool + availableAfter int + }{ + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr1: InUse(1), + }, + args: args{addr: &addr1}, + wantOk: true, + availableAfter: 1, + }, + { + name: "sad test with excluding", + addrSet: AddrSet{ + &addr1: InUse(1), + }, + args: args{addr: &addr2}, + wantOk: false, + availableAfter: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotOk := tt.addrSet.GiveBack(tt.args.addr); gotOk != tt.wantOk { + t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) + } + if tt.availableAfter != tt.addrSet.AvailableAddrs() { + t.Errorf("Region.AvailableAddrs() = %v, want %v", tt.addrSet.AvailableAddrs(), tt.availableAfter) + } + }) + } +} + +func TestAddrSet_GetAnyAddress(t *testing.T) { + tests := []struct { + name string + addrSet AddrSet + wantNil bool + }{ + { + name: "Sad test -- GetAnyAddress should only fail if the region is empty", + addrSet: AddrSet{}, + wantNil: true, + }, + { + name: "Happy test (all addresses unused)", + addrSet: AddrSet{ + &addr0: Unused(), + }, + wantNil: false, + }, + { + name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", + addrSet: AddrSet{ + &addr0: InUse(2), + }, + wantNil: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.addrSet.GetAnyAddress(); tt.wantNil != (got == nil) { + t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) + } + }) + } +} diff --git a/edgediscovery/allregions/discovery.go b/edgediscovery/allregions/discovery.go index e6a4103b..35f464f8 100644 --- a/edgediscovery/allregions/discovery.go +++ b/edgediscovery/allregions/discovery.go @@ -13,7 +13,7 @@ import ( const ( // Used to discover HA origintunneld servers - srvService = "origintunneld" + srvService = "v2-origintunneld" srvProto = "tcp" srvName = "argotunnel.com" @@ -115,6 +115,9 @@ func edgeDiscovery(log *zerolog.Logger, srvService string) ([][]*EdgeAddr, error if err != nil { return nil, err } + for _, e := range edgeAddrs { + log.Debug().Msgf("Edge Address: %+v", *e) + } resolvedAddrPerCNAME = append(resolvedAddrPerCNAME, edgeAddrs) } @@ -187,7 +190,6 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) { UDP: udpAddr, IPVersion: version, }) - } return } diff --git a/edgediscovery/allregions/mocks_for_test.go b/edgediscovery/allregions/mocks_for_test.go index ab99c94f..51ff746c 100644 --- a/edgediscovery/allregions/mocks_for_test.go +++ b/edgediscovery/allregions/mocks_for_test.go @@ -9,6 +9,115 @@ import ( "testing/quick" ) +var ( + v4Addrs = []*EdgeAddr{&addr0, &addr1, &addr2, &addr3} + v6Addrs = []*EdgeAddr{&addr4, &addr5, &addr6, &addr7} + addr0 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr1 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.1"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr2 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.2"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr3 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.3"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + addr4 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr5 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr6 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } + addr7 = EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + IPVersion: V6, + } +) + type mockAddrs struct { // a set of synthetic SRV records addrMap map[net.SRV][]*EdgeAddr diff --git a/edgediscovery/allregions/region.go b/edgediscovery/allregions/region.go index 4d268c77..e0ad3ab4 100644 --- a/edgediscovery/allregions/region.go +++ b/edgediscovery/allregions/region.go @@ -1,79 +1,155 @@ package allregions +import "time" + +const ( + timeoutDuration = 10 * time.Minute +) + // Region contains cloudflared edge addresses. The edge is partitioned into several regions for // redundancy purposes. type Region struct { - connFor map[*EdgeAddr]UsedBy + primaryIsActive bool + active AddrSet + primary AddrSet + secondary AddrSet + primaryTimeout time.Time + timeoutDuration time.Duration } // NewRegion creates a region with the given addresses, which are all unused. -func NewRegion(addrs []*EdgeAddr) Region { +func NewRegion(addrs []*EdgeAddr, overrideIPVersion ConfigIPVersion) Region { // The zero value of UsedBy is Unused(), so we can just initialize the map's values with their // zero values. - connFor := make(map[*EdgeAddr]UsedBy) - for _, addr := range addrs { - connFor[addr] = Unused() + connForv4 := make(AddrSet) + connForv6 := make(AddrSet) + systemPreference := V6 + for i, addr := range addrs { + if i == 0 { + // First family of IPs returned is system preference of IP + systemPreference = addr.IPVersion + } + switch addr.IPVersion { + case V4: + connForv4[addr] = Unused() + case V6: + connForv6[addr] = Unused() + } } + + // Process as system preference + var primary AddrSet + var secondary AddrSet + switch systemPreference { + case V4: + primary = connForv4 + secondary = connForv6 + case V6: + primary = connForv6 + secondary = connForv4 + } + + // Override with provided preference + switch overrideIPVersion { + case IPv4Only: + primary = connForv4 + secondary = make(AddrSet) // empty + case IPv6Only: + primary = connForv6 + secondary = make(AddrSet) // empty + case Auto: + // no change + default: + // no change + } + return Region{ - connFor: connFor, + primaryIsActive: true, + active: primary, + primary: primary, + secondary: secondary, + timeoutDuration: timeoutDuration, } } // AddrUsedBy finds the address used by the given connection in this region. // Returns nil if the connection isn't using any IP. func (r *Region) AddrUsedBy(connID int) *EdgeAddr { - for addr, used := range r.connFor { - if used.Used && used.ConnID == connID { - return addr - } + edgeAddr := r.primary.AddrUsedBy(connID) + if edgeAddr == nil { + edgeAddr = r.secondary.AddrUsedBy(connID) } - return nil + return edgeAddr } // AvailableAddrs counts how many unused addresses this region contains. func (r Region) AvailableAddrs() int { - n := 0 - for _, usedby := range r.connFor { - if !usedby.Used { - n++ - } - } - return n + return r.active.AvailableAddrs() } -// GetUnusedIP returns a random unused address in this region. -// Returns nil if all addresses are in use. -func (r Region) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr { - for addr, usedby := range r.connFor { - if !usedby.Used && addr != excluding { - return addr - } - } - return nil -} - -// Use the address, assigning it to a proxy connection. -func (r Region) Use(addr *EdgeAddr, connID int) { - if addr == nil { - return - } - r.connFor[addr] = InUse(connID) -} - -// GetAnyAddress returns an arbitrary address from the region. -func (r Region) GetAnyAddress() *EdgeAddr { - for addr := range r.connFor { +// AssignAnyAddress returns a random unused address in this region now +// assigned to the connID excluding the provided EdgeAddr. +// Returns nil if all addresses are in use for the region. +func (r Region) AssignAnyAddress(connID int, excluding *EdgeAddr) *EdgeAddr { + if addr := r.active.GetUnusedIP(excluding); addr != nil { + r.active.Use(addr, connID) return addr } return nil } +// GetAnyAddress returns an arbitrary address from the region. +func (r Region) GetAnyAddress() *EdgeAddr { + return r.active.GetAnyAddress() +} + // GiveBack the address, ensuring it is no longer assigned to an IP. // Returns true if the address is in this region. -func (r Region) GiveBack(addr *EdgeAddr) (ok bool) { - if _, ok := r.connFor[addr]; !ok { - return false +func (r *Region) GiveBack(addr *EdgeAddr, hasConnectivityError bool) (ok bool) { + if ok = r.primary.GiveBack(addr); !ok { + // Attempt to give back the address in the secondary set + if ok = r.secondary.GiveBack(addr); !ok { + // Address is not in this region + return + } } - r.connFor[addr] = Unused() - return true + + // No connectivity error: no worry + if !hasConnectivityError { + return + } + + // If using primary and returned address is IPv6 and secondary is available + if r.primaryIsActive && addr.IPVersion == V6 && len(r.secondary) > 0 { + r.active = r.secondary + r.primaryIsActive = false + r.primaryTimeout = time.Now().Add(r.timeoutDuration) + return + } + + // Do nothing for IPv4 or if secondary is empty + if r.primaryIsActive { + return + } + + // Immediately return to primary pool, regardless of current primary timeout + if addr.IPVersion == V4 { + activatePrimary(r) + return + } + + // Timeout exceeded and can be reset to primary pool + if r.primaryTimeout.Before(time.Now()) { + activatePrimary(r) + return + } + + return +} + +// activatePrimary sets the primary set to the active set and resets the timeout. +func activatePrimary(r *Region) { + r.active = r.primary + r.primaryIsActive = true + r.primaryTimeout = time.Now() // reset timeout } diff --git a/edgediscovery/allregions/region_test.go b/edgediscovery/allregions/region_test.go index d83dea61..e8d230c4 100644 --- a/edgediscovery/allregions/region_test.go +++ b/edgediscovery/allregions/region_test.go @@ -1,284 +1,357 @@ package allregions import ( - "reflect" + "net" "testing" + "time" + + "github.com/stretchr/testify/assert" ) +func makeAddrSet(addrs []*EdgeAddr) AddrSet { + addrSet := make(AddrSet, len(addrs)) + for _, addr := range addrs { + addrSet[addr] = Unused() + } + return addrSet +} + func TestRegion_New(t *testing.T) { - r := NewRegion([]*EdgeAddr{&addr0, &addr1, &addr2}) - if r.AvailableAddrs() != 3 { - t.Errorf("r.AvailableAddrs() == %v but want 3", r.AvailableAddrs()) - } -} - -func TestRegion_AddrUsedBy(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - connID int - } tests := []struct { - name string - fields fields - args args - want *EdgeAddr + name string + addrs []*EdgeAddr + mode ConfigIPVersion + expectedAddrs int + primary AddrSet + secondary AddrSet }{ { - name: "happy trivial test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - }}, - args: args{connID: 0}, - want: &addr0, + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, }, { - name: "sad trivial test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - }}, - args: args{connID: 1}, - want: nil, + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + expectedAddrs: 0, + primary: AddrSet{}, + secondary: AddrSet{}, }, { - name: "sad test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{connID: 3}, - want: nil, + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, }, { - name: "happy test", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{connID: 1}, - want: &addr1, + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + expectedAddrs: 0, + primary: AddrSet{}, + secondary: AddrSet{}, + }, + { + name: "IPv4 (first) and IPv6 addresses with Auto", + addrs: append(v4Addrs, v6Addrs...), + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: makeAddrSet(v6Addrs), + }, + { + name: "IPv6 (first) and IPv4 addresses with Auto", + addrs: append(v6Addrs, v4Addrs...), + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: makeAddrSet(v4Addrs), + }, + { + name: "IPv4 addresses with Auto", + addrs: v4Addrs, + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + }, + { + name: "IPv6 addresses with Auto", + addrs: v6Addrs, + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := &Region{ - connFor: tt.fields.connFor, + r := NewRegion(tt.addrs, tt.mode) + assert.Equal(t, tt.expectedAddrs, r.AvailableAddrs()) + assert.Equal(t, tt.primary, r.primary) + assert.Equal(t, tt.secondary, r.secondary) + }) + } +} + +func TestRegion_AnyAddress_EmptyActiveSet(t *testing.T) { + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv6 addresses with IPv4Only", + addrs: v6Addrs, + mode: IPv4Only, + }, + { + name: "IPv4 addresses with IPv6Only", + addrs: v4Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.GetAnyAddress() + assert.Nil(t, addr) + addr = r.AssignAnyAddress(0, nil) + assert.Nil(t, addr) + }) + } +} + +func TestRegion_AssignAnyAddress_FullyUsedActiveSet(t *testing.T) { + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + total := r.active.AvailableAddrs() + for i := 0; i < total; i++ { + addr := r.AssignAnyAddress(i, nil) + assert.NotNil(t, addr) } - if got := r.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want) + addr := r.AssignAnyAddress(9, nil) + assert.Nil(t, addr) + }) + } +} + +var giveBackTests = []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + expectedAddrs int + primary AddrSet + secondary AddrSet + primarySwap bool +}{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv4 (first) and IPv6 addresses with Auto", + addrs: append(v4Addrs, v6Addrs...), + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: makeAddrSet(v6Addrs), + primarySwap: false, + }, + { + name: "IPv6 (first) and IPv4 addresses with Auto", + addrs: append(v6Addrs, v4Addrs...), + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: makeAddrSet(v4Addrs), + primarySwap: true, + }, + { + name: "IPv4 addresses with Auto", + addrs: v4Addrs, + mode: Auto, + expectedAddrs: len(v4Addrs), + primary: makeAddrSet(v4Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, + { + name: "IPv6 addresses with Auto", + addrs: v6Addrs, + mode: Auto, + expectedAddrs: len(v6Addrs), + primary: makeAddrSet(v6Addrs), + secondary: AddrSet{}, + primarySwap: false, + }, +} + +func TestRegion_GiveBack_NoConnectivityError(t *testing.T) { + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.AssignAnyAddress(0, nil) + assert.NotNil(t, addr) + assert.True(t, r.GiveBack(addr, false)) + }) + } +} + +func TestRegion_GiveBack_ForeignAddr(t *testing.T) { + invalid := EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("123.4.5.0"), + Port: 8000, + Zone: "", + }, + IPVersion: V4, + } + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + assert.False(t, r.GiveBack(&invalid, false)) + assert.False(t, r.GiveBack(&invalid, true)) + }) + } +} + +func TestRegion_GiveBack_SwapPrimary(t *testing.T) { + for _, tt := range giveBackTests { + t.Run(tt.name, func(t *testing.T) { + r := NewRegion(tt.addrs, tt.mode) + addr := r.AssignAnyAddress(0, nil) + assert.NotNil(t, addr) + assert.True(t, r.GiveBack(addr, true)) + assert.Equal(t, tt.primarySwap, !r.primaryIsActive) + if tt.primarySwap { + assert.Equal(t, r.secondary, r.active) + assert.False(t, r.primaryTimeout.IsZero()) + } else { + assert.Equal(t, r.primary, r.active) + assert.True(t, r.primaryTimeout.IsZero()) } }) } } -func TestRegion_AvailableAddrs(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - tests := []struct { - name string - fields fields - want int - }{ - { - name: "contains addresses", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }}, - want: 1, - }, - { - name: "all free", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: Unused(), - }}, - want: 3, - }, - { - name: "all used", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - want: 0, - }, - { - name: "empty", - fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, - want: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.AvailableAddrs(); got != tt.want { - t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want) - } - }) - } +func TestRegion_GiveBack_IPv4_ResetPrimary(t *testing.T) { + r := NewRegion(append(v6Addrs, v4Addrs...), Auto) + // Exhaust all IPv6 addresses + a0 := r.AssignAnyAddress(0, nil) + a1 := r.AssignAnyAddress(1, nil) + a2 := r.AssignAnyAddress(2, nil) + a3 := r.AssignAnyAddress(3, nil) + assert.NotNil(t, a0) + assert.NotNil(t, a1) + assert.NotNil(t, a2) + assert.NotNil(t, a3) + // Give back the first IPv6 address to fallback to secondary IPv4 address set + assert.True(t, r.GiveBack(a0, true)) + assert.False(t, r.primaryIsActive) + // Give back another IPv6 address + assert.True(t, r.GiveBack(a1, true)) + // Primary shouldn't change + assert.False(t, r.primaryIsActive) + // Request an address (should be IPv4 from secondary) + a4_v4 := r.AssignAnyAddress(4, nil) + assert.NotNil(t, a4_v4) + assert.Equal(t, V4, a4_v4.IPVersion) + a5_v4 := r.AssignAnyAddress(5, nil) + assert.NotNil(t, a5_v4) + assert.Equal(t, V4, a5_v4.IPVersion) + a6_v4 := r.AssignAnyAddress(6, nil) + assert.NotNil(t, a6_v4) + assert.Equal(t, V4, a6_v4.IPVersion) + // Return IPv4 address (without failure) + // Primary shouldn't change because it is not a connectivity failure + assert.True(t, r.GiveBack(a4_v4, false)) + assert.False(t, r.primaryIsActive) + // Return IPv4 address (with failure) + // Primary should change because it is a connectivity failure + assert.True(t, r.GiveBack(a5_v4, true)) + assert.True(t, r.primaryIsActive) + // Return IPv4 address (with failure) + // Primary shouldn't change because the address is returned to the inactive + // secondary address set + assert.True(t, r.GiveBack(a6_v4, true)) + assert.True(t, r.primaryIsActive) + // Return IPv6 address (without failure) + // Primary shoudn't change because it is not a connectivity failure + assert.True(t, r.GiveBack(a2, false)) + assert.True(t, r.primaryIsActive) } -func TestRegion_GetUnusedIP(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - excluding *EdgeAddr - } - tests := []struct { - name string - fields fields - args args - want *EdgeAddr - }{ - { - name: "happy test with excluding set", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: Unused(), - &addr2: InUse(2), - }}, - args: args{excluding: &addr0}, - want: &addr1, - }, - { - name: "happy test with no excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: Unused(), - &addr2: InUse(2), - }}, - args: args{excluding: nil}, - want: &addr1, - }, - { - name: "sad test with no excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(0), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{excluding: nil}, - want: nil, - }, - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - &addr1: InUse(1), - &addr2: InUse(2), - }}, - args: args{excluding: &addr0}, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestRegion_GiveBack(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - type args struct { - addr *EdgeAddr - } - tests := []struct { - name string - fields fields - args args - wantOk bool - availableAfter int - }{ - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr1: InUse(1), - }}, - args: args{addr: &addr1}, - wantOk: true, - availableAfter: 1, - }, - { - name: "sad test with excluding", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr1: InUse(1), - }}, - args: args{addr: &addr2}, - wantOk: false, - availableAfter: 0, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if gotOk := r.GiveBack(tt.args.addr); gotOk != tt.wantOk { - t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk) - } - if tt.availableAfter != r.AvailableAddrs() { - t.Errorf("Region.AvailableAddrs() = %v, want %v", r.AvailableAddrs(), tt.availableAfter) - } - }) - } -} - -func TestRegion_GetAnyAddress(t *testing.T) { - type fields struct { - connFor map[*EdgeAddr]UsedBy - } - tests := []struct { - name string - fields fields - wantNil bool - }{ - { - name: "Sad test -- GetAnyAddress should only fail if the region is empty", - fields: fields{connFor: map[*EdgeAddr]UsedBy{}}, - wantNil: true, - }, - { - name: "Happy test (all addresses unused)", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: Unused(), - }}, - wantNil: false, - }, - { - name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)", - fields: fields{connFor: map[*EdgeAddr]UsedBy{ - &addr0: InUse(2), - }}, - wantNil: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := Region{ - connFor: tt.fields.connFor, - } - if got := r.GetAnyAddress(); tt.wantNil != (got == nil) { - t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil) - } - }) - } +func TestRegion_GiveBack_Timeout(t *testing.T) { + r := NewRegion(append(v6Addrs, v4Addrs...), Auto) + a0 := r.AssignAnyAddress(0, nil) + a1 := r.AssignAnyAddress(1, nil) + a2 := r.AssignAnyAddress(2, nil) + assert.NotNil(t, a0) + assert.NotNil(t, a1) + assert.NotNil(t, a2) + // Give back IPv6 address to set timeout + assert.True(t, r.GiveBack(a0, true)) + assert.False(t, r.primaryIsActive) + assert.False(t, r.primaryTimeout.IsZero()) + // Request an address (should be IPv4 from secondary) + a3_v4 := r.AssignAnyAddress(3, nil) + assert.NotNil(t, a3_v4) + assert.Equal(t, V4, a3_v4.IPVersion) + assert.False(t, r.primaryIsActive) + // Give back IPv6 address inside timeout (no change) + assert.True(t, r.GiveBack(a2, true)) + assert.False(t, r.primaryIsActive) + assert.False(t, r.primaryTimeout.IsZero()) + // Accelerate timeout + r.primaryTimeout = time.Now().Add(-time.Minute) + // Return IPv6 address + assert.True(t, r.GiveBack(a1, true)) + assert.True(t, r.primaryIsActive) + // Returning an IPv4 address after primary is active shouldn't change primary + // even with a connectivity error + assert.True(t, r.GiveBack(a3_v4, true)) + assert.True(t, r.primaryIsActive) } diff --git a/edgediscovery/allregions/regions.go b/edgediscovery/allregions/regions.go index cec79240..4b2a841b 100644 --- a/edgediscovery/allregions/regions.go +++ b/edgediscovery/allregions/regions.go @@ -18,7 +18,7 @@ type Regions struct { // ------------------------------------ // ResolveEdge resolves the Cloudflare edge, returning all regions discovered. -func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) { +func ResolveEdge(log *zerolog.Logger, region string, overrideIPVersion ConfigIPVersion) (*Regions, error) { edgeAddrs, err := edgeDiscovery(log, getRegionalServiceName(region)) if err != nil { return nil, err @@ -27,8 +27,8 @@ func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) { return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(edgeAddrs)) } return &Regions{ - region1: NewRegion(edgeAddrs[0]), - region2: NewRegion(edgeAddrs[1]), + region1: NewRegion(edgeAddrs[0], overrideIPVersion), + region2: NewRegion(edgeAddrs[1], overrideIPVersion), }, nil } @@ -56,8 +56,8 @@ func NewNoResolve(addrs []*EdgeAddr) *Regions { } return &Regions{ - region1: NewRegion(region1), - region2: NewRegion(region2), + region1: NewRegion(region1, Auto), + region2: NewRegion(region2, Auto), } } @@ -95,14 +95,12 @@ func (rs *Regions) GetUnusedAddr(excluding *EdgeAddr, connID int) *EdgeAddr { // getAddrs tries to grab address form `first` region, then `second` region // this is an unrolled loop over 2 element array func getAddrs(excluding *EdgeAddr, connID int, first *Region, second *Region) *EdgeAddr { - addr := first.GetUnusedIP(excluding) + addr := first.AssignAnyAddress(connID, excluding) if addr != nil { - first.Use(addr, connID) return addr } - addr = second.GetUnusedIP(excluding) + addr = second.AssignAnyAddress(connID, excluding) if addr != nil { - second.Use(addr, connID) return addr } @@ -116,18 +114,18 @@ func (rs *Regions) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (rs *Regions) GiveBack(addr *EdgeAddr) bool { - if found := rs.region1.GiveBack(addr); found { +func (rs *Regions) GiveBack(addr *EdgeAddr, hasConnectivityError bool) bool { + if found := rs.region1.GiveBack(addr, hasConnectivityError); found { return found } - return rs.region2.GiveBack(addr) + return rs.region2.GiveBack(addr, hasConnectivityError) } // Return regionalized service name if `region` isn't empty, otherwise return the global service name for origintunneld func getRegionalServiceName(region string) string { if region != "" { - return region + "-" + srvService // Example: `us-origintunneld` + return region + "-" + srvService // Example: `us-v2-origintunneld` } - return srvService // Global service is just `origintunneld` + return srvService // Global service is just `v2-origintunneld` } diff --git a/edgediscovery/allregions/regions_test.go b/edgediscovery/allregions/regions_test.go index d6446df2..e399c4ee 100644 --- a/edgediscovery/allregions/regions_test.go +++ b/edgediscovery/allregions/regions_test.go @@ -1,134 +1,215 @@ package allregions import ( - "net" "testing" "github.com/stretchr/testify/assert" ) -var ( - addr0 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.0"), - Port: 8000, - Zone: "", - }, +func makeRegions(addrs []*EdgeAddr, mode ConfigIPVersion) Regions { + r1addrs := make([]*EdgeAddr, 0) + r2addrs := make([]*EdgeAddr, 0) + for i, addr := range addrs { + if i%2 == 0 { + r1addrs = append(r1addrs, addr) + } else { + r2addrs = append(r2addrs, addr) + } } - addr1 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.1"), - Port: 8000, - Zone: "", - }, - } - addr2 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.2"), - Port: 8000, - Zone: "", - }, - } - addr3 = EdgeAddr{ - TCP: &net.TCPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - UDP: &net.UDPAddr{ - IP: net.ParseIP("123.4.5.3"), - Port: 8000, - Zone: "", - }, - } -) - -func makeRegions() Regions { - r1 := NewRegion([]*EdgeAddr{&addr0, &addr1}) - r2 := NewRegion([]*EdgeAddr{&addr2, &addr3}) + r1 := NewRegion(r1addrs, mode) + r2 := NewRegion(r2addrs, mode) return Regions{region1: r1, region2: r2} } func TestRegions_AddrUsedBy(t *testing.T) { - rs := makeRegions() - addr1 := rs.GetUnusedAddr(nil, 1) - assert.Equal(t, addr1, rs.AddrUsedBy(1)) - addr2 := rs.GetUnusedAddr(nil, 2) - assert.Equal(t, addr2, rs.AddrUsedBy(2)) - addr3 := rs.GetUnusedAddr(nil, 3) - assert.Equal(t, addr3, rs.AddrUsedBy(3)) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + addr1 := rs.GetUnusedAddr(nil, 1) + assert.Equal(t, addr1, rs.AddrUsedBy(1)) + addr2 := rs.GetUnusedAddr(nil, 2) + assert.Equal(t, addr2, rs.AddrUsedBy(2)) + addr3 := rs.GetUnusedAddr(nil, 3) + assert.Equal(t, addr3, rs.AddrUsedBy(3)) + }) + } } func TestRegions_Giveback_Region1(t *testing.T) { - rs := makeRegions() - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - rs.region2.Use(&addr3, 3) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + addr := rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + rs.region2.AssignAnyAddress(2, nil) + rs.region2.AssignAnyAddress(3, nil) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(&addr0) - assert.Equal(t, &addr0, rs.GetUnusedAddr(nil, 3)) + rs.GiveBack(addr, false) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 0)) + }) + } } func TestRegions_Giveback_Region2(t *testing.T) { - rs := makeRegions() - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - rs.region2.Use(&addr3, 3) + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + addr := rs.region2.AssignAnyAddress(2, nil) + rs.region2.AssignAnyAddress(3, nil) - assert.Equal(t, 0, rs.AvailableAddrs()) + assert.Equal(t, 0, rs.AvailableAddrs()) - rs.GiveBack(&addr2) - assert.Equal(t, &addr2, rs.GetUnusedAddr(nil, 2)) + rs.GiveBack(addr, false) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 2)) + }) + } } func TestRegions_GetUnusedAddr_OneAddrLeft(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + rs.region2.AssignAnyAddress(2, nil) + addr := rs.region2.active.GetUnusedIP(nil) - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) - rs.region2.Use(&addr2, 2) - - assert.Equal(t, 1, rs.AvailableAddrs()) - assert.Equal(t, &addr3, rs.GetUnusedAddr(nil, 3)) + assert.Equal(t, 1, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(nil, 3)) + }) + } } func TestRegions_GetUnusedAddr_Excluding_Region1(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) - rs.region1.Use(&addr0, 0) - rs.region1.Use(&addr1, 1) + rs.region1.AssignAnyAddress(0, nil) + rs.region1.AssignAnyAddress(1, nil) + addr := rs.region2.active.GetUnusedIP(nil) + a2 := rs.region2.active.GetUnusedIP(addr) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, &addr3, rs.GetUnusedAddr(&addr2, 3)) + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(a2, 3)) + }) + } } func TestRegions_GetUnusedAddr_Excluding_Region2(t *testing.T) { - rs := makeRegions() + tests := []struct { + name string + addrs []*EdgeAddr + mode ConfigIPVersion + }{ + { + name: "IPv4 addresses with IPv4Only", + addrs: v4Addrs, + mode: IPv4Only, + }, + { + name: "IPv6 addresses with IPv6Only", + addrs: v6Addrs, + mode: IPv6Only, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rs := makeRegions(tt.addrs, tt.mode) - rs.region2.Use(&addr2, 0) - rs.region2.Use(&addr3, 1) + rs.region2.AssignAnyAddress(0, nil) + rs.region2.AssignAnyAddress(1, nil) + addr := rs.region1.active.GetUnusedIP(nil) + a2 := rs.region1.active.GetUnusedIP(addr) - assert.Equal(t, 2, rs.AvailableAddrs()) - assert.Equal(t, &addr1, rs.GetUnusedAddr(&addr0, 1)) + assert.Equal(t, 2, rs.AvailableAddrs()) + assert.Equal(t, addr, rs.GetUnusedAddr(a2, 1)) + }) + } } func TestNewNoResolveBalancesRegions(t *testing.T) { diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index df9c42cc..b5f338e8 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -11,9 +11,10 @@ import ( const ( LogFieldConnIndex = "connIndex" + LogFieldIPAddress = "ip" ) -var errNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") +var ErrNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") // Edge finds addresses on the Cloudflare edge and hands them out to connections. type Edge struct { @@ -28,8 +29,8 @@ type Edge struct { // ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated // to connections. -func ResolveEdge(log *zerolog.Logger, region string) (*Edge, error) { - regions, err := allregions.ResolveEdge(log, region) +func ResolveEdge(log *zerolog.Logger, region string, edgeIpVersion allregions.ConfigIPVersion) (*Edge, error) { + regions, err := allregions.ResolveEdge(log, region, edgeIpVersion) if err != nil { return new(Edge), err } @@ -51,15 +52,6 @@ func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) { }, nil } -// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. -func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { - regions := allregions.NewNoResolve(addrs) - return &Edge{ - log: log, - regions: regions, - } -} - // ------------------------------------ // Methods // ------------------------------------ @@ -70,7 +62,7 @@ func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) { defer ed.Unlock() addr := ed.regions.GetAnyAddress() if addr == nil { - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } return addr, nil } @@ -91,14 +83,17 @@ func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) { addr := ed.regions.GetUnusedAddr(nil, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetAddr: No addresses left to give proxy connection") - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } - log.Debug().Msg("edgediscovery - GetAddr: Giving connection its new address") + log = ed.log.With(). + Int(LogFieldConnIndex, connIndex). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GetAddr: Giving connection its new address") return addr, nil } // GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one. -func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { +func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*allregions.EdgeAddr, error) { log := ed.log.With().Int(LogFieldConnIndex, connIndex).Logger() ed.Lock() @@ -106,16 +101,18 @@ func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) { oldAddr := ed.regions.AddrUsedBy(connIndex) if oldAddr != nil { - ed.regions.GiveBack(oldAddr) + ed.regions.GiveBack(oldAddr, hasConnectivityError) } addr := ed.regions.GetUnusedAddr(oldAddr, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection") // note: if oldAddr were not nil, it will become available on the next iteration - return nil, errNoAddressesLeft + return nil, ErrNoAddressesLeft } - log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address: %v from the address list: %v", - addr, ed.regions.AvailableAddrs()) + log = ed.log.With(). + Int(LogFieldConnIndex, connIndex). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address from the address list: %v", ed.regions.AvailableAddrs()) return addr, nil } @@ -128,9 +125,11 @@ func (ed *Edge) AvailableAddrs() int { // GiveBack the address so that other connections can use it. // Returns true if the address is in this edge. -func (ed *Edge) GiveBack(addr *allregions.EdgeAddr) bool { +func (ed *Edge) GiveBack(addr *allregions.EdgeAddr, hasConnectivityError bool) bool { ed.Lock() defer ed.Unlock() - ed.log.Debug().Msg("edgediscovery - GiveBack: Address now unused") - return ed.regions.GiveBack(addr) + log := ed.log.With(). + IPAddr(LogFieldIPAddress, addr.UDP.IP).Logger() + log.Debug().Msgf("edgediscovery - GiveBack: Address now unused") + return ed.regions.GiveBack(addr, hasConnectivityError) } diff --git a/edgediscovery/edgediscovery_test.go b/edgediscovery/edgediscovery_test.go index 9cc93807..55288ce5 100644 --- a/edgediscovery/edgediscovery_test.go +++ b/edgediscovery/edgediscovery_test.go @@ -11,56 +11,113 @@ import ( ) var ( - addr0 = allregions.EdgeAddr{ + testLogger = zerolog.Nop() + v4Addrs = []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3} + v6Addrs = []*allregions.EdgeAddr{&addr4, &addr5, &addr6, &addr7} + addr0 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.0"), + IP: net.ParseIP("123.4.5.0"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.0"), + IP: net.ParseIP("123.4.5.0"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr1 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.1"), + IP: net.ParseIP("123.4.5.1"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.1"), + IP: net.ParseIP("123.4.5.1"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr2 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.2"), + IP: net.ParseIP("123.4.5.2"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.2"), + IP: net.ParseIP("123.4.5.2"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, } addr3 = allregions.EdgeAddr{ TCP: &net.TCPAddr{ - IP: net.ParseIP("123.0.0.3"), + IP: net.ParseIP("123.4.5.3"), Port: 8000, Zone: "", }, UDP: &net.UDPAddr{ - IP: net.ParseIP("123.0.0.3"), + IP: net.ParseIP("123.4.5.3"), Port: 8000, Zone: "", }, + IPVersion: allregions.V4, + } + addr4 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::1"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr5 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::2"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr6 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::3"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, + } + addr7 = allregions.EdgeAddr{ + TCP: &net.TCPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + UDP: &net.UDPAddr{ + IP: net.ParseIP("2606:4700:a0::4"), + Port: 8000, + Zone: "", + }, + IPVersion: allregions.V6, } - - testLogger = zerolog.Nop() ) func TestGiveBack(t *testing.T) { @@ -75,7 +132,7 @@ func TestGiveBack(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr) + edge.GiveBack(addr, false) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -107,7 +164,7 @@ func TestGetAddrForRPC(t *testing.T) { assert.Equal(t, 4, edge.AvailableAddrs()) // Get it back - edge.GiveBack(addr) + edge.GiveBack(addr, false) assert.Equal(t, 4, edge.AvailableAddrs()) } @@ -122,13 +179,13 @@ func TestOnePerRegion(t *testing.T) { assert.NotNil(t, a1) // if the first address is bad, get the second one - a2, err := edge.GetDifferentAddr(connID) + a2, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotNil(t, a2) assert.NotEqual(t, a1, a2) // now that second one is bad, get the first one again - a3, err := edge.GetDifferentAddr(connID) + a3, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.Equal(t, a1, a3) } @@ -144,11 +201,11 @@ func TestOnlyOneAddrLeft(t *testing.T) { assert.NotNil(t, addr) // If that edge address is "bad", there's no alternative address. - _, err = edge.GetDifferentAddr(connID) + _, err = edge.GetDifferentAddr(connID, false) assert.Error(t, err) // previously bad address should become available again on next iteration. - addr, err = edge.GetDifferentAddr(connID) + addr, err = edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotNil(t, addr) } @@ -190,8 +247,17 @@ func TestGetDifferentAddr(t *testing.T) { assert.Equal(t, 3, edge.AvailableAddrs()) // If the same connection requests another address, it should get the same one. - addr2, err := edge.GetDifferentAddr(connID) + addr2, err := edge.GetDifferentAddr(connID, false) assert.NoError(t, err) assert.NotEqual(t, addr, addr2) assert.Equal(t, 3, edge.AvailableAddrs()) } + +// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing. +func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge { + regions := allregions.NewNoResolve(addrs) + return &Edge{ + log: log, + regions: regions, + } +} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 62bf6ec3..4bec3f56 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -7,7 +7,6 @@ import ( "time" "github.com/google/uuid" - "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/connection" @@ -42,6 +41,7 @@ type Supervisor struct { config *TunnelConfig orchestrator *orchestration.Orchestrator edgeIPs *edgediscovery.Edge + edgeTunnelServer EdgeTunnelServer tunnelErrors chan tunnelError tunnelsConnecting map[int]chan struct{} // nextConnectedIndex and nextConnectedSignal are used to wait for all @@ -76,12 +76,34 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato if len(config.EdgeAddrs) > 0 { edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs) } else { - edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region) + edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region, config.EdgeIPVersion) } if err != nil { return nil, err } + reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) + log := NewConnAwareLogger(config.Log, config.Observer) + + var edgeAddrHandler EdgeAddrHandler + if config.EdgeIPVersion == allregions.IPv6Only || config.EdgeIPVersion == allregions.Auto { + edgeAddrHandler = &IPAddrFallback{} + } else { // IPv4Only + edgeAddrHandler = &DefaultAddrFallback{} + } + + edgeTunnelServer := EdgeTunnelServer{ + config: config, + cloudflaredUUID: cloudflaredUUID, + orchestrator: orchestrator, + credentialManager: reconnectCredentialManager, + edgeAddrs: edgeIPs, + edgeAddrHandler: edgeAddrHandler, + reconnectCh: reconnectCh, + gracefulShutdownC: gracefulShutdownC, + connAwareLogger: log, + } + useReconnectToken := false if config.ClassicTunnel != nil { useReconnectToken = config.ClassicTunnel.UseReconnectToken @@ -92,11 +114,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato config: config, orchestrator: orchestrator, edgeIPs: edgeIPs, + edgeTunnelServer: edgeTunnelServer, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, - log: NewConnAwareLogger(config.Log, config.Observer), + log: log, logTransport: config.LogTransport, - reconnectCredentialManager: newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections), + reconnectCredentialManager: reconnectCredentialManager, useReconnectToken: useReconnectToken, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, @@ -143,11 +166,18 @@ func (s *Supervisor) Run( tunnelsActive-- } return nil - // startTunnel returned with error + // startTunnel completed with a response // (note that this may also be caused by context cancellation) case tunnelError := <-s.tunnelErrors: tunnelsActive-- if tunnelError.err != nil && !shuttingDown { + switch tunnelError.err.(type) { + case ReconnectSignal: + // For tunnels that closed with reconnect signal, we reconnect immediately + go s.startTunnel(ctx, tunnelError.index, s.newConnectedTunnelSignal(tunnelError.index)) + tunnelsActive++ + continue + } s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated") tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) s.waitForNextTunnel(tunnelError.index) @@ -155,10 +185,9 @@ func (s *Supervisor) Run( if backoffTimer == nil { backoffTimer = backoff.BackoffTimer() } - - // Previously we'd mark the edge address as bad here, but now we'll just silently use another. } else if tunnelsActive == 0 { - // all connected tunnels exited gracefully, no more work to do + s.log.ConnAwareLogger().Msg("no more connections active and exiting") + // All connected tunnels exited gracefully, no more work to do return nil } // Backoff was set and its timer expired @@ -192,6 +221,8 @@ func (s *Supervisor) Run( } // Returns nil if initialization succeeded, else the initialization error. +// Attempts here will be made to connect one tunnel, if successful, it will +// connect the available tunnels up to config.HAConnections. func (s *Supervisor) initialize( ctx context.Context, connectedSignal *signal.Signal, @@ -203,6 +234,8 @@ func (s *Supervisor) initialize( } go s.startFirstTunnel(ctx, connectedSignal) + + // Wait for response from first tunnel before proceeding to attempt other HA edge tunnels select { case <-ctx.Done(): <-s.tunnelErrors @@ -213,6 +246,7 @@ func (s *Supervisor) initialize( return errEarlyShutdown case <-connectedSignal.Wait(): } + // At least one successful connection, so start the rest for i := 1; i < s.config.HAConnections; i++ { ch := signal.New(make(chan struct{})) @@ -229,102 +263,42 @@ func (s *Supervisor) startFirstTunnel( connectedSignal *signal.Signal, ) { var ( - addr *allregions.EdgeAddr - err error + err error ) const firstConnIndex = 0 defer func() { s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err} }() - addr, err = s.edgeIPs.GetAddr(firstConnIndex) - if err != nil { - return - } + err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - firstConnIndex, - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) // If the first tunnel disconnects, keep restarting it. - edgeErrors := 0 for s.unusedIPs() { if ctx.Err() != nil { return } - switch err.(type) { - case nil: - return - // try the next address if it was a quic.IdleTimeoutError, dialError(network problem) or - // dupConnRegisterTunnelError - case *quic.IdleTimeoutError, edgediscovery.DialError, connection.DupConnRegisterTunnelError: - edgeErrors++ - default: + if err == nil { return } - if edgeErrors >= 2 { - addr, err = s.edgeIPs.GetDifferentAddr(firstConnIndex) - if err != nil { - return - } - } - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - firstConnIndex, - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) + err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) } } // startTunnel starts a new tunnel connection. The resulting error will be sent on -// s.tunnelErrors. +// s.tunnelError as this is expected to run in a goroutine. func (s *Supervisor) startTunnel( ctx context.Context, index int, connectedSignal *signal.Signal, ) { var ( - addr *allregions.EdgeAddr - err error + err error ) defer func() { s.tunnelErrors <- tunnelError{index: index, err: err} }() - addr, err = s.edgeIPs.GetDifferentAddr(index) - if err != nil { - return - } - err = ServeTunnelLoop( - ctx, - s.reconnectCredentialManager, - s.config, - s.orchestrator, - addr, - s.log, - uint8(index), - connectedSignal, - s.cloudflaredUUID, - s.reconnectCh, - s.gracefulShutdownC, - ) + err = s.edgeTunnelServer.Serve(ctx, uint8(index), connectedSignal) } func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal { diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index a75fe7b7..8d3fe9f0 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -122,28 +122,84 @@ func StartTunnelDaemon( return s.Run(ctx, connectedSignal) } -func ServeTunnelLoop( - ctx context.Context, - credentialManager *reconnectCredentialManager, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, - addr *allregions.EdgeAddr, - connAwareLogger *ConnAwareLogger, - connIndex uint8, - connectedSignal *signal.Signal, - cloudflaredUUID uuid.UUID, - reconnectCh chan ReconnectSignal, - gracefulShutdownC <-chan struct{}, -) error { +// EdgeAddrHandler provides a mechanism switch between behaviors in ServeTunnel +// for handling the errors when attempting to make edge connections. +type EdgeAddrHandler interface { + // ShouldGetNewAddress will check the edge connection error and determine if + // the edge address should be replaced with a new one. Also, will return if the + // error should be recognized as a connectivity error, or otherwise, a general + // application error. + ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) +} + +// DefaultAddrFallback will always return false for isConnectivityError since this +// handler is a way to provide the legacy behavior in the new edge discovery algorithm. +type DefaultAddrFallback struct { + edgeErrors int +} + +func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { + switch err.(type) { + case nil: // maintain current IP address + // Try the next address if it was a quic.IdleTimeoutError or + // dupConnRegisterTunnelError + case *quic.IdleTimeoutError, + connection.DupConnRegisterTunnelError, + edgediscovery.DialError, + *connection.EdgeQuicDialError: + // Wait for two failures before falling back to a new address + f.edgeErrors++ + if f.edgeErrors >= 2 { + f.edgeErrors = 0 + return true, false + } + default: // maintain current IP address + } + return false, false +} + +// IPAddrFallback will have more conditions to fall back to a new address for certain +// edge connection errors. This means that this handler will return true for isConnectivityError +// for more cases like duplicate connection register and edge quic dial errors. +type IPAddrFallback struct{} + +func (f IPAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { + switch err.(type) { + case nil: // maintain current IP address + // Try the next address if it was a quic.IdleTimeoutError + // DupConnRegisterTunnelError needs to also receive a new ip address + case connection.DupConnRegisterTunnelError, + *quic.IdleTimeoutError: + return true, false + // Network problems should be retried with new address immediately and report + // as connectivity error + case edgediscovery.DialError, *connection.EdgeQuicDialError: + return true, true + default: // maintain current IP address + } + return false, false +} + +type EdgeTunnelServer struct { + config *TunnelConfig + cloudflaredUUID uuid.UUID + orchestrator *orchestration.Orchestrator + credentialManager *reconnectCredentialManager + edgeAddrHandler EdgeAddrHandler + edgeAddrs *edgediscovery.Edge + reconnectCh chan ReconnectSignal + gracefulShutdownC <-chan struct{} + + connAwareLogger *ConnAwareLogger +} + +func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedSignal *signal.Signal) error { haConnections.Inc() defer haConnections.Dec() - logger := config.Log.With().Uint8(connection.LogFieldConnIndex, connIndex).Logger() - connLog := connAwareLogger.ReplaceLogger(&logger) - protocolFallback := &protocolFallback{ - retry.BackoffHandler{MaxRetries: config.Retries}, - config.ProtocolSelector.Current(), + retry.BackoffHandler{MaxRetries: e.config.Retries}, + e.config.ProtocolSelector.Current(), false, } connectedFuse := h2mux.NewBooleanFuse() @@ -154,54 +210,81 @@ func ServeTunnelLoop( }() // Ensure the above goroutine will terminate if we return without connecting defer connectedFuse.Fuse(false) + + // Fetch IP address to associated connection index + addr, err := e.edgeAddrs.GetAddr(int(connIndex)) + switch err { + case nil: // no error + case edgediscovery.ErrNoAddressesLeft: + return err + default: + return err + } + + logger := e.config.Log.With(). + IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). + Uint8(connection.LogFieldConnIndex, connIndex). + Logger() + connLog := e.connAwareLogger.ReplaceLogger(&logger) // Each connection to keep its own copy of protocol, because individual connections might fallback // to another protocol when a particular metal doesn't support new protocol - for { - err, recoverable := ServeTunnel( - ctx, - connLog, - credentialManager, - config, - orchestrator, - addr, - connIndex, - connectedFuse, - protocolFallback, - cloudflaredUUID, - reconnectCh, - protocolFallback.protocol, - gracefulShutdownC, - ) + // Each connection can also have it's own IP version because individual connections might fallback + // to another IP version. + err, recoverable := ServeTunnel( + ctx, + connLog, + e.credentialManager, + e.config, + e.orchestrator, + addr, + connIndex, + connectedFuse, + protocolFallback, + e.cloudflaredUUID, + e.reconnectCh, + protocolFallback.protocol, + e.gracefulShutdownC, + ) - if recoverable { - duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) - if !ok { - return err - } - config.Observer.SendReconnect(connIndex) - connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) + // If the connection is recoverable, we want to maintain the same IP + // but backoff a reconnect with some duration. + if recoverable { + duration, ok := protocolFallback.GetMaxBackoffDuration(ctx) + if !ok { + return err + } + e.config.Observer.SendReconnect(connIndex) + connLog.Logger().Info().Msgf("Retrying connection in up to %s seconds", duration) + } + + // Check if the connection error was from an IP issue with the host or + // establishing a connection to the edge and if so, rotate the IP address. + yes, hasConnectivityError := e.edgeAddrHandler.ShouldGetNewAddress(err) + if yes { + e.edgeAddrs.GetDifferentAddr(int(connIndex), hasConnectivityError) + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-e.gracefulShutdownC: + return nil + case <-protocolFallback.BackoffTimer(): + if !recoverable { + return err } - select { - case <-ctx.Done(): - return ctx.Err() - case <-gracefulShutdownC: - return nil - case <-protocolFallback.BackoffTimer(): - if !recoverable { - return err - } - - if !selectNextProtocol( - connLog.Logger(), - protocolFallback, - config.ProtocolSelector, - err, - ) { - return err - } + if !selectNextProtocol( + connLog.Logger(), + protocolFallback, + e.config.ProtocolSelector, + err, + ) { + return err } } + + return err } // protocolFallback is a wrapper around backoffHandler that will try fallback option when backoff reaches @@ -233,6 +316,10 @@ func selectNextProtocol( ) bool { var idleTimeoutError *quic.IdleTimeoutError isNetworkActivityTimeout := errors.As(cause, &idleTimeoutError) + edgeQuicDialError, ok := cause.(*connection.EdgeQuicDialError) + if !isNetworkActivityTimeout && ok { + isNetworkActivityTimeout = errors.As(edgeQuicDialError.Cause, &idleTimeoutError) + } _, hasFallback := selector.Fallback() if protocolBackoff.ReachedMaxRetries() || (hasFallback && isNetworkActivityTimeout) { @@ -241,7 +328,7 @@ func selectNextProtocol( "Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " + "UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips/\n" + - "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work" + + "If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work " + "unless your cloudflared can connect with Cloudflare Network with `quic`.") } @@ -326,8 +413,12 @@ func ServeTunnel( connLog.ConnAwareLogger().Msg(activeIncidentsMsg(incidents)) } return err.Cause, !err.Permanent + case *connection.EdgeQuicDialError: + // Don't retry connection for a dial error + return err, false case ReconnectSignal: connLog.Logger().Info(). + IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). Uint8(connection.LogFieldConnIndex, connIndex). Msgf("Restarting connection due to reconnect signal in %s", err.Delay) err.DelayBeforeReconnect() @@ -526,6 +617,7 @@ func ServeHTTP2( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + connLog.Logger().Debug().Msg("Forcefully breaking http2 connection") _ = tlsServerConn.Close() } return err @@ -584,6 +676,7 @@ func ServeQUIC( err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + connLogger.Logger().Debug().Msg("Forcefully breaking quic connection") quicConn.Close() } return err From 2fa50acc2d845bcbb960717becf3a4576b9d8541 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 13 Jun 2022 10:36:40 -0700 Subject: [PATCH 127/238] TUN-6384: Correct duplicate connection error to fetch new IP first (cherry picked from commit 76add5ca77a5d9fd1e474b325952ae7c678e44f1) --- supervisor/tunnel.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 8d3fe9f0..fcbace54 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -141,10 +141,11 @@ type DefaultAddrFallback struct { func (f DefaultAddrFallback) ShouldGetNewAddress(err error) (needsNewAddress bool, isConnectivityError bool) { switch err.(type) { case nil: // maintain current IP address - // Try the next address if it was a quic.IdleTimeoutError or - // dupConnRegisterTunnelError + // DupConnRegisterTunnelError should indicate to get a new address immediately + case connection.DupConnRegisterTunnelError: + return true, false + // Try the next address if it was a quic.IdleTimeoutError case *quic.IdleTimeoutError, - connection.DupConnRegisterTunnelError, edgediscovery.DialError, *connection.EdgeQuicDialError: // Wait for two failures before falling back to a new address From ae7fbc14f351ca403395e7c23c18c21589e46844 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 13 Jun 2022 10:40:21 -0700 Subject: [PATCH 128/238] TUN-6373: Add edge-ip-version to remotely pushed configuration (cherry picked from commit 8e9091cc483e83a14845148faa6353ef77638334) --- cmd/cloudflared/tunnel/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 1e46b487..36257cc7 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -45,7 +45,7 @@ var ( secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} - configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq"} + configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq", "edge-ip-version"} ) // returns the first path that contains a cert.pem file. If none of the DefaultConfigSearchDirectories From e921ab35d56c5e5c93036c487a38d9e11c083d42 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 7 Jun 2022 12:32:29 -0700 Subject: [PATCH 129/238] TUN-6010: Add component tests for --edge-ip-version (cherry picked from commit 978e01f77eedf4f098f71423b42e639d12355015) --- component-tests/cli.py | 95 ++++++++++++++ component-tests/test_edge_discovery.py | 165 +++++++++++++++++++++++++ component-tests/test_service.py | 8 +- component-tests/util.py | 18 +++ 4 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 component-tests/cli.py create mode 100644 component-tests/test_edge_discovery.py diff --git a/component-tests/cli.py b/component-tests/cli.py new file mode 100644 index 00000000..ae4a1b12 --- /dev/null +++ b/component-tests/cli.py @@ -0,0 +1,95 @@ +import json +import subprocess +from time import sleep + +from setup import get_config_from_file + +SINGLE_CASE_TIMEOUT = 600 + +class CloudflaredCli: + def __init__(self, config, config_path, logger): + self.basecmd = [config.cloudflared_binary, "tunnel"] + if config_path is not None: + self.basecmd += ["--config", str(config_path)] + origincert = get_config_from_file()["origincert"] + if origincert: + self.basecmd += ["--origincert", origincert] + self.logger = logger + + def _run_command(self, subcmd, subcmd_name, needs_to_pass=True): + cmd = self.basecmd + subcmd + # timeout limits the time a subprocess can run. This is useful to guard against running a tunnel when + # command/args are in wrong order. + result = run_subprocess(cmd, subcmd_name, self.logger, check=needs_to_pass, capture_output=True, timeout=15) + return result + + def list_tunnels(self): + cmd_args = ["list", "--output", "json"] + listed = self._run_command(cmd_args, "list") + return json.loads(listed.stdout) + + def get_tunnel_info(self, tunnel_id): + info = self._run_command(["info", "--output", "json", tunnel_id], "info") + return json.loads(info.stdout) + + def __enter__(self): + self.basecmd += ["run"] + self.process = subprocess.Popen(self.basecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.logger.info(f"Run cmd {self.basecmd}") + return self.process + + def __exit__(self, exc_type, exc_value, exc_traceback): + terminate_gracefully(self.process, self.logger, self.basecmd) + self.logger.debug(f"{self.basecmd} logs: {self.process.stderr.read()}") + + +def terminate_gracefully(process, logger, cmd): + process.terminate() + process_terminated = wait_for_terminate(process) + if not process_terminated: + process.kill() + logger.warning(f"{cmd}: cloudflared did not terminate within wait period. Killing process. logs: \ + stdout: {process.stdout.read()}, stderr: {process.stderr.read()}") + + +def wait_for_terminate(opened_subprocess, attempts=10, poll_interval=1): + """ + wait_for_terminate polls the opened_subprocess every x seconds for a given number of attempts. + It returns true if the subprocess was terminated and false if it didn't. + """ + for _ in range(attempts): + if _is_process_stopped(opened_subprocess): + return True + sleep(poll_interval) + return False + + +def _is_process_stopped(process): + return process.poll() is not None + + +def cert_path(): + return get_config_from_file()["origincert"] + + +class SubprocessError(Exception): + def __init__(self, program, exit_code, cause): + self.program = program + self.exit_code = exit_code + self.cause = cause + + +def run_subprocess(cmd, cmd_name, logger, timeout=SINGLE_CASE_TIMEOUT, **kargs): + kargs["timeout"] = timeout + try: + result = subprocess.run(cmd, **kargs) + logger.debug(f"{cmd} log: {result.stdout}", extra={"cmd": cmd_name}) + return result + except subprocess.CalledProcessError as e: + err = f"{cmd} return exit code {e.returncode}, stderr" + e.stderr.decode("utf-8") + logger.error(err, extra={"cmd": cmd_name, "return_code": e.returncode}) + raise SubprocessError(cmd[0], e.returncode, e) + except subprocess.TimeoutExpired as e: + err = f"{cmd} timeout after {e.timeout} seconds, stdout: {e.stdout}, stderr: {e.stderr}" + logger.error(err, extra={"cmd": cmd_name, "return_code": "timeout"}) + raise e \ No newline at end of file diff --git a/component-tests/test_edge_discovery.py b/component-tests/test_edge_discovery.py new file mode 100644 index 00000000..61e036a3 --- /dev/null +++ b/component-tests/test_edge_discovery.py @@ -0,0 +1,165 @@ +import ipaddress +import socket + +import pytest + +from constants import protocols +from cli import CloudflaredCli +from util import get_tunnel_connector_id, LOGGER, wait_tunnel_ready, write_config + + +class TestEdgeDiscovery: + def _extra_config(self, protocol, edge_ip_version): + config = { + "protocol": protocol, + } + if edge_ip_version: + config["edge-ip-version"] = edge_ip_version + return config + + @pytest.mark.parametrize("protocol", protocols()) + def test_default_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv4-only edge addresses (default is unset "--edge-ip-version 4") + """ + if self.has_ipv6_only(): + pytest.skip("Host has IPv6 only support and current default is IPv4 only") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, None, self.expect_ipv4_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_ipv4_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv4-only edge addresses + """ + if self.has_ipv6_only(): + pytest.skip("Host has IPv6 only support") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "4", self.expect_ipv4_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_ipv6_only(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via IPv6-only edge addresses + """ + if self.has_ipv4_only(): + pytest.skip("Host has IPv4 only support") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "6", self.expect_ipv6_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_auto_ip64(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via auto with a preference of IPv6 then IPv4 addresses for a dual stack host + + This test also assumes that the host has IPv6 preference. + """ + if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET6): + pytest.skip("Host does not support dual stack with IPv6 preference") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "auto", self.expect_ipv6_address) + + @pytest.mark.parametrize("protocol", protocols()) + def test_auto_ip46(self, tmp_path, component_tests_config, protocol): + """ + This test runs a tunnel to connect via auto with a preference of IPv4 then IPv6 addresses for a dual stack host + + This test also assumes that the host has IPv4 preference. + """ + if not self.has_dual_stack(address_family_preference=socket.AddressFamily.AF_INET): + pytest.skip("Host does not support dual stack with IPv4 preference") + self.expect_address_connections( + tmp_path, component_tests_config, protocol, "auto", self.expect_ipv4_address) + + def expect_address_connections(self, tmp_path, component_tests_config, protocol, edge_ip_version, assert_address_type): + config = component_tests_config( + self._extra_config(protocol, edge_ip_version)) + config_path = write_config(tmp_path, config.full_config) + LOGGER.debug(config) + with CloudflaredCli(config, config_path, LOGGER): + wait_tunnel_ready(tunnel_url=config.get_url(), + require_min_connections=4) + cfd_cli = CloudflaredCli(config, config_path, LOGGER) + tunnel_id = config.get_tunnel_id() + info = cfd_cli.get_tunnel_info(tunnel_id) + connector_id = get_tunnel_connector_id() + connector = next( + (c for c in info["conns"] if c["id"] == connector_id), None) + assert connector, f"Expected connection info from get tunnel info for the connected instance: {info}" + conns = connector["conns"] + assert conns == None or len( + conns) == 4, f"There should be 4 connections registered: {conns}" + for conn in conns: + origin_ip = conn["origin_ip"] + assert origin_ip, f"No available origin_ip for this connection: {conn}" + assert_address_type(origin_ip) + + def expect_ipv4_address(self, address): + assert type(ipaddress.ip_address( + address)) is ipaddress.IPv4Address, f"Expected connection from origin to be a valid IPv4 address: {address}" + + def expect_ipv6_address(self, address): + assert type(ipaddress.ip_address( + address)) is ipaddress.IPv6Address, f"Expected connection from origin to be a valid IPv6 address: {address}" + + def get_addresses(self): + """ + Returns a list of addresses for the host. + """ + host_addresses = socket.getaddrinfo( + "region1.v2.argotunnel.com", 7844, socket.AF_UNSPEC, socket.SOCK_STREAM) + assert len( + host_addresses) > 0, "No addresses returned from getaddrinfo" + return host_addresses + + def has_dual_stack(self, address_family_preference=None): + """ + Returns true if the host has dual stack support and can optionally check + the provided IP family preference. + """ + dual_stack = not self.has_ipv6_only() and not self.has_ipv4_only() + if address_family_preference: + address = self.get_addresses()[0] + return dual_stack and address[0] == address_family_preference + + return dual_stack + + def has_ipv6_only(self): + """ + Returns True if the host has only IPv6 address support. + """ + return self.attempt_connection(socket.AddressFamily.AF_INET6) and not self.attempt_connection(socket.AddressFamily.AF_INET) + + def has_ipv4_only(self): + """ + Returns True if the host has only IPv4 address support. + """ + return self.attempt_connection(socket.AddressFamily.AF_INET) and not self.attempt_connection(socket.AddressFamily.AF_INET6) + + def attempt_connection(self, address_family): + """ + Returns True if a successful socket connection can be made to the + remote host with the provided address family to validate host support + for the provided address family. + """ + address = None + for a in self.get_addresses(): + if a[0] == address_family: + address = a + break + if address is None: + # Couldn't even lookup the address family so we can't connect + return False + af, socktype, proto, canonname, sockaddr = address + s = None + try: + s = socket.socket(af, socktype, proto) + except OSError: + return False + try: + s.connect(sockaddr) + except OSError: + s.close() + return False + s.close() + return True diff --git a/component-tests/test_service.py b/component-tests/test_service.py index f8ace641..e1c155e6 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import os import pathlib -import platform import subprocess from contextlib import contextmanager from pathlib import Path @@ -10,12 +9,7 @@ import pytest import test_logging from conftest import CfdModes -from util import start_cloudflared, wait_tunnel_ready, write_config - - -def select_platform(plat): - return pytest.mark.skipif( - platform.system() != plat, reason=f"Only runs on {plat}") +from util import select_platform, start_cloudflared, wait_tunnel_ready, write_config def default_config_dir(): diff --git a/component-tests/util.py b/component-tests/util.py index db8f925d..aae9b85f 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -1,9 +1,12 @@ import logging import os +import platform import subprocess from contextlib import contextmanager from time import sleep +import pytest + import requests import yaml from retrying import retry @@ -12,6 +15,10 @@ from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS LOGGER = logging.getLogger(__name__) +def select_platform(plat): + return pytest.mark.skipif( + platform.system() != plat, reason=f"Only runs on {plat}") + def write_config(directory, config): config_path = directory / "config.yml" @@ -111,6 +118,17 @@ def check_tunnel_not_connected(): LOGGER.warning(f"Failed to connect to {url}, error: {e}") +def get_tunnel_connector_id(): + url = f'http://localhost:{METRICS_PORT}/ready' + + try: + resp = requests.get(url, timeout=1) + return resp.json()["connectorId"] + # cloudflared might already terminated + except requests.exceptions.ConnectionError as e: + LOGGER.warning(f"Failed to connect to {url}, error: {e}") + + # In some cases we don't need to check response status, such as when sending batch requests to generate logs def send_requests(url, count, require_ok=True): errors = 0 From dd540af695448ea1bfef28762060fb98455d455d Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 17 Jun 2022 17:24:37 -0700 Subject: [PATCH 130/238] TUN-6388: Fix first tunnel connection not retrying --- connection/control.go | 6 ++- connection/http2_test.go | 5 +++ connection/rpc.go | 6 ++- edgediscovery/edgediscovery.go | 15 ++++--- supervisor/supervisor.go | 73 +++++++++++++++++++++++++++------- supervisor/tunnel.go | 14 +++---- 6 files changed, 89 insertions(+), 30 deletions(-) diff --git a/connection/control.go b/connection/control.go index 1b28ceb1..a7d49772 100644 --- a/connection/control.go +++ b/connection/control.go @@ -3,6 +3,7 @@ package connection import ( "context" "io" + "net" "time" "github.com/rs/zerolog" @@ -19,6 +20,7 @@ type controlStream struct { connectedFuse ConnectedFuse namedTunnelProperties *NamedTunnelProperties connIndex uint8 + edgeAddress net.IP newRPCClientFunc RPCClientFunc @@ -45,6 +47,7 @@ func NewControlStream( connectedFuse ConnectedFuse, namedTunnelConfig *NamedTunnelProperties, connIndex uint8, + edgeAddress net.IP, newRPCClientFunc RPCClientFunc, gracefulShutdownC <-chan struct{}, gracePeriod time.Duration, @@ -58,6 +61,7 @@ func NewControlStream( namedTunnelProperties: namedTunnelConfig, newRPCClientFunc: newRPCClientFunc, connIndex: connIndex, + edgeAddress: edgeAddress, gracefulShutdownC: gracefulShutdownC, gracePeriod: gracePeriod, } @@ -71,7 +75,7 @@ func (c *controlStream) ServeControlStream( ) error { rpcClient := c.newRPCClientFunc(ctx, rw, c.observer.log) - registrationDetails, err := rpcClient.RegisterConnection(ctx, c.namedTunnelProperties, connOptions, c.connIndex, c.observer) + registrationDetails, err := rpcClient.RegisterConnection(ctx, c.namedTunnelProperties, connOptions, c.connIndex, c.edgeAddress, c.observer) if err != nil { rpcClient.Close() return err diff --git a/connection/http2_test.go b/connection/http2_test.go index 18e688eb..82368d67 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -41,6 +41,7 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { connIndex, nil, nil, + nil, 1*time.Second, ) return NewHTTP2Connection( @@ -176,6 +177,7 @@ func (mc mockNamedTunnelRPCClient) RegisterConnection( properties *NamedTunnelProperties, options *tunnelpogs.ConnectionOptions, connIndex uint8, + edgeAddress net.IP, observer *Observer, ) (*tunnelpogs.ConnectionDetails, error) { if mc.shouldFail != nil { @@ -360,6 +362,7 @@ func TestServeControlStream(t *testing.T) { mockConnectedFuse{}, &NamedTunnelProperties{}, 1, + nil, rpcClientFactory.newMockRPCClient, nil, 1*time.Second, @@ -410,6 +413,7 @@ func TestFailRegistration(t *testing.T) { mockConnectedFuse{}, &NamedTunnelProperties{}, http2Conn.connIndex, + nil, rpcClientFactory.newMockRPCClient, nil, 1*time.Second, @@ -456,6 +460,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { mockConnectedFuse{}, &NamedTunnelProperties{}, http2Conn.connIndex, + nil, rpcClientFactory.newMockRPCClient, shutdownC, 1*time.Second, diff --git a/connection/rpc.go b/connection/rpc.go index a4444ea3..b288a0f8 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -58,6 +58,7 @@ type NamedTunnelRPCClient interface { config *NamedTunnelProperties, options *tunnelpogs.ConnectionOptions, connIndex uint8, + edgeAddress net.IP, observer *Observer, ) (*tunnelpogs.ConnectionDetails, error) SendLocalConfiguration( @@ -95,6 +96,7 @@ func (rsc *registrationServerClient) RegisterConnection( properties *NamedTunnelProperties, options *tunnelpogs.ConnectionOptions, connIndex uint8, + edgeAddress net.IP, observer *Observer, ) (*tunnelpogs.ConnectionDetails, error) { conn, err := rsc.client.RegisterConnection( @@ -115,7 +117,7 @@ func (rsc *registrationServerClient) RegisterConnection( observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() - observer.logServerInfo(connIndex, conn.Location, options.OriginLocalIP, fmt.Sprintf("Connection %s registered", conn.UUID)) + observer.logServerInfo(connIndex, conn.Location, edgeAddress, fmt.Sprintf("Connection %s registered", conn.UUID)) observer.sendConnectedEvent(connIndex, conn.Location) return conn, nil @@ -291,7 +293,7 @@ func (h *h2muxConnection) registerNamedTunnel( rpcClient := h.newRPCClientFunc(ctx, stream, h.observer.log) defer rpcClient.Close() - if _, err = rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, h.observer); err != nil { + if _, err = rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, nil, h.observer); err != nil { return err } return nil diff --git a/edgediscovery/edgediscovery.go b/edgediscovery/edgediscovery.go index b5f338e8..88788b10 100644 --- a/edgediscovery/edgediscovery.go +++ b/edgediscovery/edgediscovery.go @@ -1,7 +1,6 @@ package edgediscovery import ( - "fmt" "sync" "github.com/rs/zerolog" @@ -14,7 +13,13 @@ const ( LogFieldIPAddress = "ip" ) -var ErrNoAddressesLeft = fmt.Errorf("there are no free edge addresses left") +var errNoAddressesLeft = ErrNoAddressesLeft{} + +type ErrNoAddressesLeft struct{} + +func (e ErrNoAddressesLeft) Error() string { + return "there are no free edge addresses left to resolve to" +} // Edge finds addresses on the Cloudflare edge and hands them out to connections. type Edge struct { @@ -62,7 +67,7 @@ func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) { defer ed.Unlock() addr := ed.regions.GetAnyAddress() if addr == nil { - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } return addr, nil } @@ -83,7 +88,7 @@ func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) { addr := ed.regions.GetUnusedAddr(nil, connIndex) if addr == nil { log.Debug().Msg("edgediscovery - GetAddr: No addresses left to give proxy connection") - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } log = ed.log.With(). Int(LogFieldConnIndex, connIndex). @@ -107,7 +112,7 @@ func (ed *Edge) GetDifferentAddr(connIndex int, hasConnectivityError bool) (*all if addr == nil { log.Debug().Msg("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection") // note: if oldAddr were not nil, it will become available on the next iteration - return nil, ErrNoAddressesLeft + return nil, errNoAddressesLeft } log = ed.log.With(). Int(LogFieldConnIndex, connIndex). diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 4bec3f56..c3f08844 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -4,9 +4,11 @@ import ( "context" "errors" "fmt" + "strings" "time" "github.com/google/uuid" + "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/cloudflare/cloudflared/connection" @@ -37,13 +39,14 @@ const ( // Supervisor manages non-declarative tunnels. Establishes TCP connections with the edge, and // reconnects them if they disconnect. type Supervisor struct { - cloudflaredUUID uuid.UUID - config *TunnelConfig - orchestrator *orchestration.Orchestrator - edgeIPs *edgediscovery.Edge - edgeTunnelServer EdgeTunnelServer - tunnelErrors chan tunnelError - tunnelsConnecting map[int]chan struct{} + cloudflaredUUID uuid.UUID + config *TunnelConfig + orchestrator *orchestration.Orchestrator + edgeIPs *edgediscovery.Edge + edgeTunnelServer EdgeTunnelServer + tunnelErrors chan tunnelError + tunnelsConnecting map[int]chan struct{} + tunnelsProtocolFallback map[int]*protocolFallback // nextConnectedIndex and nextConnectedSignal are used to wait for all // currently-connecting tunnels to finish connecting so we can reset backoff timer nextConnectedIndex int @@ -72,8 +75,10 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato return nil, fmt.Errorf("failed to generate cloudflared instance ID: %w", err) } + isStaticEdge := len(config.EdgeAddrs) > 0 + var edgeIPs *edgediscovery.Edge - if len(config.EdgeAddrs) > 0 { + if isStaticEdge { // static edge addresses edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs) } else { edgeIPs, err = edgediscovery.ResolveEdge(config.Log, config.Region, config.EdgeIPVersion) @@ -86,7 +91,9 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato log := NewConnAwareLogger(config.Log, config.Observer) var edgeAddrHandler EdgeAddrHandler - if config.EdgeIPVersion == allregions.IPv6Only || config.EdgeIPVersion == allregions.Auto { + if isStaticEdge { // static edge addresses + edgeAddrHandler = &IPAddrFallback{} + } else if config.EdgeIPVersion == allregions.IPv6Only || config.EdgeIPVersion == allregions.Auto { edgeAddrHandler = &IPAddrFallback{} } else { // IPv4Only edgeAddrHandler = &DefaultAddrFallback{} @@ -117,6 +124,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato edgeTunnelServer: edgeTunnelServer, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, + tunnelsProtocolFallback: map[int]*protocolFallback{}, log: log, logTransport: config.LogTransport, reconnectCredentialManager: reconnectCredentialManager, @@ -178,6 +186,10 @@ func (s *Supervisor) Run( tunnelsActive++ continue } + // Make sure we don't continue if there is no more fallback allowed + if _, retry := s.tunnelsProtocolFallback[tunnelError.index].GetMaxBackoffDuration(ctx); !retry { + continue + } s.log.ConnAwareLogger().Err(tunnelError.err).Int(connection.LogFieldConnIndex, tunnelError.index).Msg("Connection terminated") tunnelsWaiting = append(tunnelsWaiting, tunnelError.index) s.waitForNextTunnel(tunnelError.index) @@ -232,6 +244,11 @@ func (s *Supervisor) initialize( s.log.Logger().Info().Msgf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, availableAddrs) s.config.HAConnections = availableAddrs } + s.tunnelsProtocolFallback[0] = &protocolFallback{ + retry.BackoffHandler{MaxRetries: s.config.Retries}, + s.config.ProtocolSelector.Current(), + false, + } go s.startFirstTunnel(ctx, connectedSignal) @@ -249,6 +266,11 @@ func (s *Supervisor) initialize( // At least one successful connection, so start the rest for i := 1; i < s.config.HAConnections; i++ { + s.tunnelsProtocolFallback[i] = &protocolFallback{ + retry.BackoffHandler{MaxRetries: s.config.Retries}, + s.config.ProtocolSelector.Current(), + false, + } ch := signal.New(make(chan struct{})) go s.startTunnel(ctx, i, ch) time.Sleep(registrationInterval) @@ -266,21 +288,44 @@ func (s *Supervisor) startFirstTunnel( err error ) const firstConnIndex = 0 + isStaticEdge := len(s.config.EdgeAddrs) > 0 defer func() { s.tunnelErrors <- tunnelError{index: firstConnIndex, err: err} }() - err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) - // If the first tunnel disconnects, keep restarting it. - for s.unusedIPs() { + for { + err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, s.tunnelsProtocolFallback[firstConnIndex], connectedSignal) if ctx.Err() != nil { return } if err == nil { return } - err = s.edgeTunnelServer.Serve(ctx, firstConnIndex, connectedSignal) + // Make sure we don't continue if there is no more fallback allowed + if _, retry := s.tunnelsProtocolFallback[firstConnIndex].GetMaxBackoffDuration(ctx); !retry { + return + } + // Try again for Unauthorized errors because we hope them to be + // transient due to edge propagation lag on new Tunnels. + if strings.Contains(err.Error(), "Unauthorized") { + continue + } + switch err.(type) { + case edgediscovery.ErrNoAddressesLeft: + // If your provided addresses are not available, we will keep trying regardless. + if !isStaticEdge { + return + } + case connection.DupConnRegisterTunnelError, + *quic.IdleTimeoutError, + edgediscovery.DialError, + *connection.EdgeQuicDialError: + // Try again for these types of errors + default: + // Uncaught errors should bail startup + return + } } } @@ -298,7 +343,7 @@ func (s *Supervisor) startTunnel( s.tunnelErrors <- tunnelError{index: index, err: err} }() - err = s.edgeTunnelServer.Serve(ctx, uint8(index), connectedSignal) + err = s.edgeTunnelServer.Serve(ctx, uint8(index), s.tunnelsProtocolFallback[index], connectedSignal) } func (s *Supervisor) newConnectedTunnelSignal(index int) *signal.Signal { diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index fcbace54..a92113e9 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -194,15 +194,10 @@ type EdgeTunnelServer struct { connAwareLogger *ConnAwareLogger } -func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedSignal *signal.Signal) error { +func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFallback *protocolFallback, connectedSignal *signal.Signal) error { haConnections.Inc() defer haConnections.Dec() - protocolFallback := &protocolFallback{ - retry.BackoffHandler{MaxRetries: e.config.Retries}, - e.config.ProtocolSelector.Current(), - false, - } connectedFuse := h2mux.NewBooleanFuse() go func() { if connectedFuse.Await() { @@ -214,7 +209,7 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedS // Fetch IP address to associated connection index addr, err := e.edgeAddrs.GetAddr(int(connIndex)) - switch err { + switch err.(type) { case nil: // no error case edgediscovery.ErrNoAddressesLeft: return err @@ -262,7 +257,9 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, connectedS // establishing a connection to the edge and if so, rotate the IP address. yes, hasConnectivityError := e.edgeAddrHandler.ShouldGetNewAddress(err) if yes { - e.edgeAddrs.GetDifferentAddr(int(connIndex), hasConnectivityError) + if _, err := e.edgeAddrs.GetDifferentAddr(int(connIndex), hasConnectivityError); err != nil { + return err + } } select { @@ -461,6 +458,7 @@ func serveTunnel( connectedFuse, config.NamedTunnel, connIndex, + addr.UDP.IP, nil, gracefulShutdownC, config.GracePeriod, From b849def673f5047eba318d42f10d65fbb6b69ab5 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 24 Jun 2022 09:46:22 -0700 Subject: [PATCH 131/238] TUN-6460: Rename metric label location to edge_location For Google's managed prometheus, it seems they reserved certain labels for their internal service regions/locations. This causes customers to run into issues using our metrics since our metric: `cloudflared_tunnel_server_locations` has a `location` label. Renaming this to `edge_location` should unblock the conflict and usage. --- connection/metrics.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connection/metrics.go b/connection/metrics.go index a171f1a4..385708db 100644 --- a/connection/metrics.go +++ b/connection/metrics.go @@ -367,7 +367,7 @@ func initTunnelMetrics() *tunnelMetrics { Name: "server_locations", Help: "Where each tunnel is connected to. 1 means current location, 0 means previous locations.", }, - []string{"connection_id", "location"}, + []string{"connection_id", "edge_location"}, ) prometheus.MustRegister(serverLocations) From 2e2718b7e31298785ea2972004d2380530a4b085 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 24 Jun 2022 11:51:53 -0700 Subject: [PATCH 132/238] TUN-6459: Add cloudflared user-agent to access calls --- cmd/cloudflared/access/cmd.go | 6 ++++-- cmd/cloudflared/main.go | 4 +++- token/token.go | 10 ++++++++++ token/transfer.go | 7 ++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/cmd/cloudflared/access/cmd.go b/cmd/cloudflared/access/cmd.go index 50ccb663..fa06fd08 100644 --- a/cmd/cloudflared/access/cmd.go +++ b/cmd/cloudflared/access/cmd.go @@ -56,11 +56,13 @@ const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878" var ( shutdownC chan struct{} + userAgent = "DEV" ) // Init will initialize and store vars from the main program -func Init(shutdown chan struct{}) { +func Init(shutdown chan struct{}, version string) { shutdownC = shutdown + userAgent = fmt.Sprintf("cloudflared/%s", version) } // Flags return the global flags for Access related commands (hopefully none) @@ -505,7 +507,7 @@ func isTokenValid(options *carrier.StartOptions, log *zerolog.Logger) (bool, err if err != nil { return false, errors.Wrap(err, "Could not create access request") } - + req.Header.Set("User-Agent", userAgent) // Do not follow redirects client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 07271eb4..1ac06c3e 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -21,6 +21,7 @@ import ( "github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/metrics" "github.com/cloudflare/cloudflared/overwatch" + "github.com/cloudflare/cloudflared/token" "github.com/cloudflare/cloudflared/tracing" "github.com/cloudflare/cloudflared/watcher" ) @@ -85,9 +86,10 @@ func main() { app.Commands = commands(cli.ShowVersion) tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command... - access.Init(graceShutdownC) + access.Init(graceShutdownC, Version) updater.Init(Version) tracing.Init(Version) + token.Init(Version) runApp(app, graceShutdownC) } diff --git a/token/token.go b/token/token.go index abccf257..329ace79 100644 --- a/token/token.go +++ b/token/token.go @@ -29,6 +29,10 @@ const ( AccessLoginWorkerPath = "/cdn-cgi/access/login" ) +var ( + userAgent = "DEV" +) + type AppInfo struct { AuthDomain string AppAUD string @@ -144,6 +148,10 @@ func isTokenLocked(lockFilePath string) bool { return exists && err == nil } +func Init(version string) { + userAgent = fmt.Sprintf("cloudflared/%s", version) +} + // FetchTokenWithRedirect will either load a stored token or generate a new one // it appends the full url as the redirect URL to the access cli request if opening the browser func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, log *zerolog.Logger) (string, error) { @@ -261,6 +269,7 @@ func GetAppInfo(reqURL *url.URL) (*AppInfo, error) { if err != nil { return nil, errors.Wrap(err, "failed to create app info request") } + appInfoReq.Header.Add("User-Agent", userAgent) resp, err := client.Do(appInfoReq) if err != nil { return nil, errors.Wrap(err, "failed to get app info") @@ -311,6 +320,7 @@ func exchangeOrgToken(appURL *url.URL, orgToken string) (string, error) { if err != nil { return "", errors.Wrap(err, "failed to create app token request") } + appTokenRequest.Header.Add("User-Agent", userAgent) resp, err := client.Do(appTokenRequest) if err != nil { return "", errors.Wrap(err, "failed to get app token") diff --git a/token/transfer.go b/token/transfer.go index e285c49e..cc40cb69 100644 --- a/token/transfer.go +++ b/token/transfer.go @@ -113,7 +113,12 @@ func transferRequest(requestURL string, log *zerolog.Logger) ([]byte, string, er // poll the endpoint for the request resource, waiting for the user interaction func poll(client *http.Client, requestURL string, log *zerolog.Logger) ([]byte, string, error) { - resp, err := client.Get(requestURL) + req, err := http.NewRequest(http.MethodGet, requestURL, nil) + if err != nil { + return nil, "", err + } + req.Header.Set("User-Agent", userAgent) + resp, err := client.Do(req) if err != nil { return nil, "", err } From efd455654627516d2bf133f55a462a101f233019 Mon Sep 17 00:00:00 2001 From: Stephen Heckler Date: Fri, 18 Feb 2022 11:06:49 -0600 Subject: [PATCH 133/238] Ensure service install directories are created before writing file --- cmd/cloudflared/service_template.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index 3040766f..feaf86cc 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "os/exec" + "path" "text/template" homedir "github.com/mitchellh/go-homedir" @@ -52,10 +53,17 @@ func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error { if err != nil { return fmt.Errorf("error generating %s: %v", st.Path, err) } - fileMode := os.FileMode(0644) + fileMode := os.FileMode(0o644) if st.FileMode != 0 { fileMode = st.FileMode } + + plistFolder := path.Dir(resolvedPath) + err = os.MkdirAll(plistFolder, 0o755) + if err != nil { + return fmt.Errorf("error creating %s: %v", plistFolder, err) + } + err = ioutil.WriteFile(resolvedPath, buffer.Bytes(), fileMode) if err != nil { return fmt.Errorf("error writing %s: %v", resolvedPath, err) From f3ba506880929981412ae76b8fe5c8ad45a9feb5 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 5 Jul 2022 17:40:11 +0100 Subject: [PATCH 134/238] TUN-6499: Remove log that is per datagram --- quic/datagram.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/quic/datagram.go b/quic/datagram.go index d86d524b..350b601f 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -39,7 +39,6 @@ func (dm *DatagramMuxer) SendTo(sessionID uuid.UUID, payload []byte) error { if err := dm.session.SendMessage(msgWithID); err != nil { return errors.Wrap(err, "Failed to send datagram back to edge") } - dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Send datagram back to edge") return nil } @@ -55,7 +54,6 @@ func (dm *DatagramMuxer) ReceiveFrom() (uuid.UUID, []byte, error) { if err != nil { return uuid.Nil, nil, err } - dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Received datagram from edge") return sessionID, payload, nil } From ac7fdd5572007bb8b5ccb7d466f0461ee9977627 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 5 Jul 2022 11:33:48 -0700 Subject: [PATCH 135/238] Release 2022.7.0 --- RELEASE_NOTES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3e50405c..a2a8f3ff 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,15 @@ +2022.7.0 +- 2022-07-05 TUN-6499: Remove log that is per datagram +- 2022-06-24 TUN-6460: Rename metric label location to edge_location +- 2022-06-24 TUN-6459: Add cloudflared user-agent to access calls +- 2022-06-17 TUN-6427: Differentiate between upstream request closed/canceled and failed origin requests +- 2022-06-17 TUN-6388: Fix first tunnel connection not retrying +- 2022-06-13 TUN-6384: Correct duplicate connection error to fetch new IP first +- 2022-06-13 TUN-6373: Add edge-ip-version to remotely pushed configuration +- 2022-06-07 TUN-6010: Add component tests for --edge-ip-version +- 2022-05-20 TUN-6007: Implement new edge discovery algorithm +- 2022-02-18 Ensure service install directories are created before writing file + 2022.6.3 - 2022-06-20 TUN-6362: Add armhf support to cloudflare packaging From 7607ead143646c4c01d35d0fe8d17663c31b65de Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 6 Jul 2022 12:58:33 +0100 Subject: [PATCH 136/238] TUN-6503: Fix transport fallback from QUIC in face of dial error "no network activity" --- CHANGES.md | 7 +++++++ cmd/cloudflared/tunnel/cmd.go | 2 +- supervisor/tunnel.go | 3 +-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 21d581cf..511cb600 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +## 2022.7.1 +### New Features +- It is now possible to connect cloudflared tunnel to Cloudflare Global Network with IPv6. See `cloudflared tunnel --help` and look for `edge-ip-version` for more information. For now, the default behavior is to still connect with IPv4 only. + +### Bug Fixes +- Several bug fixes related with QUIC transport (used between cloudflared tunnel and Cloudflare Global Network). Updating to this version is highly recommended. + ## 2022.4.0 ### Bug Fixes - `cloudflared tunnel run` no longer logs the Tunnel token or JSON credentials in clear text as those are the secret diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 710a79fc..344333b0 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -528,7 +528,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Usage: "Cloudflare Edge ip address version to connect with. {4, 6, auto}", EnvVars: []string{"TUNNEL_EDGE_IP_VERSION"}, Value: "4", - Hidden: true, + Hidden: false, }), altsrc.NewStringFlag(&cli.StringFlag{ Name: tlsconfig.CaCertFlag, diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index a92113e9..4f208dd4 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -412,8 +412,7 @@ func ServeTunnel( } return err.Cause, !err.Permanent case *connection.EdgeQuicDialError: - // Don't retry connection for a dial error - return err, false + return err, true case ReconnectSignal: connLog.Logger().Info(). IPAddr(connection.LogFieldIPAddress, addr.UDP.IP). From 06f7ba45233942b72349685fb7ede38c2783dd65 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 6 Jul 2022 13:18:24 +0100 Subject: [PATCH 137/238] Release 2022.7.1 --- RELEASE_NOTES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index a2a8f3ff..fd144c9a 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +2022.7.1 +- 2022-07-06 TUN-6503: Fix transport fallback from QUIC in face of dial error "no network activity" + 2022.7.0 - 2022-07-05 TUN-6499: Remove log that is per datagram - 2022-06-24 TUN-6460: Rename metric label location to edge_location From 1733fe8c656c6104547684367f3a70899e790402 Mon Sep 17 00:00:00 2001 From: Igor Postelnik Date: Thu, 7 Jul 2022 18:01:37 -0500 Subject: [PATCH 138/238] TUN-6517: Use QUIC stream context while proxying HTTP requests and TCP connections --- connection/quic.go | 21 +++++++++++---------- connection/quic_test.go | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 4c530b19..7adc03c2 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -143,15 +143,16 @@ func (q *QUICConnection) acceptStream(ctx context.Context) error { } func (q *QUICConnection) runStream(quicStream quic.Stream) { + ctx := quicStream.Context() stream := quicpogs.NewSafeStreamCloser(quicStream) defer stream.Close() - if err := q.handleStream(stream); err != nil { + if err := q.handleStream(ctx, stream); err != nil { q.logger.Err(err).Msg("Failed to handle QUIC stream") } } -func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { +func (q *QUICConnection) handleStream(ctx context.Context, stream io.ReadWriteCloser) error { signature, err := quicpogs.DetermineProtocol(stream) if err != nil { return err @@ -162,7 +163,7 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { if err != nil { return err } - return q.handleDataStream(reqServerStream) + return q.handleDataStream(ctx, reqServerStream) case quicpogs.RPCStreamProtocolSignature: rpcStream, err := quicpogs.NewRPCServerStream(stream, signature) if err != nil { @@ -174,13 +175,13 @@ func (q *QUICConnection) handleStream(stream io.ReadWriteCloser) error { } } -func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) error { +func (q *QUICConnection) handleDataStream(ctx context.Context, stream *quicpogs.RequestServerStream) error { request, err := stream.ReadConnectRequestData() if err != nil { return err } - if err := q.dispatchRequest(stream, err, request); err != nil { + if err := q.dispatchRequest(ctx, stream, err, request); err != nil { _ = stream.WriteConnectResponseData(err) q.logger.Err(err).Str("type", request.Type.String()).Str("dest", request.Dest).Msg("Request failed") } @@ -188,7 +189,7 @@ func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) return nil } -func (q *QUICConnection) dispatchRequest(stream *quicpogs.RequestServerStream, err error, request *quicpogs.ConnectRequest) error { +func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.RequestServerStream, err error, request *quicpogs.ConnectRequest) error { originProxy, err := q.orchestrator.GetOriginProxy() if err != nil { return err @@ -196,7 +197,7 @@ func (q *QUICConnection) dispatchRequest(stream *quicpogs.RequestServerStream, e switch request.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - tracedReq, err := buildHTTPRequest(request, stream) + tracedReq, err := buildHTTPRequest(ctx, request, stream) if err != nil { return err } @@ -206,7 +207,7 @@ func (q *QUICConnection) dispatchRequest(stream *quicpogs.RequestServerStream, e case quicpogs.ConnectionTypeTCP: rwa := &streamReadWriteAcker{stream} metadata := request.MetadataMap() - return originProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{ + return originProxy.ProxyTCP(ctx, rwa, &TCPRequest{ Dest: request.Dest, FlowID: metadata[QUICMetadataFlowID], }) @@ -324,14 +325,14 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) { hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } -func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { +func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { metadata := connectRequest.MetadataMap() dest := connectRequest.Dest method := metadata[HTTPMethodKey] host := metadata[HTTPHostKey] isWebsocket := connectRequest.Type == quicpogs.ConnectionTypeWebsocket - req, err := http.NewRequest(method, dest, body) + req, err := http.NewRequestWithContext(ctx, method, dest, body) if err != nil { return nil, err } diff --git a/connection/quic_test.go b/connection/quic_test.go index cbfaf714..33da98ac 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -477,7 +477,7 @@ func TestBuildHTTPRequest(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - req, err := buildHTTPRequest(test.connectRequest, test.body) + req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body) assert.NoError(t, err) test.req = test.req.WithContext(req.Context()) assert.Equal(t, test.req, req.Request) From 2a177e0fc4c58734896ca30e5d9db2e7bf355860 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 20 Jul 2022 16:17:29 -0700 Subject: [PATCH 139/238] TUN-6583: Remove legacy --ui flag --- cmd/cloudflared/tunnel/cmd.go | 24 +- cmd/cloudflared/tunnel/quick_tunnel.go | 1 - cmd/cloudflared/tunnel/subcommand_context.go | 20 +- .../tunnel/subcommand_context_test.go | 3 - cmd/cloudflared/ui/launch_ui.go | 223 --- connection/h2mux_test.go | 2 +- connection/http2_test.go | 8 +- connection/observer.go | 4 +- connection/observer_test.go | 4 +- go.mod | 6 - go.sum | 16 - .../github.com/gdamore/encoding/.appveyor.yml | 13 - .../github.com/gdamore/encoding/.travis.yml | 7 - vendor/github.com/gdamore/encoding/LICENSE | 202 -- vendor/github.com/gdamore/encoding/README.md | 19 - vendor/github.com/gdamore/encoding/ascii.go | 36 - vendor/github.com/gdamore/encoding/charmap.go | 196 -- vendor/github.com/gdamore/encoding/doc.go | 17 - vendor/github.com/gdamore/encoding/ebcdic.go | 273 --- vendor/github.com/gdamore/encoding/latin1.go | 33 - vendor/github.com/gdamore/encoding/latin5.go | 35 - vendor/github.com/gdamore/encoding/utf8.go | 35 - vendor/github.com/gdamore/tcell/.appveyor.yml | 13 - vendor/github.com/gdamore/tcell/.gitignore | 1 - vendor/github.com/gdamore/tcell/.travis.yml | 15 - vendor/github.com/gdamore/tcell/AUTHORS | 4 - vendor/github.com/gdamore/tcell/LICENSE | 202 -- vendor/github.com/gdamore/tcell/README.adoc | 272 --- vendor/github.com/gdamore/tcell/attr.go | 32 - vendor/github.com/gdamore/tcell/cell.go | 177 -- .../github.com/gdamore/tcell/charset_stub.go | 21 - .../github.com/gdamore/tcell/charset_unix.go | 49 - .../gdamore/tcell/charset_windows.go | 21 - vendor/github.com/gdamore/tcell/color.go | 1019 ---------- vendor/github.com/gdamore/tcell/colorfit.go | 52 - .../github.com/gdamore/tcell/console_stub.go | 23 - .../github.com/gdamore/tcell/console_win.go | 1033 ---------- vendor/github.com/gdamore/tcell/doc.go | 48 - vendor/github.com/gdamore/tcell/encoding.go | 139 -- vendor/github.com/gdamore/tcell/errors.go | 73 - vendor/github.com/gdamore/tcell/event.go | 53 - vendor/github.com/gdamore/tcell/interrupt.go | 41 - vendor/github.com/gdamore/tcell/key.go | 464 ----- vendor/github.com/gdamore/tcell/mouse.go | 97 - vendor/github.com/gdamore/tcell/resize.go | 42 - vendor/github.com/gdamore/tcell/runes.go | 111 -- vendor/github.com/gdamore/tcell/screen.go | 209 --- vendor/github.com/gdamore/tcell/simulation.go | 508 ----- vendor/github.com/gdamore/tcell/style.go | 126 -- .../gdamore/tcell/terminfo/.gitignore | 1 - .../gdamore/tcell/terminfo/README.md | 25 - .../gdamore/tcell/terminfo/TERMINALS.md | 7 - .../gdamore/tcell/terminfo/a/adm3a/term.go | 25 - .../gdamore/tcell/terminfo/a/aixterm/term.go | 81 - .../tcell/terminfo/a/alacritty/term.go | 156 -- .../gdamore/tcell/terminfo/a/ansi/term.go | 41 - .../gdamore/tcell/terminfo/a/aterm/term.go | 110 -- .../gdamore/tcell/terminfo/b/beterm/term.go | 54 - .../gdamore/tcell/terminfo/b/bsdos_pc/term.go | 42 - .../gdamore/tcell/terminfo/base/base.go | 32 - .../gdamore/tcell/terminfo/c/cygwin/term.go | 63 - .../gdamore/tcell/terminfo/d/d200/term.go | 97 - .../gdamore/tcell/terminfo/d/d210/term.go | 95 - .../gdamore/tcell/terminfo/d/dtterm/term.go | 67 - .../gdamore/tcell/terminfo/dynamic/dynamic.go | 425 ----- .../gdamore/tcell/terminfo/e/emacs/term.go | 60 - .../gdamore/tcell/terminfo/e/eterm/term.go | 309 --- .../tcell/terminfo/extended/extended.go | 69 - .../gdamore/tcell/terminfo/g/gnome/term.go | 306 --- .../github.com/gdamore/tcell/terminfo/gen.sh | 18 - .../gdamore/tcell/terminfo/h/hpterm/term.go | 50 - .../gdamore/tcell/terminfo/h/hz1500/term.go | 26 - .../gdamore/tcell/terminfo/k/konsole/term.go | 304 --- .../gdamore/tcell/terminfo/k/kterm/term.go | 67 - .../gdamore/tcell/terminfo/l/linux/term.go | 69 - .../gdamore/tcell/terminfo/models.txt | 43 - .../gdamore/tcell/terminfo/p/pcansi/term.go | 39 - .../gdamore/tcell/terminfo/r/rxvt/term.go | 466 ----- .../gdamore/tcell/terminfo/s/screen/term.go | 126 -- .../tcell/terminfo/s/simpleterm/term.go | 308 --- .../gdamore/tcell/terminfo/s/sun/term.go | 90 - .../gdamore/tcell/terminfo/t/termite/term.go | 154 -- .../gdamore/tcell/terminfo/t/tvi910/term.go | 39 - .../gdamore/tcell/terminfo/t/tvi912/term.go | 38 - .../gdamore/tcell/terminfo/t/tvi921/term.go | 34 - .../gdamore/tcell/terminfo/t/tvi925/term.go | 43 - .../gdamore/tcell/terminfo/t/tvi950/term.go | 46 - .../gdamore/tcell/terminfo/t/tvi970/term.go | 40 - .../gdamore/tcell/terminfo/terminfo.go | 781 -------- .../gdamore/tcell/terminfo/v/vt100/term.go | 48 - .../gdamore/tcell/terminfo/v/vt102/term.go | 47 - .../gdamore/tcell/terminfo/v/vt220/term.go | 58 - .../gdamore/tcell/terminfo/v/vt320/term.go | 63 - .../gdamore/tcell/terminfo/v/vt400/term.go | 46 - .../gdamore/tcell/terminfo/v/vt420/term.go | 53 - .../gdamore/tcell/terminfo/v/vt52/term.go | 29 - .../gdamore/tcell/terminfo/w/wy50/term.go | 59 - .../gdamore/tcell/terminfo/w/wy60/term.go | 63 - .../tcell/terminfo/w/wy99_ansi/term.go | 114 -- .../gdamore/tcell/terminfo/x/xfce/term.go | 156 -- .../gdamore/tcell/terminfo/x/xnuppc/term.go | 34 - .../gdamore/tcell/terminfo/x/xterm/term.go | 453 ----- .../tcell/terminfo/x/xterm_kitty/term.go | 155 -- .../github.com/gdamore/tcell/terms_default.go | 23 - .../github.com/gdamore/tcell/terms_dynamic.go | 37 - .../github.com/gdamore/tcell/terms_static.go | 27 - vendor/github.com/gdamore/tcell/tscreen.go | 1434 -------------- .../github.com/gdamore/tcell/tscreen_bsd.go | 116 -- .../gdamore/tcell/tscreen_darwin.go | 137 -- .../github.com/gdamore/tcell/tscreen_linux.go | 119 -- .../gdamore/tcell/tscreen_solaris.go | 117 -- .../github.com/gdamore/tcell/tscreen_stub.go | 32 - .../gdamore/tcell/tscreen_windows.go | 40 - .../lucasb-eyer/go-colorful/.gitignore | 28 - .../lucasb-eyer/go-colorful/.travis.yml | 7 - .../lucasb-eyer/go-colorful/LICENSE | 7 - .../lucasb-eyer/go-colorful/README.md | 492 ----- .../lucasb-eyer/go-colorful/colorgens.go | 55 - .../lucasb-eyer/go-colorful/colors.go | 903 --------- .../go-colorful/happy_palettegen.go | 25 - .../lucasb-eyer/go-colorful/hexcolor.go | 37 - .../go-colorful/soft_palettegen.go | 185 -- .../go-colorful/warm_palettegen.go | 25 - .../github.com/mattn/go-runewidth/.travis.yml | 16 - vendor/github.com/mattn/go-runewidth/LICENSE | 21 - .../github.com/mattn/go-runewidth/README.md | 27 - .../github.com/mattn/go-runewidth/go.test.sh | 12 - .../mattn/go-runewidth/runewidth.go | 257 --- .../mattn/go-runewidth/runewidth_appengine.go | 8 - .../mattn/go-runewidth/runewidth_js.go | 9 - .../mattn/go-runewidth/runewidth_posix.go | 79 - .../mattn/go-runewidth/runewidth_table.go | 429 ----- .../mattn/go-runewidth/runewidth_windows.go | 28 - .../github.com/rivo/tview/CODE_OF_CONDUCT.md | 73 - vendor/github.com/rivo/tview/CONTRIBUTING.md | 35 - vendor/github.com/rivo/tview/LICENSE.txt | 21 - vendor/github.com/rivo/tview/README.md | 62 - vendor/github.com/rivo/tview/ansi.go | 258 --- vendor/github.com/rivo/tview/application.go | 728 -------- vendor/github.com/rivo/tview/borders.go | 45 - vendor/github.com/rivo/tview/box.go | 412 ---- vendor/github.com/rivo/tview/button.go | 157 -- vendor/github.com/rivo/tview/checkbox.go | 226 --- vendor/github.com/rivo/tview/doc.go | 180 -- vendor/github.com/rivo/tview/dropdown.go | 547 ------ vendor/github.com/rivo/tview/flex.go | 225 --- vendor/github.com/rivo/tview/focusable.go | 8 - vendor/github.com/rivo/tview/form.go | 663 ------- vendor/github.com/rivo/tview/frame.go | 169 -- vendor/github.com/rivo/tview/grid.go | 684 ------- vendor/github.com/rivo/tview/inputfield.go | 625 ------- vendor/github.com/rivo/tview/list.go | 628 ------- vendor/github.com/rivo/tview/modal.go | 190 -- vendor/github.com/rivo/tview/pages.go | 302 --- vendor/github.com/rivo/tview/primitive.go | 57 - vendor/github.com/rivo/tview/semigraphics.go | 296 --- vendor/github.com/rivo/tview/styles.go | 35 - vendor/github.com/rivo/tview/table.go | 1266 ------------- vendor/github.com/rivo/tview/textview.go | 1189 ------------ vendor/github.com/rivo/tview/treeview.go | 775 -------- vendor/github.com/rivo/tview/tview.gif | Bin 2226085 -> 0 bytes vendor/github.com/rivo/tview/util.go | 630 ------- vendor/github.com/rivo/uniseg/LICENSE.txt | 21 - vendor/github.com/rivo/uniseg/README.md | 62 - vendor/github.com/rivo/uniseg/doc.go | 8 - vendor/github.com/rivo/uniseg/grapheme.go | 258 --- vendor/github.com/rivo/uniseg/properties.go | 1658 ----------------- vendor/golang.org/x/text/encoding/encoding.go | 335 ---- .../internal/identifier/identifier.go | 81 - .../text/encoding/internal/identifier/mib.go | 1619 ---------------- vendor/modules.txt | 69 - 171 files changed, 19 insertions(+), 31556 deletions(-) delete mode 100644 cmd/cloudflared/ui/launch_ui.go delete mode 100644 vendor/github.com/gdamore/encoding/.appveyor.yml delete mode 100644 vendor/github.com/gdamore/encoding/.travis.yml delete mode 100644 vendor/github.com/gdamore/encoding/LICENSE delete mode 100644 vendor/github.com/gdamore/encoding/README.md delete mode 100644 vendor/github.com/gdamore/encoding/ascii.go delete mode 100644 vendor/github.com/gdamore/encoding/charmap.go delete mode 100644 vendor/github.com/gdamore/encoding/doc.go delete mode 100644 vendor/github.com/gdamore/encoding/ebcdic.go delete mode 100644 vendor/github.com/gdamore/encoding/latin1.go delete mode 100644 vendor/github.com/gdamore/encoding/latin5.go delete mode 100644 vendor/github.com/gdamore/encoding/utf8.go delete mode 100644 vendor/github.com/gdamore/tcell/.appveyor.yml delete mode 100644 vendor/github.com/gdamore/tcell/.gitignore delete mode 100644 vendor/github.com/gdamore/tcell/.travis.yml delete mode 100644 vendor/github.com/gdamore/tcell/AUTHORS delete mode 100644 vendor/github.com/gdamore/tcell/LICENSE delete mode 100644 vendor/github.com/gdamore/tcell/README.adoc delete mode 100644 vendor/github.com/gdamore/tcell/attr.go delete mode 100644 vendor/github.com/gdamore/tcell/cell.go delete mode 100644 vendor/github.com/gdamore/tcell/charset_stub.go delete mode 100644 vendor/github.com/gdamore/tcell/charset_unix.go delete mode 100644 vendor/github.com/gdamore/tcell/charset_windows.go delete mode 100644 vendor/github.com/gdamore/tcell/color.go delete mode 100644 vendor/github.com/gdamore/tcell/colorfit.go delete mode 100644 vendor/github.com/gdamore/tcell/console_stub.go delete mode 100644 vendor/github.com/gdamore/tcell/console_win.go delete mode 100644 vendor/github.com/gdamore/tcell/doc.go delete mode 100644 vendor/github.com/gdamore/tcell/encoding.go delete mode 100644 vendor/github.com/gdamore/tcell/errors.go delete mode 100644 vendor/github.com/gdamore/tcell/event.go delete mode 100644 vendor/github.com/gdamore/tcell/interrupt.go delete mode 100644 vendor/github.com/gdamore/tcell/key.go delete mode 100644 vendor/github.com/gdamore/tcell/mouse.go delete mode 100644 vendor/github.com/gdamore/tcell/resize.go delete mode 100644 vendor/github.com/gdamore/tcell/runes.go delete mode 100644 vendor/github.com/gdamore/tcell/screen.go delete mode 100644 vendor/github.com/gdamore/tcell/simulation.go delete mode 100644 vendor/github.com/gdamore/tcell/style.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/.gitignore delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/README.md delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/TERMINALS.md delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/a/adm3a/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/a/aixterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/a/alacritty/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/a/ansi/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/a/aterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/b/beterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/b/bsdos_pc/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/base/base.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/c/cygwin/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/d/d200/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/d/d210/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/d/dtterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/dynamic/dynamic.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/e/emacs/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/e/eterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/extended/extended.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/g/gnome/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/gen.sh delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/h/hpterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/h/hz1500/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/k/konsole/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/k/kterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/l/linux/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/models.txt delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/p/pcansi/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/r/rxvt/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/s/screen/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/s/simpleterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/s/sun/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/termite/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi910/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi912/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi921/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi925/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi950/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/t/tvi970/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/terminfo.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt100/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt102/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt220/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt320/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt400/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt420/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/v/vt52/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/w/wy50/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/w/wy60/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/w/wy99_ansi/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/x/xfce/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/x/xnuppc/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/x/xterm/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terminfo/x/xterm_kitty/term.go delete mode 100644 vendor/github.com/gdamore/tcell/terms_default.go delete mode 100644 vendor/github.com/gdamore/tcell/terms_dynamic.go delete mode 100644 vendor/github.com/gdamore/tcell/terms_static.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_bsd.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_darwin.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_linux.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_solaris.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_stub.go delete mode 100644 vendor/github.com/gdamore/tcell/tscreen_windows.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/.gitignore delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/.travis.yml delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/LICENSE delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/README.md delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/colorgens.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/colors.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go delete mode 100644 vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go delete mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml delete mode 100644 vendor/github.com/mattn/go-runewidth/LICENSE delete mode 100644 vendor/github.com/mattn/go-runewidth/README.md delete mode 100644 vendor/github.com/mattn/go-runewidth/go.test.sh delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth.go delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_appengine.go delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_js.go delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_posix.go delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_table.go delete mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_windows.go delete mode 100644 vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md delete mode 100644 vendor/github.com/rivo/tview/CONTRIBUTING.md delete mode 100644 vendor/github.com/rivo/tview/LICENSE.txt delete mode 100644 vendor/github.com/rivo/tview/README.md delete mode 100644 vendor/github.com/rivo/tview/ansi.go delete mode 100644 vendor/github.com/rivo/tview/application.go delete mode 100644 vendor/github.com/rivo/tview/borders.go delete mode 100644 vendor/github.com/rivo/tview/box.go delete mode 100644 vendor/github.com/rivo/tview/button.go delete mode 100644 vendor/github.com/rivo/tview/checkbox.go delete mode 100644 vendor/github.com/rivo/tview/doc.go delete mode 100644 vendor/github.com/rivo/tview/dropdown.go delete mode 100644 vendor/github.com/rivo/tview/flex.go delete mode 100644 vendor/github.com/rivo/tview/focusable.go delete mode 100644 vendor/github.com/rivo/tview/form.go delete mode 100644 vendor/github.com/rivo/tview/frame.go delete mode 100644 vendor/github.com/rivo/tview/grid.go delete mode 100644 vendor/github.com/rivo/tview/inputfield.go delete mode 100644 vendor/github.com/rivo/tview/list.go delete mode 100644 vendor/github.com/rivo/tview/modal.go delete mode 100644 vendor/github.com/rivo/tview/pages.go delete mode 100644 vendor/github.com/rivo/tview/primitive.go delete mode 100644 vendor/github.com/rivo/tview/semigraphics.go delete mode 100644 vendor/github.com/rivo/tview/styles.go delete mode 100644 vendor/github.com/rivo/tview/table.go delete mode 100644 vendor/github.com/rivo/tview/textview.go delete mode 100644 vendor/github.com/rivo/tview/treeview.go delete mode 100644 vendor/github.com/rivo/tview/tview.gif delete mode 100644 vendor/github.com/rivo/tview/util.go delete mode 100644 vendor/github.com/rivo/uniseg/LICENSE.txt delete mode 100644 vendor/github.com/rivo/uniseg/README.md delete mode 100644 vendor/github.com/rivo/uniseg/doc.go delete mode 100644 vendor/github.com/rivo/uniseg/grapheme.go delete mode 100644 vendor/github.com/rivo/uniseg/properties.go delete mode 100644 vendor/golang.org/x/text/encoding/encoding.go delete mode 100644 vendor/golang.org/x/text/encoding/internal/identifier/identifier.go delete mode 100644 vendor/golang.org/x/text/encoding/internal/identifier/mib.go diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 344333b0..793f8c2b 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -25,7 +25,6 @@ import ( "github.com/cloudflare/cloudflared/cfapi" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/proxydns" - "github.com/cloudflare/cloudflared/cmd/cloudflared/ui" "github.com/cloudflare/cloudflared/cmd/cloudflared/updater" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" @@ -218,7 +217,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri // runClassicTunnel creates a "classic" non-named tunnel func runClassicTunnel(sc *subcommandContext) error { - return StartServer(sc.c, buildInfo, nil, sc.log, sc.isUIEnabled) + return StartServer(sc.c, buildInfo, nil, sc.log) } func routeFromFlag(c *cli.Context) (route cfapi.HostnameRoute, ok bool) { @@ -236,7 +235,6 @@ func StartServer( info *cliutil.BuildInfo, namedTunnel *connection.NamedTunnelProperties, log *zerolog.Logger, - isUIEnabled bool, ) error { _ = raven.SetDSN(sentryDSN) var wg sync.WaitGroup @@ -331,9 +329,9 @@ func StartServer( return fmt.Errorf(errText) } - logTransport := logger.CreateTransportLoggerFromContext(c, isUIEnabled) + logTransport := logger.CreateTransportLoggerFromContext(c, logger.EnableTerminalLog) - observer := connection.NewObserver(log, logTransport, isUIEnabled) + observer := connection.NewObserver(log, logTransport) // Send Quick Tunnel URL to UI if applicable var quickTunnelURL string @@ -392,18 +390,6 @@ func StartServer( errC <- supervisor.StartTunnelDaemon(ctx, tunnelConfig, orchestrator, connectedSignal, reconnectCh, graceShutdownC) }() - if isUIEnabled { - tunnelUI := ui.NewUIModel( - info.Version(), - hostname, - metricsListener.Addr().String(), - orchestratorConfig.Ingress, - tunnelConfig.HAConnections, - ) - app := tunnelUI.Launch(ctx, log, logTransport) - observer.RegisterSink(app) - } - gracePeriod, err := gracePeriod(c) if err != nil { return err @@ -663,9 +649,9 @@ func tunnelFlags(shouldHide bool) []cli.Flag { }), altsrc.NewBoolFlag(&cli.BoolFlag{ Name: uiFlag, - Usage: "Launch tunnel UI. Tunnel logs are scrollable via 'j', 'k', or arrow keys.", + Usage: "(depreciated) Launch tunnel UI. Tunnel logs are scrollable via 'j', 'k', or arrow keys.", Value: false, - Hidden: shouldHide, + Hidden: true, }), altsrc.NewStringFlag(&cli.StringFlag{ Name: "quick-service", diff --git a/cmd/cloudflared/tunnel/quick_tunnel.go b/cmd/cloudflared/tunnel/quick_tunnel.go index 0dd7747b..51662b7a 100644 --- a/cmd/cloudflared/tunnel/quick_tunnel.go +++ b/cmd/cloudflared/tunnel/quick_tunnel.go @@ -78,7 +78,6 @@ func RunQuickTunnel(sc *subcommandContext) error { buildInfo, &connection.NamedTunnelProperties{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname}, sc.log, - sc.isUIEnabled, ) } diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index df1d95f0..650781e8 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -31,10 +31,9 @@ func (e errInvalidJSONCredential) Error() string { // subcommandContext carries structs shared between subcommands, to reduce number of arguments needed to // pass between subcommands, and make sure they are only initialized once type subcommandContext struct { - c *cli.Context - log *zerolog.Logger - isUIEnabled bool - fs fileSystem + c *cli.Context + log *zerolog.Logger + fs fileSystem // These fields should be accessed using their respective Getter tunnelstoreClient cfapi.Client @@ -42,16 +41,10 @@ type subcommandContext struct { } func newSubcommandContext(c *cli.Context) (*subcommandContext, error) { - isUIEnabled := c.IsSet(uiFlag) && c.String("name") != "" - - // If UI is enabled, terminal log output should be disabled -- log should be written into a UI log window instead - log := logger.CreateLoggerFromContext(c, isUIEnabled) - return &subcommandContext{ - c: c, - log: log, - isUIEnabled: isUIEnabled, - fs: realFileSystem{}, + c: c, + log: logger.CreateLoggerFromContext(c, logger.EnableTerminalLog), + fs: realFileSystem{}, }, nil } @@ -312,7 +305,6 @@ func (sc *subcommandContext) runWithCredentials(credentials connection.Credentia buildInfo, &connection.NamedTunnelProperties{Credentials: credentials}, sc.log, - sc.isUIEnabled, ) } diff --git a/cmd/cloudflared/tunnel/subcommand_context_test.go b/cmd/cloudflared/tunnel/subcommand_context_test.go index 82b866d9..35cc46e7 100644 --- a/cmd/cloudflared/tunnel/subcommand_context_test.go +++ b/cmd/cloudflared/tunnel/subcommand_context_test.go @@ -35,7 +35,6 @@ func Test_subcommandContext_findCredentials(t *testing.T) { type fields struct { c *cli.Context log *zerolog.Logger - isUIEnabled bool fs fileSystem tunnelstoreClient cfapi.Client userCredential *userCredential @@ -168,7 +167,6 @@ func Test_subcommandContext_findCredentials(t *testing.T) { sc := &subcommandContext{ c: tt.fields.c, log: tt.fields.log, - isUIEnabled: tt.fields.isUIEnabled, fs: tt.fields.fs, tunnelstoreClient: tt.fields.tunnelstoreClient, userCredential: tt.fields.userCredential, @@ -307,7 +305,6 @@ func Test_subcommandContext_Delete(t *testing.T) { sc := &subcommandContext{ c: tt.fields.c, log: tt.fields.log, - isUIEnabled: tt.fields.isUIEnabled, fs: tt.fields.fs, tunnelstoreClient: tt.fields.tunnelstoreClient, userCredential: tt.fields.userCredential, diff --git a/cmd/cloudflared/ui/launch_ui.go b/cmd/cloudflared/ui/launch_ui.go deleted file mode 100644 index 023359ca..00000000 --- a/cmd/cloudflared/ui/launch_ui.go +++ /dev/null @@ -1,223 +0,0 @@ -package ui - -import ( - "context" - "fmt" - "strings" - - "github.com/gdamore/tcell" - "github.com/rivo/tview" - "github.com/rs/zerolog" - - "github.com/cloudflare/cloudflared/connection" - "github.com/cloudflare/cloudflared/ingress" -) - -type connState struct { - location string -} - -type uiModel struct { - version string - edgeURL string - metricsURL string - localServices []string - connections []connState -} - -type palette struct { - url string - connected string - defaultText string - disconnected string - reconnecting string - unregistered string -} - -func NewUIModel(version, hostname, metricsURL string, ing *ingress.Ingress, haConnections int) *uiModel { - localServices := make([]string, len(ing.Rules)) - for i, rule := range ing.Rules { - localServices[i] = rule.Service.String() - } - return &uiModel{ - version: version, - edgeURL: hostname, - metricsURL: metricsURL, - localServices: localServices, - connections: make([]connState, haConnections), - } -} - -func (data *uiModel) Launch( - ctx context.Context, - log, transportLog *zerolog.Logger, -) connection.EventSink { - // Configure the logger to stream logs into the textview - - // Add TextView as a group to write output to - logTextView := NewDynamicColorTextView() - // TODO: Format log for UI - //log.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...) - //transportLog.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...) - - // Construct the UI - palette := palette{ - url: "lightblue", - connected: "lime", - defaultText: "white", - disconnected: "red", - reconnecting: "orange", - unregistered: "orange", - } - - app := tview.NewApplication() - - grid := tview.NewGrid().SetGap(1, 0) - frame := tview.NewFrame(grid) - header := fmt.Sprintf("cloudflared [::b]%s", data.version) - - frame.AddText(header, true, tview.AlignLeft, tcell.ColorWhite) - - // Create table to store connection info and status - connTable := tview.NewTable() - // SetColumns takes a value for each column, representing the size of the column - // Numbers <= 0 represent proportional widths and positive numbers represent absolute widths - grid.SetColumns(20, 0) - - // SetRows takes a value for each row, representing the size of the row - grid.SetRows(1, 1, len(data.connections), 1, 0) - - // AddItem takes a primitive tview type, row, column, rowSpan, columnSpan, minGridHeight, minGridWidth, and focus - grid.AddItem(tview.NewTextView().SetText("Tunnel:"), 0, 0, 1, 1, 0, 0, false) - grid.AddItem(tview.NewTextView().SetText("Status:"), 1, 0, 1, 1, 0, 0, false) - grid.AddItem(tview.NewTextView().SetText("Connections:"), 2, 0, 1, 1, 0, 0, false) - - grid.AddItem(tview.NewTextView().SetText("Metrics:"), 3, 0, 1, 1, 0, 0, false) - - tunnelHostText := tview.NewTextView().SetText(data.edgeURL) - - grid.AddItem(tunnelHostText, 0, 1, 1, 1, 0, 0, false) - status := fmt.Sprintf("[%s]\u2022[%s] Proxying to [%s::b]%s", palette.connected, palette.defaultText, palette.url, strings.Join(data.localServices, ", ")) - grid.AddItem(NewDynamicColorTextView().SetText(status), 1, 1, 1, 1, 0, 0, false) - - grid.AddItem(connTable, 2, 1, 1, 1, 0, 0, false) - - grid.AddItem(NewDynamicColorTextView().SetText(fmt.Sprintf("Metrics at [%s::b]http://%s/metrics", palette.url, data.metricsURL)), 3, 1, 1, 1, 0, 0, false) - - // Add TextView to stream logs - // Logs are displayed in a new grid so a border can be set around them - logGrid := tview.NewGrid().SetBorders(true).AddItem(logTextView.SetChangedFunc(handleNewText(app, logTextView)), 0, 0, 5, 2, 0, 0, false) - // LogFrame holds the Logs header as well as the grid with the textView for streamed logs - logFrame := tview.NewFrame(logGrid).AddText("[::b]Logs:[::-]", true, tview.AlignLeft, tcell.ColorWhite).SetBorders(0, 0, 0, 0, 0, 0) - // Footer for log frame - logFrame.AddText("[::d]Use Ctrl+C to exit[::-]", false, tview.AlignRight, tcell.ColorWhite) - grid.AddItem(logFrame, 4, 0, 5, 2, 0, 0, false) - - go func() { - <-ctx.Done() - app.Stop() - return - }() - - go func() { - if err := app.SetRoot(frame, true).Run(); err != nil { - log.Error().Msgf("Error launching UI: %s", err) - } - }() - - return connection.EventSinkFunc(func(event connection.Event) { - switch event.EventType { - case connection.Connected: - data.setConnTableCell(event, connTable, palette) - case connection.Disconnected, connection.Reconnecting, connection.Unregistering: - data.changeConnStatus(event, connTable, log, palette) - case connection.SetURL: - tunnelHostText.SetText(event.URL) - data.edgeURL = event.URL - case connection.RegisteringTunnel: - if data.edgeURL == "" { - tunnelHostText.SetText(fmt.Sprintf("Registering tunnel connection %d...", event.Index)) - } - } - app.Draw() - }) -} - -func NewDynamicColorTextView() *tview.TextView { - return tview.NewTextView().SetDynamicColors(true) -} - -// Re-draws application when new logs are streamed to UI -func handleNewText(app *tview.Application, logTextView *tview.TextView) func() { - return func() { - app.Draw() - // SetFocus to enable scrolling in textview - app.SetFocus(logTextView) - } -} - -func (data *uiModel) changeConnStatus(event connection.Event, table *tview.Table, log *zerolog.Logger, palette palette) { - index := int(event.Index) - // Get connection location and state - connState := data.getConnState(index) - // Check if connection is already displayed in UI - if connState == nil { - log.Info().Msg("Connection is not in the UI table") - return - } - - locationState := event.Location - - if event.EventType == connection.Reconnecting { - locationState = "Reconnecting..." - } - - connectionNum := index + 1 - // Get table cell - cell := table.GetCell(index, 0) - // Change dot color in front of text as well as location state - text := newCellText(palette, connectionNum, locationState, event.EventType) - cell.SetText(text) -} - -// Return connection location and row in UI table -func (data *uiModel) getConnState(connID int) *connState { - if connID < len(data.connections) { - return &data.connections[connID] - } - - return nil -} - -func (data *uiModel) setConnTableCell(event connection.Event, table *tview.Table, palette palette) { - index := int(event.Index) - connectionNum := index + 1 - - // Update slice to keep track of connection location and state in UI table - data.connections[index].location = event.Location - - // Update text in table cell to show disconnected state - text := newCellText(palette, connectionNum, event.Location, event.EventType) - cell := tview.NewTableCell(text) - table.SetCell(index, 0, cell) -} - -func newCellText(palette palette, connectionNum int, location string, connectedStatus connection.Status) string { - // HA connection indicator formatted as: "• #: ", - // where the left middle dot's color depends on the status of the connection - const connFmtString = "[%s]\u2022[%s] #%d: %s" - - var dotColor string - switch connectedStatus { - case connection.Connected: - dotColor = palette.connected - case connection.Disconnected: - dotColor = palette.disconnected - case connection.Reconnecting: - dotColor = palette.reconnecting - case connection.Unregistering: - dotColor = palette.unregistered - } - - return fmt.Sprintf(connFmtString, dotColor, palette.defaultText, connectionNum, location) -} diff --git a/connection/h2mux_test.go b/connection/h2mux_test.go index 787cfd17..cf2337ec 100644 --- a/connection/h2mux_test.go +++ b/connection/h2mux_test.go @@ -47,7 +47,7 @@ func newH2MuxConnection(t require.TestingT) (*h2muxConnection, *h2mux.Muxer) { edgeMuxChan <- edgeMux }() var connIndex = uint8(0) - testObserver := NewObserver(&log, &log, false) + testObserver := NewObserver(&log, &log) h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil) require.NoError(t, err) return h2muxConn, <-edgeMuxChan diff --git a/connection/http2_test.go b/connection/http2_test.go index 82368d67..e962353f 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -33,7 +33,7 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { edgeConn, cfdConn := net.Pipe() var connIndex = uint8(0) log := zerolog.Nop() - obs := NewObserver(&log, &log, false) + obs := NewObserver(&log, &log) controlStream := NewControlStream( obs, mockConnectedFuse{}, @@ -356,7 +356,7 @@ func TestServeControlStream(t *testing.T) { unregistered: make(chan struct{}), } - obs := NewObserver(&log, &log, false) + obs := NewObserver(&log, &log) controlStream := NewControlStream( obs, mockConnectedFuse{}, @@ -407,7 +407,7 @@ func TestFailRegistration(t *testing.T) { unregistered: make(chan struct{}), } - obs := NewObserver(&log, &log, false) + obs := NewObserver(&log, &log) controlStream := NewControlStream( obs, mockConnectedFuse{}, @@ -453,7 +453,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { events := &eventCollectorSink{} shutdownC := make(chan struct{}) - obs := NewObserver(&log, &log, false) + obs := NewObserver(&log, &log) obs.RegisterSink(events) controlStream := NewControlStream( obs, diff --git a/connection/observer.go b/connection/observer.go index 1e8b62b5..3a855878 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -18,7 +18,6 @@ type Observer struct { logTransport *zerolog.Logger metrics *tunnelMetrics tunnelEventChan chan Event - uiEnabled bool addSinkChan chan EventSink } @@ -26,12 +25,11 @@ type EventSink interface { OnTunnelEvent(event Event) } -func NewObserver(log, logTransport *zerolog.Logger, uiEnabled bool) *Observer { +func NewObserver(log, logTransport *zerolog.Logger) *Observer { o := &Observer{ log: log, logTransport: logTransport, metrics: newTunnelMetrics(), - uiEnabled: uiEnabled, tunnelEventChan: make(chan Event, observerChannelBufferSize), addSinkChan: make(chan EventSink, observerChannelBufferSize), } diff --git a/connection/observer_test.go b/connection/observer_test.go index a8c68e9f..b5ea41b0 100644 --- a/connection/observer_test.go +++ b/connection/observer_test.go @@ -12,7 +12,7 @@ import ( ) func TestSendUrl(t *testing.T) { - observer := NewObserver(&log, &log, false) + observer := NewObserver(&log, &log) observer.SendURL("my-url.com") assert.Equal(t, 1.0, getCounterValue(t, observer.metrics.userHostnamesCounts, "https://my-url.com")) @@ -63,7 +63,7 @@ func TestRegisterServerLocation(t *testing.T) { } func TestObserverEventsDontBlock(t *testing.T) { - observer := NewObserver(&log, &log, false) + observer := NewObserver(&log, &log) var mu sync.Mutex observer.RegisterSink(EventSinkFunc(func(_ Event) { // callback will block if lock is already held diff --git a/go.mod b/go.mod index 1793f367..1f97b27c 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 github.com/fsnotify/fsnotify v1.4.9 - github.com/gdamore/tcell v1.3.0 github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 github.com/gobwas/ws v1.0.4 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 @@ -24,7 +23,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_model v0.2.0 - github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 github.com/rs/zerolog v1.20.0 github.com/stretchr/testify v1.7.1 github.com/urfave/cli/v2 v2.3.0 @@ -63,7 +61,6 @@ require ( github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect - github.com/gdamore/encoding v1.0.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect @@ -73,12 +70,10 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lucasb-eyer/go-colorful v1.0.3 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect github.com/mattn/go-isatty v0.0.12 // indirect - github.com/mattn/go-runewidth v0.0.8 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -89,7 +84,6 @@ require ( github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/rivo/uniseg v0.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect diff --git a/go.sum b/go.sum index f6a15c92..c5b3b0d6 100644 --- a/go.sum +++ b/go.sum @@ -70,7 +70,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k= @@ -189,10 +188,6 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 h1:YO10pIIBftO/kkTFdWhctH96grJ7qiy7bMdiZcIvPKs= github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= @@ -396,9 +391,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= -github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -415,9 +407,6 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= @@ -513,10 +502,6 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 h1:rqaqSUdaW+OBbjnsrOoiaJv43mSRARuvsAuirmdxu7E= -github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -773,7 +758,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/gdamore/encoding/.appveyor.yml b/vendor/github.com/gdamore/encoding/.appveyor.yml deleted file mode 100644 index 19a4c5dd..00000000 --- a/vendor/github.com/gdamore/encoding/.appveyor.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: 1.0.{build} -clone_folder: c:\gopath\src\github.com\gdamore\encoding -environment: - GOPATH: c:\gopath -build_script: -- go version -- go env -- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH% -- go get -t ./... -- go build -- go install ./... -test_script: -- go test ./... diff --git a/vendor/github.com/gdamore/encoding/.travis.yml b/vendor/github.com/gdamore/encoding/.travis.yml deleted file mode 100644 index 50424138..00000000 --- a/vendor/github.com/gdamore/encoding/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go - -go: - - 1.9.x - - 1.10.x - - 1.11.x - - tip diff --git a/vendor/github.com/gdamore/encoding/LICENSE b/vendor/github.com/gdamore/encoding/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/github.com/gdamore/encoding/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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/gdamore/encoding/README.md b/vendor/github.com/gdamore/encoding/README.md deleted file mode 100644 index 3db2b4c5..00000000 --- a/vendor/github.com/gdamore/encoding/README.md +++ /dev/null @@ -1,19 +0,0 @@ -## encoding - -[![Linux Status](https://img.shields.io/travis/gdamore/encoding.svg?label=linux)](https://travis-ci.org/gdamore/encoding) -[![Windows Status](https://img.shields.io/appveyor/ci/gdamore/encoding.svg?label=windows)](https://ci.appveyor.com/project/gdamore/encoding) -[![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/encoding/blob/master/LICENSE) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/encoding) -[![Go Report Card](http://goreportcard.com/badge/gdamore/encoding)](http://goreportcard.com/report/gdamore/encoding) - -Package encoding provides a number of encodings that are missing from the -standard Go [encoding]("https://godoc.org/golang.org/x/text/encoding") package. - -We hope that we can contribute these to the standard Go library someday. It -turns out that some of these are useful for dealing with I/O streams coming -from non-UTF friendly sources. - -The UTF8 Encoder is also useful for situations where valid UTF-8 might be -carried in streams that contain non-valid UTF; in particular I use it for -helping me cope with terminals that embed escape sequences in otherwise -valid UTF-8. diff --git a/vendor/github.com/gdamore/encoding/ascii.go b/vendor/github.com/gdamore/encoding/ascii.go deleted file mode 100644 index b7321f43..00000000 --- a/vendor/github.com/gdamore/encoding/ascii.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "golang.org/x/text/encoding" -) - -// ASCII represents the 7-bit US-ASCII scheme. It decodes directly to -// UTF-8 without change, as all ASCII values are legal UTF-8. -// Unicode values less than 128 (i.e. 7 bits) map 1:1 with ASCII. -// It encodes runes outside of that to 0x1A, the ASCII substitution character. -var ASCII encoding.Encoding - -func init() { - amap := make(map[byte]rune) - for i := 128; i <= 255; i++ { - amap[byte(i)] = RuneError - } - - cm := &Charmap{Map: amap} - cm.Init() - ASCII = cm -} diff --git a/vendor/github.com/gdamore/encoding/charmap.go b/vendor/github.com/gdamore/encoding/charmap.go deleted file mode 100644 index db1c33ef..00000000 --- a/vendor/github.com/gdamore/encoding/charmap.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "sync" - "unicode/utf8" - - "golang.org/x/text/encoding" - "golang.org/x/text/transform" -) - -const ( - // RuneError is an alias for the UTF-8 replacement rune, '\uFFFD'. - RuneError = '\uFFFD' - - // RuneSelf is the rune below which UTF-8 and the Unicode values are - // identical. Its also the limit for ASCII. - RuneSelf = 0x80 - - // ASCIISub is the ASCII substitution character. - ASCIISub = '\x1a' -) - -// Charmap is a structure for setting up encodings for 8-bit character sets, -// for transforming between UTF8 and that other character set. It has some -// ideas borrowed from golang.org/x/text/encoding/charmap, but it uses a -// different implementation. This implementation uses maps, and supports -// user-defined maps. -// -// We do assume that a character map has a reasonable substitution character, -// and that valid encodings are stable (exactly a 1:1 map) and stateless -// (that is there is no shift character or anything like that.) Hence this -// approach will not work for many East Asian character sets. -// -// Measurement shows little or no measurable difference in the performance of -// the two approaches. The difference was down to a couple of nsec/op, and -// no consistent pattern as to which ran faster. With the conversion to -// UTF-8 the code takes about 25 nsec/op. The conversion in the reverse -// direction takes about 100 nsec/op. (The larger cost for conversion -// from UTF-8 is most likely due to the need to convert the UTF-8 byte stream -// to a rune before conversion. -// -type Charmap struct { - transform.NopResetter - bytes map[rune]byte - runes [256][]byte - once sync.Once - - // The map between bytes and runes. To indicate that a specific - // byte value is invalid for a charcter set, use the rune - // utf8.RuneError. Values that are absent from this map will - // be assumed to have the identity mapping -- that is the default - // is to assume ISO8859-1, where all 8-bit characters have the same - // numeric value as their Unicode runes. (Not to be confused with - // the UTF-8 values, which *will* be different for non-ASCII runes.) - // - // If no values less than RuneSelf are changed (or have non-identity - // mappings), then the character set is assumed to be an ASCII - // superset, and certain assumptions and optimizations become - // available for ASCII bytes. - Map map[byte]rune - - // The ReplacementChar is the byte value to use for substitution. - // It should normally be ASCIISub for ASCII encodings. This may be - // unset (left to zero) for mappings that are strictly ASCII supersets. - // In that case ASCIISub will be assumed instead. - ReplacementChar byte -} - -type cmapDecoder struct { - transform.NopResetter - runes [256][]byte -} - -type cmapEncoder struct { - transform.NopResetter - bytes map[rune]byte - replace byte -} - -// Init initializes internal values of a character map. This should -// be done early, to minimize the cost of allocation of transforms -// later. It is not strictly necessary however, as the allocation -// functions will arrange to call it if it has not already been done. -func (c *Charmap) Init() { - c.once.Do(c.initialize) -} - -func (c *Charmap) initialize() { - c.bytes = make(map[rune]byte) - ascii := true - - for i := 0; i < 256; i++ { - r, ok := c.Map[byte(i)] - if !ok { - r = rune(i) - } - if r < 128 && r != rune(i) { - ascii = false - } - if r != RuneError { - c.bytes[r] = byte(i) - } - utf := make([]byte, utf8.RuneLen(r)) - utf8.EncodeRune(utf, r) - c.runes[i] = utf - } - if ascii && c.ReplacementChar == '\x00' { - c.ReplacementChar = ASCIISub - } -} - -// NewDecoder returns a Decoder the converts from the 8-bit -// character set to UTF-8. Unknown mappings, if any, are mapped -// to '\uFFFD'. -func (c *Charmap) NewDecoder() *encoding.Decoder { - c.Init() - return &encoding.Decoder{Transformer: &cmapDecoder{runes: c.runes}} -} - -// NewEncoder returns a Transformer that converts from UTF8 to the -// 8-bit character set. Unknown mappings are mapped to 0x1A. -func (c *Charmap) NewEncoder() *encoding.Encoder { - c.Init() - return &encoding.Encoder{ - Transformer: &cmapEncoder{ - bytes: c.bytes, - replace: c.ReplacementChar, - }, - } -} - -func (d *cmapDecoder) Transform(dst, src []byte, atEOF bool) (int, int, error) { - var e error - var ndst, nsrc int - - for _, c := range src { - b := d.runes[c] - l := len(b) - - if ndst+l > len(dst) { - e = transform.ErrShortDst - break - } - for i := 0; i < l; i++ { - dst[ndst] = b[i] - ndst++ - } - nsrc++ - } - return ndst, nsrc, e -} - -func (d *cmapEncoder) Transform(dst, src []byte, atEOF bool) (int, int, error) { - var e error - var ndst, nsrc int - for nsrc < len(src) { - if ndst >= len(dst) { - e = transform.ErrShortDst - break - } - - r, sz := utf8.DecodeRune(src[nsrc:]) - if r == utf8.RuneError && sz == 1 { - // If its inconclusive due to insufficient data in - // in the source, report it - if !atEOF && !utf8.FullRune(src[nsrc:]) { - e = transform.ErrShortSrc - break - } - } - - if c, ok := d.bytes[r]; ok { - dst[ndst] = c - } else { - dst[ndst] = d.replace - } - nsrc += sz - ndst++ - } - - return ndst, nsrc, e -} diff --git a/vendor/github.com/gdamore/encoding/doc.go b/vendor/github.com/gdamore/encoding/doc.go deleted file mode 100644 index 8a7b48d7..00000000 --- a/vendor/github.com/gdamore/encoding/doc.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding provides a few of the encoding structures that are -// missing from the Go x/text/encoding tree. -package encoding diff --git a/vendor/github.com/gdamore/encoding/ebcdic.go b/vendor/github.com/gdamore/encoding/ebcdic.go deleted file mode 100644 index 8e13f1a9..00000000 --- a/vendor/github.com/gdamore/encoding/ebcdic.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "golang.org/x/text/encoding" -) - -// EBCDIC represents the 8-bit EBCDIC scheme, found in some mainframe -// environments. If you don't know what this is, consider yourself lucky. -var EBCDIC encoding.Encoding - -func init() { - cm := &Charmap{ - ReplacementChar: '\x3f', - Map: map[byte]rune{ - // 0x00-0x03 match - 0x04: RuneError, - 0x05: '\t', - 0x06: RuneError, - 0x07: '\x7f', - 0x08: RuneError, - 0x09: RuneError, - 0x0a: RuneError, - // 0x0b-0x13 match - 0x14: RuneError, - 0x15: '\x85', // Not in any ISO code - 0x16: '\x08', - 0x17: RuneError, - // 0x18-0x19 match - 0x1a: RuneError, - 0x1b: RuneError, - // 0x1c-0x1f match - 0x20: RuneError, - 0x21: RuneError, - 0x22: RuneError, - 0x23: RuneError, - 0x24: RuneError, - 0x25: '\n', - 0x26: '\x17', - 0x27: '\x1b', - 0x28: RuneError, - 0x29: RuneError, - 0x2a: RuneError, - 0x2b: RuneError, - 0x2c: RuneError, - 0x2d: '\x05', - 0x2e: '\x06', - 0x2f: '\x07', - 0x30: RuneError, - 0x31: RuneError, - 0x32: '\x16', - 0x33: RuneError, - 0x34: RuneError, - 0x35: RuneError, - 0x36: RuneError, - 0x37: '\x04', - 0x38: RuneError, - 0x39: RuneError, - 0x3a: RuneError, - 0x3b: RuneError, - 0x3c: '\x14', - 0x3d: '\x15', - 0x3e: RuneError, - 0x3f: '\x1a', // also replacement char - 0x40: ' ', - 0x41: '\xa0', - 0x42: RuneError, - 0x43: RuneError, - 0x44: RuneError, - 0x45: RuneError, - 0x46: RuneError, - 0x47: RuneError, - 0x48: RuneError, - 0x49: RuneError, - 0x4a: RuneError, - 0x4b: '.', - 0x4c: '<', - 0x4d: '(', - 0x4e: '+', - 0x4f: '|', - 0x50: '&', - 0x51: RuneError, - 0x52: RuneError, - 0x53: RuneError, - 0x54: RuneError, - 0x55: RuneError, - 0x56: RuneError, - 0x57: RuneError, - 0x58: RuneError, - 0x59: RuneError, - 0x5a: '!', - 0x5b: '$', - 0x5c: '*', - 0x5d: ')', - 0x5e: ';', - 0x5f: '¬', - 0x60: '-', - 0x61: '/', - 0x62: RuneError, - 0x63: RuneError, - 0x64: RuneError, - 0x65: RuneError, - 0x66: RuneError, - 0x67: RuneError, - 0x68: RuneError, - 0x69: RuneError, - 0x6a: '¦', - 0x6b: ',', - 0x6c: '%', - 0x6d: '_', - 0x6e: '>', - 0x6f: '?', - 0x70: RuneError, - 0x71: RuneError, - 0x72: RuneError, - 0x73: RuneError, - 0x74: RuneError, - 0x75: RuneError, - 0x76: RuneError, - 0x77: RuneError, - 0x78: RuneError, - 0x79: '`', - 0x7a: ':', - 0x7b: '#', - 0x7c: '@', - 0x7d: '\'', - 0x7e: '=', - 0x7f: '"', - 0x80: RuneError, - 0x81: 'a', - 0x82: 'b', - 0x83: 'c', - 0x84: 'd', - 0x85: 'e', - 0x86: 'f', - 0x87: 'g', - 0x88: 'h', - 0x89: 'i', - 0x8a: RuneError, - 0x8b: RuneError, - 0x8c: RuneError, - 0x8d: RuneError, - 0x8e: RuneError, - 0x8f: '±', - 0x90: RuneError, - 0x91: 'j', - 0x92: 'k', - 0x93: 'l', - 0x94: 'm', - 0x95: 'n', - 0x96: 'o', - 0x97: 'p', - 0x98: 'q', - 0x99: 'r', - 0x9a: RuneError, - 0x9b: RuneError, - 0x9c: RuneError, - 0x9d: RuneError, - 0x9e: RuneError, - 0x9f: RuneError, - 0xa0: RuneError, - 0xa1: '~', - 0xa2: 's', - 0xa3: 't', - 0xa4: 'u', - 0xa5: 'v', - 0xa6: 'w', - 0xa7: 'x', - 0xa8: 'y', - 0xa9: 'z', - 0xaa: RuneError, - 0xab: RuneError, - 0xac: RuneError, - 0xad: RuneError, - 0xae: RuneError, - 0xaf: RuneError, - 0xb0: '^', - 0xb1: RuneError, - 0xb2: RuneError, - 0xb3: RuneError, - 0xb4: RuneError, - 0xb5: RuneError, - 0xb6: RuneError, - 0xb7: RuneError, - 0xb8: RuneError, - 0xb9: RuneError, - 0xba: '[', - 0xbb: ']', - 0xbc: RuneError, - 0xbd: RuneError, - 0xbe: RuneError, - 0xbf: RuneError, - 0xc0: '{', - 0xc1: 'A', - 0xc2: 'B', - 0xc3: 'C', - 0xc4: 'D', - 0xc5: 'E', - 0xc6: 'F', - 0xc7: 'G', - 0xc8: 'H', - 0xc9: 'I', - 0xca: '\xad', // NB: soft hyphen - 0xcb: RuneError, - 0xcc: RuneError, - 0xcd: RuneError, - 0xce: RuneError, - 0xcf: RuneError, - 0xd0: '}', - 0xd1: 'J', - 0xd2: 'K', - 0xd3: 'L', - 0xd4: 'M', - 0xd5: 'N', - 0xd6: 'O', - 0xd7: 'P', - 0xd8: 'Q', - 0xd9: 'R', - 0xda: RuneError, - 0xdb: RuneError, - 0xdc: RuneError, - 0xdd: RuneError, - 0xde: RuneError, - 0xdf: RuneError, - 0xe0: '\\', - 0xe1: '\u2007', // Non-breaking space - 0xe2: 'S', - 0xe3: 'T', - 0xe4: 'U', - 0xe5: 'V', - 0xe6: 'W', - 0xe7: 'X', - 0xe8: 'Y', - 0xe9: 'Z', - 0xea: RuneError, - 0xeb: RuneError, - 0xec: RuneError, - 0xed: RuneError, - 0xee: RuneError, - 0xef: RuneError, - 0xf0: '0', - 0xf1: '1', - 0xf2: '2', - 0xf3: '3', - 0xf4: '4', - 0xf5: '5', - 0xf6: '6', - 0xf7: '7', - 0xf8: '8', - 0xf9: '9', - 0xfa: RuneError, - 0xfb: RuneError, - 0xfc: RuneError, - 0xfd: RuneError, - 0xfe: RuneError, - 0xff: RuneError, - }} - cm.Init() - EBCDIC = cm -} diff --git a/vendor/github.com/gdamore/encoding/latin1.go b/vendor/github.com/gdamore/encoding/latin1.go deleted file mode 100644 index 226bf01d..00000000 --- a/vendor/github.com/gdamore/encoding/latin1.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "golang.org/x/text/encoding" -) - -// ISO8859_1 represents the 8-bit ISO8859-1 scheme. It decodes directly to -// UTF-8 without change, as all ISO8859-1 values are legal UTF-8. -// Unicode values less than 256 (i.e. 8 bits) map 1:1 with 8859-1. -// It encodes runes outside of that to 0x1A, the ASCII substitution character. -var ISO8859_1 encoding.Encoding - -func init() { - cm := &Charmap{} - cm.Init() - - // 8859-1 is the 8-bit identity map for Unicode. - ISO8859_1 = cm -} diff --git a/vendor/github.com/gdamore/encoding/latin5.go b/vendor/github.com/gdamore/encoding/latin5.go deleted file mode 100644 index c75ecf27..00000000 --- a/vendor/github.com/gdamore/encoding/latin5.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "golang.org/x/text/encoding" -) - -// ISO8859_9 represents the 8-bit ISO8859-9 scheme. -var ISO8859_9 encoding.Encoding - -func init() { - cm := &Charmap{Map: map[byte]rune{ - 0xD0: 'Ğ', - 0xDD: 'İ', - 0xDE: 'Ş', - 0xF0: 'ğ', - 0xFD: 'ı', - 0xFE: 'ş', - }} - cm.Init() - ISO8859_9 = cm -} diff --git a/vendor/github.com/gdamore/encoding/utf8.go b/vendor/github.com/gdamore/encoding/utf8.go deleted file mode 100644 index 2d59f4b3..00000000 --- a/vendor/github.com/gdamore/encoding/utf8.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015 Garrett D'Amore -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 encoding - -import ( - "golang.org/x/text/encoding" -) - -type validUtf8 struct{} - -// UTF8 is an encoding for UTF-8. All it does is verify that the UTF-8 -// in is valid. The main reason for its existence is that it will detect -// and report ErrSrcShort or ErrDstShort, whereas the Nop encoding just -// passes every byte, blithely. -var UTF8 encoding.Encoding = validUtf8{} - -func (validUtf8) NewDecoder() *encoding.Decoder { - return &encoding.Decoder{Transformer: encoding.UTF8Validator} -} - -func (validUtf8) NewEncoder() *encoding.Encoder { - return &encoding.Encoder{Transformer: encoding.UTF8Validator} -} diff --git a/vendor/github.com/gdamore/tcell/.appveyor.yml b/vendor/github.com/gdamore/tcell/.appveyor.yml deleted file mode 100644 index 435dfe3a..00000000 --- a/vendor/github.com/gdamore/tcell/.appveyor.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: 1.0.{build} -clone_folder: c:\gopath\src\github.com\gdamore\tcell -environment: - GOPATH: c:\gopath -build_script: -- go version -- go env -- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH% -- go get -t ./... -- go build -- go install ./... -test_script: -- go test ./... diff --git a/vendor/github.com/gdamore/tcell/.gitignore b/vendor/github.com/gdamore/tcell/.gitignore deleted file mode 100644 index c57100a5..00000000 --- a/vendor/github.com/gdamore/tcell/.gitignore +++ /dev/null @@ -1 +0,0 @@ -coverage.txt diff --git a/vendor/github.com/gdamore/tcell/.travis.yml b/vendor/github.com/gdamore/tcell/.travis.yml deleted file mode 100644 index fec3f669..00000000 --- a/vendor/github.com/gdamore/tcell/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - 1.10.x - - 1.11.x - - master - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/gdamore/tcell/AUTHORS b/vendor/github.com/gdamore/tcell/AUTHORS deleted file mode 100644 index 53f87ee6..00000000 --- a/vendor/github.com/gdamore/tcell/AUTHORS +++ /dev/null @@ -1,4 +0,0 @@ -Garrett D'Amore -Zachary Yedidia -Junegunn Choi -Staysail Systems, Inc. diff --git a/vendor/github.com/gdamore/tcell/LICENSE b/vendor/github.com/gdamore/tcell/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/github.com/gdamore/tcell/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - 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/gdamore/tcell/README.adoc b/vendor/github.com/gdamore/tcell/README.adoc deleted file mode 100644 index bfea1e79..00000000 --- a/vendor/github.com/gdamore/tcell/README.adoc +++ /dev/null @@ -1,272 +0,0 @@ -= tcell - - -image:https://img.shields.io/travis/gdamore/tcell.svg?label=linux[Linux Status,link="https://travis-ci.org/gdamore/tcell"] -image:https://img.shields.io/appveyor/ci/gdamore/tcell.svg?label=windows[Windows Status,link="https://ci.appveyor.com/project/gdamore/tcell"] -image:https://img.shields.io/badge/license-APACHE2-blue.svg[Apache License,link="https://github.com/gdamore/tcell/blob/master/LICENSE"] -image:https://img.shields.io/badge/gitter-join-brightgreen.svg[Gitter,link="https://gitter.im/gdamore/tcell"] -image:https://img.shields.io/badge/godoc-reference-blue.svg[GoDoc,link="https://godoc.org/github.com/gdamore/tcell"] -image:http://goreportcard.com/badge/gdamore/tcell[Go Report Card,link="http://goreportcard.com/report/gdamore/tcell"] -image:https://codecov.io/gh/gdamore/tcell/branch/master/graph/badge.svg[codecov,link="https://codecov.io/gh/gdamore/tcell"] -image:https://tidelift.com/badges/github/gdamore/tcell?style=flat[Dependencies] - -[cols="2",grid="none"] -|=== -|_Tcell_ is a _Go_ package that provides a cell based view for text terminals, like _xterm_. -It was inspired by _termbox_, but includes many additional improvements. -a|[.right] -image::logos/tcell.png[float="right"] -|=== - -## Examples - -* https://github.com/gdamore/proxima5[proxima5] - space shooter (https://youtu.be/jNxKTCmY_bQ[video]) -* https://github.com/gdamore/govisor[govisor] - service management UI (http://2.bp.blogspot.com/--OsvnfzSNow/Vf7aqMw3zXI/AAAAAAAAARo/uOMtOvw4Sbg/s1600/Screen%2BShot%2B2015-09-20%2Bat%2B9.08.41%2BAM.png[screenshot]) -* mouse demo - included mouse test (http://2.bp.blogspot.com/-fWvW5opT0es/VhIdItdKqJI/AAAAAAAAATE/7Ojc0L1SpB0/s1600/Screen%2BShot%2B2015-10-04%2Bat%2B11.47.13%2BPM.png[screenshot]) -* https://github.com/gdamore/gomatrix[gomatrix] - converted from Termbox -* https://github.com/zyedidia/micro/[micro] - lightweight text editor with syntax-highlighting and themes -* https://github.com/viktomas/godu[godu] - simple golang utility helping to discover large files/folders. -* https://github.com/rivo/tview[tview] - rich interactive widgets for terminal UIs -* https://github.com/marcusolsson/tui-go[tui-go] - UI library for terminal apps -* https://github.com/rgm3/gomandelbrot[gomandelbrot] - Mandelbrot! -* https://github.com/senorprogrammer/wtf[WTF]- Personal information dashboard for your terminal -* https://github.com/browsh-org/browsh[browsh] - A fully-modern text-based browser, rendering to TTY and browsers (https://www.youtube.com/watch?v=HZq86XfBoRo[video]) -* https://github.com/sachaos/go-life[go-life] - Conway's Game of Life. -* https://github.com/gcla/gowid[gowid] - compositional widgets for terminal UIs, inspired by urwid -* https://termshark.io[termshark] - a terminal UI for tshark, inspired by Wireshark, built on gowid - -## Pure Go Terminfo Database - -_Tcell_ includes a full parser and expander for terminfo capability strings, -so that it can avoid hard coding escape strings for formatting. It also favors -portability, and includes support for all POSIX systems. - -The database is also flexible & extensible, and can modified by either running -a program to build the entire database, or an entry for just a single terminal. - -## More Portable - -_Tcell_ is portable to a wide variety of systems. -_Tcell_ is believed -to work with all of the systems officially supported by golang with -the exception of nacl (which lacks any kind of a terminal interface). -(Plan9 is not supported by _Tcell_, but it is experimental status only -in golang.) For all of these systems *except Solaris/illumos*, _Tcell_ -is pure Go, with no need for CGO. - -## No Async IO - -_Tcell_ is able to operate without requiring `SIGIO` signals (unlike _termbox_), -or asynchronous I/O, and can instead use standard Go file -objects and Go routines. -This means it should be safe, especially for -use with programs that use exec, or otherwise need to manipulate the -tty streams. -This model is also much closer to idiomatic Go, leading -to fewer surprises. - -## Rich Unicode & non-Unicode support - -_Tcell_ includes enhanced support for Unicode, including wide characters and -combining characters, provided your terminal can support them. -Note that -Windows terminals generally don't support the full Unicode repertoire. - -It will also convert to and from Unicode locales, so that the program -can work with UTF-8 internally, and get reasonable output in other locales. -_Tcell_ tries hard to convert to native characters on both input and output, and -on output _Tcell_ even makes use of the alternate character set to facilitate -drawing certain characters. - -## More Function Keys - -_Tcell_ also has richer support for a larger number of special keys that some terminals can send. - -## Better Color Handling - -_Tcell_ will respect your terminal's color space as specified within your terminfo -entries, so that for example attempts to emit color sequences on VT100 terminals -won't result in unintended consequences. - -In Windows mode, _Tcell_ supports 16 colors, bold, dim, and reverse, -instead of just termbox's 8 colors with reverse. (Note that there is some -conflation with bold/dim and colors.) - -_Tcell_ maps 16 colors down to 8, for terminals that need it. -(The upper 8 colors are just brighter versions of the lower 8.) - -## Better Mouse Support - -_Tcell_ supports enhanced mouse tracking mode, so your application can receive -regular mouse motion events, and wheel events, if your terminal supports it. - -## _Termbox_ Compatibility - -A compatibility layer for _termbox_ is provided in the `compat` directory. -To use it, try importing `github.com/gdamore/tcell/termbox` -instead. Most _termbox-go_ programs will probably work without further -modification. - -## Working With Unicode - -Internally Tcell uses UTF-8, just like Go. -However, Tcell understands how to -convert to and from other character sets, using the capabilities of -the `golang.org/x/text/encoding packages`. -Your application must supply -them, as the full set of the most common ones bloats the program by about 2MB. -If you're lazy, and want them all anyway, see the `encoding` sub-directory. - -## Wide & Combining Characters - -The `SetContent()` API takes a primary rune, and an optional list of combining runes. -If any of the runes is a wide (East Asian) rune occupying two cells, -then the library will skip output from the following cell, but care must be -taken in the application to avoid explicitly attempting to set content in the -next cell, otherwise the results are undefined. (Normally wide character -is displayed, and the other character is not; do not depend on that behavior.) - -Experience has shown that the vanilla Windows 8 console application does not -support any of these characters properly, but at least some options like -_ConEmu_ do support Wide characters. - -## Colors - -_Tcell_ assumes the ANSI/XTerm color model, including the 256 color map that -XTerm uses when it supports 256 colors. The terminfo guidance will be -honored, with respect to the number of colors supported. Also, only -terminals which expose ANSI style `setaf` and `setab` will support color; -if you have a color terminal that only has `setf` and `setb`, please let me -know; it wouldn't be hard to add that if there is need. - -## 24-bit Color - -_Tcell_ _supports true color_! (That is, if your terminal can support it, -_Tcell_ can accurately display 24-bit color.) - -To use 24-bit color, you need to use a terminal that supports it. Modern -xterm and similar teminal emulators can support this. As terminfo lacks any -way to describe this capability, we fabricate the capability for -terminals with names ending in `*-truecolor`. The stock distribution ships -with a database that defines `xterm-truecolor`. -To try it out, set your -`TERM` variable to `xterm-truecolor`. - -When using TrueColor, programs will display the colors that the programmer -intended, overriding any "`themes`" you may have set in your terminal -emulator. (For some cases, accurate color fidelity is more important -than respecting themes. For other cases, such as typical text apps that -only use a few colors, its more desirable to respect the themes that -the user has established.) - -If you find this undesirable, you can either use a `TERM` variable -that lacks the `TRUECOLOR` setting, or set `TCELL_TRUECOLOR=disable` in your -environment. - -## Performance - -Reasonable attempts have been made to minimize sending data to terminals, -avoiding repeated sequences or drawing the same cell on refresh updates. - -## Terminfo - -(Not relevent for Windows users.) - -The Terminfo implementation operates with two forms of database. The first -is the built-in go database, which contains a number of real database entries -that are compiled into the program directly. This should minimize calling -out to database file searches. - -The second is in the form of JSON files, that contain the same information, -which can be located either by the `$TCELLDB` environment file, `$HOME/.tcelldb`, -or is located in the Go source directory as `database.json`. - -These files (both the Go and the JSON files) can be generated using the -mkinfo.go program. If you need to regnerate the entire set for some reason, -run the mkdatabase.sh file. The generation uses the infocmp(1) program on -the system to collect the necessary information. - -The `mkinfo.go` program can also be used to generate specific database entries -for named terminals, in case your favorite terminal is missing. (If you -find that this is the case, please let me know and I'll try to add it!) - -_Tcell_ requires that the terminal support the `cup` mode of cursor addressing. -Terminals without absolute cursor addressability are not supported. -This is unlikely to be a problem; such terminals have not been mass produced -since the early 1970s. - -## Mouse Support - -Mouse support is detected via the `kmous` terminfo variable, however, -enablement/disablement and decoding mouse events is done using hard coded -sequences based on the XTerm X11 model. As of this writing all popular -terminals with mouse tracking support this model. (Full terminfo support -is not possible as terminfo sequences are not defined.) - -On Windows, the mouse works normally. - -Mouse wheel buttons on various terminals are known to work, but the support -in terminal emulators, as well as support for various buttons and -live mouse tracking, varies widely. Modern _xterm_, macOS _Terminal_, and _iTerm_ all work well. - -## Testablity - -There is a `SimulationScreen`, that can be used to simulate a real screen -for automated testing. The supplied tests do this. The simulation contains -event delivery, screen resizing support, and capabilities to inject events -and examine "`physical`" screen contents. - -## Platforms - -### POSIX (Linux, FreeBSD, macOS, Solaris, etc.) - -For mainstream systems with a suitably well defined system call interface -to tty settings, everything works using pure Go. - -For the remainder (right now means only Solaris/illumos) we use POSIX function -calls to manage termios, which implies that CGO is required on those platforms. - -### Windows - -Windows console mode applications are supported. Unfortunately _mintty_ -and other _cygwin_ style applications are not supported. - -Modern console applications like ConEmu, as well as the Windows 10 -console itself, support all the good features (resize, mouse tracking, etc.) - -I haven't figured out how to cleanly resolve the dichotomy between cygwin -style termios and the Windows Console API; it seems that perhaps nobody else -has either. If anyone has suggestions, let me know! Really, if you're -using a Windows application, you should use the native Windows console or a -fully compatible console implementation. - -### Plan9 and Native Client (Nacl) - -The nacl and plan9 platforms won't work, but compilation stubs are supplied -for folks that want to include parts of this in software targetting those -platforms. The Simulation screen works, but as Tcell doesn't know how to -allocate a real screen object on those platforms, `NewScreen()` will fail. - -If anyone has wisdom about how to improve support for either of these, -please let me know. PRs are especially welcome. - -### Commercial Support - -_Tcell_ is absolutely free, but if you want to obtain commercial, professional support, there are options. - -[cols="2",align="center",frame="none", grid="none"] -|=== -^.^| -image:logos/tidelift.png[100,100] -a| -https://tidelift.com/[Tidelift] subscriptions include support for _Tcell_, as well as many other open source packages. - -^.^| -image:logos/staysail.png[100,100] -a| -mailto:info@staysail.tech[Staysail Systems, Inc.] offers direct support, and custom development around _Tcell_ on an hourly basis. - -^.^| -image:logos/patreon.png[100,100] -a|I also welcome donations at https://www.patreon.com/gedamore/[Patreon], if you just want to make a contribution. -|=== diff --git a/vendor/github.com/gdamore/tcell/attr.go b/vendor/github.com/gdamore/tcell/attr.go deleted file mode 100644 index 866c0ebd..00000000 --- a/vendor/github.com/gdamore/tcell/attr.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// AttrMask represents a mask of text attributes, apart from color. -// Note that support for attributes may vary widely across terminals. -type AttrMask int - -// Attributes are not colors, but affect the display of text. They can -// be combined. -const ( - AttrBold AttrMask = 1 << (25 + iota) - AttrBlink - AttrReverse - AttrUnderline - AttrDim - AttrNone AttrMask = 0 // Just normal text. -) - -const attrAll = AttrBold | AttrBlink | AttrReverse | AttrUnderline | AttrDim diff --git a/vendor/github.com/gdamore/tcell/cell.go b/vendor/github.com/gdamore/tcell/cell.go deleted file mode 100644 index c7f8f1ad..00000000 --- a/vendor/github.com/gdamore/tcell/cell.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - runewidth "github.com/mattn/go-runewidth" -) - -type cell struct { - currMain rune - currComb []rune - currStyle Style - lastMain rune - lastStyle Style - lastComb []rune - width int -} - -// CellBuffer represents a two dimensional array of character cells. -// This is primarily intended for use by Screen implementors; it -// contains much of the common code they need. To create one, just -// declare a variable of its type; no explicit initialization is necessary. -// -// CellBuffer is not thread safe. -type CellBuffer struct { - w int - h int - cells []cell -} - -// SetContent sets the contents (primary rune, combining runes, -// and style) for a cell at a given location. -func (cb *CellBuffer) SetContent(x int, y int, - mainc rune, combc []rune, style Style) { - - if x >= 0 && y >= 0 && x < cb.w && y < cb.h { - c := &cb.cells[(y*cb.w)+x] - - c.currComb = append([]rune{}, combc...) - - if c.currMain != mainc { - c.width = runewidth.RuneWidth(mainc) - } - c.currMain = mainc - c.currStyle = style - } -} - -// GetContent returns the contents of a character cell, including the -// primary rune, any combining character runes (which will usually be -// nil), the style, and the display width in cells. (The width can be -// either 1, normally, or 2 for East Asian full-width characters.) -func (cb *CellBuffer) GetContent(x, y int) (rune, []rune, Style, int) { - var mainc rune - var combc []rune - var style Style - var width int - if x >= 0 && y >= 0 && x < cb.w && y < cb.h { - c := &cb.cells[(y*cb.w)+x] - mainc, combc, style = c.currMain, c.currComb, c.currStyle - if width = c.width; width == 0 || mainc < ' ' { - width = 1 - mainc = ' ' - } - } - return mainc, combc, style, width -} - -// Size returns the (width, height) in cells of the buffer. -func (cb *CellBuffer) Size() (int, int) { - return cb.w, cb.h -} - -// Invalidate marks all characters within the buffer as dirty. -func (cb *CellBuffer) Invalidate() { - for i := range cb.cells { - cb.cells[i].lastMain = rune(0) - } -} - -// Dirty checks if a character at the given location needs an -// to be refreshed on the physical display. This returns true -// if the cell content is different since the last time it was -// marked clean. -func (cb *CellBuffer) Dirty(x, y int) bool { - if x >= 0 && y >= 0 && x < cb.w && y < cb.h { - c := &cb.cells[(y*cb.w)+x] - if c.lastMain == rune(0) { - return true - } - if c.lastMain != c.currMain { - return true - } - if c.lastStyle != c.currStyle { - return true - } - if len(c.lastComb) != len(c.currComb) { - return true - } - for i := range c.lastComb { - if c.lastComb[i] != c.currComb[i] { - return true - } - } - } - return false -} - -// SetDirty is normally used to indicate that a cell has -// been displayed (in which case dirty is false), or to manually -// force a cell to be marked dirty. -func (cb *CellBuffer) SetDirty(x, y int, dirty bool) { - if x >= 0 && y >= 0 && x < cb.w && y < cb.h { - c := &cb.cells[(y*cb.w)+x] - if dirty { - c.lastMain = rune(0) - } else { - if c.currMain == rune(0) { - c.currMain = ' ' - } - c.lastMain = c.currMain - c.lastComb = c.currComb - c.lastStyle = c.currStyle - } - } -} - -// Resize is used to resize the cells array, with different dimensions, -// while preserving the original contents. The cells will be invalidated -// so that they can be redrawn. -func (cb *CellBuffer) Resize(w, h int) { - - if cb.h == h && cb.w == w { - return - } - - newc := make([]cell, w*h) - for y := 0; y < h && y < cb.h; y++ { - for x := 0; x < w && x < cb.w; x++ { - oc := &cb.cells[(y*cb.w)+x] - nc := &newc[(y*w)+x] - nc.currMain = oc.currMain - nc.currComb = oc.currComb - nc.currStyle = oc.currStyle - nc.width = oc.width - nc.lastMain = rune(0) - } - } - cb.cells = newc - cb.h = h - cb.w = w -} - -// Fill fills the entire cell buffer array with the specified character -// and style. Normally choose ' ' to clear the screen. This API doesn't -// support combining characters, or characters with a width larger than one. -func (cb *CellBuffer) Fill(r rune, style Style) { - for i := range cb.cells { - c := &cb.cells[i] - c.currMain = r - c.currComb = nil - c.currStyle = style - c.width = 1 - } -} diff --git a/vendor/github.com/gdamore/tcell/charset_stub.go b/vendor/github.com/gdamore/tcell/charset_stub.go deleted file mode 100644 index c1c1594c..00000000 --- a/vendor/github.com/gdamore/tcell/charset_stub.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build plan9 nacl - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -func getCharset() string { - return "" -} diff --git a/vendor/github.com/gdamore/tcell/charset_unix.go b/vendor/github.com/gdamore/tcell/charset_unix.go deleted file mode 100644 index d9f9d8e1..00000000 --- a/vendor/github.com/gdamore/tcell/charset_unix.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build !windows,!nacl,!plan9 - -// Copyright 2016 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "os" - "strings" -) - -func getCharset() string { - // Determine the character set. This can help us later. - // Per POSIX, we search for LC_ALL first, then LC_CTYPE, and - // finally LANG. First one set wins. - locale := "" - if locale = os.Getenv("LC_ALL"); locale == "" { - if locale = os.Getenv("LC_CTYPE"); locale == "" { - locale = os.Getenv("LANG") - } - } - if locale == "POSIX" || locale == "C" { - return "US-ASCII" - } - if i := strings.IndexRune(locale, '@'); i >= 0 { - locale = locale[:i] - } - if i := strings.IndexRune(locale, '.'); i >= 0 { - locale = locale[i+1:] - } else { - // Default assumption, and on Linux we can see LC_ALL - // without a character set, which we assume implies UTF-8. - return "UTF-8" - } - // XXX: add support for aliases - return locale -} diff --git a/vendor/github.com/gdamore/tcell/charset_windows.go b/vendor/github.com/gdamore/tcell/charset_windows.go deleted file mode 100644 index 2400aa8a..00000000 --- a/vendor/github.com/gdamore/tcell/charset_windows.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build windows - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -func getCharset() string { - return "UTF-16" -} diff --git a/vendor/github.com/gdamore/tcell/color.go b/vendor/github.com/gdamore/tcell/color.go deleted file mode 100644 index 2e096c70..00000000 --- a/vendor/github.com/gdamore/tcell/color.go +++ /dev/null @@ -1,1019 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import "strconv" - -// Color represents a color. The low numeric values are the same as used -// by ECMA-48, and beyond that XTerm. A 24-bit RGB value may be used by -// adding in the ColorIsRGB flag. For Color names we use the W3C approved -// color names. -// -// Note that on various terminals colors may be approximated however, or -// not supported at all. If no suitable representation for a color is known, -// the library will simply not set any color, deferring to whatever default -// attributes the terminal uses. -type Color int32 - -const ( - // ColorDefault is used to leave the Color unchanged from whatever - // system or teminal default may exist. - ColorDefault Color = -1 - - // ColorIsRGB is used to indicate that the numeric value is not - // a known color constant, but rather an RGB value. The lower - // order 3 bytes are RGB. - ColorIsRGB Color = 1 << 24 -) - -// Note that the order of these options is important -- it follows the -// definitions used by ECMA and XTerm. Hence any further named colors -// must begin at a value not less than 256. -const ( - ColorBlack Color = iota - ColorMaroon - ColorGreen - ColorOlive - ColorNavy - ColorPurple - ColorTeal - ColorSilver - ColorGray - ColorRed - ColorLime - ColorYellow - ColorBlue - ColorFuchsia - ColorAqua - ColorWhite - Color16 - Color17 - Color18 - Color19 - Color20 - Color21 - Color22 - Color23 - Color24 - Color25 - Color26 - Color27 - Color28 - Color29 - Color30 - Color31 - Color32 - Color33 - Color34 - Color35 - Color36 - Color37 - Color38 - Color39 - Color40 - Color41 - Color42 - Color43 - Color44 - Color45 - Color46 - Color47 - Color48 - Color49 - Color50 - Color51 - Color52 - Color53 - Color54 - Color55 - Color56 - Color57 - Color58 - Color59 - Color60 - Color61 - Color62 - Color63 - Color64 - Color65 - Color66 - Color67 - Color68 - Color69 - Color70 - Color71 - Color72 - Color73 - Color74 - Color75 - Color76 - Color77 - Color78 - Color79 - Color80 - Color81 - Color82 - Color83 - Color84 - Color85 - Color86 - Color87 - Color88 - Color89 - Color90 - Color91 - Color92 - Color93 - Color94 - Color95 - Color96 - Color97 - Color98 - Color99 - Color100 - Color101 - Color102 - Color103 - Color104 - Color105 - Color106 - Color107 - Color108 - Color109 - Color110 - Color111 - Color112 - Color113 - Color114 - Color115 - Color116 - Color117 - Color118 - Color119 - Color120 - Color121 - Color122 - Color123 - Color124 - Color125 - Color126 - Color127 - Color128 - Color129 - Color130 - Color131 - Color132 - Color133 - Color134 - Color135 - Color136 - Color137 - Color138 - Color139 - Color140 - Color141 - Color142 - Color143 - Color144 - Color145 - Color146 - Color147 - Color148 - Color149 - Color150 - Color151 - Color152 - Color153 - Color154 - Color155 - Color156 - Color157 - Color158 - Color159 - Color160 - Color161 - Color162 - Color163 - Color164 - Color165 - Color166 - Color167 - Color168 - Color169 - Color170 - Color171 - Color172 - Color173 - Color174 - Color175 - Color176 - Color177 - Color178 - Color179 - Color180 - Color181 - Color182 - Color183 - Color184 - Color185 - Color186 - Color187 - Color188 - Color189 - Color190 - Color191 - Color192 - Color193 - Color194 - Color195 - Color196 - Color197 - Color198 - Color199 - Color200 - Color201 - Color202 - Color203 - Color204 - Color205 - Color206 - Color207 - Color208 - Color209 - Color210 - Color211 - Color212 - Color213 - Color214 - Color215 - Color216 - Color217 - Color218 - Color219 - Color220 - Color221 - Color222 - Color223 - Color224 - Color225 - Color226 - Color227 - Color228 - Color229 - Color230 - Color231 - Color232 - Color233 - Color234 - Color235 - Color236 - Color237 - Color238 - Color239 - Color240 - Color241 - Color242 - Color243 - Color244 - Color245 - Color246 - Color247 - Color248 - Color249 - Color250 - Color251 - Color252 - Color253 - Color254 - Color255 - ColorAliceBlue - ColorAntiqueWhite - ColorAquaMarine - ColorAzure - ColorBeige - ColorBisque - ColorBlanchedAlmond - ColorBlueViolet - ColorBrown - ColorBurlyWood - ColorCadetBlue - ColorChartreuse - ColorChocolate - ColorCoral - ColorCornflowerBlue - ColorCornsilk - ColorCrimson - ColorDarkBlue - ColorDarkCyan - ColorDarkGoldenrod - ColorDarkGray - ColorDarkGreen - ColorDarkKhaki - ColorDarkMagenta - ColorDarkOliveGreen - ColorDarkOrange - ColorDarkOrchid - ColorDarkRed - ColorDarkSalmon - ColorDarkSeaGreen - ColorDarkSlateBlue - ColorDarkSlateGray - ColorDarkTurquoise - ColorDarkViolet - ColorDeepPink - ColorDeepSkyBlue - ColorDimGray - ColorDodgerBlue - ColorFireBrick - ColorFloralWhite - ColorForestGreen - ColorGainsboro - ColorGhostWhite - ColorGold - ColorGoldenrod - ColorGreenYellow - ColorHoneydew - ColorHotPink - ColorIndianRed - ColorIndigo - ColorIvory - ColorKhaki - ColorLavender - ColorLavenderBlush - ColorLawnGreen - ColorLemonChiffon - ColorLightBlue - ColorLightCoral - ColorLightCyan - ColorLightGoldenrodYellow - ColorLightGray - ColorLightGreen - ColorLightPink - ColorLightSalmon - ColorLightSeaGreen - ColorLightSkyBlue - ColorLightSlateGray - ColorLightSteelBlue - ColorLightYellow - ColorLimeGreen - ColorLinen - ColorMediumAquamarine - ColorMediumBlue - ColorMediumOrchid - ColorMediumPurple - ColorMediumSeaGreen - ColorMediumSlateBlue - ColorMediumSpringGreen - ColorMediumTurquoise - ColorMediumVioletRed - ColorMidnightBlue - ColorMintCream - ColorMistyRose - ColorMoccasin - ColorNavajoWhite - ColorOldLace - ColorOliveDrab - ColorOrange - ColorOrangeRed - ColorOrchid - ColorPaleGoldenrod - ColorPaleGreen - ColorPaleTurquoise - ColorPaleVioletRed - ColorPapayaWhip - ColorPeachPuff - ColorPeru - ColorPink - ColorPlum - ColorPowderBlue - ColorRebeccaPurple - ColorRosyBrown - ColorRoyalBlue - ColorSaddleBrown - ColorSalmon - ColorSandyBrown - ColorSeaGreen - ColorSeashell - ColorSienna - ColorSkyblue - ColorSlateBlue - ColorSlateGray - ColorSnow - ColorSpringGreen - ColorSteelBlue - ColorTan - ColorThistle - ColorTomato - ColorTurquoise - ColorViolet - ColorWheat - ColorWhiteSmoke - ColorYellowGreen -) - -// These are aliases for the color gray, because some of us spell -// it as grey. -const ( - ColorGrey = ColorGray - ColorDimGrey = ColorDimGray - ColorDarkGrey = ColorDarkGray - ColorDarkSlateGrey = ColorDarkSlateGray - ColorLightGrey = ColorLightGray - ColorLightSlateGrey = ColorLightSlateGray - ColorSlateGrey = ColorSlateGray -) - -// ColorValues maps color constants to their RGB values. -var ColorValues = map[Color]int32{ - ColorBlack: 0x000000, - ColorMaroon: 0x800000, - ColorGreen: 0x008000, - ColorOlive: 0x808000, - ColorNavy: 0x000080, - ColorPurple: 0x800080, - ColorTeal: 0x008080, - ColorSilver: 0xC0C0C0, - ColorGray: 0x808080, - ColorRed: 0xFF0000, - ColorLime: 0x00FF00, - ColorYellow: 0xFFFF00, - ColorBlue: 0x0000FF, - ColorFuchsia: 0xFF00FF, - ColorAqua: 0x00FFFF, - ColorWhite: 0xFFFFFF, - Color16: 0x000000, // black - Color17: 0x00005F, - Color18: 0x000087, - Color19: 0x0000AF, - Color20: 0x0000D7, - Color21: 0x0000FF, // blue - Color22: 0x005F00, - Color23: 0x005F5F, - Color24: 0x005F87, - Color25: 0x005FAF, - Color26: 0x005FD7, - Color27: 0x005FFF, - Color28: 0x008700, - Color29: 0x00875F, - Color30: 0x008787, - Color31: 0x0087Af, - Color32: 0x0087D7, - Color33: 0x0087FF, - Color34: 0x00AF00, - Color35: 0x00AF5F, - Color36: 0x00AF87, - Color37: 0x00AFAF, - Color38: 0x00AFD7, - Color39: 0x00AFFF, - Color40: 0x00D700, - Color41: 0x00D75F, - Color42: 0x00D787, - Color43: 0x00D7AF, - Color44: 0x00D7D7, - Color45: 0x00D7FF, - Color46: 0x00FF00, // lime - Color47: 0x00FF5F, - Color48: 0x00FF87, - Color49: 0x00FFAF, - Color50: 0x00FFd7, - Color51: 0x00FFFF, // aqua - Color52: 0x5F0000, - Color53: 0x5F005F, - Color54: 0x5F0087, - Color55: 0x5F00AF, - Color56: 0x5F00D7, - Color57: 0x5F00FF, - Color58: 0x5F5F00, - Color59: 0x5F5F5F, - Color60: 0x5F5F87, - Color61: 0x5F5FAF, - Color62: 0x5F5FD7, - Color63: 0x5F5FFF, - Color64: 0x5F8700, - Color65: 0x5F875F, - Color66: 0x5F8787, - Color67: 0x5F87AF, - Color68: 0x5F87D7, - Color69: 0x5F87FF, - Color70: 0x5FAF00, - Color71: 0x5FAF5F, - Color72: 0x5FAF87, - Color73: 0x5FAFAF, - Color74: 0x5FAFD7, - Color75: 0x5FAFFF, - Color76: 0x5FD700, - Color77: 0x5FD75F, - Color78: 0x5FD787, - Color79: 0x5FD7AF, - Color80: 0x5FD7D7, - Color81: 0x5FD7FF, - Color82: 0x5FFF00, - Color83: 0x5FFF5F, - Color84: 0x5FFF87, - Color85: 0x5FFFAF, - Color86: 0x5FFFD7, - Color87: 0x5FFFFF, - Color88: 0x870000, - Color89: 0x87005F, - Color90: 0x870087, - Color91: 0x8700AF, - Color92: 0x8700D7, - Color93: 0x8700FF, - Color94: 0x875F00, - Color95: 0x875F5F, - Color96: 0x875F87, - Color97: 0x875FAF, - Color98: 0x875FD7, - Color99: 0x875FFF, - Color100: 0x878700, - Color101: 0x87875F, - Color102: 0x878787, - Color103: 0x8787AF, - Color104: 0x8787D7, - Color105: 0x8787FF, - Color106: 0x87AF00, - Color107: 0x87AF5F, - Color108: 0x87AF87, - Color109: 0x87AFAF, - Color110: 0x87AFD7, - Color111: 0x87AFFF, - Color112: 0x87D700, - Color113: 0x87D75F, - Color114: 0x87D787, - Color115: 0x87D7AF, - Color116: 0x87D7D7, - Color117: 0x87D7FF, - Color118: 0x87FF00, - Color119: 0x87FF5F, - Color120: 0x87FF87, - Color121: 0x87FFAF, - Color122: 0x87FFD7, - Color123: 0x87FFFF, - Color124: 0xAF0000, - Color125: 0xAF005F, - Color126: 0xAF0087, - Color127: 0xAF00AF, - Color128: 0xAF00D7, - Color129: 0xAF00FF, - Color130: 0xAF5F00, - Color131: 0xAF5F5F, - Color132: 0xAF5F87, - Color133: 0xAF5FAF, - Color134: 0xAF5FD7, - Color135: 0xAF5FFF, - Color136: 0xAF8700, - Color137: 0xAF875F, - Color138: 0xAF8787, - Color139: 0xAF87AF, - Color140: 0xAF87D7, - Color141: 0xAF87FF, - Color142: 0xAFAF00, - Color143: 0xAFAF5F, - Color144: 0xAFAF87, - Color145: 0xAFAFAF, - Color146: 0xAFAFD7, - Color147: 0xAFAFFF, - Color148: 0xAFD700, - Color149: 0xAFD75F, - Color150: 0xAFD787, - Color151: 0xAFD7AF, - Color152: 0xAFD7D7, - Color153: 0xAFD7FF, - Color154: 0xAFFF00, - Color155: 0xAFFF5F, - Color156: 0xAFFF87, - Color157: 0xAFFFAF, - Color158: 0xAFFFD7, - Color159: 0xAFFFFF, - Color160: 0xD70000, - Color161: 0xD7005F, - Color162: 0xD70087, - Color163: 0xD700AF, - Color164: 0xD700D7, - Color165: 0xD700FF, - Color166: 0xD75F00, - Color167: 0xD75F5F, - Color168: 0xD75F87, - Color169: 0xD75FAF, - Color170: 0xD75FD7, - Color171: 0xD75FFF, - Color172: 0xD78700, - Color173: 0xD7875F, - Color174: 0xD78787, - Color175: 0xD787AF, - Color176: 0xD787D7, - Color177: 0xD787FF, - Color178: 0xD7AF00, - Color179: 0xD7AF5F, - Color180: 0xD7AF87, - Color181: 0xD7AFAF, - Color182: 0xD7AFD7, - Color183: 0xD7AFFF, - Color184: 0xD7D700, - Color185: 0xD7D75F, - Color186: 0xD7D787, - Color187: 0xD7D7AF, - Color188: 0xD7D7D7, - Color189: 0xD7D7FF, - Color190: 0xD7FF00, - Color191: 0xD7FF5F, - Color192: 0xD7FF87, - Color193: 0xD7FFAF, - Color194: 0xD7FFD7, - Color195: 0xD7FFFF, - Color196: 0xFF0000, // red - Color197: 0xFF005F, - Color198: 0xFF0087, - Color199: 0xFF00AF, - Color200: 0xFF00D7, - Color201: 0xFF00FF, // fuchsia - Color202: 0xFF5F00, - Color203: 0xFF5F5F, - Color204: 0xFF5F87, - Color205: 0xFF5FAF, - Color206: 0xFF5FD7, - Color207: 0xFF5FFF, - Color208: 0xFF8700, - Color209: 0xFF875F, - Color210: 0xFF8787, - Color211: 0xFF87AF, - Color212: 0xFF87D7, - Color213: 0xFF87FF, - Color214: 0xFFAF00, - Color215: 0xFFAF5F, - Color216: 0xFFAF87, - Color217: 0xFFAFAF, - Color218: 0xFFAFD7, - Color219: 0xFFAFFF, - Color220: 0xFFD700, - Color221: 0xFFD75F, - Color222: 0xFFD787, - Color223: 0xFFD7AF, - Color224: 0xFFD7D7, - Color225: 0xFFD7FF, - Color226: 0xFFFF00, // yellow - Color227: 0xFFFF5F, - Color228: 0xFFFF87, - Color229: 0xFFFFAF, - Color230: 0xFFFFD7, - Color231: 0xFFFFFF, // white - Color232: 0x080808, - Color233: 0x121212, - Color234: 0x1C1C1C, - Color235: 0x262626, - Color236: 0x303030, - Color237: 0x3A3A3A, - Color238: 0x444444, - Color239: 0x4E4E4E, - Color240: 0x585858, - Color241: 0x626262, - Color242: 0x6C6C6C, - Color243: 0x767676, - Color244: 0x808080, // grey - Color245: 0x8A8A8A, - Color246: 0x949494, - Color247: 0x9E9E9E, - Color248: 0xA8A8A8, - Color249: 0xB2B2B2, - Color250: 0xBCBCBC, - Color251: 0xC6C6C6, - Color252: 0xD0D0D0, - Color253: 0xDADADA, - Color254: 0xE4E4E4, - Color255: 0xEEEEEE, - ColorAliceBlue: 0xF0F8FF, - ColorAntiqueWhite: 0xFAEBD7, - ColorAquaMarine: 0x7FFFD4, - ColorAzure: 0xF0FFFF, - ColorBeige: 0xF5F5DC, - ColorBisque: 0xFFE4C4, - ColorBlanchedAlmond: 0xFFEBCD, - ColorBlueViolet: 0x8A2BE2, - ColorBrown: 0xA52A2A, - ColorBurlyWood: 0xDEB887, - ColorCadetBlue: 0x5F9EA0, - ColorChartreuse: 0x7FFF00, - ColorChocolate: 0xD2691E, - ColorCoral: 0xFF7F50, - ColorCornflowerBlue: 0x6495ED, - ColorCornsilk: 0xFFF8DC, - ColorCrimson: 0xDC143C, - ColorDarkBlue: 0x00008B, - ColorDarkCyan: 0x008B8B, - ColorDarkGoldenrod: 0xB8860B, - ColorDarkGray: 0xA9A9A9, - ColorDarkGreen: 0x006400, - ColorDarkKhaki: 0xBDB76B, - ColorDarkMagenta: 0x8B008B, - ColorDarkOliveGreen: 0x556B2F, - ColorDarkOrange: 0xFF8C00, - ColorDarkOrchid: 0x9932CC, - ColorDarkRed: 0x8B0000, - ColorDarkSalmon: 0xE9967A, - ColorDarkSeaGreen: 0x8FBC8F, - ColorDarkSlateBlue: 0x483D8B, - ColorDarkSlateGray: 0x2F4F4F, - ColorDarkTurquoise: 0x00CED1, - ColorDarkViolet: 0x9400D3, - ColorDeepPink: 0xFF1493, - ColorDeepSkyBlue: 0x00BFFF, - ColorDimGray: 0x696969, - ColorDodgerBlue: 0x1E90FF, - ColorFireBrick: 0xB22222, - ColorFloralWhite: 0xFFFAF0, - ColorForestGreen: 0x228B22, - ColorGainsboro: 0xDCDCDC, - ColorGhostWhite: 0xF8F8FF, - ColorGold: 0xFFD700, - ColorGoldenrod: 0xDAA520, - ColorGreenYellow: 0xADFF2F, - ColorHoneydew: 0xF0FFF0, - ColorHotPink: 0xFF69B4, - ColorIndianRed: 0xCD5C5C, - ColorIndigo: 0x4B0082, - ColorIvory: 0xFFFFF0, - ColorKhaki: 0xF0E68C, - ColorLavender: 0xE6E6FA, - ColorLavenderBlush: 0xFFF0F5, - ColorLawnGreen: 0x7CFC00, - ColorLemonChiffon: 0xFFFACD, - ColorLightBlue: 0xADD8E6, - ColorLightCoral: 0xF08080, - ColorLightCyan: 0xE0FFFF, - ColorLightGoldenrodYellow: 0xFAFAD2, - ColorLightGray: 0xD3D3D3, - ColorLightGreen: 0x90EE90, - ColorLightPink: 0xFFB6C1, - ColorLightSalmon: 0xFFA07A, - ColorLightSeaGreen: 0x20B2AA, - ColorLightSkyBlue: 0x87CEFA, - ColorLightSlateGray: 0x778899, - ColorLightSteelBlue: 0xB0C4DE, - ColorLightYellow: 0xFFFFE0, - ColorLimeGreen: 0x32CD32, - ColorLinen: 0xFAF0E6, - ColorMediumAquamarine: 0x66CDAA, - ColorMediumBlue: 0x0000CD, - ColorMediumOrchid: 0xBA55D3, - ColorMediumPurple: 0x9370DB, - ColorMediumSeaGreen: 0x3CB371, - ColorMediumSlateBlue: 0x7B68EE, - ColorMediumSpringGreen: 0x00FA9A, - ColorMediumTurquoise: 0x48D1CC, - ColorMediumVioletRed: 0xC71585, - ColorMidnightBlue: 0x191970, - ColorMintCream: 0xF5FFFA, - ColorMistyRose: 0xFFE4E1, - ColorMoccasin: 0xFFE4B5, - ColorNavajoWhite: 0xFFDEAD, - ColorOldLace: 0xFDF5E6, - ColorOliveDrab: 0x6B8E23, - ColorOrange: 0xFFA500, - ColorOrangeRed: 0xFF4500, - ColorOrchid: 0xDA70D6, - ColorPaleGoldenrod: 0xEEE8AA, - ColorPaleGreen: 0x98FB98, - ColorPaleTurquoise: 0xAFEEEE, - ColorPaleVioletRed: 0xDB7093, - ColorPapayaWhip: 0xFFEFD5, - ColorPeachPuff: 0xFFDAB9, - ColorPeru: 0xCD853F, - ColorPink: 0xFFC0CB, - ColorPlum: 0xDDA0DD, - ColorPowderBlue: 0xB0E0E6, - ColorRebeccaPurple: 0x663399, - ColorRosyBrown: 0xBC8F8F, - ColorRoyalBlue: 0x4169E1, - ColorSaddleBrown: 0x8B4513, - ColorSalmon: 0xFA8072, - ColorSandyBrown: 0xF4A460, - ColorSeaGreen: 0x2E8B57, - ColorSeashell: 0xFFF5EE, - ColorSienna: 0xA0522D, - ColorSkyblue: 0x87CEEB, - ColorSlateBlue: 0x6A5ACD, - ColorSlateGray: 0x708090, - ColorSnow: 0xFFFAFA, - ColorSpringGreen: 0x00FF7F, - ColorSteelBlue: 0x4682B4, - ColorTan: 0xD2B48C, - ColorThistle: 0xD8BFD8, - ColorTomato: 0xFF6347, - ColorTurquoise: 0x40E0D0, - ColorViolet: 0xEE82EE, - ColorWheat: 0xF5DEB3, - ColorWhiteSmoke: 0xF5F5F5, - ColorYellowGreen: 0x9ACD32, -} - -// ColorNames holds the written names of colors. Useful to present a list of -// recognized named colors. -var ColorNames = map[string]Color{ - "black": ColorBlack, - "maroon": ColorMaroon, - "green": ColorGreen, - "olive": ColorOlive, - "navy": ColorNavy, - "purple": ColorPurple, - "teal": ColorTeal, - "silver": ColorSilver, - "gray": ColorGray, - "red": ColorRed, - "lime": ColorLime, - "yellow": ColorYellow, - "blue": ColorBlue, - "fuchsia": ColorFuchsia, - "aqua": ColorAqua, - "white": ColorWhite, - "aliceblue": ColorAliceBlue, - "antiquewhite": ColorAntiqueWhite, - "aquamarine": ColorAquaMarine, - "azure": ColorAzure, - "beige": ColorBeige, - "bisque": ColorBisque, - "blanchedalmond": ColorBlanchedAlmond, - "blueviolet": ColorBlueViolet, - "brown": ColorBrown, - "burlywood": ColorBurlyWood, - "cadetblue": ColorCadetBlue, - "chartreuse": ColorChartreuse, - "chocolate": ColorChocolate, - "coral": ColorCoral, - "cornflowerblue": ColorCornflowerBlue, - "cornsilk": ColorCornsilk, - "crimson": ColorCrimson, - "darkblue": ColorDarkBlue, - "darkcyan": ColorDarkCyan, - "darkgoldenrod": ColorDarkGoldenrod, - "darkgray": ColorDarkGray, - "darkgreen": ColorDarkGreen, - "darkkhaki": ColorDarkKhaki, - "darkmagenta": ColorDarkMagenta, - "darkolivegreen": ColorDarkOliveGreen, - "darkorange": ColorDarkOrange, - "darkorchid": ColorDarkOrchid, - "darkred": ColorDarkRed, - "darksalmon": ColorDarkSalmon, - "darkseagreen": ColorDarkSeaGreen, - "darkslateblue": ColorDarkSlateBlue, - "darkslategray": ColorDarkSlateGray, - "darkturquoise": ColorDarkTurquoise, - "darkviolet": ColorDarkViolet, - "deeppink": ColorDeepPink, - "deepskyblue": ColorDeepSkyBlue, - "dimgray": ColorDimGray, - "dodgerblue": ColorDodgerBlue, - "firebrick": ColorFireBrick, - "floralwhite": ColorFloralWhite, - "forestgreen": ColorForestGreen, - "gainsboro": ColorGainsboro, - "ghostwhite": ColorGhostWhite, - "gold": ColorGold, - "goldenrod": ColorGoldenrod, - "greenyellow": ColorGreenYellow, - "honeydew": ColorHoneydew, - "hotpink": ColorHotPink, - "indianred": ColorIndianRed, - "indigo": ColorIndigo, - "ivory": ColorIvory, - "khaki": ColorKhaki, - "lavender": ColorLavender, - "lavenderblush": ColorLavenderBlush, - "lawngreen": ColorLawnGreen, - "lemonchiffon": ColorLemonChiffon, - "lightblue": ColorLightBlue, - "lightcoral": ColorLightCoral, - "lightcyan": ColorLightCyan, - "lightgoldenrodyellow": ColorLightGoldenrodYellow, - "lightgray": ColorLightGray, - "lightgreen": ColorLightGreen, - "lightpink": ColorLightPink, - "lightsalmon": ColorLightSalmon, - "lightseagreen": ColorLightSeaGreen, - "lightskyblue": ColorLightSkyBlue, - "lightslategray": ColorLightSlateGray, - "lightsteelblue": ColorLightSteelBlue, - "lightyellow": ColorLightYellow, - "limegreen": ColorLimeGreen, - "linen": ColorLinen, - "mediumaquamarine": ColorMediumAquamarine, - "mediumblue": ColorMediumBlue, - "mediumorchid": ColorMediumOrchid, - "mediumpurple": ColorMediumPurple, - "mediumseagreen": ColorMediumSeaGreen, - "mediumslateblue": ColorMediumSlateBlue, - "mediumspringgreen": ColorMediumSpringGreen, - "mediumturquoise": ColorMediumTurquoise, - "mediumvioletred": ColorMediumVioletRed, - "midnightblue": ColorMidnightBlue, - "mintcream": ColorMintCream, - "mistyrose": ColorMistyRose, - "moccasin": ColorMoccasin, - "navajowhite": ColorNavajoWhite, - "oldlace": ColorOldLace, - "olivedrab": ColorOliveDrab, - "orange": ColorOrange, - "orangered": ColorOrangeRed, - "orchid": ColorOrchid, - "palegoldenrod": ColorPaleGoldenrod, - "palegreen": ColorPaleGreen, - "paleturquoise": ColorPaleTurquoise, - "palevioletred": ColorPaleVioletRed, - "papayawhip": ColorPapayaWhip, - "peachpuff": ColorPeachPuff, - "peru": ColorPeru, - "pink": ColorPink, - "plum": ColorPlum, - "powderblue": ColorPowderBlue, - "rebeccapurple": ColorRebeccaPurple, - "rosybrown": ColorRosyBrown, - "royalblue": ColorRoyalBlue, - "saddlebrown": ColorSaddleBrown, - "salmon": ColorSalmon, - "sandybrown": ColorSandyBrown, - "seagreen": ColorSeaGreen, - "seashell": ColorSeashell, - "sienna": ColorSienna, - "skyblue": ColorSkyblue, - "slateblue": ColorSlateBlue, - "slategray": ColorSlateGray, - "snow": ColorSnow, - "springgreen": ColorSpringGreen, - "steelblue": ColorSteelBlue, - "tan": ColorTan, - "thistle": ColorThistle, - "tomato": ColorTomato, - "turquoise": ColorTurquoise, - "violet": ColorViolet, - "wheat": ColorWheat, - "whitesmoke": ColorWhiteSmoke, - "yellowgreen": ColorYellowGreen, - "grey": ColorGray, - "dimgrey": ColorDimGray, - "darkgrey": ColorDarkGray, - "darkslategrey": ColorDarkSlateGray, - "lightgrey": ColorLightGray, - "lightslategrey": ColorLightSlateGray, - "slategrey": ColorSlateGray, -} - -// Hex returns the color's hexadecimal RGB 24-bit value with each component -// consisting of a single byte, ala R << 16 | G << 8 | B. If the color -// is unknown or unset, -1 is returned. -func (c Color) Hex() int32 { - if c&ColorIsRGB != 0 { - return (int32(c) & 0xffffff) - } - if v, ok := ColorValues[c]; ok { - return v - } - return -1 -} - -// RGB returns the red, green, and blue components of the color, with -// each component represented as a value 0-255. In the event that the -// color cannot be broken up (not set usually), -1 is returned for each value. -func (c Color) RGB() (int32, int32, int32) { - v := c.Hex() - if v < 0 { - return -1, -1, -1 - } - return (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff -} - -// NewRGBColor returns a new color with the given red, green, and blue values. -// Each value must be represented in the range 0-255. -func NewRGBColor(r, g, b int32) Color { - return NewHexColor(((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)) -} - -// NewHexColor returns a color using the given 24-bit RGB value. -func NewHexColor(v int32) Color { - return ColorIsRGB | Color(v) -} - -// GetColor creates a Color from a color name (W3C name). A hex value may -// be supplied as a string in the format "#ffffff". -func GetColor(name string) Color { - if c, ok := ColorNames[name]; ok { - return c - } - if len(name) == 7 && name[0] == '#' { - if v, e := strconv.ParseInt(name[1:], 16, 32); e == nil { - return NewHexColor(int32(v)) - } - } - return ColorDefault -} diff --git a/vendor/github.com/gdamore/tcell/colorfit.go b/vendor/github.com/gdamore/tcell/colorfit.go deleted file mode 100644 index b7740b8a..00000000 --- a/vendor/github.com/gdamore/tcell/colorfit.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "github.com/lucasb-eyer/go-colorful" - "math" -) - -// FindColor attempts to find a given color, or the best match possible for it, -// from the palette given. This is an expensive operation, so results should -// be cached by the caller. -func FindColor(c Color, palette []Color) Color { - match := ColorDefault - dist := float64(0) - r, g, b := c.RGB() - c1 := colorful.Color{ - R: float64(r) / 255.0, - G: float64(g) / 255.0, - B: float64(b) / 255.0, - } - for _, d := range palette { - r, g, b = d.RGB() - c2 := colorful.Color{ - R: float64(r) / 255.0, - G: float64(g) / 255.0, - B: float64(b) / 255.0, - } - // CIE94 is more accurate, but really really expensive. - nd := c1.DistanceCIE76(c2) - if math.IsNaN(nd) { - nd = math.Inf(1) - } - if match == ColorDefault || nd < dist { - match = d - dist = nd - } - } - return match -} diff --git a/vendor/github.com/gdamore/tcell/console_stub.go b/vendor/github.com/gdamore/tcell/console_stub.go deleted file mode 100644 index fda2f092..00000000 --- a/vendor/github.com/gdamore/tcell/console_stub.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build !windows - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// NewConsoleScreen returns a console based screen. This platform -// doesn't have support for any, so it returns nil and a suitable error. -func NewConsoleScreen() (Screen, error) { - return nil, ErrNoScreen -} diff --git a/vendor/github.com/gdamore/tcell/console_win.go b/vendor/github.com/gdamore/tcell/console_win.go deleted file mode 100644 index 3ebcc713..00000000 --- a/vendor/github.com/gdamore/tcell/console_win.go +++ /dev/null @@ -1,1033 +0,0 @@ -// +build windows - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "errors" - "sync" - "syscall" - "unicode/utf16" - "unsafe" -) - -type cScreen struct { - in syscall.Handle - out syscall.Handle - cancelflag syscall.Handle - scandone chan struct{} - evch chan Event - quit chan struct{} - curx int - cury int - style Style - clear bool - fini bool - - w int - h int - - oscreen consoleInfo - ocursor cursorInfo - oimode uint32 - oomode uint32 - cells CellBuffer - colors map[Color]Color - - sync.Mutex -} - -var winLock sync.Mutex - -var winPalette = []Color{ - ColorBlack, - ColorMaroon, - ColorGreen, - ColorNavy, - ColorOlive, - ColorPurple, - ColorTeal, - ColorSilver, - ColorGray, - ColorRed, - ColorLime, - ColorBlue, - ColorYellow, - ColorFuchsia, - ColorAqua, - ColorWhite, -} - -var winColors = map[Color]Color{ - ColorBlack: ColorBlack, - ColorMaroon: ColorMaroon, - ColorGreen: ColorGreen, - ColorNavy: ColorNavy, - ColorOlive: ColorOlive, - ColorPurple: ColorPurple, - ColorTeal: ColorTeal, - ColorSilver: ColorSilver, - ColorGray: ColorGray, - ColorRed: ColorRed, - ColorLime: ColorLime, - ColorBlue: ColorBlue, - ColorYellow: ColorYellow, - ColorFuchsia: ColorFuchsia, - ColorAqua: ColorAqua, - ColorWhite: ColorWhite, -} - -var k32 = syscall.NewLazyDLL("kernel32.dll") - -// We have to bring in the kernel32.dll directly, so we can get access to some -// system calls that the core Go API lacks. -// -// Note that Windows appends some functions with W to indicate that wide -// characters (Unicode) are in use. The documentation refers to them -// without this suffix, as the resolution is made via preprocessor. -var ( - procReadConsoleInput = k32.NewProc("ReadConsoleInputW") - procWaitForMultipleObjects = k32.NewProc("WaitForMultipleObjects") - procCreateEvent = k32.NewProc("CreateEventW") - procSetEvent = k32.NewProc("SetEvent") - procGetConsoleCursorInfo = k32.NewProc("GetConsoleCursorInfo") - procSetConsoleCursorInfo = k32.NewProc("SetConsoleCursorInfo") - procSetConsoleCursorPosition = k32.NewProc("SetConsoleCursorPosition") - procSetConsoleMode = k32.NewProc("SetConsoleMode") - procGetConsoleMode = k32.NewProc("GetConsoleMode") - procGetConsoleScreenBufferInfo = k32.NewProc("GetConsoleScreenBufferInfo") - procFillConsoleOutputAttribute = k32.NewProc("FillConsoleOutputAttribute") - procFillConsoleOutputCharacter = k32.NewProc("FillConsoleOutputCharacterW") - procSetConsoleWindowInfo = k32.NewProc("SetConsoleWindowInfo") - procSetConsoleScreenBufferSize = k32.NewProc("SetConsoleScreenBufferSize") - procSetConsoleTextAttribute = k32.NewProc("SetConsoleTextAttribute") -) - -const ( - w32Infinite = ^uintptr(0) - w32WaitObject0 = uintptr(0) -) - -// NewConsoleScreen returns a Screen for the Windows console associated -// with the current process. The Screen makes use of the Windows Console -// API to display content and read events. -func NewConsoleScreen() (Screen, error) { - return &cScreen{}, nil -} - -func (s *cScreen) Init() error { - s.evch = make(chan Event, 10) - s.quit = make(chan struct{}) - s.scandone = make(chan struct{}) - - in, e := syscall.Open("CONIN$", syscall.O_RDWR, 0) - if e != nil { - return e - } - s.in = in - out, e := syscall.Open("CONOUT$", syscall.O_RDWR, 0) - if e != nil { - syscall.Close(s.in) - return e - } - s.out = out - - cf, _, e := procCreateEvent.Call( - uintptr(0), - uintptr(1), - uintptr(0), - uintptr(0)) - if cf == uintptr(0) { - return e - } - s.cancelflag = syscall.Handle(cf) - - s.Lock() - - s.curx = -1 - s.cury = -1 - s.style = StyleDefault - s.getCursorInfo(&s.ocursor) - s.getConsoleInfo(&s.oscreen) - s.getOutMode(&s.oomode) - s.getInMode(&s.oimode) - s.resize() - - s.fini = false - s.setInMode(modeResizeEn) - s.setOutMode(0) - s.clearScreen(s.style) - s.hideCursor() - s.Unlock() - go s.scanInput() - - return nil -} - -func (s *cScreen) CharacterSet() string { - // We are always UTF-16LE on Windows - return "UTF-16LE" -} - -func (s *cScreen) EnableMouse() { - s.setInMode(modeResizeEn | modeMouseEn | modeExtndFlg) -} - -func (s *cScreen) DisableMouse() { - s.setInMode(modeResizeEn) -} - -func (s *cScreen) Fini() { - s.Lock() - s.style = StyleDefault - s.curx = -1 - s.cury = -1 - s.fini = true - s.Unlock() - - s.setCursorInfo(&s.ocursor) - s.setInMode(s.oimode) - s.setOutMode(s.oomode) - s.setBufferSize(int(s.oscreen.size.x), int(s.oscreen.size.y)) - s.clearScreen(StyleDefault) - s.setCursorPos(0, 0) - procSetConsoleTextAttribute.Call( - uintptr(s.out), - uintptr(s.mapStyle(StyleDefault))) - - close(s.quit) - procSetEvent.Call(uintptr(s.cancelflag)) - // Block until scanInput returns; this prevents a race condition on Win 8+ - // which causes syscall.Close to block until another keypress is read. - <-s.scandone - syscall.Close(s.in) - syscall.Close(s.out) -} - -func (s *cScreen) PostEventWait(ev Event) { - s.evch <- ev -} - -func (s *cScreen) PostEvent(ev Event) error { - select { - case s.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - -func (s *cScreen) PollEvent() Event { - select { - case <-s.quit: - return nil - case ev := <-s.evch: - return ev - } -} - -type cursorInfo struct { - size uint32 - visible uint32 -} - -type coord struct { - x int16 - y int16 -} - -func (c coord) uintptr() uintptr { - // little endian, put x first - return uintptr(c.x) | (uintptr(c.y) << 16) -} - -type rect struct { - left int16 - top int16 - right int16 - bottom int16 -} - -func (s *cScreen) showCursor() { - s.setCursorInfo(&cursorInfo{size: 100, visible: 1}) -} - -func (s *cScreen) hideCursor() { - s.setCursorInfo(&cursorInfo{size: 1, visible: 0}) -} - -func (s *cScreen) ShowCursor(x, y int) { - s.Lock() - if !s.fini { - s.curx = x - s.cury = y - } - s.doCursor() - s.Unlock() -} - -func (s *cScreen) doCursor() { - x, y := s.curx, s.cury - - if x < 0 || y < 0 || x >= s.w || y >= s.h { - s.hideCursor() - } else { - s.setCursorPos(x, y) - s.showCursor() - } -} - -func (s *cScreen) HideCursor() { - s.ShowCursor(-1, -1) -} - -type charInfo struct { - ch uint16 - attr uint16 -} - -type inputRecord struct { - typ uint16 - _ uint16 - data [16]byte -} - -const ( - keyEvent uint16 = 1 - mouseEvent uint16 = 2 - resizeEvent uint16 = 4 - menuEvent uint16 = 8 // don't use - focusEvent uint16 = 16 // don't use -) - -type mouseRecord struct { - x int16 - y int16 - btns uint32 - mod uint32 - flags uint32 -} - -const ( - mouseDoubleClick uint32 = 0x2 - mouseHWheeled uint32 = 0x8 - mouseVWheeled uint32 = 0x4 - mouseMoved uint32 = 0x1 -) - -type resizeRecord struct { - x int16 - y int16 -} - -type keyRecord struct { - isdown int32 - repeat uint16 - kcode uint16 - scode uint16 - ch uint16 - mod uint32 -} - -const ( - // Constants per Microsoft. We don't put the modifiers - // here. - vkCancel = 0x03 - vkBack = 0x08 // Backspace - vkTab = 0x09 - vkClear = 0x0c - vkReturn = 0x0d - vkPause = 0x13 - vkEscape = 0x1b - vkSpace = 0x20 - vkPrior = 0x21 // PgUp - vkNext = 0x22 // PgDn - vkEnd = 0x23 - vkHome = 0x24 - vkLeft = 0x25 - vkUp = 0x26 - vkRight = 0x27 - vkDown = 0x28 - vkPrint = 0x2a - vkPrtScr = 0x2c - vkInsert = 0x2d - vkDelete = 0x2e - vkHelp = 0x2f - vkF1 = 0x70 - vkF2 = 0x71 - vkF3 = 0x72 - vkF4 = 0x73 - vkF5 = 0x74 - vkF6 = 0x75 - vkF7 = 0x76 - vkF8 = 0x77 - vkF9 = 0x78 - vkF10 = 0x79 - vkF11 = 0x7a - vkF12 = 0x7b - vkF13 = 0x7c - vkF14 = 0x7d - vkF15 = 0x7e - vkF16 = 0x7f - vkF17 = 0x80 - vkF18 = 0x81 - vkF19 = 0x82 - vkF20 = 0x83 - vkF21 = 0x84 - vkF22 = 0x85 - vkF23 = 0x86 - vkF24 = 0x87 -) - -var vkKeys = map[uint16]Key{ - vkCancel: KeyCancel, - vkBack: KeyBackspace, - vkTab: KeyTab, - vkClear: KeyClear, - vkPause: KeyPause, - vkPrint: KeyPrint, - vkPrtScr: KeyPrint, - vkPrior: KeyPgUp, - vkNext: KeyPgDn, - vkReturn: KeyEnter, - vkEnd: KeyEnd, - vkHome: KeyHome, - vkLeft: KeyLeft, - vkUp: KeyUp, - vkRight: KeyRight, - vkDown: KeyDown, - vkInsert: KeyInsert, - vkDelete: KeyDelete, - vkHelp: KeyHelp, - vkF1: KeyF1, - vkF2: KeyF2, - vkF3: KeyF3, - vkF4: KeyF4, - vkF5: KeyF5, - vkF6: KeyF6, - vkF7: KeyF7, - vkF8: KeyF8, - vkF9: KeyF9, - vkF10: KeyF10, - vkF11: KeyF11, - vkF12: KeyF12, - vkF13: KeyF13, - vkF14: KeyF14, - vkF15: KeyF15, - vkF16: KeyF16, - vkF17: KeyF17, - vkF18: KeyF18, - vkF19: KeyF19, - vkF20: KeyF20, - vkF21: KeyF21, - vkF22: KeyF22, - vkF23: KeyF23, - vkF24: KeyF24, -} - -// NB: All Windows platforms are little endian. We assume this -// never, ever change. The following code is endian safe. and does -// not use unsafe pointers. -func getu32(v []byte) uint32 { - return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24) -} -func geti32(v []byte) int32 { - return int32(getu32(v)) -} -func getu16(v []byte) uint16 { - return uint16(v[0]) + (uint16(v[1]) << 8) -} -func geti16(v []byte) int16 { - return int16(getu16(v)) -} - -// Convert windows dwControlKeyState to modifier mask -func mod2mask(cks uint32) ModMask { - mm := ModNone - // Left or right control - if (cks & (0x0008 | 0x0004)) != 0 { - mm |= ModCtrl - } - // Left or right alt - if (cks & (0x0002 | 0x0001)) != 0 { - mm |= ModAlt - } - // Any shift - if (cks & 0x0010) != 0 { - mm |= ModShift - } - return mm -} - -func mrec2btns(mbtns, flags uint32) ButtonMask { - btns := ButtonNone - if mbtns&0x1 != 0 { - btns |= Button1 - } - if mbtns&0x2 != 0 { - btns |= Button2 - } - if mbtns&0x4 != 0 { - btns |= Button3 - } - if mbtns&0x8 != 0 { - btns |= Button4 - } - if mbtns&0x10 != 0 { - btns |= Button5 - } - if mbtns&0x20 != 0 { - btns |= Button6 - } - if mbtns&0x40 != 0 { - btns |= Button7 - } - if mbtns&0x80 != 0 { - btns |= Button8 - } - - if flags&mouseVWheeled != 0 { - if mbtns&0x80000000 == 0 { - btns |= WheelUp - } else { - btns |= WheelDown - } - } - if flags&mouseHWheeled != 0 { - if mbtns&0x80000000 == 0 { - btns |= WheelRight - } else { - btns |= WheelLeft - } - } - return btns -} - -func (s *cScreen) getConsoleInput() error { - // cancelFlag comes first as WaitForMultipleObjects returns the lowest index - // in the event that both events are signalled. - waitObjects := []syscall.Handle{s.cancelflag, s.in} - // As arrays are contiguous in memory, a pointer to the first object is the - // same as a pointer to the array itself. - pWaitObjects := unsafe.Pointer(&waitObjects[0]) - - rv, _, er := procWaitForMultipleObjects.Call( - uintptr(len(waitObjects)), - uintptr(pWaitObjects), - uintptr(0), - w32Infinite) - // WaitForMultipleObjects returns WAIT_OBJECT_0 + the index. - switch rv { - case w32WaitObject0: // s.cancelFlag - return errors.New("cancelled") - case w32WaitObject0 + 1: // s.in - rec := &inputRecord{} - var nrec int32 - rv, _, er := procReadConsoleInput.Call( - uintptr(s.in), - uintptr(unsafe.Pointer(rec)), - uintptr(1), - uintptr(unsafe.Pointer(&nrec))) - if rv == 0 { - return er - } - if nrec != 1 { - return nil - } - switch rec.typ { - case keyEvent: - krec := &keyRecord{} - krec.isdown = geti32(rec.data[0:]) - krec.repeat = getu16(rec.data[4:]) - krec.kcode = getu16(rec.data[6:]) - krec.scode = getu16(rec.data[8:]) - krec.ch = getu16(rec.data[10:]) - krec.mod = getu32(rec.data[12:]) - - if krec.isdown == 0 || krec.repeat < 1 { - // its a key release event, ignore it - return nil - } - if krec.ch != 0 { - // synthesized key code - for krec.repeat > 0 { - // convert shift+tab to backtab - if mod2mask(krec.mod) == ModShift && krec.ch == vkTab { - s.PostEvent(NewEventKey(KeyBacktab, 0, - ModNone)) - } else { - s.PostEvent(NewEventKey(KeyRune, rune(krec.ch), - mod2mask(krec.mod))) - } - krec.repeat-- - } - return nil - } - key := KeyNUL // impossible on Windows - ok := false - if key, ok = vkKeys[krec.kcode]; !ok { - return nil - } - for krec.repeat > 0 { - s.PostEvent(NewEventKey(key, rune(krec.ch), - mod2mask(krec.mod))) - krec.repeat-- - } - - case mouseEvent: - var mrec mouseRecord - mrec.x = geti16(rec.data[0:]) - mrec.y = geti16(rec.data[2:]) - mrec.btns = getu32(rec.data[4:]) - mrec.mod = getu32(rec.data[8:]) - mrec.flags = getu32(rec.data[12:]) - btns := mrec2btns(mrec.btns, mrec.flags) - // we ignore double click, events are delivered normally - s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, - mod2mask(mrec.mod))) - - case resizeEvent: - var rrec resizeRecord - rrec.x = geti16(rec.data[0:]) - rrec.y = geti16(rec.data[2:]) - s.PostEvent(NewEventResize(int(rrec.x), int(rrec.y))) - - default: - } - default: - return er - } - - return nil -} - -func (s *cScreen) scanInput() { - for { - if e := s.getConsoleInput(); e != nil { - close(s.scandone) - return - } - } -} - -// Windows console can display 8 characters, in either low or high intensity -func (s *cScreen) Colors() int { - return 16 -} - -var vgaColors = map[Color]uint16{ - ColorBlack: 0, - ColorMaroon: 0x4, - ColorGreen: 0x2, - ColorNavy: 0x1, - ColorOlive: 0x6, - ColorPurple: 0x5, - ColorTeal: 0x3, - ColorSilver: 0x7, - ColorGrey: 0x8, - ColorRed: 0xc, - ColorLime: 0xa, - ColorBlue: 0x9, - ColorYellow: 0xe, - ColorFuchsia: 0xd, - ColorAqua: 0xb, - ColorWhite: 0xf, -} - -// Windows uses RGB signals -func mapColor2RGB(c Color) uint16 { - winLock.Lock() - if v, ok := winColors[c]; ok { - c = v - } else { - v = FindColor(c, winPalette) - winColors[c] = v - c = v - } - winLock.Unlock() - - if vc, ok := vgaColors[c]; ok { - return vc - } - return 0 -} - -// Map a tcell style to Windows attributes -func (s *cScreen) mapStyle(style Style) uint16 { - f, b, a := style.Decompose() - fa := s.oscreen.attrs & 0xf - ba := (s.oscreen.attrs) >> 4 & 0xf - if f != ColorDefault { - fa = mapColor2RGB(f) - } - if b != ColorDefault { - ba = mapColor2RGB(b) - } - var attr uint16 - // We simulate reverse by doing the color swap ourselves. - // Apparently windows cannot really do this except in DBCS - // views. - if a&AttrReverse != 0 { - attr = ba - attr |= (fa << 4) - } else { - attr = fa - attr |= (ba << 4) - } - if a&AttrBold != 0 { - attr |= 0x8 - } - if a&AttrDim != 0 { - attr &^= 0x8 - } - if a&AttrUnderline != 0 { - // Best effort -- doesn't seem to work though. - attr |= 0x8000 - } - // Blink is unsupported - return attr -} - -func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) { - if len(ch) > 0 { - s.SetContent(x, y, ch[0], ch[1:], style) - } else { - s.SetContent(x, y, ' ', nil, style) - } -} - -func (s *cScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { - s.Lock() - if !s.fini { - s.cells.SetContent(x, y, mainc, combc, style) - } - s.Unlock() -} - -func (s *cScreen) GetContent(x, y int) (rune, []rune, Style, int) { - s.Lock() - mainc, combc, style, width := s.cells.GetContent(x, y) - s.Unlock() - return mainc, combc, style, width -} - -func (s *cScreen) writeString(x, y int, style Style, ch []uint16) { - // we assume the caller has hidden the cursor - if len(ch) == 0 { - return - } - nw := uint32(len(ch)) - procSetConsoleTextAttribute.Call( - uintptr(s.out), - uintptr(s.mapStyle(style))) - s.setCursorPos(x, y) - syscall.WriteConsole(s.out, &ch[0], nw, &nw, nil) -} - -func (s *cScreen) draw() { - // allocate a scratch line bit enough for no combining chars. - // if you have combining characters, you may pay for extra allocs. - if s.clear { - s.clearScreen(s.style) - s.clear = false - s.cells.Invalidate() - } - buf := make([]uint16, 0, s.w) - wcs := buf[:] - lstyle := Style(-1) // invalid attribute - - lx, ly := -1, -1 - ra := make([]rune, 1) - - for y := 0; y < int(s.h); y++ { - for x := 0; x < int(s.w); x++ { - mainc, combc, style, width := s.cells.GetContent(x, y) - dirty := s.cells.Dirty(x, y) - if style == StyleDefault { - style = s.style - } - - if !dirty || style != lstyle { - // write out any data queued thus far - // because we are going to skip over some - // cells, or because we need to change styles - s.writeString(lx, ly, lstyle, wcs) - wcs = buf[0:0] - lstyle = Style(-1) - if !dirty { - continue - } - } - if x > s.w-width { - mainc = ' ' - combc = nil - width = 1 - } - if len(wcs) == 0 { - lstyle = style - lx = x - ly = y - } - ra[0] = mainc - wcs = append(wcs, utf16.Encode(ra)...) - if len(combc) != 0 { - wcs = append(wcs, utf16.Encode(combc)...) - } - for dx := 0; dx < width; dx++ { - s.cells.SetDirty(x+dx, y, false) - } - x += width - 1 - } - s.writeString(lx, ly, lstyle, wcs) - wcs = buf[0:0] - lstyle = Style(-1) - } -} - -func (s *cScreen) Show() { - s.Lock() - if !s.fini { - s.hideCursor() - s.resize() - s.draw() - s.doCursor() - } - s.Unlock() -} - -func (s *cScreen) Sync() { - s.Lock() - if !s.fini { - s.cells.Invalidate() - s.hideCursor() - s.resize() - s.draw() - s.doCursor() - } - s.Unlock() -} - -type consoleInfo struct { - size coord - pos coord - attrs uint16 - win rect - maxsz coord -} - -func (s *cScreen) getConsoleInfo(info *consoleInfo) { - procGetConsoleScreenBufferInfo.Call( - uintptr(s.out), - uintptr(unsafe.Pointer(info))) -} - -func (s *cScreen) getCursorInfo(info *cursorInfo) { - procGetConsoleCursorInfo.Call( - uintptr(s.out), - uintptr(unsafe.Pointer(info))) -} - -func (s *cScreen) setCursorInfo(info *cursorInfo) { - procSetConsoleCursorInfo.Call( - uintptr(s.out), - uintptr(unsafe.Pointer(info))) -} - -func (s *cScreen) setCursorPos(x, y int) { - procSetConsoleCursorPosition.Call( - uintptr(s.out), - coord{int16(x), int16(y)}.uintptr()) -} - -func (s *cScreen) setBufferSize(x, y int) { - procSetConsoleScreenBufferSize.Call( - uintptr(s.out), - coord{int16(x), int16(y)}.uintptr()) -} - -func (s *cScreen) Size() (int, int) { - s.Lock() - w, h := s.w, s.h - s.Unlock() - - return w, h -} - -func (s *cScreen) resize() { - info := consoleInfo{} - s.getConsoleInfo(&info) - - w := int((info.win.right - info.win.left) + 1) - h := int((info.win.bottom - info.win.top) + 1) - - if s.w == w && s.h == h { - return - } - - s.cells.Resize(w, h) - s.w = w - s.h = h - - s.setBufferSize(w, h) - - r := rect{0, 0, int16(w - 1), int16(h - 1)} - procSetConsoleWindowInfo.Call( - uintptr(s.out), - uintptr(1), - uintptr(unsafe.Pointer(&r))) - - s.PostEvent(NewEventResize(w, h)) -} - -func (s *cScreen) Clear() { - s.Fill(' ', s.style) -} - -func (s *cScreen) Fill(r rune, style Style) { - s.Lock() - if !s.fini { - s.cells.Fill(r, style) - s.clear = true - } - s.Unlock() -} - -func (s *cScreen) clearScreen(style Style) { - pos := coord{0, 0} - attr := s.mapStyle(style) - x, y := s.w, s.h - scratch := uint32(0) - count := uint32(x * y) - - procFillConsoleOutputAttribute.Call( - uintptr(s.out), - uintptr(attr), - uintptr(count), - pos.uintptr(), - uintptr(unsafe.Pointer(&scratch))) - procFillConsoleOutputCharacter.Call( - uintptr(s.out), - uintptr(' '), - uintptr(count), - pos.uintptr(), - uintptr(unsafe.Pointer(&scratch))) -} - -const ( - modeExtndFlg uint32 = 0x0080 - modeMouseEn uint32 = 0x0010 - modeResizeEn uint32 = 0x0008 - modeWrapEOL uint32 = 0x0002 - modeCooked uint32 = 0x0001 -) - -func (s *cScreen) setInMode(mode uint32) error { - rv, _, err := procSetConsoleMode.Call( - uintptr(s.in), - uintptr(mode)) - if rv == 0 { - return err - } - return nil -} - -func (s *cScreen) setOutMode(mode uint32) error { - rv, _, err := procSetConsoleMode.Call( - uintptr(s.out), - uintptr(mode)) - if rv == 0 { - return err - } - return nil -} - -func (s *cScreen) getInMode(v *uint32) { - procGetConsoleMode.Call( - uintptr(s.in), - uintptr(unsafe.Pointer(v))) -} - -func (s *cScreen) getOutMode(v *uint32) { - procGetConsoleMode.Call( - uintptr(s.out), - uintptr(unsafe.Pointer(v))) -} - -func (s *cScreen) SetStyle(style Style) { - s.Lock() - s.style = style - s.Unlock() -} - -// No fallback rune support, since we have Unicode. Yay! - -func (s *cScreen) RegisterRuneFallback(r rune, subst string) { -} - -func (s *cScreen) UnregisterRuneFallback(r rune) { -} - -func (s *cScreen) CanDisplay(r rune, checkFallbacks bool) bool { - // We presume we can display anything -- we're Unicode. - // (Sadly this not precisely true. Combinings are especially - // poorly supported under Windows.) - return true -} - -func (s *cScreen) HasMouse() bool { - return true -} - -func (s *cScreen) Resize(int, int, int, int) {} - -func (s *cScreen) HasKey(k Key) bool { - // Microsoft has codes for some keys, but they are unusual, - // so we don't include them. We include all the typical - // 101, 105 key layout keys. - valid := map[Key]bool{ - KeyBackspace: true, - KeyTab: true, - KeyEscape: true, - KeyPause: true, - KeyPrint: true, - KeyPgUp: true, - KeyPgDn: true, - KeyEnter: true, - KeyEnd: true, - KeyHome: true, - KeyLeft: true, - KeyUp: true, - KeyRight: true, - KeyDown: true, - KeyInsert: true, - KeyDelete: true, - KeyF1: true, - KeyF2: true, - KeyF3: true, - KeyF4: true, - KeyF5: true, - KeyF6: true, - KeyF7: true, - KeyF8: true, - KeyF9: true, - KeyF10: true, - KeyF11: true, - KeyF12: true, - KeyRune: true, - } - - return valid[k] -} diff --git a/vendor/github.com/gdamore/tcell/doc.go b/vendor/github.com/gdamore/tcell/doc.go deleted file mode 100644 index b6719613..00000000 --- a/vendor/github.com/gdamore/tcell/doc.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell provides a lower-level, portable API for building -// programs that interact with terminals or consoles. It works with -// both common (and many uncommon!) terminals or terminal emulators, -// and Windows console implementations. -// -// It provides support for up to 256 colors, text attributes, and box drawing -// elements. A database of terminals built from a real terminfo database -// is provided, along with code to generate new database entries. -// -// Tcell offers very rich support for mice, dependent upon the terminal -// of course. (Windows, XTerm, and iTerm 2 are known to work very well.) -// -// If the environment is not Unicode by default, such as an ISO8859 based -// locale or GB18030, Tcell can convert input and output, so that your -// terminal can operate in whatever locale is most convenient, while the -// application program can just assume "everything is UTF-8". Reasonable -// defaults are used for updating characters to something suitable for -// display. Unicode box drawing characters will be converted to use the -// alternate character set of your terminal, if native conversions are -// not available. If no ACS is available, then some ASCII fallbacks will -// be used. -// -// Note that support for non-UTF-8 locales (other than C) must be enabled -// by the application using RegisterEncoding() -- we don't have them all -// enabled by default to avoid bloating the application unneccessarily. -// (These days UTF-8 is good enough for almost everyone, and nobody should -// be using legacy locales anymore.) Also, actual glyphs for various code -// point will only be displayed if your terminal or emulator (or the font -// the emulator is using) supports them. -// -// A rich set of keycodes is supported, with support for up to 65 function -// keys, and various other special keys. -// -package tcell diff --git a/vendor/github.com/gdamore/tcell/encoding.go b/vendor/github.com/gdamore/tcell/encoding.go deleted file mode 100644 index 596a6e80..00000000 --- a/vendor/github.com/gdamore/tcell/encoding.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "strings" - "sync" - - "golang.org/x/text/encoding" - - gencoding "github.com/gdamore/encoding" -) - -var encodings map[string]encoding.Encoding -var encodingLk sync.Mutex -var encodingFallback EncodingFallback = EncodingFallbackFail - -// RegisterEncoding may be called by the application to register an encoding. -// The presence of additional encodings will facilitate application usage with -// terminal environments where the I/O subsystem does not support Unicode. -// -// Windows systems use Unicode natively, and do not need any of the encoding -// subsystem when using Windows Console screens. -// -// Please see the Go documentation for golang.org/x/text/encoding -- most of -// the common ones exist already as stock variables. For example, ISO8859-15 -// can be registered using the following code: -// -// import "golang.org/x/text/encoding/charmap" -// -// ... -// RegisterEncoding("ISO8859-15", charmap.ISO8859_15) -// -// Aliases can be registered as well, for example "8859-15" could be an alias -// for "ISO8859-15". -// -// For POSIX systems, the tcell package will check the environment variables -// LC_ALL, LC_CTYPE, and LANG (in that order) to determine the character set. -// These are expected to have the following pattern: -// -// $language[.$codeset[@$variant] -// -// We extract only the $codeset part, which will usually be something like -// UTF-8 or ISO8859-15 or KOI8-R. Note that if the locale is either "POSIX" -// or "C", then we assume US-ASCII (the POSIX 'portable character set' -// and assume all other characters are somehow invalid.) -// -// Modern POSIX systems and terminal emulators may use UTF-8, and for those -// systems, this API is also unnecessary. For example, Darwin (MacOS X) and -// modern Linux running modern xterm generally will out of the box without -// any of this. Use of UTF-8 is recommended when possible, as it saves -// quite a lot processing overhead. -// -// Note that some encodings are quite large (for example GB18030 which is a -// superset of Unicode) and so the application size can be expected ot -// increase quite a bit as each encoding is added. The East Asian encodings -// have been seen to add 100-200K per encoding to the application size. -// -func RegisterEncoding(charset string, enc encoding.Encoding) { - encodingLk.Lock() - charset = strings.ToLower(charset) - encodings[charset] = enc - encodingLk.Unlock() -} - -// EncodingFallback describes how the system behavees when the locale -// requires a character set that we do not support. The system always -// supports UTF-8 and US-ASCII. On Windows consoles, UTF-16LE is also -// supported automatically. Other character sets must be added using the -// RegisterEncoding API. (A large group of nearly all of them can be -// added using the RegisterAll function in the encoding sub package.) -type EncodingFallback int - -const ( - // EncodingFallbackFail behavior causes GetEncoding to fail - // when it cannot find an encoding. - EncodingFallbackFail = iota - - // EncodingFallbackASCII behaviore causes GetEncoding to fall back - // to a 7-bit ASCII encoding, if no other encoding can be found. - EncodingFallbackASCII - - // EncodingFallbackUTF8 behavior causes GetEncoding to assume - // UTF8 can pass unmodified upon failure. Note that this behavior - // is not recommended, unless you are sure your terminal can cope - // with real UTF8 sequences. - EncodingFallbackUTF8 -) - -// SetEncodingFallback changes the behavior of GetEncoding when a suitable -// encoding is not found. The default is EncodingFallbackFail, which -// causes GetEncoding to simply return nil. -func SetEncodingFallback(fb EncodingFallback) { - encodingLk.Lock() - encodingFallback = fb - encodingLk.Unlock() -} - -// GetEncoding is used by Screen implementors who want to locate an encoding -// for the given character set name. Note that this will return nil for -// either the Unicode (UTF-8) or ASCII encodings, since we don't use -// encodings for them but instead have our own native methods. -func GetEncoding(charset string) encoding.Encoding { - charset = strings.ToLower(charset) - encodingLk.Lock() - defer encodingLk.Unlock() - if enc, ok := encodings[charset]; ok { - return enc - } - switch encodingFallback { - case EncodingFallbackASCII: - return gencoding.ASCII - case EncodingFallbackUTF8: - return encoding.Nop - } - return nil -} - -func init() { - // We always support UTF-8 and ASCII. - encodings = make(map[string]encoding.Encoding) - encodings["utf-8"] = gencoding.UTF8 - encodings["utf8"] = gencoding.UTF8 - encodings["us-ascii"] = gencoding.ASCII - encodings["ascii"] = gencoding.ASCII - encodings["iso646"] = gencoding.ASCII -} diff --git a/vendor/github.com/gdamore/tcell/errors.go b/vendor/github.com/gdamore/tcell/errors.go deleted file mode 100644 index 920b64b2..00000000 --- a/vendor/github.com/gdamore/tcell/errors.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "errors" - "time" - - "github.com/gdamore/tcell/terminfo" -) - -var ( - // ErrTermNotFound indicates that a suitable terminal entry could - // not be found. This can result from either not having TERM set, - // or from the TERM failing to support certain minimal functionality, - // in particular absolute cursor addressability (the cup capability) - // is required. For example, legacy "adm3" lacks this capability, - // whereas the slightly newer "adm3a" supports it. This failure - // occurs most often with "dumb". - ErrTermNotFound = terminfo.ErrTermNotFound - - // ErrNoScreen indicates that no suitable screen could be found. - // This may result from attempting to run on a platform where there - // is no support for either termios or console I/O (such as nacl), - // or from running in an environment where there is no access to - // a suitable console/terminal device. (For example, running on - // without a controlling TTY or with no /dev/tty on POSIX platforms.) - ErrNoScreen = errors.New("no suitable screen available") - - // ErrNoCharset indicates that the locale environment the - // program is not supported by the program, because no suitable - // encoding was found for it. This problem never occurs if - // the environment is UTF-8 or UTF-16. - ErrNoCharset = errors.New("character set not supported") - - // ErrEventQFull indicates that the event queue is full, and - // cannot accept more events. - ErrEventQFull = errors.New("event queue full") -) - -// An EventError is an event representing some sort of error, and carries -// an error payload. -type EventError struct { - t time.Time - err error -} - -// When returns the time when the event was created. -func (ev *EventError) When() time.Time { - return ev.t -} - -// Error implements the error. -func (ev *EventError) Error() string { - return ev.err.Error() -} - -// NewEventError creates an ErrorEvent with the given error payload. -func NewEventError(err error) *EventError { - return &EventError{t: time.Now(), err: err} -} diff --git a/vendor/github.com/gdamore/tcell/event.go b/vendor/github.com/gdamore/tcell/event.go deleted file mode 100644 index a3b77006..00000000 --- a/vendor/github.com/gdamore/tcell/event.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "time" -) - -// Event is a generic interface used for passing around Events. -// Concrete types follow. -type Event interface { - // When reports the time when the event was generated. - When() time.Time -} - -// EventTime is a simple base event class, suitable for easy reuse. -// It can be used to deliver actual timer events as well. -type EventTime struct { - when time.Time -} - -// When returns the time stamp when the event occurred. -func (e *EventTime) When() time.Time { - return e.when -} - -// SetEventTime sets the time of occurrence for the event. -func (e *EventTime) SetEventTime(t time.Time) { - e.when = t -} - -// SetEventNow sets the time of occurrence for the event to the current time. -func (e *EventTime) SetEventNow() { - e.SetEventTime(time.Now()) -} - -// EventHandler is anything that handles events. If the handler has -// consumed the event, it should return true. False otherwise. -type EventHandler interface { - HandleEvent(Event) bool -} diff --git a/vendor/github.com/gdamore/tcell/interrupt.go b/vendor/github.com/gdamore/tcell/interrupt.go deleted file mode 100644 index 70dddfce..00000000 --- a/vendor/github.com/gdamore/tcell/interrupt.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "time" -) - -// EventInterrupt is a generic wakeup event. Its can be used to -// to request a redraw. It can carry an arbitrary payload, as well. -type EventInterrupt struct { - t time.Time - v interface{} -} - -// When returns the time when this event was created. -func (ev *EventInterrupt) When() time.Time { - return ev.t -} - -// Data is used to obtain the opaque event payload. -func (ev *EventInterrupt) Data() interface{} { - return ev.v -} - -// NewEventInterrupt creates an EventInterrupt with the given payload. -func NewEventInterrupt(data interface{}) *EventInterrupt { - return &EventInterrupt{t: time.Now(), v: data} -} diff --git a/vendor/github.com/gdamore/tcell/key.go b/vendor/github.com/gdamore/tcell/key.go deleted file mode 100644 index 3545215a..00000000 --- a/vendor/github.com/gdamore/tcell/key.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright 2016 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "fmt" - "strings" - "time" -) - -// EventKey represents a key press. Usually this is a key press followed -// by a key release, but since terminal programs don't have a way to report -// key release events, we usually get just one event. If a key is held down -// then the terminal may synthesize repeated key presses at some predefined -// rate. We have no control over that, nor visibility into it. -// -// In some cases, we can have a modifier key, such as ModAlt, that can be -// generated with a key press. (This usually is represented by having the -// high bit set, or in some cases, by sending an ESC prior to the rune.) -// -// If the value of Key() is KeyRune, then the actual key value will be -// available with the Rune() method. This will be the case for most keys. -// In most situations, the modifiers will not be set. For example, if the -// rune is 'A', this will be reported without the ModShift bit set, since -// really can't tell if the Shift key was pressed (it might have been CAPSLOCK, -// or a terminal that only can send capitals, or keyboard with separate -// capital letters from lower case letters). -// -// Generally, terminal applications have far less visibility into keyboard -// activity than graphical applications. Hence, they should avoid depending -// overly much on availability of modifiers, or the availability of any -// specific keys. -type EventKey struct { - t time.Time - mod ModMask - key Key - ch rune -} - -// When returns the time when this Event was created, which should closely -// match the time when the key was pressed. -func (ev *EventKey) When() time.Time { - return ev.t -} - -// Rune returns the rune corresponding to the key press, if it makes sense. -// The result is only defined if the value of Key() is KeyRune. -func (ev *EventKey) Rune() rune { - return ev.ch -} - -// Key returns a virtual key code. We use this to identify specific key -// codes, such as KeyEnter, etc. Most control and function keys are reported -// with unique Key values. Normal alphanumeric and punctuation keys will -// generally return KeyRune here; the specific key can be further decoded -// using the Rune() function. -func (ev *EventKey) Key() Key { - return ev.key -} - -// Modifiers returns the modifiers that were present with the key press. Note -// that not all platforms and terminals support this equally well, and some -// cases we will not not know for sure. Hence, applications should avoid -// using this in most circumstances. -func (ev *EventKey) Modifiers() ModMask { - return ev.mod -} - -// KeyNames holds the written names of special keys. Useful to echo back a key -// name, or to look up a key from a string value. -var KeyNames = map[Key]string{ - KeyEnter: "Enter", - KeyBackspace: "Backspace", - KeyTab: "Tab", - KeyBacktab: "Backtab", - KeyEsc: "Esc", - KeyBackspace2: "Backspace2", - KeyDelete: "Delete", - KeyInsert: "Insert", - KeyUp: "Up", - KeyDown: "Down", - KeyLeft: "Left", - KeyRight: "Right", - KeyHome: "Home", - KeyEnd: "End", - KeyUpLeft: "UpLeft", - KeyUpRight: "UpRight", - KeyDownLeft: "DownLeft", - KeyDownRight: "DownRight", - KeyCenter: "Center", - KeyPgDn: "PgDn", - KeyPgUp: "PgUp", - KeyClear: "Clear", - KeyExit: "Exit", - KeyCancel: "Cancel", - KeyPause: "Pause", - KeyPrint: "Print", - KeyF1: "F1", - KeyF2: "F2", - KeyF3: "F3", - KeyF4: "F4", - KeyF5: "F5", - KeyF6: "F6", - KeyF7: "F7", - KeyF8: "F8", - KeyF9: "F9", - KeyF10: "F10", - KeyF11: "F11", - KeyF12: "F12", - KeyF13: "F13", - KeyF14: "F14", - KeyF15: "F15", - KeyF16: "F16", - KeyF17: "F17", - KeyF18: "F18", - KeyF19: "F19", - KeyF20: "F20", - KeyF21: "F21", - KeyF22: "F22", - KeyF23: "F23", - KeyF24: "F24", - KeyF25: "F25", - KeyF26: "F26", - KeyF27: "F27", - KeyF28: "F28", - KeyF29: "F29", - KeyF30: "F30", - KeyF31: "F31", - KeyF32: "F32", - KeyF33: "F33", - KeyF34: "F34", - KeyF35: "F35", - KeyF36: "F36", - KeyF37: "F37", - KeyF38: "F38", - KeyF39: "F39", - KeyF40: "F40", - KeyF41: "F41", - KeyF42: "F42", - KeyF43: "F43", - KeyF44: "F44", - KeyF45: "F45", - KeyF46: "F46", - KeyF47: "F47", - KeyF48: "F48", - KeyF49: "F49", - KeyF50: "F50", - KeyF51: "F51", - KeyF52: "F52", - KeyF53: "F53", - KeyF54: "F54", - KeyF55: "F55", - KeyF56: "F56", - KeyF57: "F57", - KeyF58: "F58", - KeyF59: "F59", - KeyF60: "F60", - KeyF61: "F61", - KeyF62: "F62", - KeyF63: "F63", - KeyF64: "F64", - KeyCtrlA: "Ctrl-A", - KeyCtrlB: "Ctrl-B", - KeyCtrlC: "Ctrl-C", - KeyCtrlD: "Ctrl-D", - KeyCtrlE: "Ctrl-E", - KeyCtrlF: "Ctrl-F", - KeyCtrlG: "Ctrl-G", - KeyCtrlJ: "Ctrl-J", - KeyCtrlK: "Ctrl-K", - KeyCtrlL: "Ctrl-L", - KeyCtrlN: "Ctrl-N", - KeyCtrlO: "Ctrl-O", - KeyCtrlP: "Ctrl-P", - KeyCtrlQ: "Ctrl-Q", - KeyCtrlR: "Ctrl-R", - KeyCtrlS: "Ctrl-S", - KeyCtrlT: "Ctrl-T", - KeyCtrlU: "Ctrl-U", - KeyCtrlV: "Ctrl-V", - KeyCtrlW: "Ctrl-W", - KeyCtrlX: "Ctrl-X", - KeyCtrlY: "Ctrl-Y", - KeyCtrlZ: "Ctrl-Z", - KeyCtrlSpace: "Ctrl-Space", - KeyCtrlUnderscore: "Ctrl-_", - KeyCtrlRightSq: "Ctrl-]", - KeyCtrlBackslash: "Ctrl-\\", - KeyCtrlCarat: "Ctrl-^", -} - -// Name returns a printable value or the key stroke. This can be used -// when printing the event, for example. -func (ev *EventKey) Name() string { - s := "" - m := []string{} - if ev.mod&ModShift != 0 { - m = append(m, "Shift") - } - if ev.mod&ModAlt != 0 { - m = append(m, "Alt") - } - if ev.mod&ModMeta != 0 { - m = append(m, "Meta") - } - if ev.mod&ModCtrl != 0 { - m = append(m, "Ctrl") - } - - ok := false - if s, ok = KeyNames[ev.key]; !ok { - if ev.key == KeyRune { - s = "Rune[" + string(ev.ch) + "]" - } else { - s = fmt.Sprintf("Key[%d,%d]", ev.key, int(ev.ch)) - } - } - if len(m) != 0 { - if ev.mod&ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") { - s = s[5:] - } - return fmt.Sprintf("%s+%s", strings.Join(m, "+"), s) - } - return s -} - -// NewEventKey attempts to create a suitable event. It parses the various -// ASCII control sequences if KeyRune is passed for Key, but if the caller -// has more precise information it should set that specifically. Callers -// that aren't sure about modifier state (most) should just pass ModNone. -func NewEventKey(k Key, ch rune, mod ModMask) *EventKey { - if k == KeyRune && (ch < ' ' || ch == 0x7f) { - // Turn specials into proper key codes. This is for - // control characters and the DEL. - k = Key(ch) - if mod == ModNone && ch < ' ' { - switch Key(ch) { - case KeyBackspace, KeyTab, KeyEsc, KeyEnter: - // these keys are directly typeable without CTRL - default: - // most likely entered with a CTRL keypress - mod = ModCtrl - } - } - } - return &EventKey{t: time.Now(), key: k, ch: ch, mod: mod} -} - -// ModMask is a mask of modifier keys. Note that it will not always be -// possible to report modifier keys. -type ModMask int16 - -// These are the modifiers keys that can be sent either with a key press, -// or a mouse event. Note that as of now, due to the confusion associated -// with Meta, and the lack of support for it on many/most platforms, the -// current implementations never use it. Instead, they use ModAlt, even for -// events that could possibly have been distinguished from ModAlt. -const ( - ModShift ModMask = 1 << iota - ModCtrl - ModAlt - ModMeta - ModNone ModMask = 0 -) - -// Key is a generic value for representing keys, and especially special -// keys (function keys, cursor movement keys, etc.) For normal keys, like -// ASCII letters, we use KeyRune, and then expect the application to -// inspect the Rune() member of the EventKey. -type Key int16 - -// This is the list of named keys. KeyRune is special however, in that it is -// a place holder key indicating that a printable character was sent. The -// actual value of the rune will be transported in the Rune of the associated -// EventKey. -const ( - KeyRune Key = iota + 256 - KeyUp - KeyDown - KeyRight - KeyLeft - KeyUpLeft - KeyUpRight - KeyDownLeft - KeyDownRight - KeyCenter - KeyPgUp - KeyPgDn - KeyHome - KeyEnd - KeyInsert - KeyDelete - KeyHelp - KeyExit - KeyClear - KeyCancel - KeyPrint - KeyPause - KeyBacktab - KeyF1 - KeyF2 - KeyF3 - KeyF4 - KeyF5 - KeyF6 - KeyF7 - KeyF8 - KeyF9 - KeyF10 - KeyF11 - KeyF12 - KeyF13 - KeyF14 - KeyF15 - KeyF16 - KeyF17 - KeyF18 - KeyF19 - KeyF20 - KeyF21 - KeyF22 - KeyF23 - KeyF24 - KeyF25 - KeyF26 - KeyF27 - KeyF28 - KeyF29 - KeyF30 - KeyF31 - KeyF32 - KeyF33 - KeyF34 - KeyF35 - KeyF36 - KeyF37 - KeyF38 - KeyF39 - KeyF40 - KeyF41 - KeyF42 - KeyF43 - KeyF44 - KeyF45 - KeyF46 - KeyF47 - KeyF48 - KeyF49 - KeyF50 - KeyF51 - KeyF52 - KeyF53 - KeyF54 - KeyF55 - KeyF56 - KeyF57 - KeyF58 - KeyF59 - KeyF60 - KeyF61 - KeyF62 - KeyF63 - KeyF64 -) - -// These are the control keys. Note that they overlap with other keys, -// perhaps. For example, KeyCtrlH is the same as KeyBackspace. -const ( - KeyCtrlSpace Key = iota - KeyCtrlA - KeyCtrlB - KeyCtrlC - KeyCtrlD - KeyCtrlE - KeyCtrlF - KeyCtrlG - KeyCtrlH - KeyCtrlI - KeyCtrlJ - KeyCtrlK - KeyCtrlL - KeyCtrlM - KeyCtrlN - KeyCtrlO - KeyCtrlP - KeyCtrlQ - KeyCtrlR - KeyCtrlS - KeyCtrlT - KeyCtrlU - KeyCtrlV - KeyCtrlW - KeyCtrlX - KeyCtrlY - KeyCtrlZ - KeyCtrlLeftSq // Escape - KeyCtrlBackslash - KeyCtrlRightSq - KeyCtrlCarat - KeyCtrlUnderscore -) - -// Special values - these are fixed in an attempt to make it more likely -// that aliases will encode the same way. - -// These are the defined ASCII values for key codes. They generally match -// with KeyCtrl values. -const ( - KeyNUL Key = iota - KeySOH - KeySTX - KeyETX - KeyEOT - KeyENQ - KeyACK - KeyBEL - KeyBS - KeyTAB - KeyLF - KeyVT - KeyFF - KeyCR - KeySO - KeySI - KeyDLE - KeyDC1 - KeyDC2 - KeyDC3 - KeyDC4 - KeyNAK - KeySYN - KeyETB - KeyCAN - KeyEM - KeySUB - KeyESC - KeyFS - KeyGS - KeyRS - KeyUS - KeyDEL Key = 0x7F -) - -// These keys are aliases for other names. -const ( - KeyBackspace = KeyBS - KeyTab = KeyTAB - KeyEsc = KeyESC - KeyEscape = KeyESC - KeyEnter = KeyCR - KeyBackspace2 = KeyDEL -) diff --git a/vendor/github.com/gdamore/tcell/mouse.go b/vendor/github.com/gdamore/tcell/mouse.go deleted file mode 100644 index 8c51c98e..00000000 --- a/vendor/github.com/gdamore/tcell/mouse.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "time" -) - -// EventMouse is a mouse event. It is sent on either mouse up or mouse down -// events. It is also sent on mouse motion events - if the terminal supports -// it. We make every effort to ensure that mouse release events are delivered. -// Hence, click drag can be identified by a motion event with the mouse down, -// without any intervening button release. On some terminals only the initiating -// press and terminating release event will be delivered. -// -// Mouse wheel events, when reported, may appear on their own as individual -// impulses; that is, there will normally not be a release event delivered -// for mouse wheel movements. -// -// Most terminals cannot report the state of more than one button at a time -- -// and some cannot report motion events unless a button is pressed. -// -// Applications can inspect the time between events to resolve double or -// triple clicks. -type EventMouse struct { - t time.Time - btn ButtonMask - mod ModMask - x int - y int -} - -// When returns the time when this EventMouse was created. -func (ev *EventMouse) When() time.Time { - return ev.t -} - -// Buttons returns the list of buttons that were pressed or wheel motions. -func (ev *EventMouse) Buttons() ButtonMask { - return ev.btn -} - -// Modifiers returns a list of keyboard modifiers that were pressed -// with the mouse button(s). -func (ev *EventMouse) Modifiers() ModMask { - return ev.mod -} - -// Position returns the mouse position in character cells. The origin -// 0, 0 is at the upper left corner. -func (ev *EventMouse) Position() (int, int) { - return ev.x, ev.y -} - -// NewEventMouse is used to create a new mouse event. Applications -// shouldn't need to use this; its mostly for screen implementors. -func NewEventMouse(x, y int, btn ButtonMask, mod ModMask) *EventMouse { - return &EventMouse{t: time.Now(), x: x, y: y, btn: btn, mod: mod} -} - -// ButtonMask is a mask of mouse buttons and wheel events. Mouse button presses -// are normally delivered as both press and release events. Mouse wheel events -// are normally just single impulse events. Windows supports up to eight -// separate buttons plus all four wheel directions, but XTerm can only support -// mouse buttons 1-3 and wheel up/down. Its not unheard of for terminals -// to support only one or two buttons (think Macs). Old terminals, and true -// emulations (such as vt100) won't support mice at all, of course. -type ButtonMask int16 - -// These are the actual button values. -const ( - Button1 ButtonMask = 1 << iota // Usually left mouse button. - Button2 // Usually the middle mouse button. - Button3 // Usually the right mouse button. - Button4 // Often a side button (thumb/next). - Button5 // Often a side button (thumb/prev). - Button6 - Button7 - Button8 - WheelUp // Wheel motion up/away from user. - WheelDown // Wheel motion down/towards user. - WheelLeft // Wheel motion to left. - WheelRight // Wheel motion to right. - ButtonNone ButtonMask = 0 // No button or wheel events. -) diff --git a/vendor/github.com/gdamore/tcell/resize.go b/vendor/github.com/gdamore/tcell/resize.go deleted file mode 100644 index 0385673c..00000000 --- a/vendor/github.com/gdamore/tcell/resize.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "time" -) - -// EventResize is sent when the window size changes. -type EventResize struct { - t time.Time - w int - h int -} - -// NewEventResize creates an EventResize with the new updated window size, -// which is given in character cells. -func NewEventResize(width, height int) *EventResize { - return &EventResize{t: time.Now(), w: width, h: height} -} - -// When returns the time when the Event was created. -func (ev *EventResize) When() time.Time { - return ev.t -} - -// Size returns the new window size as width, height in character cells. -func (ev *EventResize) Size() (int, int) { - return ev.w, ev.h -} diff --git a/vendor/github.com/gdamore/tcell/runes.go b/vendor/github.com/gdamore/tcell/runes.go deleted file mode 100644 index ed9c63b5..00000000 --- a/vendor/github.com/gdamore/tcell/runes.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// The names of these constants are chosen to match Terminfo names, -// modulo case, and changing the prefix from ACS_ to Rune. These are -// the runes we provide extra special handling for, with ASCII fallbacks -// for terminals that lack them. -const ( - RuneSterling = '£' - RuneDArrow = '↓' - RuneLArrow = '←' - RuneRArrow = '→' - RuneUArrow = '↑' - RuneBullet = '·' - RuneBoard = '░' - RuneCkBoard = '▒' - RuneDegree = '°' - RuneDiamond = '◆' - RuneGEqual = '≥' - RunePi = 'π' - RuneHLine = '─' - RuneLantern = '§' - RunePlus = '┼' - RuneLEqual = '≤' - RuneLLCorner = '└' - RuneLRCorner = '┘' - RuneNEqual = '≠' - RunePlMinus = '±' - RuneS1 = '⎺' - RuneS3 = '⎻' - RuneS7 = '⎼' - RuneS9 = '⎽' - RuneBlock = '█' - RuneTTee = '┬' - RuneRTee = '┤' - RuneLTee = '├' - RuneBTee = '┴' - RuneULCorner = '┌' - RuneURCorner = '┐' - RuneVLine = '│' -) - -// RuneFallbacks is the default map of fallback strings that will be -// used to replace a rune when no other more appropriate transformation -// is available, and the rune cannot be displayed directly. -// -// New entries may be added to this map over time, as it becomes clear -// that such is desirable. Characters that represent either letters or -// numbers should not be added to this list unless it is certain that -// the meaning will still convey unambiguously. -// -// As an example, it would be appropriate to add an ASCII mapping for -// the full width form of the letter 'A', but it would not be appropriate -// to do so a glyph representing the country China. -// -// Programs that desire richer fallbacks may register additional ones, -// or change or even remove these mappings with Screen.RegisterRuneFallback -// Screen.UnregisterRuneFallback methods. -// -// Note that Unicode is presumed to be able to display all glyphs. -// This is a pretty poor assumption, but there is no easy way to -// figure out which glyphs are supported in a given font. Hence, -// some care in selecting the characters you support in your application -// is still appropriate. -var RuneFallbacks = map[rune]string{ - RuneSterling: "f", - RuneDArrow: "v", - RuneLArrow: "<", - RuneRArrow: ">", - RuneUArrow: "^", - RuneBullet: "o", - RuneBoard: "#", - RuneCkBoard: ":", - RuneDegree: "\\", - RuneDiamond: "+", - RuneGEqual: ">", - RunePi: "*", - RuneHLine: "-", - RuneLantern: "#", - RunePlus: "+", - RuneLEqual: "<", - RuneLLCorner: "+", - RuneLRCorner: "+", - RuneNEqual: "!", - RunePlMinus: "#", - RuneS1: "~", - RuneS3: "-", - RuneS7: "-", - RuneS9: "_", - RuneBlock: "#", - RuneTTee: "+", - RuneRTee: "+", - RuneLTee: "+", - RuneBTee: "+", - RuneULCorner: "+", - RuneURCorner: "+", - RuneVLine: "|", -} diff --git a/vendor/github.com/gdamore/tcell/screen.go b/vendor/github.com/gdamore/tcell/screen.go deleted file mode 100644 index 99816a01..00000000 --- a/vendor/github.com/gdamore/tcell/screen.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// Screen represents the physical (or emulated) screen. -// This can be a terminal window or a physical console. Platforms implement -// this differerently. -type Screen interface { - // Init initializes the screen for use. - Init() error - - // Fini finalizes the screen also releasing resources. - Fini() - - // Clear erases the screen. The contents of any screen buffers - // will also be cleared. This has the logical effect of - // filling the screen with spaces, using the global default style. - Clear() - - // Fill fills the screen with the given character and style. - Fill(rune, Style) - - // SetCell is an older API, and will be removed. Please use - // SetContent instead; SetCell is implemented in terms of SetContent. - SetCell(x int, y int, style Style, ch ...rune) - - // GetContent returns the contents at the given location. If the - // coordinates are out of range, then the values will be 0, nil, - // StyleDefault. Note that the contents returned are logical contents - // and may not actually be what is displayed, but rather are what will - // be displayed if Show() or Sync() is called. The width is the width - // in screen cells; most often this will be 1, but some East Asian - // characters require two cells. - GetContent(x, y int) (mainc rune, combc []rune, style Style, width int) - - // SetContent sets the contents of the given cell location. If - // the coordinates are out of range, then the operation is ignored. - // - // The first rune is the primary non-zero width rune. The array - // that follows is a possible list of combining characters to append, - // and will usually be nil (no combining characters.) - // - // The results are not displayd until Show() or Sync() is called. - // - // Note that wide (East Asian full width) runes occupy two cells, - // and attempts to place character at next cell to the right will have - // undefined effects. Wide runes that are printed in the - // last column will be replaced with a single width space on output. - SetContent(x int, y int, mainc rune, combc []rune, style Style) - - // SetStyle sets the default style to use when clearing the screen - // or when StyleDefault is specified. If it is also StyleDefault, - // then whatever system/terminal default is relevant will be used. - SetStyle(style Style) - - // ShowCursor is used to display the cursor at a given location. - // If the coordinates -1, -1 are given or are otherwise outside the - // dimensions of the screen, the cursor will be hidden. - ShowCursor(x int, y int) - - // HideCursor is used to hide the cursor. Its an alias for - // ShowCursor(-1, -1). - HideCursor() - - // Size returns the screen size as width, height. This changes in - // response to a call to Clear or Flush. - Size() (int, int) - - // PollEvent waits for events to arrive. Main application loops - // must spin on this to prevent the application from stalling. - // Furthermore, this will return nil if the Screen is finalized. - PollEvent() Event - - // PostEvent tries to post an event into the event stream. This - // can fail if the event queue is full. In that case, the event - // is dropped, and ErrEventQFull is returned. - PostEvent(ev Event) error - - // PostEventWait is like PostEvent, but if the queue is full, it - // blocks until there is space in the queue, making delivery - // reliable. However, it is VERY important that this function - // never be called from within whatever event loop is polling - // with PollEvent(), otherwise a deadlock may arise. - // - // For this reason, when using this function, the use of a - // Goroutine is recommended to ensure no deadlock can occur. - PostEventWait(ev Event) - - // EnableMouse enables the mouse. (If your terminal supports it.) - EnableMouse() - - // DisableMouse disables the mouse. - DisableMouse() - - // HasMouse returns true if the terminal (apparently) supports a - // mouse. Note that the a return value of true doesn't guarantee that - // a mouse/pointing device is present; a false return definitely - // indicates no mouse support is available. - HasMouse() bool - - // Colors returns the number of colors. All colors are assumed to - // use the ANSI color map. If a terminal is monochrome, it will - // return 0. - Colors() int - - // Show makes all the content changes made using SetContent() visible - // on the display. - // - // It does so in the most efficient and least visually disruptive - // manner possible. - Show() - - // Sync works like Show(), but it updates every visible cell on the - // physical display, assuming that it is not synchronized with any - // internal model. This may be both expensive and visually jarring, - // so it should only be used when believed to actually be necessary. - // - // Typically this is called as a result of a user-requested redraw - // (e.g. to clear up on screen corruption caused by some other program), - // or during a resize event. - Sync() - - // CharacterSet returns information about the character set. - // This isn't the full locale, but it does give us the input/output - // character set. Note that this is just for diagnostic purposes, - // we normally translate input/output to/from UTF-8, regardless of - // what the user's environment is. - CharacterSet() string - - // RegisterRuneFallback adds a fallback for runes that are not - // part of the character set -- for example one coudld register - // o as a fallback for ø. This should be done cautiously for - // characters that might be displayed ordinarily in language - // specific text -- characters that could change the meaning of - // of written text would be dangerous. The intention here is to - // facilitate fallback characters in pseudo-graphical applications. - // - // If the terminal has fallbacks already in place via an alternate - // character set, those are used in preference. Also, standard - // fallbacks for graphical characters in the ACSC terminfo string - // are registered implicitly. - - // The display string should be the same width as original rune. - // This makes it possible to register two character replacements - // for full width East Asian characters, for example. - // - // It is recommended that replacement strings consist only of - // 7-bit ASCII, since other characters may not display everywhere. - RegisterRuneFallback(r rune, subst string) - - // UnregisterRuneFallback unmaps a replacement. It will unmap - // the implicit ASCII replacements for alternate characters as well. - // When an unmapped char needs to be displayed, but no suitable - // glyph is available, '?' is emitted instead. It is not possible - // to "disable" the use of alternate characters that are supported - // by your terminal except by changing the terminal database. - UnregisterRuneFallback(r rune) - - // CanDisplay returns true if the given rune can be displayed on - // this screen. Note that this is a best guess effort -- whether - // your fonts support the character or not may be questionable. - // Mostly this is for folks who work outside of Unicode. - // - // If checkFallbacks is true, then if any (possibly imperfect) - // fallbacks are registered, this will return true. This will - // also return true if the terminal can replace the glyph with - // one that is visually indistinguishable from the one requested. - CanDisplay(r rune, checkFallbacks bool) bool - - // Resize does nothing, since its generally not possible to - // ask a screen to resize, but it allows the Screen to implement - // the View interface. - Resize(int, int, int, int) - - // HasKey returns true if the keyboard is believed to have the - // key. In some cases a keyboard may have keys with this name - // but no support for them, while in others a key may be reported - // as supported but not actually be usable (such as some emulators - // that hijack certain keys). Its best not to depend to strictly - // on this function, but it can be used for hinting when building - // menus, displayed hot-keys, etc. Note that KeyRune (literal - // runes) is always true. - HasKey(Key) bool -} - -// NewScreen returns a default Screen suitable for the user's terminal -// environment. -func NewScreen() (Screen, error) { - // Windows is happier if we try for a console screen first. - if s, _ := NewConsoleScreen(); s != nil { - return s, nil - } else if s, e := NewTerminfoScreen(); s != nil { - return s, nil - } else { - return nil, e - } -} diff --git a/vendor/github.com/gdamore/tcell/simulation.go b/vendor/github.com/gdamore/tcell/simulation.go deleted file mode 100644 index 850a7b3d..00000000 --- a/vendor/github.com/gdamore/tcell/simulation.go +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright 2016 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "sync" - "unicode/utf8" - - "golang.org/x/text/transform" -) - -// NewSimulationScreen returns a SimulationScreen. Note that -// SimulationScreen is also a Screen. -func NewSimulationScreen(charset string) SimulationScreen { - if charset == "" { - charset = "UTF-8" - } - s := &simscreen{charset: charset} - return s -} - -// SimulationScreen represents a screen simulation. This is intended to -// be a superset of normal Screens, but also adds some important interfaces -// for testing. -type SimulationScreen interface { - // InjectKeyBytes injects a stream of bytes corresponding to - // the native encoding (see charset). It turns true if the entire - // set of bytes were processed and delivered as KeyEvents, false - // if any bytes were not fully understood. Any bytes that are not - // fully converted are discarded. - InjectKeyBytes(buf []byte) bool - - // InjectKey injects a key event. The rune is a UTF-8 rune, post - // any translation. - InjectKey(key Key, r rune, mod ModMask) - - // InjectMouse injects a mouse event. - InjectMouse(x, y int, buttons ButtonMask, mod ModMask) - - // SetSize resizes the underlying physical screen. It also causes - // a resize event to be injected during the next Show() or Sync(). - // A new physical contents array will be allocated (with data from - // the old copied), so any prior value obtained with GetContents - // won't be used anymore - SetSize(width, height int) - - // GetContents returns screen contents as an array of - // cells, along with the physical width & height. Note that the - // physical contents will be used until the next time SetSize() - // is called. - GetContents() (cells []SimCell, width int, height int) - - // GetCursor returns the cursor details. - GetCursor() (x int, y int, visible bool) - - Screen -} - -// SimCell represents a simulated screen cell. The purpose of this -// is to track on screen content. -type SimCell struct { - // Bytes is the actual character bytes. Normally this is - // rune data, but it could be be data in another encoding system. - Bytes []byte - - // Style is the style used to display the data. - Style Style - - // Runes is the list of runes, unadulterated, in UTF-8. - Runes []rune -} - -type simscreen struct { - physw int - physh int - fini bool - style Style - evch chan Event - quit chan struct{} - - front []SimCell - back CellBuffer - clear bool - cursorx int - cursory int - cursorvis bool - mouse bool - charset string - encoder transform.Transformer - decoder transform.Transformer - fillchar rune - fillstyle Style - fallback map[rune]string - - sync.Mutex -} - -func (s *simscreen) Init() error { - s.evch = make(chan Event, 10) - s.quit = make(chan struct{}) - s.fillchar = 'X' - s.fillstyle = StyleDefault - s.mouse = false - s.physw = 80 - s.physh = 25 - s.cursorx = -1 - s.cursory = -1 - s.style = StyleDefault - - if enc := GetEncoding(s.charset); enc != nil { - s.encoder = enc.NewEncoder() - s.decoder = enc.NewDecoder() - } else { - return ErrNoCharset - } - - s.front = make([]SimCell, s.physw*s.physh) - s.back.Resize(80, 25) - - // default fallbacks - s.fallback = make(map[rune]string) - for k, v := range RuneFallbacks { - s.fallback[k] = v - } - return nil -} - -func (s *simscreen) Fini() { - s.Lock() - s.fini = true - s.back.Resize(0, 0) - s.Unlock() - if s.quit != nil { - close(s.quit) - } - s.physw = 0 - s.physh = 0 - s.front = nil -} - -func (s *simscreen) SetStyle(style Style) { - s.Lock() - s.style = style - s.Unlock() -} - -func (s *simscreen) Clear() { - s.Fill(' ', s.style) -} - -func (s *simscreen) Fill(r rune, style Style) { - s.Lock() - s.back.Fill(r, style) - s.Unlock() -} - -func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) { - - if len(ch) > 0 { - s.SetContent(x, y, ch[0], ch[1:], style) - } else { - s.SetContent(x, y, ' ', nil, style) - } -} - -func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) { - - s.Lock() - s.back.SetContent(x, y, mainc, combc, st) - s.Unlock() -} - -func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) { - var mainc rune - var combc []rune - var style Style - var width int - s.Lock() - mainc, combc, style, width = s.back.GetContent(x, y) - s.Unlock() - return mainc, combc, style, width -} - -func (s *simscreen) drawCell(x, y int) int { - - mainc, combc, style, width := s.back.GetContent(x, y) - if !s.back.Dirty(x, y) { - return width - } - if x >= s.physw || y >= s.physh || x < 0 || y < 0 { - return width - } - simc := &s.front[(y*s.physw)+x] - - if style == StyleDefault { - style = s.style - } - simc.Style = style - simc.Runes = append([]rune{mainc}, combc...) - - // now emit runes - taking care to not overrun width with a - // wide character, and to ensure that we emit exactly one regular - // character followed up by any residual combing characters - - simc.Bytes = nil - - if x > s.physw-width { - simc.Runes = []rune{' '} - simc.Bytes = []byte{' '} - return width - } - - lbuf := make([]byte, 12) - ubuf := make([]byte, 12) - nout := 0 - - for _, r := range simc.Runes { - - l := utf8.EncodeRune(ubuf, r) - - nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true) - - if nout == 0 || lbuf[0] == '\x1a' { - - // skip combining - - if subst, ok := s.fallback[r]; ok { - simc.Bytes = append(simc.Bytes, - []byte(subst)...) - - } else if r >= ' ' && r <= '~' { - simc.Bytes = append(simc.Bytes, byte(r)) - - } else if simc.Bytes == nil { - simc.Bytes = append(simc.Bytes, '?') - } - } else { - simc.Bytes = append(simc.Bytes, lbuf[:nout]...) - } - } - s.back.SetDirty(x, y, false) - return width -} - -func (s *simscreen) ShowCursor(x, y int) { - s.Lock() - s.cursorx, s.cursory = x, y - s.showCursor() - s.Unlock() -} - -func (s *simscreen) HideCursor() { - s.ShowCursor(-1, -1) -} - -func (s *simscreen) showCursor() { - - x, y := s.cursorx, s.cursory - if x < 0 || y < 0 || x >= s.physw || y >= s.physh { - s.cursorvis = false - } else { - s.cursorvis = true - } -} - -func (s *simscreen) hideCursor() { - // does not update cursor position - s.cursorvis = false -} - -func (s *simscreen) Show() { - s.Lock() - s.resize() - s.draw() - s.Unlock() -} - -func (s *simscreen) clearScreen() { - // We emulate a hardware clear by filling with a specific pattern - for i := range s.front { - s.front[i].Style = s.fillstyle - s.front[i].Runes = []rune{s.fillchar} - s.front[i].Bytes = []byte{byte(s.fillchar)} - } - s.clear = false -} - -func (s *simscreen) draw() { - s.hideCursor() - if s.clear { - s.clearScreen() - } - - w, h := s.back.Size() - for y := 0; y < h; y++ { - for x := 0; x < w; x++ { - width := s.drawCell(x, y) - x += width - 1 - } - } - s.showCursor() -} - -func (s *simscreen) EnableMouse() { - s.mouse = true -} - -func (s *simscreen) DisableMouse() { - s.mouse = false -} - -func (s *simscreen) Size() (int, int) { - s.Lock() - w, h := s.back.Size() - s.Unlock() - return w, h -} - -func (s *simscreen) resize() { - w, h := s.physw, s.physh - ow, oh := s.back.Size() - if w != ow || h != oh { - s.back.Resize(w, h) - ev := NewEventResize(w, h) - s.PostEvent(ev) - } -} - -func (s *simscreen) Colors() int { - return 256 -} - -func (s *simscreen) PollEvent() Event { - select { - case <-s.quit: - return nil - case ev := <-s.evch: - return ev - } -} - -func (s *simscreen) PostEventWait(ev Event) { - s.evch <- ev -} - -func (s *simscreen) PostEvent(ev Event) error { - select { - case s.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - -func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) { - ev := NewEventMouse(x, y, buttons, mod) - s.PostEvent(ev) -} - -func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) { - ev := NewEventKey(key, r, mod) - s.PostEvent(ev) -} - -func (s *simscreen) InjectKeyBytes(b []byte) bool { - failed := false - -outer: - for len(b) > 0 { - if b[0] >= ' ' && b[0] <= 0x7F { - // printable ASCII easy to deal with -- no encodings - ev := NewEventKey(KeyRune, rune(b[0]), ModNone) - s.PostEvent(ev) - b = b[1:] - continue - } - - if b[0] < 0x80 { - mod := ModNone - // No encodings start with low numbered values - if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ { - mod = ModCtrl - } - ev := NewEventKey(Key(b[0]), 0, mod) - s.PostEvent(ev) - continue - } - - utfb := make([]byte, len(b)*4) // worst case - for l := 1; l < len(b); l++ { - s.decoder.Reset() - nout, nin, _ := s.decoder.Transform(utfb, b[:l], true) - - if nout != 0 { - r, _ := utf8.DecodeRune(utfb[:nout]) - if r != utf8.RuneError { - ev := NewEventKey(KeyRune, r, ModNone) - s.PostEvent(ev) - } - b = b[nin:] - continue outer - } - } - failed = true - b = b[1:] - continue - } - - return !failed -} - -func (s *simscreen) Sync() { - s.Lock() - s.clear = true - s.resize() - s.back.Invalidate() - s.draw() - s.Unlock() -} - -func (s *simscreen) CharacterSet() string { - return s.charset -} - -func (s *simscreen) SetSize(w, h int) { - s.Lock() - newc := make([]SimCell, w*h) - for row := 0; row < h && row < s.physh; row++ { - for col := 0; col < w && col < s.physw; col++ { - newc[(row*w)+col] = s.front[(row*s.physw)+col] - } - } - s.cursorx, s.cursory = -1, -1 - s.physw, s.physh = w, h - s.front = newc - s.back.Resize(w, h) - s.Unlock() -} - -func (s *simscreen) GetContents() ([]SimCell, int, int) { - s.Lock() - cells, w, h := s.front, s.physw, s.physh - s.Unlock() - return cells, w, h -} - -func (s *simscreen) GetCursor() (int, int, bool) { - s.Lock() - x, y, vis := s.cursorx, s.cursory, s.cursorvis - s.Unlock() - return x, y, vis -} - -func (s *simscreen) RegisterRuneFallback(r rune, subst string) { - s.Lock() - s.fallback[r] = subst - s.Unlock() -} - -func (s *simscreen) UnregisterRuneFallback(r rune) { - s.Lock() - delete(s.fallback, r) - s.Unlock() -} - -func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool { - - if enc := s.encoder; enc != nil { - nb := make([]byte, 6) - ob := make([]byte, 6) - num := utf8.EncodeRune(ob, r) - - enc.Reset() - dst, _, err := enc.Transform(nb, ob[:num], true) - if dst != 0 && err == nil && nb[0] != '\x1A' { - return true - } - } - if !checkFallbacks { - return false - } - if _, ok := s.fallback[r]; ok { - return true - } - return false -} - -func (s *simscreen) HasMouse() bool { - return false -} - -func (s *simscreen) Resize(int, int, int, int) {} - -func (s *simscreen) HasKey(Key) bool { - return true -} diff --git a/vendor/github.com/gdamore/tcell/style.go b/vendor/github.com/gdamore/tcell/style.go deleted file mode 100644 index c4ee9351..00000000 --- a/vendor/github.com/gdamore/tcell/style.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// Style represents a complete text style, including both foreground -// and background color. We encode it in a 64-bit int for efficiency. -// The coding is (MSB): <7b flags><1b><24b fgcolor><7b attr><1b><24b bgcolor>. -// The <1b> is set true to indicate that the color is an RGB color, rather -// than a named index. -// -// This gives 24bit color options, if it ever becomes truly necessary. -// However, applications must not rely on this encoding. -// -// Note that not all terminals can display all colors or attributes, and -// many might have specific incompatibilities between specific attributes -// and color combinations. -// -// The intention is to extend styles to support paletting, in which case -// some flag bit(s) would be set, and the foreground and background colors -// would be replaced with a palette number and palette index. -// -// To use Style, just declare a variable of its type. -type Style int64 - -// StyleDefault represents a default style, based upon the context. -// It is the zero value. -const StyleDefault Style = 0 - -// styleFlags -- used internally for now. -const ( - styleBgSet = 1 << (iota + 57) - styleFgSet - stylePalette -) - -// Foreground returns a new style based on s, with the foreground color set -// as requested. ColorDefault can be used to select the global default. -func (s Style) Foreground(c Color) Style { - if c == ColorDefault { - return (s &^ (0x1ffffff00000000 | styleFgSet)) - } - return (s &^ Style(0x1ffffff00000000)) | - ((Style(c) & 0x1ffffff) << 32) | styleFgSet -} - -// Background returns a new style based on s, with the background color set -// as requested. ColorDefault can be used to select the global default. -func (s Style) Background(c Color) Style { - if c == ColorDefault { - return (s &^ (0x1ffffff | styleBgSet)) - } - return (s &^ (0x1ffffff)) | (Style(c) & 0x1ffffff) | styleBgSet -} - -// Decompose breaks a style up, returning the foreground, background, -// and other attributes. -func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) { - if s&styleFgSet != 0 { - fg = Color(s>>32) & 0x1ffffff - } else { - fg = ColorDefault - } - if s&styleBgSet != 0 { - bg = Color(s & 0x1ffffff) - } else { - bg = ColorDefault - } - attr = AttrMask(s) & attrAll - - return fg, bg, attr -} - -func (s Style) setAttrs(attrs Style, on bool) Style { - if on { - return s | attrs - } - return s &^ attrs -} - -// Normal returns the style with all attributes disabled. -func (s Style) Normal() Style { - return s &^ Style(attrAll) -} - -// Bold returns a new style based on s, with the bold attribute set -// as requested. -func (s Style) Bold(on bool) Style { - return s.setAttrs(Style(AttrBold), on) -} - -// Blink returns a new style based on s, with the blink attribute set -// as requested. -func (s Style) Blink(on bool) Style { - return s.setAttrs(Style(AttrBlink), on) -} - -// Dim returns a new style based on s, with the dim attribute set -// as requested. -func (s Style) Dim(on bool) Style { - return s.setAttrs(Style(AttrDim), on) -} - -// Reverse returns a new style based on s, with the reverse attribute set -// as requested. (Reverse usually changes the foreground and background -// colors.) -func (s Style) Reverse(on bool) Style { - return s.setAttrs(Style(AttrReverse), on) -} - -// Underline returns a new style based on s, with the underline attribute set -// as requested. -func (s Style) Underline(on bool) Style { - return s.setAttrs(Style(AttrUnderline), on) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/.gitignore b/vendor/github.com/gdamore/tcell/terminfo/.gitignore deleted file mode 100644 index 74f3c04f..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mkinfo diff --git a/vendor/github.com/gdamore/tcell/terminfo/README.md b/vendor/github.com/gdamore/tcell/terminfo/README.md deleted file mode 100644 index 20ae937f..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/README.md +++ /dev/null @@ -1,25 +0,0 @@ -This package represents the parent for all terminals. - -In older versions of tcell we had (a couple of) different -external file formats for the terminal database. Those are -now removed. All terminal definitions are supplied by -one of two methods: - -1. Compiled Go code - -2. For systems with terminfo and infocmp, dynamically - generated at runtime. - -The Go code can be generated using the mkinfo utility in -this directory. The database entry should be generated -into a package in a directory named as the first character -of the package name. (This permits us to group them all -without having a huge directory of little packages.) - -It may be desirable to add new packages to the extended -package, or -- rarely -- the base package. - -Applications which want to have the large set of terminal -descriptions built into the binary can simply import the -extended package. Otherwise a smaller reasonable default -set (the base package) will be included instead. diff --git a/vendor/github.com/gdamore/tcell/terminfo/TERMINALS.md b/vendor/github.com/gdamore/tcell/terminfo/TERMINALS.md deleted file mode 100644 index 85c1e61c..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/TERMINALS.md +++ /dev/null @@ -1,7 +0,0 @@ -TERMINALS -========= - -The best way to populate terminals on Debian is to install ncurses, -ncurses-term, screen, tmux, rxvt-unicode, and dvtm. This populates the -the terminfo database so that we can have a reasonable set of starting -terminals. diff --git a/vendor/github.com/gdamore/tcell/terminfo/a/adm3a/term.go b/vendor/github.com/gdamore/tcell/terminfo/a/adm3a/term.go deleted file mode 100644 index dd8cf0f4..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/a/adm3a/term.go +++ /dev/null @@ -1,25 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package adm3a - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // lsi adm3a - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "adm3a", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1a$<1/>", - PadChar: "\x00", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\n", - KeyRight: "\f", - KeyLeft: "\b", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/a/aixterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/a/aixterm/term.go deleted file mode 100644 index 9dc082c8..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/a/aixterm/term.go +++ /dev/null @@ -1,81 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package aixterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // IBM Aixterm Terminal Emulator - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "aixterm", - Columns: 80, - Lines: 25, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[0;10m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "jjkkllmmnnqqttuuvvwwxx", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[139q", - KeyDelete: "\x1b[P", - KeyBackspace: "\b", - KeyHome: "\x1b[H", - KeyEnd: "\x1b[146q", - KeyPgUp: "\x1b[150q", - KeyPgDn: "\x1b[154q", - KeyF1: "\x1b[001q", - KeyF2: "\x1b[002q", - KeyF3: "\x1b[003q", - KeyF4: "\x1b[004q", - KeyF5: "\x1b[005q", - KeyF6: "\x1b[006q", - KeyF7: "\x1b[007q", - KeyF8: "\x1b[008q", - KeyF9: "\x1b[009q", - KeyF10: "\x1b[010q", - KeyF11: "\x1b[011q", - KeyF12: "\x1b[012q", - KeyF13: "\x1b[013q", - KeyF14: "\x1b[014q", - KeyF15: "\x1b[015q", - KeyF16: "\x1b[016q", - KeyF17: "\x1b[017q", - KeyF18: "\x1b[018q", - KeyF19: "\x1b[019q", - KeyF20: "\x1b[020q", - KeyF21: "\x1b[021q", - KeyF22: "\x1b[022q", - KeyF23: "\x1b[023q", - KeyF24: "\x1b[024q", - KeyF25: "\x1b[025q", - KeyF26: "\x1b[026q", - KeyF27: "\x1b[027q", - KeyF28: "\x1b[028q", - KeyF29: "\x1b[029q", - KeyF30: "\x1b[030q", - KeyF31: "\x1b[031q", - KeyF32: "\x1b[032q", - KeyF33: "\x1b[033q", - KeyF34: "\x1b[034q", - KeyF35: "\x1b[035q", - KeyF36: "\x1b[036q", - KeyClear: "\x1b[144q", - KeyBacktab: "\x1b[Z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/a/alacritty/term.go b/vendor/github.com/gdamore/tcell/terminfo/a/alacritty/term.go deleted file mode 100644 index e65a8833..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/a/alacritty/term.go +++ /dev/null @@ -1,156 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package alacritty - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // alacritty terminal emulator - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "alacritty", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/a/ansi/term.go b/vendor/github.com/gdamore/tcell/terminfo/a/ansi/term.go deleted file mode 100644 index 4500c17c..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/a/ansi/term.go +++ /dev/null @@ -1,41 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package ansi - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // ansi/pc-term compatible with color - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "ansi", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[0;10m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", - EnterAcs: "\x1b[11m", - ExitAcs: "\x1b[10m", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\x1b[D", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[L", - KeyBackspace: "\b", - KeyHome: "\x1b[H", - KeyBacktab: "\x1b[Z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/a/aterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/a/aterm/term.go deleted file mode 100644 index 429d29e6..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/a/aterm/term.go +++ /dev/null @@ -1,110 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package aterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // AfterStep terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "aterm", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/b/beterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/b/beterm/term.go deleted file mode 100644 index 184d0467..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/b/beterm/term.go +++ /dev/null @@ -1,54 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package beterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // BeOS Terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "beterm", - Columns: 80, - Lines: 25, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[0;10m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?4h", - ExitKeypad: "\x1b[?4l", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[16~", - KeyF7: "\x1b[17~", - KeyF8: "\x1b[18~", - KeyF9: "\x1b[19~", - KeyF10: "\x1b[20~", - KeyF11: "\x1b[21~", - KeyF12: "\x1b[22~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/b/bsdos_pc/term.go b/vendor/github.com/gdamore/tcell/terminfo/b/bsdos_pc/term.go deleted file mode 100644 index 3174cc7d..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/b/bsdos_pc/term.go +++ /dev/null @@ -1,42 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package bsdos_pc - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // IBM PC BSD/OS Console - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "bsdos-pc", - Columns: 80, - Lines: 25, - Colors: 8, - Bell: "\a", - Clear: "\x1bc", - AttrOff: "\x1b[0;10m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", - EnterAcs: "\x1b[11m", - ExitAcs: "\x1b[10m", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[L", - KeyBackspace: "\b", - KeyHome: "\x1b[H", - KeyPgUp: "\x1b[I", - KeyPgDn: "\x1b[G", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/base/base.go b/vendor/github.com/gdamore/tcell/terminfo/base/base.go deleted file mode 100644 index 49e6c013..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/base/base.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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. - -// This is just a "minimalist" set of the base terminal descriptions. -// It should be sufficient for most applications. - -// Package base contains the base terminal descriptions that are likely -// to be needed by any stock application. It is imported by default in the -// terminfo package, so terminal types listed here will be available to any -// tcell application. -package base - -import ( - // The following imports just register themselves -- - // thse are the terminal types we aggregate in this package. - _ "github.com/gdamore/tcell/terminfo/a/ansi" - _ "github.com/gdamore/tcell/terminfo/v/vt100" - _ "github.com/gdamore/tcell/terminfo/v/vt102" - _ "github.com/gdamore/tcell/terminfo/v/vt220" - _ "github.com/gdamore/tcell/terminfo/x/xterm" -) diff --git a/vendor/github.com/gdamore/tcell/terminfo/c/cygwin/term.go b/vendor/github.com/gdamore/tcell/terminfo/c/cygwin/term.go deleted file mode 100644 index e734ef5c..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/c/cygwin/term.go +++ /dev/null @@ -1,63 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package cygwin - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // ansi emulation for Cygwin - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "cygwin", - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - AttrOff: "\x1b[0;10m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", - EnterAcs: "\x1b[11m", - ExitAcs: "\x1b[10m", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[[A", - KeyF2: "\x1b[[B", - KeyF3: "\x1b[[C", - KeyF4: "\x1b[[D", - KeyF5: "\x1b[[E", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/d/d200/term.go b/vendor/github.com/gdamore/tcell/terminfo/d/d200/term.go deleted file mode 100644 index 5950d5c8..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/d/d200/term.go +++ /dev/null @@ -1,97 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package d200 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Data General DASHER D200 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "d200", - Aliases: []string{"d200-dg"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\f", - AttrOff: "\x0f\x15\x1d\x1eE", - Underline: "\x14", - Bold: "\x1eD\x14", - Dim: "\x1c", - Blink: "\x0e", - Reverse: "\x1eD", - PadChar: "\x00", - SetCursor: "\x10%p2%c%p1%c", - CursorBack1: "\x19", - CursorUp1: "\x17", - KeyUp: "\x17", - KeyDown: "\x1a", - KeyRight: "\x18", - KeyLeft: "\x19", - KeyHome: "\b", - KeyF1: "\x1eq", - KeyF2: "\x1er", - KeyF3: "\x1es", - KeyF4: "\x1et", - KeyF5: "\x1eu", - KeyF6: "\x1ev", - KeyF7: "\x1ew", - KeyF8: "\x1ex", - KeyF9: "\x1ey", - KeyF10: "\x1ez", - KeyF11: "\x1e{", - KeyF12: "\x1e|", - KeyF13: "\x1e}", - KeyF14: "\x1e~", - KeyF15: "\x1ep", - KeyF16: "\x1ea", - KeyF17: "\x1eb", - KeyF18: "\x1ec", - KeyF19: "\x1ed", - KeyF20: "\x1ee", - KeyF21: "\x1ef", - KeyF22: "\x1eg", - KeyF23: "\x1eh", - KeyF24: "\x1ei", - KeyF25: "\x1ej", - KeyF26: "\x1ek", - KeyF27: "\x1el", - KeyF28: "\x1em", - KeyF29: "\x1en", - KeyF30: "\x1e`", - KeyF31: "\x1e1", - KeyF32: "\x1e2", - KeyF33: "\x1e3", - KeyF34: "\x1e4", - KeyF35: "\x1e5", - KeyF36: "\x1e6", - KeyF37: "\x1e7", - KeyF38: "\x1e8", - KeyF39: "\x1e9", - KeyF40: "\x1e:", - KeyF41: "\x1e;", - KeyF42: "\x1e<", - KeyF43: "\x1e=", - KeyF44: "\x1e>", - KeyF45: "\x1e0", - KeyF46: "\x1e!", - KeyF47: "\x1e\"", - KeyF48: "\x1e#", - KeyF49: "\x1e$", - KeyF50: "\x1e%%", - KeyF51: "\x1e&", - KeyF52: "\x1e'", - KeyF53: "\x1e(", - KeyF54: "\x1e)", - KeyF55: "\x1e*", - KeyF56: "\x1e+", - KeyF57: "\x1e,", - KeyF58: "\x1e-", - KeyF59: "\x1e.", - KeyF60: "\x1e ", - KeyClear: "\f", - KeyShfLeft: "\x1e\x19", - KeyShfRight: "\x1e\x18", - KeyShfHome: "\x1e\b", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/d/d210/term.go b/vendor/github.com/gdamore/tcell/terminfo/d/d210/term.go deleted file mode 100644 index 13a56cce..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/d/d210/term.go +++ /dev/null @@ -1,95 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package d210 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Data General DASHER D210 series - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "d210", - Aliases: []string{"d214"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[2J", - AttrOff: "\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[4;7m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyHome: "\x1b[H", - KeyF1: "\x1b[001z", - KeyF2: "\x1b[002z", - KeyF3: "\x1b[003z", - KeyF4: "\x1b[004z", - KeyF5: "\x1b[005z", - KeyF6: "\x1b[006z", - KeyF7: "\x1b[007z", - KeyF8: "\x1b[008z", - KeyF9: "\x1b[009z", - KeyF10: "\x1b[010z", - KeyF11: "\x1b[011z", - KeyF12: "\x1b[012z", - KeyF13: "\x1b[013z", - KeyF14: "\x1b[014z", - KeyF15: "\x1b[000z", - KeyF16: "\x1b[101z", - KeyF17: "\x1b[102z", - KeyF18: "\x1b[103z", - KeyF19: "\x1b[104z", - KeyF20: "\x1b[105z", - KeyF21: "\x1b[106z", - KeyF22: "\x1b[107z", - KeyF23: "\x1b[108z", - KeyF24: "\x1b[109z", - KeyF25: "\x1b[110z", - KeyF26: "\x1b[111z", - KeyF27: "\x1b[112z", - KeyF28: "\x1b[113z", - KeyF29: "\x1b[114z", - KeyF30: "\x1b[100z", - KeyF31: "\x1b[201z", - KeyF32: "\x1b[202z", - KeyF33: "\x1b[203z", - KeyF34: "\x1b[204z", - KeyF35: "\x1b[205z", - KeyF36: "\x1b[206z", - KeyF37: "\x1b[207z", - KeyF38: "\x1b[208z", - KeyF39: "\x1b[209z", - KeyF40: "\x1b[210z", - KeyF41: "\x1b[211z", - KeyF42: "\x1b[212z", - KeyF43: "\x1b[213z", - KeyF44: "\x1b[214z", - KeyF45: "\x1b[200z", - KeyF46: "\x1b[301z", - KeyF47: "\x1b[302z", - KeyF48: "\x1b[303z", - KeyF49: "\x1b[304z", - KeyF50: "\x1b[305z", - KeyF51: "\x1b[306z", - KeyF52: "\x1b[307z", - KeyF53: "\x1b[308z", - KeyF54: "\x1b[309z", - KeyF55: "\x1b[310z", - KeyF56: "\x1b[311z", - KeyF57: "\x1b[312z", - KeyF58: "\x1b[313z", - KeyF59: "\x1b[314z", - KeyF60: "\x1b[300z", - KeyPrint: "\x1b[i", - KeyClear: "\x1b[2J", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/d/dtterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/d/dtterm/term.go deleted file mode 100644 index a6366f22..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/d/dtterm/term.go +++ /dev/null @@ -1,67 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package dtterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // CDE desktop terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "dtterm", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyHelp: "\x1b[28~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/dynamic/dynamic.go b/vendor/github.com/gdamore/tcell/terminfo/dynamic/dynamic.go deleted file mode 100644 index 71de2080..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/dynamic/dynamic.go +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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. - -// The dynamic package is used to generate a terminal description dynamically, -// using infocmp. This is really a method of last resort, as the performance -// will be slow, and it requires a working infocmp. But, the hope is that it -// will assist folks who have to deal with a terminal description that isn't -// already built in. This requires infocmp to be in the user's path, and to -// support reasonably the -1 option. - -package dynamic - -import ( - "bytes" - "errors" - "os/exec" - "regexp" - "strconv" - "strings" - - "github.com/gdamore/tcell/terminfo" -) - -type termcap struct { - name string - desc string - aliases []string - bools map[string]bool - nums map[string]int - strs map[string]string -} - -func (tc *termcap) getnum(s string) int { - return (tc.nums[s]) -} - -func (tc *termcap) getflag(s string) bool { - return (tc.bools[s]) -} - -func (tc *termcap) getstr(s string) string { - return (tc.strs[s]) -} - -const ( - none = iota - control - escaped -) - -var errNotAddressable = errors.New("terminal not cursor addressable") - -func unescape(s string) string { - // Various escapes are in \x format. Control codes are - // encoded as ^M (carat followed by ASCII equivalent). - // escapes are: \e, \E - escape - // \0 NULL, \n \l \r \t \b \f \s for equivalent C escape. - buf := &bytes.Buffer{} - esc := none - - for i := 0; i < len(s); i++ { - c := s[i] - switch esc { - case none: - switch c { - case '\\': - esc = escaped - case '^': - esc = control - default: - buf.WriteByte(c) - } - case control: - buf.WriteByte(c - 0x40) - esc = none - case escaped: - switch c { - case 'E', 'e': - buf.WriteByte(0x1b) - case '0', '1', '2', '3', '4', '5', '6', '7': - if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' { - buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0')) - i = i + 2 - } else if c == '0' { - buf.WriteByte(0) - } - case 'n': - buf.WriteByte('\n') - case 'r': - buf.WriteByte('\r') - case 't': - buf.WriteByte('\t') - case 'b': - buf.WriteByte('\b') - case 'f': - buf.WriteByte('\f') - case 's': - buf.WriteByte(' ') - default: - buf.WriteByte(c) - } - esc = none - } - } - return (buf.String()) -} - -func (tc *termcap) setupterm(name string) error { - cmd := exec.Command("infocmp", "-1", name) - output := &bytes.Buffer{} - cmd.Stdout = output - - tc.strs = make(map[string]string) - tc.bools = make(map[string]bool) - tc.nums = make(map[string]int) - - if err := cmd.Run(); err != nil { - return err - } - - // Now parse the output. - // We get comment lines (starting with "#"), followed by - // a header line that looks like "||...|" - // then capabilities, one per line, starting with a tab and ending - // with a comma and newline. - lines := strings.Split(output.String(), "\n") - for len(lines) > 0 && strings.HasPrefix(lines[0], "#") { - lines = lines[1:] - } - - // Ditch trailing empty last line - if lines[len(lines)-1] == "" { - lines = lines[:len(lines)-1] - } - header := lines[0] - if strings.HasSuffix(header, ",") { - header = header[:len(header)-1] - } - names := strings.Split(header, "|") - tc.name = names[0] - names = names[1:] - if len(names) > 0 { - tc.desc = names[len(names)-1] - names = names[:len(names)-1] - } - tc.aliases = names - for _, val := range lines[1:] { - if (!strings.HasPrefix(val, "\t")) || - (!strings.HasSuffix(val, ",")) { - return (errors.New("malformed infocmp: " + val)) - } - - val = val[1:] - val = val[:len(val)-1] - - if k := strings.SplitN(val, "=", 2); len(k) == 2 { - tc.strs[k[0]] = unescape(k[1]) - } else if k := strings.SplitN(val, "#", 2); len(k) == 2 { - u, err := strconv.ParseUint(k[1], 0, 0) - if err != nil { - return (err) - } - tc.nums[k[0]] = int(u) - } else { - tc.bools[val] = true - } - } - return nil -} - -// LoadTerminfo creates a Terminfo by for named terminal by attempting to parse -// the output from infocmp. This returns the terminfo entry, a description of -// the terminal, and either nil or an error. -func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) { - var tc termcap - if err := tc.setupterm(name); err != nil { - if err != nil { - return nil, "", err - } - } - t := &terminfo.Terminfo{} - // If this is an alias record, then just emit the alias - t.Name = tc.name - if t.Name != name { - return t, "", nil - } - t.Aliases = tc.aliases - t.Colors = tc.getnum("colors") - t.Columns = tc.getnum("cols") - t.Lines = tc.getnum("lines") - t.Bell = tc.getstr("bel") - t.Clear = tc.getstr("clear") - t.EnterCA = tc.getstr("smcup") - t.ExitCA = tc.getstr("rmcup") - t.ShowCursor = tc.getstr("cnorm") - t.HideCursor = tc.getstr("civis") - t.AttrOff = tc.getstr("sgr0") - t.Underline = tc.getstr("smul") - t.Bold = tc.getstr("bold") - t.Blink = tc.getstr("blink") - t.Dim = tc.getstr("dim") - t.Reverse = tc.getstr("rev") - t.EnterKeypad = tc.getstr("smkx") - t.ExitKeypad = tc.getstr("rmkx") - t.SetFg = tc.getstr("setaf") - t.SetBg = tc.getstr("setab") - t.SetCursor = tc.getstr("cup") - t.CursorBack1 = tc.getstr("cub1") - t.CursorUp1 = tc.getstr("cuu1") - t.KeyF1 = tc.getstr("kf1") - t.KeyF2 = tc.getstr("kf2") - t.KeyF3 = tc.getstr("kf3") - t.KeyF4 = tc.getstr("kf4") - t.KeyF5 = tc.getstr("kf5") - t.KeyF6 = tc.getstr("kf6") - t.KeyF7 = tc.getstr("kf7") - t.KeyF8 = tc.getstr("kf8") - t.KeyF9 = tc.getstr("kf9") - t.KeyF10 = tc.getstr("kf10") - t.KeyF11 = tc.getstr("kf11") - t.KeyF12 = tc.getstr("kf12") - t.KeyF13 = tc.getstr("kf13") - t.KeyF14 = tc.getstr("kf14") - t.KeyF15 = tc.getstr("kf15") - t.KeyF16 = tc.getstr("kf16") - t.KeyF17 = tc.getstr("kf17") - t.KeyF18 = tc.getstr("kf18") - t.KeyF19 = tc.getstr("kf19") - t.KeyF20 = tc.getstr("kf20") - t.KeyF21 = tc.getstr("kf21") - t.KeyF22 = tc.getstr("kf22") - t.KeyF23 = tc.getstr("kf23") - t.KeyF24 = tc.getstr("kf24") - t.KeyF25 = tc.getstr("kf25") - t.KeyF26 = tc.getstr("kf26") - t.KeyF27 = tc.getstr("kf27") - t.KeyF28 = tc.getstr("kf28") - t.KeyF29 = tc.getstr("kf29") - t.KeyF30 = tc.getstr("kf30") - t.KeyF31 = tc.getstr("kf31") - t.KeyF32 = tc.getstr("kf32") - t.KeyF33 = tc.getstr("kf33") - t.KeyF34 = tc.getstr("kf34") - t.KeyF35 = tc.getstr("kf35") - t.KeyF36 = tc.getstr("kf36") - t.KeyF37 = tc.getstr("kf37") - t.KeyF38 = tc.getstr("kf38") - t.KeyF39 = tc.getstr("kf39") - t.KeyF40 = tc.getstr("kf40") - t.KeyF41 = tc.getstr("kf41") - t.KeyF42 = tc.getstr("kf42") - t.KeyF43 = tc.getstr("kf43") - t.KeyF44 = tc.getstr("kf44") - t.KeyF45 = tc.getstr("kf45") - t.KeyF46 = tc.getstr("kf46") - t.KeyF47 = tc.getstr("kf47") - t.KeyF48 = tc.getstr("kf48") - t.KeyF49 = tc.getstr("kf49") - t.KeyF50 = tc.getstr("kf50") - t.KeyF51 = tc.getstr("kf51") - t.KeyF52 = tc.getstr("kf52") - t.KeyF53 = tc.getstr("kf53") - t.KeyF54 = tc.getstr("kf54") - t.KeyF55 = tc.getstr("kf55") - t.KeyF56 = tc.getstr("kf56") - t.KeyF57 = tc.getstr("kf57") - t.KeyF58 = tc.getstr("kf58") - t.KeyF59 = tc.getstr("kf59") - t.KeyF60 = tc.getstr("kf60") - t.KeyF61 = tc.getstr("kf61") - t.KeyF62 = tc.getstr("kf62") - t.KeyF63 = tc.getstr("kf63") - t.KeyF64 = tc.getstr("kf64") - t.KeyInsert = tc.getstr("kich1") - t.KeyDelete = tc.getstr("kdch1") - t.KeyBackspace = tc.getstr("kbs") - t.KeyHome = tc.getstr("khome") - t.KeyEnd = tc.getstr("kend") - t.KeyUp = tc.getstr("kcuu1") - t.KeyDown = tc.getstr("kcud1") - t.KeyRight = tc.getstr("kcuf1") - t.KeyLeft = tc.getstr("kcub1") - t.KeyPgDn = tc.getstr("knp") - t.KeyPgUp = tc.getstr("kpp") - t.KeyBacktab = tc.getstr("kcbt") - t.KeyExit = tc.getstr("kext") - t.KeyCancel = tc.getstr("kcan") - t.KeyPrint = tc.getstr("kprt") - t.KeyHelp = tc.getstr("khlp") - t.KeyClear = tc.getstr("kclr") - t.AltChars = tc.getstr("acsc") - t.EnterAcs = tc.getstr("smacs") - t.ExitAcs = tc.getstr("rmacs") - t.EnableAcs = tc.getstr("enacs") - t.Mouse = tc.getstr("kmous") - t.KeyShfRight = tc.getstr("kRIT") - t.KeyShfLeft = tc.getstr("kLFT") - t.KeyShfHome = tc.getstr("kHOM") - t.KeyShfEnd = tc.getstr("kEND") - - // Terminfo lacks descriptions for a bunch of modified keys, - // but modern XTerm and emulators often have them. Let's add them, - // if the shifted right and left arrows are defined. - if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { - t.KeyShfUp = "\x1b[1;2A" - t.KeyShfDown = "\x1b[1;2B" - t.KeyMetaUp = "\x1b[1;9A" - t.KeyMetaDown = "\x1b[1;9B" - t.KeyMetaRight = "\x1b[1;9C" - t.KeyMetaLeft = "\x1b[1;9D" - t.KeyAltUp = "\x1b[1;3A" - t.KeyAltDown = "\x1b[1;3B" - t.KeyAltRight = "\x1b[1;3C" - t.KeyAltLeft = "\x1b[1;3D" - t.KeyCtrlUp = "\x1b[1;5A" - t.KeyCtrlDown = "\x1b[1;5B" - t.KeyCtrlRight = "\x1b[1;5C" - t.KeyCtrlLeft = "\x1b[1;5D" - t.KeyAltShfUp = "\x1b[1;4A" - t.KeyAltShfDown = "\x1b[1;4B" - t.KeyAltShfRight = "\x1b[1;4C" - t.KeyAltShfLeft = "\x1b[1;4D" - - t.KeyMetaShfUp = "\x1b[1;10A" - t.KeyMetaShfDown = "\x1b[1;10B" - t.KeyMetaShfRight = "\x1b[1;10C" - t.KeyMetaShfLeft = "\x1b[1;10D" - - t.KeyCtrlShfUp = "\x1b[1;6A" - t.KeyCtrlShfDown = "\x1b[1;6B" - t.KeyCtrlShfRight = "\x1b[1;6C" - t.KeyCtrlShfLeft = "\x1b[1;6D" - } - // And also for Home and End - if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" { - t.KeyCtrlHome = "\x1b[1;5H" - t.KeyCtrlEnd = "\x1b[1;5F" - t.KeyAltHome = "\x1b[1;9H" - t.KeyAltEnd = "\x1b[1;9F" - t.KeyCtrlShfHome = "\x1b[1;6H" - t.KeyCtrlShfEnd = "\x1b[1;6F" - t.KeyAltShfHome = "\x1b[1;4H" - t.KeyAltShfEnd = "\x1b[1;4F" - t.KeyMetaShfHome = "\x1b[1;10H" - t.KeyMetaShfEnd = "\x1b[1;10F" - } - - // And the same thing for rxvt and workalikes (Eterm, aterm, etc.) - // It seems that urxvt at least send escaped as ALT prefix for these, - // although some places seem to indicate a separate ALT key sesquence. - if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { - t.KeyShfUp = "\x1b[a" - t.KeyShfDown = "\x1b[b" - t.KeyCtrlUp = "\x1b[Oa" - t.KeyCtrlDown = "\x1b[Ob" - t.KeyCtrlRight = "\x1b[Oc" - t.KeyCtrlLeft = "\x1b[Od" - } - if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { - t.KeyCtrlHome = "\x1b[7^" - t.KeyCtrlEnd = "\x1b[8^" - } - - // If the kmous entry is present, then we need to record the - // the codes to enter and exit mouse mode. Sadly, this is not - // part of the terminfo databases anywhere that I've found, but - // is an extension. The escapedape codes are documented in the XTerm - // manual, and all terminals that have kmous are expected to - // use these same codes, unless explicitly configured otherwise - // vi XM. Note that in any event, we only known how to parse either - // x11 or SGR mouse events -- if your terminal doesn't support one - // of these two forms, you maybe out of luck. - t.MouseMode = tc.getstr("XM") - if t.Mouse != "" && t.MouseMode == "" { - // we anticipate that all xterm mouse tracking compatible - // terminals understand mouse tracking (1000), but we hope - // that those that don't understand any-event tracking (1003) - // will at least ignore it. Likewise we hope that terminals - // that don't understand SGR reporting (1006) just ignore it. - t.MouseMode = "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;" + - "\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c" - } - - // We only support colors in ANSI 8 or 256 color mode. - if t.Colors < 8 || t.SetFg == "" { - t.Colors = 0 - } - if t.SetCursor == "" { - return nil, "", errNotAddressable - } - - // For padding, we lookup the pad char. If that isn't present, - // and npc is *not* set, then we assume a null byte. - t.PadChar = tc.getstr("pad") - if t.PadChar == "" { - if !tc.getflag("npc") { - t.PadChar = "\u0000" - } - } - - // For terminals that use "standard" SGR sequences, lets combine the - // foreground and background together. - if strings.HasPrefix(t.SetFg, "\x1b[") && - strings.HasPrefix(t.SetBg, "\x1b[") && - strings.HasSuffix(t.SetFg, "m") && - strings.HasSuffix(t.SetBg, "m") { - fg := t.SetFg[:len(t.SetFg)-1] - r := regexp.MustCompile("%p1") - bg := r.ReplaceAllString(t.SetBg[2:], "%p2") - t.SetFgBg = fg + ";" + bg - } - - return t, tc.desc, nil -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/e/emacs/term.go b/vendor/github.com/gdamore/tcell/terminfo/e/emacs/term.go deleted file mode 100644 index 91418b64..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/e/emacs/term.go +++ /dev/null @@ -1,60 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package emacs - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // gnu emacs term.el terminal emulation - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "eterm", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - AttrOff: "\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - }) - - // Emacs term.el terminal emulator term-protocol-version 0.96 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "eterm-color", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[%p1%{30}%+%dm", - SetBg: "\x1b[%p1%'('%+%dm", - SetFgBg: "\x1b[%p1%{30}%+%d;%p2%'('%+%dm", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/e/eterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/e/eterm/term.go deleted file mode 100644 index e5bd99fd..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/e/eterm/term.go +++ /dev/null @@ -1,309 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package eterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Eterm with xterm-style color support (X Window System) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "Eterm", - Aliases: []string{"Eterm-color"}, - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyHelp: "\x1b[28~", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // Eterm with 88 colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "Eterm-88color", - Columns: 80, - Lines: 24, - Colors: 88, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyHelp: "\x1b[28~", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // Eterm with xterm 256-colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "Eterm-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyHelp: "\x1b[28~", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/extended/extended.go b/vendor/github.com/gdamore/tcell/terminfo/extended/extended.go deleted file mode 100644 index cc0af120..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/extended/extended.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 extended contains an extended set of terminal descriptions. -// Applications desiring to have a better chance of Just Working by -// default should include this package. This will significantly increase -// the size of the program. -package extended - -import ( - // The following imports just register themselves -- - // these are the terminal types we aggregate in this package. - _ "github.com/gdamore/tcell/terminfo/a/adm3a" - _ "github.com/gdamore/tcell/terminfo/a/aixterm" - _ "github.com/gdamore/tcell/terminfo/a/alacritty" - _ "github.com/gdamore/tcell/terminfo/a/ansi" - _ "github.com/gdamore/tcell/terminfo/a/aterm" - _ "github.com/gdamore/tcell/terminfo/b/beterm" - _ "github.com/gdamore/tcell/terminfo/b/bsdos_pc" - _ "github.com/gdamore/tcell/terminfo/c/cygwin" - _ "github.com/gdamore/tcell/terminfo/d/d200" - _ "github.com/gdamore/tcell/terminfo/d/d210" - _ "github.com/gdamore/tcell/terminfo/d/dtterm" - _ "github.com/gdamore/tcell/terminfo/e/emacs" - _ "github.com/gdamore/tcell/terminfo/e/eterm" - _ "github.com/gdamore/tcell/terminfo/g/gnome" - _ "github.com/gdamore/tcell/terminfo/h/hpterm" - _ "github.com/gdamore/tcell/terminfo/h/hz1500" - _ "github.com/gdamore/tcell/terminfo/k/konsole" - _ "github.com/gdamore/tcell/terminfo/k/kterm" - _ "github.com/gdamore/tcell/terminfo/l/linux" - _ "github.com/gdamore/tcell/terminfo/p/pcansi" - _ "github.com/gdamore/tcell/terminfo/r/rxvt" - _ "github.com/gdamore/tcell/terminfo/s/screen" - _ "github.com/gdamore/tcell/terminfo/s/simpleterm" - _ "github.com/gdamore/tcell/terminfo/s/sun" - _ "github.com/gdamore/tcell/terminfo/t/termite" - _ "github.com/gdamore/tcell/terminfo/t/tvi910" - _ "github.com/gdamore/tcell/terminfo/t/tvi912" - _ "github.com/gdamore/tcell/terminfo/t/tvi921" - _ "github.com/gdamore/tcell/terminfo/t/tvi925" - _ "github.com/gdamore/tcell/terminfo/t/tvi950" - _ "github.com/gdamore/tcell/terminfo/t/tvi970" - _ "github.com/gdamore/tcell/terminfo/v/vt100" - _ "github.com/gdamore/tcell/terminfo/v/vt102" - _ "github.com/gdamore/tcell/terminfo/v/vt220" - _ "github.com/gdamore/tcell/terminfo/v/vt320" - _ "github.com/gdamore/tcell/terminfo/v/vt400" - _ "github.com/gdamore/tcell/terminfo/v/vt420" - _ "github.com/gdamore/tcell/terminfo/v/vt52" - _ "github.com/gdamore/tcell/terminfo/w/wy50" - _ "github.com/gdamore/tcell/terminfo/w/wy60" - _ "github.com/gdamore/tcell/terminfo/w/wy99_ansi" - _ "github.com/gdamore/tcell/terminfo/x/xfce" - _ "github.com/gdamore/tcell/terminfo/x/xnuppc" - _ "github.com/gdamore/tcell/terminfo/x/xterm" - _ "github.com/gdamore/tcell/terminfo/x/xterm_kitty" -) diff --git a/vendor/github.com/gdamore/tcell/terminfo/g/gnome/term.go b/vendor/github.com/gdamore/tcell/terminfo/g/gnome/term.go deleted file mode 100644 index 08b913eb..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/g/gnome/term.go +++ /dev/null @@ -1,306 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package gnome - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // GNOME Terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "gnome", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1bO1;2P", - KeyF14: "\x1bO1;2Q", - KeyF15: "\x1bO1;2R", - KeyF16: "\x1bO1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1bO1;5P", - KeyF26: "\x1bO1;5Q", - KeyF27: "\x1bO1;5R", - KeyF28: "\x1bO1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1bO1;6P", - KeyF38: "\x1bO1;6Q", - KeyF39: "\x1bO1;6R", - KeyF40: "\x1bO1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1bO1;3P", - KeyF50: "\x1bO1;3Q", - KeyF51: "\x1bO1;3R", - KeyF52: "\x1bO1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1bO1;4P", - KeyF62: "\x1bO1;4Q", - KeyF63: "\x1bO1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) - - // GNOME Terminal with xterm 256-colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "gnome-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[<", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/gen.sh b/vendor/github.com/gdamore/tcell/terminfo/gen.sh deleted file mode 100644 index 2fc06112..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/gen.sh +++ /dev/null @@ -1,18 +0,0 @@ -while read line -do - case "$line" in - *'|'*) - alias=${line#*|} - line=${line%|*} - ;; - *) - alias=${line%%,*} - ;; - esac - - alias=${alias//-/_} - direc=${alias:0:1} - - mkdir -p ${direc}/${alias} - go run mkinfo.go -P ${alias} -go ${direc}/${alias}/term.go ${line//,/ } -done < models.txt diff --git a/vendor/github.com/gdamore/tcell/terminfo/h/hpterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/h/hpterm/term.go deleted file mode 100644 index 5e73e044..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/h/hpterm/term.go +++ /dev/null @@ -1,50 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package hpterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // hp X11 terminal emulator - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "hpterm", - Aliases: []string{"X-hpterm"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b&a0y0C\x1bJ", - AttrOff: "\x1b&d@\x0f", - Underline: "\x1b&dD", - Bold: "\x1b&dB", - Dim: "\x1b&dH", - Reverse: "\x1b&dB", - EnterKeypad: "\x1b&s1A", - ExitKeypad: "\x1b&s0A", - PadChar: "\x00", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - SetCursor: "\x1b&a%p1%dy%p2%dC", - CursorBack1: "\b", - CursorUp1: "\x1bA", - KeyUp: "\x1bA", - KeyDown: "\x1bB", - KeyRight: "\x1bC", - KeyLeft: "\x1bD", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bP", - KeyBackspace: "\b", - KeyHome: "\x1bh", - KeyPgUp: "\x1bV", - KeyPgDn: "\x1bU", - KeyF1: "\x1bp", - KeyF2: "\x1bq", - KeyF3: "\x1br", - KeyF4: "\x1bs", - KeyF5: "\x1bt", - KeyF6: "\x1bu", - KeyF7: "\x1bv", - KeyF8: "\x1bw", - KeyClear: "\x1bJ", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/h/hz1500/term.go b/vendor/github.com/gdamore/tcell/terminfo/h/hz1500/term.go deleted file mode 100644 index 3ad18109..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/h/hz1500/term.go +++ /dev/null @@ -1,26 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package hz1500 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // hazeltine 1500 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "hz1500", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "~\x1c", - PadChar: "\x00", - SetCursor: "~\x11%p2%p2%?%{30}%>%t%' '%+%;%'`'%+%c%p1%'`'%+%c", - CursorBack1: "\b", - CursorUp1: "~\f", - KeyUp: "~\f", - KeyDown: "\n", - KeyRight: "\x10", - KeyLeft: "\b", - KeyHome: "~\x12", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/k/konsole/term.go b/vendor/github.com/gdamore/tcell/terminfo/k/konsole/term.go deleted file mode 100644 index 21cd72ae..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/k/konsole/term.go +++ /dev/null @@ -1,304 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package konsole - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // KDE console window - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "konsole", - Columns: 80, - Lines: 24, - Colors: 8, - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1bO2P", - KeyF14: "\x1bO2Q", - KeyF15: "\x1bO2R", - KeyF16: "\x1bO2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1bO5P", - KeyF26: "\x1bO5Q", - KeyF27: "\x1bO5R", - KeyF28: "\x1bO5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1bO6P", - KeyF38: "\x1bO6Q", - KeyF39: "\x1bO6R", - KeyF40: "\x1bO6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1bO3P", - KeyF50: "\x1bO3Q", - KeyF51: "\x1bO3R", - KeyF52: "\x1bO3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1bO4P", - KeyF62: "\x1bO4Q", - KeyF63: "\x1bO4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) - - // KDE console window with xterm 256-colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "konsole-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1bO2P", - KeyF14: "\x1bO2Q", - KeyF15: "\x1bO2R", - KeyF16: "\x1bO2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1bO5P", - KeyF26: "\x1bO5Q", - KeyF27: "\x1bO5R", - KeyF28: "\x1bO5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1bO6P", - KeyF38: "\x1bO6Q", - KeyF39: "\x1bO6R", - KeyF40: "\x1bO6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1bO3P", - KeyF50: "\x1bO3Q", - KeyF51: "\x1bO3R", - KeyF52: "\x1bO3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1bO4P", - KeyF62: "\x1bO4Q", - KeyF63: "\x1bO4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/k/kterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/k/kterm/term.go deleted file mode 100644 index f4478404..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/k/kterm/term.go +++ /dev/null @@ -1,67 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package kterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // kterm kanji terminal emulator (X window system) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "kterm", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aajjkkllmmnnooppqqrrssttuuvvwwxx~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/l/linux/term.go b/vendor/github.com/gdamore/tcell/terminfo/l/linux/term.go deleted file mode 100644 index 1702c363..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/l/linux/term.go +++ /dev/null @@ -1,69 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package linux - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // linux console - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "linux", - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - ShowCursor: "\x1b[?25h\x1b[?0c", - HideCursor: "\x1b[?25l\x1b[?1c", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "++,,--..00__``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}c~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[[A", - KeyF2: "\x1b[[B", - KeyF3: "\x1b[[C", - KeyF4: "\x1b[[D", - KeyF5: "\x1b[[E", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyBacktab: "\x1b[Z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/models.txt b/vendor/github.com/gdamore/tcell/terminfo/models.txt deleted file mode 100644 index 7ed2adb7..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/models.txt +++ /dev/null @@ -1,43 +0,0 @@ -adm3a -aixterm -ansi -aterm -beterm -bsdos-pc -cygwin -d200 -d210 -dtterm -eterm,eterm-color|emacs -Eterm,Eterm-88color,Eterm-256color|eterm -gnome,gnome-256color -hpterm -hz1500 -konsole,konsole-256color -kterm -linux -pcansi -rxvt,rxvt-256color,rxvt-88color,rxvt-unicode,rxvt-unicode-256color -screen,screen-256color -st,st-256color|simpleterm -sun,sun-color -termite -tvi910 -tvi912 -tvi921 -tvi925 -tvi950 -tvi970 -vt52 -vt100 -vt102 -vt220 -vt320 -vt400 -vt420 -wy50 -wy60 -wy99-ansi,wy99a-ansi -xfce -xnuppc -xterm,xterm-88color,xterm-256color diff --git a/vendor/github.com/gdamore/tcell/terminfo/p/pcansi/term.go b/vendor/github.com/gdamore/tcell/terminfo/p/pcansi/term.go deleted file mode 100644 index 84bc400b..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/p/pcansi/term.go +++ /dev/null @@ -1,39 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package pcansi - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // ibm-pc terminal programs claiming to be ansi - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "pcansi", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[0;10m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "+\x10,\x11-\x18.\x190\xdb`\x04a\xb1f\xf8g\xf1h\xb0j\xd9k\xbfl\xdam\xc0n\xc5o~p\xc4q\xc4r\xc4s_t\xc3u\xb4v\xc1w\xc2x\xb3y\xf3z\xf2{\xe3|\xd8}\x9c~\xfe", - EnterAcs: "\x1b[12m", - ExitAcs: "\x1b[10m", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\x1b[D", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyBackspace: "\b", - KeyHome: "\x1b[H", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/r/rxvt/term.go b/vendor/github.com/gdamore/tcell/terminfo/r/rxvt/term.go deleted file mode 100644 index 776f750e..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/r/rxvt/term.go +++ /dev/null @@ -1,466 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package rxvt - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // rxvt terminal emulator (X Window System) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "rxvt", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // rxvt 2.7.9 with xterm 256-colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "rxvt-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // rxvt 2.7.9 with xterm 88-colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "rxvt-88color", - Columns: 80, - Lines: 24, - Colors: 88, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyF21: "\x1b[23$", - KeyF22: "\x1b[24$", - KeyF23: "\x1b[11^", - KeyF24: "\x1b[12^", - KeyF25: "\x1b[13^", - KeyF26: "\x1b[14^", - KeyF27: "\x1b[15^", - KeyF28: "\x1b[17^", - KeyF29: "\x1b[18^", - KeyF30: "\x1b[19^", - KeyF31: "\x1b[20^", - KeyF32: "\x1b[21^", - KeyF33: "\x1b[23^", - KeyF34: "\x1b[24^", - KeyF35: "\x1b[25^", - KeyF36: "\x1b[26^", - KeyF37: "\x1b[28^", - KeyF38: "\x1b[29^", - KeyF39: "\x1b[31^", - KeyF40: "\x1b[32^", - KeyF41: "\x1b[33^", - KeyF42: "\x1b[34^", - KeyF43: "\x1b[23@", - KeyF44: "\x1b[24@", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // rxvt-unicode terminal (X Window System) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "rxvt-unicode", - Columns: 80, - Lines: 24, - Colors: 88, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[r\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[38;5;%p1%dm", - SetBg: "\x1b[48;5;%p1%dm", - SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm", - AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) - - // rxvt-unicode terminal with 256 colors (X Window System) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "rxvt-unicode-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[r\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - SetFg: "\x1b[38;5;%p1%dm", - SetBg: "\x1b[48;5;%p1%dm", - SetFgBg: "\x1b[38;5;%p1%d;48;5;%p2%dm", - AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[7~", - KeyEnd: "\x1b[8~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1b[11~", - KeyF2: "\x1b[12~", - KeyF3: "\x1b[13~", - KeyF4: "\x1b[14~", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[d", - KeyShfRight: "\x1b[c", - KeyShfUp: "\x1b[a", - KeyShfDown: "\x1b[b", - KeyCtrlLeft: "\x1b[Od", - KeyCtrlRight: "\x1b[Oc", - KeyCtrlUp: "\x1b[Oa", - KeyCtrlDown: "\x1b[Ob", - KeyShfHome: "\x1b[7$", - KeyShfEnd: "\x1b[8$", - KeyCtrlHome: "\x1b[7^", - KeyCtrlEnd: "\x1b[8^", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/s/screen/term.go b/vendor/github.com/gdamore/tcell/terminfo/s/screen/term.go deleted file mode 100644 index ab0d8eed..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/s/screen/term.go +++ /dev/null @@ -1,126 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package screen - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // VT 100/ANSI X3.64 virtual terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "screen", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[34h\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1bM", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyBacktab: "\x1b[Z", - }) - - // GNU Screen with 256 colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "screen-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[34h\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - PadChar: "\x00", - AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1bM", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyBacktab: "\x1b[Z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/s/simpleterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/s/simpleterm/term.go deleted file mode 100644 index ad7c1e81..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/s/simpleterm/term.go +++ /dev/null @@ -1,308 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package simpleterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // simpleterm - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "st", - Aliases: []string{"stterm"}, - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyClear: "\x1b[3;5~", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) - - // simpleterm with 256 colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "st-256color", - Aliases: []string{"stterm-256color"}, - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "+C,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyEnd: "\x1b[4~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyClear: "\x1b[3;5~", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/s/sun/term.go b/vendor/github.com/gdamore/tcell/terminfo/s/sun/term.go deleted file mode 100644 index f83cf957..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/s/sun/term.go +++ /dev/null @@ -1,90 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package sun - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Sun Microsystems Inc. workstation console - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "sun", - Aliases: []string{"sun1", "sun2"}, - Columns: 80, - Lines: 34, - Bell: "\a", - Clear: "\f", - AttrOff: "\x1b[m", - Reverse: "\x1b[7m", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[247z", - KeyDelete: "\xff", - KeyBackspace: "\b", - KeyHome: "\x1b[214z", - KeyEnd: "\x1b[220z", - KeyPgUp: "\x1b[216z", - KeyPgDn: "\x1b[222z", - KeyF1: "\x1b[224z", - KeyF2: "\x1b[225z", - KeyF3: "\x1b[226z", - KeyF4: "\x1b[227z", - KeyF5: "\x1b[228z", - KeyF6: "\x1b[229z", - KeyF7: "\x1b[230z", - KeyF8: "\x1b[231z", - KeyF9: "\x1b[232z", - KeyF10: "\x1b[233z", - KeyF11: "\x1b[234z", - KeyF12: "\x1b[235z", - }) - - // Sun Microsystems Workstation console with color support (IA systems) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "sun-color", - Columns: 80, - Lines: 34, - Colors: 8, - Bell: "\a", - Clear: "\f", - AttrOff: "\x1b[m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[247z", - KeyDelete: "\xff", - KeyBackspace: "\b", - KeyHome: "\x1b[214z", - KeyEnd: "\x1b[220z", - KeyPgUp: "\x1b[216z", - KeyPgDn: "\x1b[222z", - KeyF1: "\x1b[224z", - KeyF2: "\x1b[225z", - KeyF3: "\x1b[226z", - KeyF4: "\x1b[227z", - KeyF5: "\x1b[228z", - KeyF6: "\x1b[229z", - KeyF7: "\x1b[230z", - KeyF8: "\x1b[231z", - KeyF9: "\x1b[232z", - KeyF10: "\x1b[233z", - KeyF11: "\x1b[234z", - KeyF12: "\x1b[235z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/termite/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/termite/term.go deleted file mode 100644 index 62b1c5b8..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/termite/term.go +++ /dev/null @@ -1,154 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package termite - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // VTE-based terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "termite", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi910/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi910/term.go deleted file mode 100644 index 1c5f7a31..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi910/term.go +++ /dev/null @@ -1,39 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi910 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // televideo model 910 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi910", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1a", - AttrOff: "\x1bG0", - Underline: "\x1bG8", - Reverse: "\x1bG4", - PadChar: "\x00", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\n", - KeyRight: "\f", - KeyLeft: "\b", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi912/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi912/term.go deleted file mode 100644 index 5e52f5ce..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi912/term.go +++ /dev/null @@ -1,38 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi912 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // old televideo 912/914/920 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi912", - Aliases: []string{"tvi914", "tvi920"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1a", - Underline: "\x1bl", - PadChar: "\x00", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\n", - KeyRight: "\f", - KeyLeft: "\b", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi921/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi921/term.go deleted file mode 100644 index 1ec02879..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi921/term.go +++ /dev/null @@ -1,34 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi921 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // televideo model 921 with sysline same as page & real vi function - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi921", - Columns: 80, - Lines: 24, - Clear: "\x1a", - ShowCursor: "\x1b.3", - AttrOff: "\x1bG0", - Underline: "\x1bG8", - Reverse: "\x1bG4", - PadChar: "\x00", - EnterAcs: "\x1b$", - ExitAcs: "\x1b%%", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c$<3/>", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\x16", - KeyRight: "\f", - KeyLeft: "\b", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bW", - KeyBackspace: "\b", - KeyClear: "\x1a", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi925/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi925/term.go deleted file mode 100644 index 2020ccb9..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi925/term.go +++ /dev/null @@ -1,43 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi925 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // televideo 925 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi925", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1a", - ShowCursor: "\x1b.4", - AttrOff: "\x1bG0", - Underline: "\x1bG8", - Reverse: "\x1bG4", - PadChar: "\x00", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\x16", - KeyRight: "\f", - KeyLeft: "\b", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bW", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - KeyClear: "\x1a", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi950/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi950/term.go deleted file mode 100644 index fc6f2575..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi950/term.go +++ /dev/null @@ -1,46 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi950 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // televideo 950 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi950", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b*", - AttrOff: "\x1bG0", - Underline: "\x1bG8", - Reverse: "\x1bG4", - PadChar: "\x00", - AltChars: "b\tc\fd\re\ni\v", - EnterAcs: "\x15", - ExitAcs: "\x18", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\x16", - KeyRight: "\f", - KeyLeft: "\b", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bW", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - KeyClear: "\x1b*", - KeyBacktab: "\x1bI", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/t/tvi970/term.go b/vendor/github.com/gdamore/tcell/terminfo/t/tvi970/term.go deleted file mode 100644 index e6a1dfb1..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/t/tvi970/term.go +++ /dev/null @@ -1,40 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package tvi970 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // televideo 970 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "tvi970", - Columns: 80, - Lines: 24, - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?20l\x1b[?7h\x1b[1Q", - AttrOff: "\x1b[m", - Underline: "\x1b[4m", - PadChar: "\x00", - EnterAcs: "\x1b(B", - ExitAcs: "\x1b(B", - SetCursor: "\x1b[%i%p1%d;%p2%df", - CursorBack1: "\b", - CursorUp1: "\x1bM", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyBackspace: "\b", - KeyHome: "\x1b[H", - KeyF1: "\x1b?a", - KeyF2: "\x1b?b", - KeyF3: "\x1b?c", - KeyF4: "\x1b?d", - KeyF5: "\x1b?e", - KeyF6: "\x1b?f", - KeyF7: "\x1b?g", - KeyF8: "\x1b?h", - KeyF9: "\x1b?i", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/terminfo.go b/vendor/github.com/gdamore/tcell/terminfo/terminfo.go deleted file mode 100644 index d0f46e93..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/terminfo.go +++ /dev/null @@ -1,781 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 terminfo - -import ( - "bytes" - "errors" - "fmt" - "io" - "os" - "strconv" - "strings" - "sync" - "time" -) - -var ( - // ErrTermNotFound indicates that a suitable terminal entry could - // not be found. This can result from either not having TERM set, - // or from the TERM failing to support certain minimal functionality, - // in particular absolute cursor addressability (the cup capability) - // is required. For example, legacy "adm3" lacks this capability, - // whereas the slightly newer "adm3a" supports it. This failure - // occurs most often with "dumb". - ErrTermNotFound = errors.New("terminal entry not found") -) - -// Terminfo represents a terminfo entry. Note that we use friendly names -// in Go, but when we write out JSON, we use the same names as terminfo. -// The name, aliases and smous, rmous fields do not come from terminfo directly. -type Terminfo struct { - Name string - Aliases []string - Columns int // cols - Lines int // lines - Colors int // colors - Bell string // bell - Clear string // clear - EnterCA string // smcup - ExitCA string // rmcup - ShowCursor string // cnorm - HideCursor string // civis - AttrOff string // sgr0 - Underline string // smul - Bold string // bold - Blink string // blink - Reverse string // rev - Dim string // dim - EnterKeypad string // smkx - ExitKeypad string // rmkx - SetFg string // setaf - SetBg string // setab - SetCursor string // cup - CursorBack1 string // cub1 - CursorUp1 string // cuu1 - PadChar string // pad - KeyBackspace string // kbs - KeyF1 string // kf1 - KeyF2 string // kf2 - KeyF3 string // kf3 - KeyF4 string // kf4 - KeyF5 string // kf5 - KeyF6 string // kf6 - KeyF7 string // kf7 - KeyF8 string // kf8 - KeyF9 string // kf9 - KeyF10 string // kf10 - KeyF11 string // kf11 - KeyF12 string // kf12 - KeyF13 string // kf13 - KeyF14 string // kf14 - KeyF15 string // kf15 - KeyF16 string // kf16 - KeyF17 string // kf17 - KeyF18 string // kf18 - KeyF19 string // kf19 - KeyF20 string // kf20 - KeyF21 string // kf21 - KeyF22 string // kf22 - KeyF23 string // kf23 - KeyF24 string // kf24 - KeyF25 string // kf25 - KeyF26 string // kf26 - KeyF27 string // kf27 - KeyF28 string // kf28 - KeyF29 string // kf29 - KeyF30 string // kf30 - KeyF31 string // kf31 - KeyF32 string // kf32 - KeyF33 string // kf33 - KeyF34 string // kf34 - KeyF35 string // kf35 - KeyF36 string // kf36 - KeyF37 string // kf37 - KeyF38 string // kf38 - KeyF39 string // kf39 - KeyF40 string // kf40 - KeyF41 string // kf41 - KeyF42 string // kf42 - KeyF43 string // kf43 - KeyF44 string // kf44 - KeyF45 string // kf45 - KeyF46 string // kf46 - KeyF47 string // kf47 - KeyF48 string // kf48 - KeyF49 string // kf49 - KeyF50 string // kf50 - KeyF51 string // kf51 - KeyF52 string // kf52 - KeyF53 string // kf53 - KeyF54 string // kf54 - KeyF55 string // kf55 - KeyF56 string // kf56 - KeyF57 string // kf57 - KeyF58 string // kf58 - KeyF59 string // kf59 - KeyF60 string // kf60 - KeyF61 string // kf61 - KeyF62 string // kf62 - KeyF63 string // kf63 - KeyF64 string // kf64 - KeyInsert string // kich1 - KeyDelete string // kdch1 - KeyHome string // khome - KeyEnd string // kend - KeyHelp string // khlp - KeyPgUp string // kpp - KeyPgDn string // knp - KeyUp string // kcuu1 - KeyDown string // kcud1 - KeyLeft string // kcub1 - KeyRight string // kcuf1 - KeyBacktab string // kcbt - KeyExit string // kext - KeyClear string // kclr - KeyPrint string // kprt - KeyCancel string // kcan - Mouse string // kmous - MouseMode string // XM - AltChars string // acsc - EnterAcs string // smacs - ExitAcs string // rmacs - EnableAcs string // enacs - KeyShfRight string // kRIT - KeyShfLeft string // kLFT - KeyShfHome string // kHOM - KeyShfEnd string // kEND - - // These are non-standard extensions to terminfo. This includes - // true color support, and some additional keys. Its kind of bizarre - // that shifted variants of left and right exist, but not up and down. - // Terminal support for these are going to vary amongst XTerm - // emulations, so don't depend too much on them in your application. - - SetFgBg string // setfgbg - SetFgBgRGB string // setfgbgrgb - SetFgRGB string // setfrgb - SetBgRGB string // setbrgb - KeyShfUp string // shift-up - KeyShfDown string // shift-down - KeyCtrlUp string // ctrl-up - KeyCtrlDown string // ctrl-left - KeyCtrlRight string // ctrl-right - KeyCtrlLeft string // ctrl-left - KeyMetaUp string // meta-up - KeyMetaDown string // meta-left - KeyMetaRight string // meta-right - KeyMetaLeft string // meta-left - KeyAltUp string // alt-up - KeyAltDown string // alt-left - KeyAltRight string // alt-right - KeyAltLeft string // alt-left - KeyCtrlHome string - KeyCtrlEnd string - KeyMetaHome string - KeyMetaEnd string - KeyAltHome string - KeyAltEnd string - KeyAltShfUp string - KeyAltShfDown string - KeyAltShfLeft string - KeyAltShfRight string - KeyMetaShfUp string - KeyMetaShfDown string - KeyMetaShfLeft string - KeyMetaShfRight string - KeyCtrlShfUp string - KeyCtrlShfDown string - KeyCtrlShfLeft string - KeyCtrlShfRight string - KeyCtrlShfHome string - KeyCtrlShfEnd string - KeyAltShfHome string - KeyAltShfEnd string - KeyMetaShfHome string - KeyMetaShfEnd string -} - -type stackElem struct { - s string - i int - isStr bool - isInt bool -} - -type stack []stackElem - -func (st stack) Push(v string) stack { - e := stackElem{ - s: v, - isStr: true, - } - return append(st, e) -} - -func (st stack) Pop() (string, stack) { - v := "" - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isStr { - v = e.s - } else { - v = strconv.Itoa(e.i) - } - } - return v, st -} - -func (st stack) PopInt() (int, stack) { - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isInt { - return e.i, st - } else if e.isStr { - i, _ := strconv.Atoi(e.s) - return i, st - } - } - return 0, st -} - -func (st stack) PopBool() (bool, stack) { - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isStr { - if e.s == "1" { - return true, st - } - return false, st - } else if e.i == 1 { - return true, st - } else { - return false, st - } - } - return false, st -} - -func (st stack) PushInt(i int) stack { - e := stackElem{ - i: i, - isInt: true, - } - return append(st, e) -} - -func (st stack) PushBool(i bool) stack { - if i { - return st.PushInt(1) - } - return st.PushInt(0) -} - -func nextch(s string, index int) (byte, int) { - if index < len(s) { - return s[index], index + 1 - } - return 0, index -} - -// static vars -var svars [26]string - -// paramsBuffer handles some persistent state for TParam. Technically we -// could probably dispense with this, but caching buffer arrays gives us -// a nice little performance boost. Furthermore, we know that TParam is -// rarely (never?) called re-entrantly, so we can just reuse the same -// buffers, making it thread-safe by stashing a lock. -type paramsBuffer struct { - out bytes.Buffer - buf bytes.Buffer - lk sync.Mutex -} - -// Start initializes the params buffer with the initial string data. -// It also locks the paramsBuffer. The caller must call End() when -// finished. -func (pb *paramsBuffer) Start(s string) { - pb.lk.Lock() - pb.out.Reset() - pb.buf.Reset() - pb.buf.WriteString(s) -} - -// End returns the final output from TParam, but it also releases the lock. -func (pb *paramsBuffer) End() string { - s := pb.out.String() - pb.lk.Unlock() - return s -} - -// NextCh returns the next input character to the expander. -func (pb *paramsBuffer) NextCh() (byte, error) { - return pb.buf.ReadByte() -} - -// PutCh "emits" (rather schedules for output) a single byte character. -func (pb *paramsBuffer) PutCh(ch byte) { - pb.out.WriteByte(ch) -} - -// PutString schedules a string for output. -func (pb *paramsBuffer) PutString(s string) { - pb.out.WriteString(s) -} - -var pb = ¶msBuffer{} - -// TParm takes a terminfo parameterized string, such as setaf or cup, and -// evaluates the string, and returns the result with the parameter -// applied. -func (t *Terminfo) TParm(s string, p ...int) string { - var stk stack - var a, b string - var ai, bi int - var ab bool - var dvars [26]string - var params [9]int - - pb.Start(s) - - // make sure we always have 9 parameters -- makes it easier - // later to skip checks - for i := 0; i < len(params) && i < len(p); i++ { - params[i] = p[i] - } - - nest := 0 - - for { - - ch, err := pb.NextCh() - if err != nil { - break - } - - if ch != '%' { - pb.PutCh(ch) - continue - } - - ch, err = pb.NextCh() - if err != nil { - // XXX Error - break - } - - switch ch { - case '%': // quoted % - pb.PutCh(ch) - - case 'i': // increment both parameters (ANSI cup support) - params[0]++ - params[1]++ - - case 'c', 's': - // NB: these, and 'd' below are special cased for - // efficiency. They could be handled by the richer - // format support below, less efficiently. - a, stk = stk.Pop() - pb.PutString(a) - - case 'd': - ai, stk = stk.PopInt() - pb.PutString(strconv.Itoa(ai)) - - case '0', '1', '2', '3', '4', 'x', 'X', 'o', ':': - // This is pretty suboptimal, but this is rarely used. - // None of the mainstream terminals use any of this, - // and it would surprise me if this code is ever - // executed outside of test cases. - f := "%" - if ch == ':' { - ch, _ = pb.NextCh() - } - f += string(ch) - for ch == '+' || ch == '-' || ch == '#' || ch == ' ' { - ch, _ = pb.NextCh() - f += string(ch) - } - for (ch >= '0' && ch <= '9') || ch == '.' { - ch, _ = pb.NextCh() - f += string(ch) - } - switch ch { - case 'd', 'x', 'X', 'o': - ai, stk = stk.PopInt() - pb.PutString(fmt.Sprintf(f, ai)) - case 'c', 's': - a, stk = stk.Pop() - pb.PutString(fmt.Sprintf(f, a)) - } - - case 'p': // push parameter - ch, _ = pb.NextCh() - ai = int(ch - '1') - if ai >= 0 && ai < len(params) { - stk = stk.PushInt(params[ai]) - } else { - stk = stk.PushInt(0) - } - - case 'P': // pop & store variable - ch, _ = pb.NextCh() - if ch >= 'A' && ch <= 'Z' { - svars[int(ch-'A')], stk = stk.Pop() - } else if ch >= 'a' && ch <= 'z' { - dvars[int(ch-'a')], stk = stk.Pop() - } - - case 'g': // recall & push variable - ch, _ = pb.NextCh() - if ch >= 'A' && ch <= 'Z' { - stk = stk.Push(svars[int(ch-'A')]) - } else if ch >= 'a' && ch <= 'z' { - stk = stk.Push(dvars[int(ch-'a')]) - } - - case '\'': // push(char) - ch, _ = pb.NextCh() - pb.NextCh() // must be ' but we don't check - stk = stk.Push(string(ch)) - - case '{': // push(int) - ai = 0 - ch, _ = pb.NextCh() - for ch >= '0' && ch <= '9' { - ai *= 10 - ai += int(ch - '0') - ch, _ = pb.NextCh() - } - // ch must be '}' but no verification - stk = stk.PushInt(ai) - - case 'l': // push(strlen(pop)) - a, stk = stk.Pop() - stk = stk.PushInt(len(a)) - - case '+': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai + bi) - - case '-': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai - bi) - - case '*': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai * bi) - - case '/': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - if bi != 0 { - stk = stk.PushInt(ai / bi) - } else { - stk = stk.PushInt(0) - } - - case 'm': // push(pop mod pop) - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - if bi != 0 { - stk = stk.PushInt(ai % bi) - } else { - stk = stk.PushInt(0) - } - - case '&': // AND - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai & bi) - - case '|': // OR - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai | bi) - - case '^': // XOR - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai ^ bi) - - case '~': // bit complement - ai, stk = stk.PopInt() - stk = stk.PushInt(ai ^ -1) - - case '!': // logical NOT - ai, stk = stk.PopInt() - stk = stk.PushBool(ai != 0) - - case '=': // numeric compare or string compare - b, stk = stk.Pop() - a, stk = stk.Pop() - stk = stk.PushBool(a == b) - - case '>': // greater than, numeric - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushBool(ai > bi) - - case '<': // less than, numeric - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushBool(ai < bi) - - case '?': // start conditional - - case 't': - ab, stk = stk.PopBool() - if ab { - // just keep going - break - } - nest = 0 - ifloop: - // this loop consumes everything until we hit our else, - // or the end of the conditional - for { - ch, err = pb.NextCh() - if err != nil { - break - } - if ch != '%' { - continue - } - ch, _ = pb.NextCh() - switch ch { - case ';': - if nest == 0 { - break ifloop - } - nest-- - case '?': - nest++ - case 'e': - if nest == 0 { - break ifloop - } - } - } - - case 'e': - // if we got here, it means we didn't use the else - // in the 't' case above, and we should skip until - // the end of the conditional - nest = 0 - elloop: - for { - ch, err = pb.NextCh() - if err != nil { - break - } - if ch != '%' { - continue - } - ch, _ = pb.NextCh() - switch ch { - case ';': - if nest == 0 { - break elloop - } - nest-- - case '?': - nest++ - } - } - - case ';': // endif - - } - } - - return pb.End() -} - -// TPuts emits the string to the writer, but expands inline padding -// indications (of the form $<[delay]> where [delay] is msec) to -// a suitable time (unless the terminfo string indicates this isn't needed -// by specifying npc - no padding). All Terminfo based strings should be -// emitted using this function. -func (t *Terminfo) TPuts(w io.Writer, s string) { - for { - beg := strings.Index(s, "$<") - if beg < 0 { - // Most strings don't need padding, which is good news! - io.WriteString(w, s) - return - } - io.WriteString(w, s[:beg]) - s = s[beg+2:] - end := strings.Index(s, ">") - if end < 0 { - // unterminated.. just emit bytes unadulterated - io.WriteString(w, "$<"+s) - return - } - val := s[:end] - s = s[end+1:] - padus := 0 - unit := time.Millisecond - dot := false - loop: - for i := range val { - switch val[i] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - padus *= 10 - padus += int(val[i] - '0') - if dot { - unit /= 10 - } - case '.': - if !dot { - dot = true - } else { - break loop - } - default: - break loop - } - } - - // Curses historically uses padding to achieve "fine grained" - // delays. We have much better clocks these days, and so we - // do not rely on padding but simply sleep a bit. - if len(t.PadChar) > 0 { - time.Sleep(unit * time.Duration(padus)) - } - } -} - -// TGoto returns a string suitable for addressing the cursor at the given -// row and column. The origin 0, 0 is in the upper left corner of the screen. -func (t *Terminfo) TGoto(col, row int) string { - return t.TParm(t.SetCursor, row, col) -} - -// TColor returns a string corresponding to the given foreground and background -// colors. Either fg or bg can be set to -1 to elide. -func (t *Terminfo) TColor(fi, bi int) string { - rv := "" - // As a special case, we map bright colors to lower versions if the - // color table only holds 8. For the remaining 240 colors, the user - // is out of luck. Someday we could create a mapping table, but its - // not worth it. - if t.Colors == 8 { - if fi > 7 && fi < 16 { - fi -= 8 - } - if bi > 7 && bi < 16 { - bi -= 8 - } - } - if t.Colors > fi && fi >= 0 { - rv += t.TParm(t.SetFg, fi) - } - if t.Colors > bi && bi >= 0 { - rv += t.TParm(t.SetBg, bi) - } - return rv -} - -var ( - dblock sync.Mutex - terminfos = make(map[string]*Terminfo) - aliases = make(map[string]string) -) - -// AddTerminfo can be called to register a new Terminfo entry. -func AddTerminfo(t *Terminfo) { - dblock.Lock() - terminfos[t.Name] = t - for _, x := range t.Aliases { - terminfos[x] = t - } - dblock.Unlock() -} - -// LookupTerminfo attempts to find a definition for the named $TERM. -func LookupTerminfo(name string) (*Terminfo, error) { - if name == "" { - // else on windows: index out of bounds - // on the name[0] reference below - return nil, ErrTermNotFound - } - - addtruecolor := false - switch os.Getenv("COLORTERM") { - case "truecolor", "24bit", "24-bit": - addtruecolor = true - } - dblock.Lock() - t := terminfos[name] - dblock.Unlock() - - // If the name ends in -truecolor, then fabricate an entry - // from the corresponding -256color, -color, or bare terminal. - if t == nil && strings.HasSuffix(name, "-truecolor") { - - suffixes := []string{ - "-256color", - "-88color", - "-color", - "", - } - base := name[:len(name)-len("-truecolor")] - for _, s := range suffixes { - if t, _ = LookupTerminfo(base + s); t != nil { - addtruecolor = true - break - } - } - } - - if t == nil { - return nil, ErrTermNotFound - } - - switch os.Getenv("TCELL_TRUECOLOR") { - case "": - case "disable": - addtruecolor = false - default: - addtruecolor = true - } - - // If the user has requested 24-bit color with $COLORTERM, then - // amend the value (unless already present). This means we don't - // need to have a value present. - if addtruecolor && - t.SetFgBgRGB == "" && - t.SetFgRGB == "" && - t.SetBgRGB == "" { - - // Supply vanilla ISO 8613-6:1994 24-bit color sequences. - t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm" - t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm" - t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" + - "48;2;%p4%d;%p5%d;%p6%dm" - } - - return t, nil -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt100/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt100/term.go deleted file mode 100644 index 666e8b11..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt100/term.go +++ /dev/null @@ -1,48 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt100 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt100 (w/advanced video) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt100", - Aliases: []string{"vt100-am"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[J$<50>", - AttrOff: "\x1b[m\x0f$<2>", - Underline: "\x1b[4m$<2>", - Bold: "\x1b[1m$<2>", - Blink: "\x1b[5m$<2>", - Reverse: "\x1b[7m$<2>", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>", - CursorBack1: "\b", - CursorUp1: "\x1b[A$<2>", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\b", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1bOt", - KeyF6: "\x1bOu", - KeyF7: "\x1bOv", - KeyF8: "\x1bOl", - KeyF9: "\x1bOw", - KeyF10: "\x1bOx", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt102/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt102/term.go deleted file mode 100644 index 0dfbe61c..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt102/term.go +++ /dev/null @@ -1,47 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt102 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt102 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt102", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[J$<50>", - AttrOff: "\x1b[m\x0f$<2>", - Underline: "\x1b[4m$<2>", - Bold: "\x1b[1m$<2>", - Blink: "\x1b[5m$<2>", - Reverse: "\x1b[7m$<2>", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b(B\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH$<5>", - CursorBack1: "\b", - CursorUp1: "\x1b[A$<2>", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\b", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1bOt", - KeyF6: "\x1bOu", - KeyF7: "\x1bOv", - KeyF8: "\x1bOl", - KeyF9: "\x1bOw", - KeyF10: "\x1bOx", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt220/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt220/term.go deleted file mode 100644 index 9104b173..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt220/term.go +++ /dev/null @@ -1,58 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt220 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt220 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt220", - Aliases: []string{"vt200"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0$<2>", - ExitAcs: "\x1b(B$<4>", - EnableAcs: "\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - KeyHelp: "\x1b[28~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt320/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt320/term.go deleted file mode 100644 index 29bd0d39..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt320/term.go +++ /dev/null @@ -1,63 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt320 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt320 7 bit terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt320", - Aliases: []string{"vt300"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1b[1~", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[25~", - KeyF14: "\x1b[26~", - KeyF15: "\x1b[28~", - KeyF16: "\x1b[29~", - KeyF17: "\x1b[31~", - KeyF18: "\x1b[32~", - KeyF19: "\x1b[33~", - KeyF20: "\x1b[34~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt400/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt400/term.go deleted file mode 100644 index 4c7b8bc0..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt400/term.go +++ /dev/null @@ -1,46 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt400 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt400 24x80 column autowrap - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt400", - Aliases: []string{"vt400-24", "dec-vt400"}, - Columns: 80, - Lines: 24, - Clear: "\x1b[H\x1b[J$<10/>", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x1b(B", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\b", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt420/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt420/term.go deleted file mode 100644 index 5ec1cef7..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt420/term.go +++ /dev/null @@ -1,53 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt420 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // DEC VT420 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt420", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b[H\x1b[2J$<50>", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x1b(B$<2>", - Underline: "\x1b[4m", - Bold: "\x1b[1m$<2>", - Blink: "\x1b[5m$<2>", - Reverse: "\x1b[7m$<2>", - EnterKeypad: "\x1b=", - ExitKeypad: "\x1b>", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0$<2>", - ExitAcs: "\x1b(B$<4>", - EnableAcs: "\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH$<10>", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1b[A", - KeyDown: "\x1b[B", - KeyRight: "\x1b[C", - KeyLeft: "\x1b[D", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\b", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[17~", - KeyF6: "\x1b[18~", - KeyF7: "\x1b[19~", - KeyF8: "\x1b[20~", - KeyF9: "\x1b[21~", - KeyF10: "\x1b[29~", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/v/vt52/term.go b/vendor/github.com/gdamore/tcell/terminfo/v/vt52/term.go deleted file mode 100644 index c9560aef..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/v/vt52/term.go +++ /dev/null @@ -1,29 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package vt52 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // dec vt52 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "vt52", - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1bH\x1bJ", - PadChar: "\x00", - AltChars: "+h.k0affggolpnqprrss", - EnterAcs: "\x1bF", - ExitAcs: "\x1bG", - SetCursor: "\x1bY%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\x1bD", - CursorUp1: "\x1bA", - KeyUp: "\x1bA", - KeyDown: "\x1bB", - KeyRight: "\x1bC", - KeyLeft: "\x1bD", - KeyBackspace: "\b", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/w/wy50/term.go b/vendor/github.com/gdamore/tcell/terminfo/w/wy50/term.go deleted file mode 100644 index 4521932f..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/w/wy50/term.go +++ /dev/null @@ -1,59 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package wy50 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Wyse 50 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "wy50", - Aliases: []string{"wyse50"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b+$<20>", - ShowCursor: "\x1b`1", - HideCursor: "\x1b`0", - AttrOff: "\x1b(\x1bH\x03", - Dim: "\x1b`7\x1b)", - Reverse: "\x1b`6\x1b)", - PadChar: "\x00", - AltChars: "0wa_h[jukslrmqnxqzttuyv]wpxv", - EnterAcs: "\x1bH\x02", - ExitAcs: "\x1bH\x03", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\n", - KeyRight: "\f", - KeyLeft: "\b", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bW", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyPgUp: "\x1bJ", - KeyPgDn: "\x1bK", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - KeyF10: "\x01I\r", - KeyF11: "\x01J\r", - KeyF12: "\x01K\r", - KeyF13: "\x01L\r", - KeyF14: "\x01M\r", - KeyF15: "\x01N\r", - KeyF16: "\x01O\r", - KeyPrint: "\x1bP", - KeyBacktab: "\x1bI", - KeyShfHome: "\x1b{", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/w/wy60/term.go b/vendor/github.com/gdamore/tcell/terminfo/w/wy60/term.go deleted file mode 100644 index ff519408..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/w/wy60/term.go +++ /dev/null @@ -1,63 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package wy60 - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Wyse 60 - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "wy60", - Aliases: []string{"wyse60"}, - Columns: 80, - Lines: 24, - Bell: "\a", - Clear: "\x1b+$<100>", - EnterCA: "\x1bw0", - ExitCA: "\x1bw1", - ShowCursor: "\x1b`1", - HideCursor: "\x1b`0", - AttrOff: "\x1b(\x1bH\x03\x1bG0\x1bcD", - Underline: "\x1bG8", - Dim: "\x1bGp", - Blink: "\x1bG2", - Reverse: "\x1bG4", - PadChar: "\x00", - AltChars: "+/,.0[a2fxgqh1ihjYk?lZm@nEqDtCu4vAwBx3yszr{c~~", - EnterAcs: "\x1bcE", - ExitAcs: "\x1bcD", - SetCursor: "\x1b=%p1%' '%+%c%p2%' '%+%c", - CursorBack1: "\b", - CursorUp1: "\v", - KeyUp: "\v", - KeyDown: "\n", - KeyRight: "\f", - KeyLeft: "\b", - KeyInsert: "\x1bQ", - KeyDelete: "\x1bW", - KeyBackspace: "\b", - KeyHome: "\x1e", - KeyPgUp: "\x1bJ", - KeyPgDn: "\x1bK", - KeyF1: "\x01@\r", - KeyF2: "\x01A\r", - KeyF3: "\x01B\r", - KeyF4: "\x01C\r", - KeyF5: "\x01D\r", - KeyF6: "\x01E\r", - KeyF7: "\x01F\r", - KeyF8: "\x01G\r", - KeyF9: "\x01H\r", - KeyF10: "\x01I\r", - KeyF11: "\x01J\r", - KeyF12: "\x01K\r", - KeyF13: "\x01L\r", - KeyF14: "\x01M\r", - KeyF15: "\x01N\r", - KeyF16: "\x01O\r", - KeyPrint: "\x1bP", - KeyBacktab: "\x1bI", - KeyShfHome: "\x1b{", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/w/wy99_ansi/term.go b/vendor/github.com/gdamore/tcell/terminfo/w/wy99_ansi/term.go deleted file mode 100644 index fbe85699..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/w/wy99_ansi/term.go +++ /dev/null @@ -1,114 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package wy99_ansi - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Wyse WY-99GT in ansi mode (int'l PC keyboard) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "wy99-ansi", - Columns: 80, - Lines: 25, - Bell: "\a", - Clear: "\x1b[H\x1b[J$<200>", - ShowCursor: "\x1b[34h\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f\x1b[\"q", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h", - ExitKeypad: "\x1b[?1l", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b$<1>", - CursorUp1: "\x1bM", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\b", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[M", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF17: "\x1b[K", - KeyF18: "\x1b[31~", - KeyF19: "\x1b[32~", - KeyF20: "\x1b[33~", - KeyF21: "\x1b[34~", - KeyF22: "\x1b[35~", - KeyF23: "\x1b[1~", - KeyF24: "\x1b[2~", - KeyBacktab: "\x1b[z", - }) - - // Wyse WY-99GT in ansi mode (US PC keyboard) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "wy99a-ansi", - Columns: 80, - Lines: 25, - Bell: "\a", - Clear: "\x1b[H\x1b[J$<200>", - ShowCursor: "\x1b[34h\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[m\x0f\x1b[\"q", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h", - ExitKeypad: "\x1b[?1l", - PadChar: "\x00", - AltChars: "``aaffggjjkkllmmnnooqqssttuuvvwwxx{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b$<1>", - CursorUp1: "\x1bM", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\b", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[M", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF17: "\x1b[K", - KeyF18: "\x1b[31~", - KeyF19: "\x1b[32~", - KeyF20: "\x1b[33~", - KeyF21: "\x1b[34~", - KeyF22: "\x1b[35~", - KeyF23: "\x1b[1~", - KeyF24: "\x1b[2~", - KeyBacktab: "\x1b[z", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/x/xfce/term.go b/vendor/github.com/gdamore/tcell/terminfo/x/xfce/term.go deleted file mode 100644 index 219cc6fd..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/x/xfce/term.go +++ /dev/null @@ -1,156 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package xfce - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Xfce Terminal - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xfce", - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b7\x1b[?47h", - ExitCA: "\x1b[2J\x1b[?47l\x1b8", - ShowCursor: "\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b[0m\x0f", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x0e", - ExitAcs: "\x0f", - EnableAcs: "\x1b)0", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1bO1;2P", - KeyF14: "\x1bO1;2Q", - KeyF15: "\x1bO1;2R", - KeyF16: "\x1bO1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1bO1;5P", - KeyF26: "\x1bO1;5Q", - KeyF27: "\x1bO1;5R", - KeyF28: "\x1bO1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1bO1;6P", - KeyF38: "\x1bO1;6Q", - KeyF39: "\x1bO1;6R", - KeyF40: "\x1bO1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1bO1;3P", - KeyF50: "\x1bO1;3Q", - KeyF51: "\x1bO1;3R", - KeyF52: "\x1bO1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1bO1;4P", - KeyF62: "\x1bO1;4Q", - KeyF63: "\x1bO1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/x/xnuppc/term.go b/vendor/github.com/gdamore/tcell/terminfo/x/xnuppc/term.go deleted file mode 100644 index b1f9ad78..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/x/xnuppc/term.go +++ /dev/null @@ -1,34 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package xnuppc - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // Darwin PowerPC Console (color) - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xnuppc", - Aliases: []string{"darwin"}, - Colors: 8, - Clear: "\x1b[H\x1b[J", - AttrOff: "\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - PadChar: "\x00", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\x1b[D", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyBackspace: "\xff", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/x/xterm/term.go b/vendor/github.com/gdamore/tcell/terminfo/x/xterm/term.go deleted file mode 100644 index 0e4c175f..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/x/xterm/term.go +++ /dev/null @@ -1,453 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package xterm - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // X11 terminal emulator - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xterm", - Aliases: []string{"xterm-debian"}, - Columns: 80, - Lines: 24, - Colors: 8, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[3%p1%dm", - SetBg: "\x1b[4%p1%dm", - SetFgBg: "\x1b[3%p1%d;4%p2%dm", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) - - // xterm with 88 colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xterm-88color", - Columns: 80, - Lines: 24, - Colors: 88, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) - - // xterm with 256 colors - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xterm-256color", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terminfo/x/xterm_kitty/term.go b/vendor/github.com/gdamore/tcell/terminfo/x/xterm_kitty/term.go deleted file mode 100644 index 8a8d69b8..00000000 --- a/vendor/github.com/gdamore/tcell/terminfo/x/xterm_kitty/term.go +++ /dev/null @@ -1,155 +0,0 @@ -// Generated automatically. DO NOT HAND-EDIT. - -package xterm_kitty - -import "github.com/gdamore/tcell/terminfo" - -func init() { - - // KovIdTTY - terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "xterm-kitty", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h", - ExitCA: "\x1b[?1049l", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h", - ExitKeypad: "\x1b[?1l", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m", - AltChars: "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - Mouse: "\x1b[M", - MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\xff", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyF13: "\x1b[1;2P", - KeyF14: "\x1b[1;2Q", - KeyF15: "\x1b[1;2R", - KeyF16: "\x1b[1;2S", - KeyF17: "\x1b[15;2~", - KeyF18: "\x1b[17;2~", - KeyF19: "\x1b[18;2~", - KeyF20: "\x1b[19;2~", - KeyF21: "\x1b[20;2~", - KeyF22: "\x1b[21;2~", - KeyF23: "\x1b[23;2~", - KeyF24: "\x1b[24;2~", - KeyF25: "\x1b[1;5P", - KeyF26: "\x1b[1;5Q", - KeyF27: "\x1b[1;5R", - KeyF28: "\x1b[1;5S", - KeyF29: "\x1b[15;5~", - KeyF30: "\x1b[17;5~", - KeyF31: "\x1b[18;5~", - KeyF32: "\x1b[19;5~", - KeyF33: "\x1b[20;5~", - KeyF34: "\x1b[21;5~", - KeyF35: "\x1b[23;5~", - KeyF36: "\x1b[24;5~", - KeyF37: "\x1b[1;6P", - KeyF38: "\x1b[1;6Q", - KeyF39: "\x1b[1;6R", - KeyF40: "\x1b[1;6S", - KeyF41: "\x1b[15;6~", - KeyF42: "\x1b[17;6~", - KeyF43: "\x1b[18;6~", - KeyF44: "\x1b[19;6~", - KeyF45: "\x1b[20;6~", - KeyF46: "\x1b[21;6~", - KeyF47: "\x1b[23;6~", - KeyF48: "\x1b[24;6~", - KeyF49: "\x1b[1;3P", - KeyF50: "\x1b[1;3Q", - KeyF51: "\x1b[1;3R", - KeyF52: "\x1b[1;3S", - KeyF53: "\x1b[15;3~", - KeyF54: "\x1b[17;3~", - KeyF55: "\x1b[18;3~", - KeyF56: "\x1b[19;3~", - KeyF57: "\x1b[20;3~", - KeyF58: "\x1b[21;3~", - KeyF59: "\x1b[23;3~", - KeyF60: "\x1b[24;3~", - KeyF61: "\x1b[1;4P", - KeyF62: "\x1b[1;4Q", - KeyF63: "\x1b[1;4R", - KeyBacktab: "\x1b[Z", - KeyShfLeft: "\x1b[1;2D", - KeyShfRight: "\x1b[1;2C", - KeyShfUp: "\x1b[1;2A", - KeyShfDown: "\x1b[1;2B", - KeyCtrlLeft: "\x1b[1;5D", - KeyCtrlRight: "\x1b[1;5C", - KeyCtrlUp: "\x1b[1;5A", - KeyCtrlDown: "\x1b[1;5B", - KeyMetaLeft: "\x1b[1;9D", - KeyMetaRight: "\x1b[1;9C", - KeyMetaUp: "\x1b[1;9A", - KeyMetaDown: "\x1b[1;9B", - KeyAltLeft: "\x1b[1;3D", - KeyAltRight: "\x1b[1;3C", - KeyAltUp: "\x1b[1;3A", - KeyAltDown: "\x1b[1;3B", - KeyAltShfLeft: "\x1b[1;4D", - KeyAltShfRight: "\x1b[1;4C", - KeyAltShfUp: "\x1b[1;4A", - KeyAltShfDown: "\x1b[1;4B", - KeyMetaShfLeft: "\x1b[1;10D", - KeyMetaShfRight: "\x1b[1;10C", - KeyMetaShfUp: "\x1b[1;10A", - KeyMetaShfDown: "\x1b[1;10B", - KeyCtrlShfLeft: "\x1b[1;6D", - KeyCtrlShfRight: "\x1b[1;6C", - KeyCtrlShfUp: "\x1b[1;6A", - KeyCtrlShfDown: "\x1b[1;6B", - KeyShfHome: "\x1b[1;2H", - KeyShfEnd: "\x1b[1;2F", - KeyCtrlHome: "\x1b[1;5H", - KeyCtrlEnd: "\x1b[1;5F", - KeyAltHome: "\x1b[1;9H", - KeyAltEnd: "\x1b[1;9F", - KeyCtrlShfHome: "\x1b[1;6H", - KeyCtrlShfEnd: "\x1b[1;6F", - KeyMetaShfHome: "\x1b[1;10H", - KeyMetaShfEnd: "\x1b[1;10F", - KeyAltShfHome: "\x1b[1;4H", - KeyAltShfEnd: "\x1b[1;4F", - }) -} diff --git a/vendor/github.com/gdamore/tcell/terms_default.go b/vendor/github.com/gdamore/tcell/terms_default.go deleted file mode 100644 index cf2dbaab..00000000 --- a/vendor/github.com/gdamore/tcell/terms_default.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build !tcell_minimal - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - // This imports the default terminal entries. To disable, use the - // tcell_minimal build tag. - _ "github.com/gdamore/tcell/terminfo/extended" -) diff --git a/vendor/github.com/gdamore/tcell/terms_dynamic.go b/vendor/github.com/gdamore/tcell/terms_dynamic.go deleted file mode 100644 index a06c087c..00000000 --- a/vendor/github.com/gdamore/tcell/terms_dynamic.go +++ /dev/null @@ -1,37 +0,0 @@ -// +build !tcell_minimal,!nacl,!js,!zos,!plan9,!windows,!android - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - // This imports a dynamic version of the terminal database, which - // is built using infocmp. This relies on a working installation - // of infocmp (typically supplied with ncurses). We only do this - // for systems likely to have that -- i.e. UNIX based hosts. We - // also don't support Android here, because you really don't want - // to run external programs there. Generally the android terminals - // will be automatically included anyway. - "github.com/gdamore/tcell/terminfo" - "github.com/gdamore/tcell/terminfo/dynamic" -) - -func loadDynamicTerminfo(term string) (*terminfo.Terminfo, error) { - ti, _, e := dynamic.LoadTerminfo(term) - if e != nil { - return nil, e - } - return ti, nil -} diff --git a/vendor/github.com/gdamore/tcell/terms_static.go b/vendor/github.com/gdamore/tcell/terms_static.go deleted file mode 100644 index 379dbe1b..00000000 --- a/vendor/github.com/gdamore/tcell/terms_static.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build tcell_minimal nacl js zos plan9 windows android - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "errors" - - "github.com/gdamore/tcell/terminfo" -) - -func loadDynamicTerminfo(term string) (*terminfo.Terminfo, error) { - return nil, errors.New("terminal type unsupported") -} diff --git a/vendor/github.com/gdamore/tcell/tscreen.go b/vendor/github.com/gdamore/tcell/tscreen.go deleted file mode 100644 index b86a6c47..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen.go +++ /dev/null @@ -1,1434 +0,0 @@ -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "bytes" - "io" - "os" - "strconv" - "sync" - "time" - "unicode/utf8" - - "golang.org/x/text/transform" - - "github.com/gdamore/tcell/terminfo" - - // import the stock terminals - _ "github.com/gdamore/tcell/terminfo/base" -) - -// NewTerminfoScreen returns a Screen that uses the stock TTY interface -// and POSIX termios, combined with a terminfo description taken from -// the $TERM environment variable. It returns an error if the terminal -// is not supported for any reason. -// -// For terminals that do not support dynamic resize events, the $LINES -// $COLUMNS environment variables can be set to the actual window size, -// otherwise defaults taken from the terminal database are used. -func NewTerminfoScreen() (Screen, error) { - ti, e := terminfo.LookupTerminfo(os.Getenv("TERM")) - if e != nil { - ti, e = loadDynamicTerminfo(os.Getenv("TERM")) - if e != nil { - return nil, e - } - terminfo.AddTerminfo(ti) - } - t := &tScreen{ti: ti} - - t.keyexist = make(map[Key]bool) - t.keycodes = make(map[string]*tKeyCode) - if len(ti.Mouse) > 0 { - t.mouse = []byte(ti.Mouse) - } - t.prepareKeys() - t.buildAcsMap() - t.sigwinch = make(chan os.Signal, 10) - t.fallback = make(map[rune]string) - for k, v := range RuneFallbacks { - t.fallback[k] = v - } - - return t, nil -} - -// tKeyCode represents a combination of a key code and modifiers. -type tKeyCode struct { - key Key - mod ModMask -} - -// tScreen represents a screen backed by a terminfo implementation. -type tScreen struct { - ti *terminfo.Terminfo - h int - w int - fini bool - cells CellBuffer - in *os.File - out *os.File - buffering bool // true if we are collecting writes to buf instead of sending directly to out - buf bytes.Buffer - curstyle Style - style Style - evch chan Event - sigwinch chan os.Signal - quit chan struct{} - indoneq chan struct{} - keyexist map[Key]bool - keycodes map[string]*tKeyCode - keychan chan []byte - keytimer *time.Timer - keyexpire time.Time - cx int - cy int - mouse []byte - clear bool - cursorx int - cursory int - tiosp *termiosPrivate - wasbtn bool - acs map[rune]string - charset string - encoder transform.Transformer - decoder transform.Transformer - fallback map[rune]string - colors map[Color]Color - palette []Color - truecolor bool - escaped bool - buttondn bool - - sync.Mutex -} - -func (t *tScreen) Init() error { - t.evch = make(chan Event, 10) - t.indoneq = make(chan struct{}) - t.keychan = make(chan []byte, 10) - t.keytimer = time.NewTimer(time.Millisecond * 50) - t.charset = "UTF-8" - - t.charset = getCharset() - if enc := GetEncoding(t.charset); enc != nil { - t.encoder = enc.NewEncoder() - t.decoder = enc.NewDecoder() - } else { - return ErrNoCharset - } - ti := t.ti - - // environment overrides - w := ti.Columns - h := ti.Lines - if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 { - h = i - } - if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 { - w = i - } - if e := t.termioInit(); e != nil { - return e - } - - if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" { - t.truecolor = true - } - // A user who wants to have his themes honored can - // set this environment variable. - if os.Getenv("TCELL_TRUECOLOR") == "disable" { - t.truecolor = false - } - if !t.truecolor { - t.colors = make(map[Color]Color) - t.palette = make([]Color, t.Colors()) - for i := 0; i < t.Colors(); i++ { - t.palette[i] = Color(i) - // identity map for our builtin colors - t.colors[Color(i)] = Color(i) - } - } - - t.TPuts(ti.EnterCA) - t.TPuts(ti.HideCursor) - t.TPuts(ti.EnableAcs) - t.TPuts(ti.Clear) - - t.quit = make(chan struct{}) - - t.Lock() - t.cx = -1 - t.cy = -1 - t.style = StyleDefault - t.cells.Resize(w, h) - t.cursorx = -1 - t.cursory = -1 - t.resize() - t.Unlock() - - go t.mainLoop() - go t.inputLoop() - - return nil -} - -func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) { - if val != "" { - // Do not overrride codes that already exist - if _, exist := t.keycodes[val]; !exist { - t.keyexist[key] = true - t.keycodes[val] = &tKeyCode{key: key, mod: mod} - } - } -} - -func (t *tScreen) prepareKey(key Key, val string) { - t.prepareKeyMod(key, ModNone, val) -} - -func (t *tScreen) prepareKeys() { - ti := t.ti - t.prepareKey(KeyBackspace, ti.KeyBackspace) - t.prepareKey(KeyF1, ti.KeyF1) - t.prepareKey(KeyF2, ti.KeyF2) - t.prepareKey(KeyF3, ti.KeyF3) - t.prepareKey(KeyF4, ti.KeyF4) - t.prepareKey(KeyF5, ti.KeyF5) - t.prepareKey(KeyF6, ti.KeyF6) - t.prepareKey(KeyF7, ti.KeyF7) - t.prepareKey(KeyF8, ti.KeyF8) - t.prepareKey(KeyF9, ti.KeyF9) - t.prepareKey(KeyF10, ti.KeyF10) - t.prepareKey(KeyF11, ti.KeyF11) - t.prepareKey(KeyF12, ti.KeyF12) - t.prepareKey(KeyF13, ti.KeyF13) - t.prepareKey(KeyF14, ti.KeyF14) - t.prepareKey(KeyF15, ti.KeyF15) - t.prepareKey(KeyF16, ti.KeyF16) - t.prepareKey(KeyF17, ti.KeyF17) - t.prepareKey(KeyF18, ti.KeyF18) - t.prepareKey(KeyF19, ti.KeyF19) - t.prepareKey(KeyF20, ti.KeyF20) - t.prepareKey(KeyF21, ti.KeyF21) - t.prepareKey(KeyF22, ti.KeyF22) - t.prepareKey(KeyF23, ti.KeyF23) - t.prepareKey(KeyF24, ti.KeyF24) - t.prepareKey(KeyF25, ti.KeyF25) - t.prepareKey(KeyF26, ti.KeyF26) - t.prepareKey(KeyF27, ti.KeyF27) - t.prepareKey(KeyF28, ti.KeyF28) - t.prepareKey(KeyF29, ti.KeyF29) - t.prepareKey(KeyF30, ti.KeyF30) - t.prepareKey(KeyF31, ti.KeyF31) - t.prepareKey(KeyF32, ti.KeyF32) - t.prepareKey(KeyF33, ti.KeyF33) - t.prepareKey(KeyF34, ti.KeyF34) - t.prepareKey(KeyF35, ti.KeyF35) - t.prepareKey(KeyF36, ti.KeyF36) - t.prepareKey(KeyF37, ti.KeyF37) - t.prepareKey(KeyF38, ti.KeyF38) - t.prepareKey(KeyF39, ti.KeyF39) - t.prepareKey(KeyF40, ti.KeyF40) - t.prepareKey(KeyF41, ti.KeyF41) - t.prepareKey(KeyF42, ti.KeyF42) - t.prepareKey(KeyF43, ti.KeyF43) - t.prepareKey(KeyF44, ti.KeyF44) - t.prepareKey(KeyF45, ti.KeyF45) - t.prepareKey(KeyF46, ti.KeyF46) - t.prepareKey(KeyF47, ti.KeyF47) - t.prepareKey(KeyF48, ti.KeyF48) - t.prepareKey(KeyF49, ti.KeyF49) - t.prepareKey(KeyF50, ti.KeyF50) - t.prepareKey(KeyF51, ti.KeyF51) - t.prepareKey(KeyF52, ti.KeyF52) - t.prepareKey(KeyF53, ti.KeyF53) - t.prepareKey(KeyF54, ti.KeyF54) - t.prepareKey(KeyF55, ti.KeyF55) - t.prepareKey(KeyF56, ti.KeyF56) - t.prepareKey(KeyF57, ti.KeyF57) - t.prepareKey(KeyF58, ti.KeyF58) - t.prepareKey(KeyF59, ti.KeyF59) - t.prepareKey(KeyF60, ti.KeyF60) - t.prepareKey(KeyF61, ti.KeyF61) - t.prepareKey(KeyF62, ti.KeyF62) - t.prepareKey(KeyF63, ti.KeyF63) - t.prepareKey(KeyF64, ti.KeyF64) - t.prepareKey(KeyInsert, ti.KeyInsert) - t.prepareKey(KeyDelete, ti.KeyDelete) - t.prepareKey(KeyHome, ti.KeyHome) - t.prepareKey(KeyEnd, ti.KeyEnd) - t.prepareKey(KeyUp, ti.KeyUp) - t.prepareKey(KeyDown, ti.KeyDown) - t.prepareKey(KeyLeft, ti.KeyLeft) - t.prepareKey(KeyRight, ti.KeyRight) - t.prepareKey(KeyPgUp, ti.KeyPgUp) - t.prepareKey(KeyPgDn, ti.KeyPgDn) - t.prepareKey(KeyHelp, ti.KeyHelp) - t.prepareKey(KeyPrint, ti.KeyPrint) - t.prepareKey(KeyCancel, ti.KeyCancel) - t.prepareKey(KeyExit, ti.KeyExit) - t.prepareKey(KeyBacktab, ti.KeyBacktab) - - t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight) - t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft) - t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp) - t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown) - t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome) - t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd) - - t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight) - t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft) - t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp) - t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown) - t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome) - t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd) - - t.prepareKeyMod(KeyRight, ModAlt, ti.KeyAltRight) - t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyAltLeft) - t.prepareKeyMod(KeyUp, ModAlt, ti.KeyAltUp) - t.prepareKeyMod(KeyDown, ModAlt, ti.KeyAltDown) - t.prepareKeyMod(KeyHome, ModAlt, ti.KeyAltHome) - t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyAltEnd) - - t.prepareKeyMod(KeyRight, ModAlt, ti.KeyMetaRight) - t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyMetaLeft) - t.prepareKeyMod(KeyUp, ModAlt, ti.KeyMetaUp) - t.prepareKeyMod(KeyDown, ModAlt, ti.KeyMetaDown) - t.prepareKeyMod(KeyHome, ModAlt, ti.KeyMetaHome) - t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyMetaEnd) - - t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyAltShfRight) - t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyAltShfLeft) - t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyAltShfUp) - t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyAltShfDown) - t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyAltShfHome) - t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyAltShfEnd) - - t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyMetaShfRight) - t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyMetaShfLeft) - t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyMetaShfUp) - t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyMetaShfDown) - t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyMetaShfHome) - t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyMetaShfEnd) - - t.prepareKeyMod(KeyRight, ModCtrl|ModShift, ti.KeyCtrlShfRight) - t.prepareKeyMod(KeyLeft, ModCtrl|ModShift, ti.KeyCtrlShfLeft) - t.prepareKeyMod(KeyUp, ModCtrl|ModShift, ti.KeyCtrlShfUp) - t.prepareKeyMod(KeyDown, ModCtrl|ModShift, ti.KeyCtrlShfDown) - t.prepareKeyMod(KeyHome, ModCtrl|ModShift, ti.KeyCtrlShfHome) - t.prepareKeyMod(KeyEnd, ModCtrl|ModShift, ti.KeyCtrlShfEnd) - - // Sadly, xterm handling of keycodes is somewhat erratic. In - // particular, different codes are sent depending on application - // mode is in use or not, and the entries for many of these are - // simply absent from terminfo on many systems. So we insert - // a number of escape sequences if they are not already used, in - // order to have the widest correct usage. Note that prepareKey - // will not inject codes if the escape sequence is already known. - // We also only do this for terminals that have the application - // mode present. - - // Cursor mode - if ti.EnterKeypad != "" { - t.prepareKey(KeyUp, "\x1b[A") - t.prepareKey(KeyDown, "\x1b[B") - t.prepareKey(KeyRight, "\x1b[C") - t.prepareKey(KeyLeft, "\x1b[D") - t.prepareKey(KeyEnd, "\x1b[F") - t.prepareKey(KeyHome, "\x1b[H") - t.prepareKey(KeyDelete, "\x1b[3~") - t.prepareKey(KeyHome, "\x1b[1~") - t.prepareKey(KeyEnd, "\x1b[4~") - t.prepareKey(KeyPgUp, "\x1b[5~") - t.prepareKey(KeyPgDn, "\x1b[6~") - - // Application mode - t.prepareKey(KeyUp, "\x1bOA") - t.prepareKey(KeyDown, "\x1bOB") - t.prepareKey(KeyRight, "\x1bOC") - t.prepareKey(KeyLeft, "\x1bOD") - t.prepareKey(KeyHome, "\x1bOH") - } - -outer: - // Add key mappings for control keys. - for i := 0; i < ' '; i++ { - // Do not insert direct key codes for ambiguous keys. - // For example, ESC is used for lots of other keys, so - // when parsing this we don't want to fast path handling - // of it, but instead wait a bit before parsing it as in - // isolation. - for esc := range t.keycodes { - if []byte(esc)[0] == byte(i) { - continue outer - } - } - - t.keyexist[Key(i)] = true - - mod := ModCtrl - switch Key(i) { - case KeyBS, KeyTAB, KeyESC, KeyCR: - // directly typeable- no control sequence - mod = ModNone - } - t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod} - } -} - -func (t *tScreen) Fini() { - t.Lock() - defer t.Unlock() - - ti := t.ti - t.cells.Resize(0, 0) - t.TPuts(ti.ShowCursor) - t.TPuts(ti.AttrOff) - t.TPuts(ti.Clear) - t.TPuts(ti.ExitCA) - t.TPuts(ti.ExitKeypad) - t.TPuts(ti.TParm(ti.MouseMode, 0)) - t.curstyle = Style(-1) - t.clear = false - t.fini = true - - select { - case <-t.quit: - // do nothing, already closed - - default: - close(t.quit) - } - - t.termioFini() -} - -func (t *tScreen) SetStyle(style Style) { - t.Lock() - if !t.fini { - t.style = style - } - t.Unlock() -} - -func (t *tScreen) Clear() { - t.Fill(' ', t.style) -} - -func (t *tScreen) Fill(r rune, style Style) { - t.Lock() - if !t.fini { - t.cells.Fill(r, style) - } - t.Unlock() -} - -func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { - t.Lock() - if !t.fini { - t.cells.SetContent(x, y, mainc, combc, style) - } - t.Unlock() -} - -func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) { - t.Lock() - mainc, combc, style, width := t.cells.GetContent(x, y) - t.Unlock() - return mainc, combc, style, width -} - -func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) { - if len(ch) > 0 { - t.SetContent(x, y, ch[0], ch[1:], style) - } else { - t.SetContent(x, y, ' ', nil, style) - } -} - -func (t *tScreen) encodeRune(r rune, buf []byte) []byte { - - nb := make([]byte, 6) - ob := make([]byte, 6) - num := utf8.EncodeRune(ob, r) - ob = ob[:num] - dst := 0 - var err error - if enc := t.encoder; enc != nil { - enc.Reset() - dst, _, err = enc.Transform(nb, ob, true) - } - if err != nil || dst == 0 || nb[0] == '\x1a' { - // Combining characters are elided - if len(buf) == 0 { - if acs, ok := t.acs[r]; ok { - buf = append(buf, []byte(acs)...) - } else if fb, ok := t.fallback[r]; ok { - buf = append(buf, []byte(fb)...) - } else { - buf = append(buf, '?') - } - } - } else { - buf = append(buf, nb[:dst]...) - } - - return buf -} - -func (t *tScreen) sendFgBg(fg Color, bg Color) { - ti := t.ti - if ti.Colors == 0 { - return - } - if t.truecolor { - if ti.SetFgBgRGB != "" && - fg != ColorDefault && bg != ColorDefault { - r1, g1, b1 := fg.RGB() - r2, g2, b2 := bg.RGB() - t.TPuts(ti.TParm(ti.SetFgBgRGB, - int(r1), int(g1), int(b1), - int(r2), int(g2), int(b2))) - } else { - if fg != ColorDefault && ti.SetFgRGB != "" { - r, g, b := fg.RGB() - t.TPuts(ti.TParm(ti.SetFgRGB, - int(r), int(g), int(b))) - } - if bg != ColorDefault && ti.SetBgRGB != "" { - r, g, b := bg.RGB() - t.TPuts(ti.TParm(ti.SetBgRGB, - int(r), int(g), int(b))) - } - } - return - } - - if fg != ColorDefault { - if v, ok := t.colors[fg]; ok { - fg = v - } else { - v = FindColor(fg, t.palette) - t.colors[fg] = v - fg = v - } - } - - if bg != ColorDefault { - if v, ok := t.colors[bg]; ok { - bg = v - } else { - v = FindColor(bg, t.palette) - t.colors[bg] = v - bg = v - } - } - - if ti.SetFgBg != "" && fg != ColorDefault && bg != ColorDefault { - t.TPuts(ti.TParm(ti.SetFgBg, int(fg), int(bg))) - } else { - if fg != ColorDefault && ti.SetFg != "" { - t.TPuts(ti.TParm(ti.SetFg, int(fg))) - } - if bg != ColorDefault && ti.SetBg != "" { - t.TPuts(ti.TParm(ti.SetBg, int(bg))) - } - } -} - -func (t *tScreen) drawCell(x, y int) int { - - ti := t.ti - - mainc, combc, style, width := t.cells.GetContent(x, y) - if !t.cells.Dirty(x, y) { - return width - } - - if t.cy != y || t.cx != x { - t.TPuts(ti.TGoto(x, y)) - t.cx = x - t.cy = y - } - - if style == StyleDefault { - style = t.style - } - if style != t.curstyle { - fg, bg, attrs := style.Decompose() - - t.TPuts(ti.AttrOff) - - t.sendFgBg(fg, bg) - if attrs&AttrBold != 0 { - t.TPuts(ti.Bold) - } - if attrs&AttrUnderline != 0 { - t.TPuts(ti.Underline) - } - if attrs&AttrReverse != 0 { - t.TPuts(ti.Reverse) - } - if attrs&AttrBlink != 0 { - t.TPuts(ti.Blink) - } - if attrs&AttrDim != 0 { - t.TPuts(ti.Dim) - } - t.curstyle = style - } - // now emit runes - taking care to not overrun width with a - // wide character, and to ensure that we emit exactly one regular - // character followed up by any residual combing characters - - if width < 1 { - width = 1 - } - - var str string - - buf := make([]byte, 0, 6) - - buf = t.encodeRune(mainc, buf) - for _, r := range combc { - buf = t.encodeRune(r, buf) - } - - str = string(buf) - if width > 1 && str == "?" { - // No FullWidth character support - str = "? " - t.cx = -1 - } - - // XXX: check for hazeltine not being able to display ~ - - if x > t.w-width { - // too wide to fit; emit a single space instead - width = 1 - str = " " - } - t.writeString(str) - t.cx += width - t.cells.SetDirty(x, y, false) - if width > 1 { - t.cx = -1 - } - - return width -} - -func (t *tScreen) ShowCursor(x, y int) { - t.Lock() - t.cursorx = x - t.cursory = y - t.Unlock() -} - -func (t *tScreen) HideCursor() { - t.ShowCursor(-1, -1) -} - -func (t *tScreen) showCursor() { - - x, y := t.cursorx, t.cursory - w, h := t.cells.Size() - if x < 0 || y < 0 || x >= w || y >= h { - t.hideCursor() - return - } - t.TPuts(t.ti.TGoto(x, y)) - t.TPuts(t.ti.ShowCursor) - t.cx = x - t.cy = y -} - -// writeString sends a string to the terminal. The string is sent as-is and -// this function does not expand inline padding indications (of the form -// $<[delay]> where [delay] is msec). In order to have these expanded, use -// TPuts. If the screen is "buffering", the string is collected in a buffer, -// with the intention that the entire buffer be sent to the terminal in one -// write operation at some point later. -func (t *tScreen) writeString(s string) { - if t.buffering { - io.WriteString(&t.buf, s) - } else { - io.WriteString(t.out, s) - } -} - -func (t *tScreen) TPuts(s string) { - if t.buffering { - t.ti.TPuts(&t.buf, s) - } else { - t.ti.TPuts(t.out, s) - } -} - -func (t *tScreen) Show() { - t.Lock() - if !t.fini { - t.resize() - t.draw() - } - t.Unlock() -} - -func (t *tScreen) clearScreen() { - fg, bg, _ := t.style.Decompose() - t.sendFgBg(fg, bg) - t.TPuts(t.ti.Clear) - t.clear = false -} - -func (t *tScreen) hideCursor() { - // does not update cursor position - if t.ti.HideCursor != "" { - t.TPuts(t.ti.HideCursor) - } else { - // No way to hide cursor, stick it - // at bottom right of screen - t.cx, t.cy = t.cells.Size() - t.TPuts(t.ti.TGoto(t.cx, t.cy)) - } -} - -func (t *tScreen) draw() { - // clobber cursor position, because we're gonna change it all - t.cx = -1 - t.cy = -1 - - t.buf.Reset() - t.buffering = true - defer func() { - t.buffering = false - }() - - // hide the cursor while we move stuff around - t.hideCursor() - - if t.clear { - t.clearScreen() - } - - for y := 0; y < t.h; y++ { - for x := 0; x < t.w; x++ { - width := t.drawCell(x, y) - if width > 1 { - if x+1 < t.w { - // this is necessary so that if we ever - // go back to drawing that cell, we - // actually will *draw* it. - t.cells.SetDirty(x+1, y, true) - } - } - x += width - 1 - } - } - - // restore the cursor - t.showCursor() - - t.buf.WriteTo(t.out) -} - -func (t *tScreen) EnableMouse() { - if len(t.mouse) != 0 { - t.TPuts(t.ti.TParm(t.ti.MouseMode, 1)) - } -} - -func (t *tScreen) DisableMouse() { - if len(t.mouse) != 0 { - t.TPuts(t.ti.TParm(t.ti.MouseMode, 0)) - } -} - -func (t *tScreen) Size() (int, int) { - t.Lock() - w, h := t.w, t.h - t.Unlock() - return w, h -} - -func (t *tScreen) resize() { - if w, h, e := t.getWinSize(); e == nil { - if w != t.w || h != t.h { - t.cx = -1 - t.cy = -1 - - t.cells.Resize(w, h) - t.cells.Invalidate() - t.h = h - t.w = w - ev := NewEventResize(w, h) - t.PostEvent(ev) - } - } -} - -func (t *tScreen) Colors() int { - // this doesn't change, no need for lock - if t.truecolor { - return 1 << 24 - } - return t.ti.Colors -} - -func (t *tScreen) PollEvent() Event { - select { - case <-t.quit: - return nil - case ev := <-t.evch: - return ev - } -} - -// vtACSNames is a map of bytes defined by terminfo that are used in -// the terminals Alternate Character Set to represent other glyphs. -// For example, the upper left corner of the box drawing set can be -// displayed by printing "l" while in the alternate character set. -// Its not quite that simple, since the "l" is the terminfo name, -// and it may be necessary to use a different character based on -// the terminal implementation (or the terminal may lack support for -// this altogether). See buildAcsMap below for detail. -var vtACSNames = map[byte]rune{ - '+': RuneRArrow, - ',': RuneLArrow, - '-': RuneUArrow, - '.': RuneDArrow, - '0': RuneBlock, - '`': RuneDiamond, - 'a': RuneCkBoard, - 'b': '␉', // VT100, Not defined by terminfo - 'c': '␌', // VT100, Not defined by terminfo - 'd': '␋', // VT100, Not defined by terminfo - 'e': '␊', // VT100, Not defined by terminfo - 'f': RuneDegree, - 'g': RunePlMinus, - 'h': RuneBoard, - 'i': RuneLantern, - 'j': RuneLRCorner, - 'k': RuneURCorner, - 'l': RuneULCorner, - 'm': RuneLLCorner, - 'n': RunePlus, - 'o': RuneS1, - 'p': RuneS3, - 'q': RuneHLine, - 'r': RuneS7, - 's': RuneS9, - 't': RuneLTee, - 'u': RuneRTee, - 'v': RuneBTee, - 'w': RuneTTee, - 'x': RuneVLine, - 'y': RuneLEqual, - 'z': RuneGEqual, - '{': RunePi, - '|': RuneNEqual, - '}': RuneSterling, - '~': RuneBullet, -} - -// buildAcsMap builds a map of characters that we translate from Unicode to -// alternate character encodings. To do this, we use the standard VT100 ACS -// maps. This is only done if the terminal lacks support for Unicode; we -// always prefer to emit Unicode glyphs when we are able. -func (t *tScreen) buildAcsMap() { - acsstr := t.ti.AltChars - t.acs = make(map[rune]string) - for len(acsstr) > 2 { - srcv := acsstr[0] - dstv := string(acsstr[1]) - if r, ok := vtACSNames[srcv]; ok { - t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs - } - acsstr = acsstr[2:] - } -} - -func (t *tScreen) PostEventWait(ev Event) { - t.evch <- ev -} - -func (t *tScreen) PostEvent(ev Event) error { - select { - case t.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - -func (t *tScreen) clip(x, y int) (int, int) { - w, h := t.cells.Size() - if x < 0 { - x = 0 - } - if y < 0 { - y = 0 - } - if x > w-1 { - x = w - 1 - } - if y > h-1 { - y = h - 1 - } - return x, y -} - -// buildMouseEvent returns an event based on the supplied coordinates and button -// state. Note that the screen's mouse button state is updated based on the -// input to this function (i.e. it mutates the receiver). -func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse { - - // XTerm mouse events only report at most one button at a time, - // which may include a wheel button. Wheel motion events are - // reported as single impulses, while other button events are reported - // as separate press & release events. - - button := ButtonNone - mod := ModNone - - // Mouse wheel has bit 6 set, no release events. It should be noted - // that wheel events are sometimes misdelivered as mouse button events - // during a click-drag, so we debounce these, considering them to be - // button press events unless we see an intervening release event. - switch btn & 0x43 { - case 0: - button = Button1 - t.wasbtn = true - case 1: - button = Button2 - t.wasbtn = true - case 2: - button = Button3 - t.wasbtn = true - case 3: - button = ButtonNone - t.wasbtn = false - case 0x40: - if !t.wasbtn { - button = WheelUp - } else { - button = Button1 - } - case 0x41: - if !t.wasbtn { - button = WheelDown - } else { - button = Button2 - } - } - - if btn&0x4 != 0 { - mod |= ModShift - } - if btn&0x8 != 0 { - mod |= ModAlt - } - if btn&0x10 != 0 { - mod |= ModCtrl - } - - // Some terminals will report mouse coordinates outside the - // screen, especially with click-drag events. Clip the coordinates - // to the screen in that case. - x, y = t.clip(x, y) - - return NewEventMouse(x, y, button, mod) -} - -// parseSgrMouse attempts to locate an SGR mouse record at the start of the -// buffer. It returns true, true if it found one, and the associated bytes -// be removed from the buffer. It returns true, false if the buffer might -// contain such an event, but more bytes are necessary (partial match), and -// false, false if the content is definitely *not* an SGR mouse record. -func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { - - b := buf.Bytes() - - var x, y, btn, state int - dig := false - neg := false - motion := false - i := 0 - val := 0 - - for i = range b { - switch b[i] { - case '\x1b': - if state != 0 { - return false, false - } - state = 1 - - case '\x9b': - if state != 0 { - return false, false - } - state = 2 - - case '[': - if state != 1 { - return false, false - } - state = 2 - - case '<': - if state != 2 { - return false, false - } - val = 0 - dig = false - neg = false - state = 3 - - case '-': - if state != 3 && state != 4 && state != 5 { - return false, false - } - if dig || neg { - return false, false - } - neg = true // stay in state - - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - if state != 3 && state != 4 && state != 5 { - return false, false - } - val *= 10 - val += int(b[i] - '0') - dig = true // stay in state - - case ';': - if neg { - val = -val - } - switch state { - case 3: - btn, val = val, 0 - neg, dig, state = false, false, 4 - case 4: - x, val = val-1, 0 - neg, dig, state = false, false, 5 - default: - return false, false - } - - case 'm', 'M': - if state != 5 { - return false, false - } - if neg { - val = -val - } - y = val - 1 - - motion = (btn & 32) != 0 - btn &^= 32 - if b[i] == 'm' { - // mouse release, clear all buttons - btn |= 3 - btn &^= 0x40 - t.buttondn = false - } else if motion { - /* - * Some broken terminals appear to send - * mouse button one motion events, instead of - * encoding 35 (no buttons) into these events. - * We resolve these by looking for a non-motion - * event first. - */ - if !t.buttondn { - btn |= 3 - btn &^= 0x40 - } - } else { - t.buttondn = true - } - // consume the event bytes - for i >= 0 { - buf.ReadByte() - i-- - } - *evs = append(*evs, t.buildMouseEvent(x, y, btn)) - return true, true - } - } - - // incomplete & inconclusve at this point - return true, false -} - -// parseXtermMouse is like parseSgrMouse, but it parses a legacy -// X11 mouse record. -func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { - - b := buf.Bytes() - - state := 0 - btn := 0 - x := 0 - y := 0 - - for i := range b { - switch state { - case 0: - switch b[i] { - case '\x1b': - state = 1 - case '\x9b': - state = 2 - default: - return false, false - } - case 1: - if b[i] != '[' { - return false, false - } - state = 2 - case 2: - if b[i] != 'M' { - return false, false - } - state++ - case 3: - btn = int(b[i]) - state++ - case 4: - x = int(b[i]) - 32 - 1 - state++ - case 5: - y = int(b[i]) - 32 - 1 - for i >= 0 { - buf.ReadByte() - i-- - } - *evs = append(*evs, t.buildMouseEvent(x, y, btn)) - return true, true - } - } - return true, false -} - -func (t *tScreen) parseFunctionKey(buf *bytes.Buffer, evs *[]Event) (bool, bool) { - b := buf.Bytes() - partial := false - for e, k := range t.keycodes { - esc := []byte(e) - if (len(esc) == 1) && (esc[0] == '\x1b') { - continue - } - if bytes.HasPrefix(b, esc) { - // matched - var r rune - if len(esc) == 1 { - r = rune(b[0]) - } - mod := k.mod - if t.escaped { - mod |= ModAlt - t.escaped = false - } - *evs = append(*evs, NewEventKey(k.key, r, mod)) - for i := 0; i < len(esc); i++ { - buf.ReadByte() - } - return true, true - } - if bytes.HasPrefix(esc, b) { - partial = true - } - } - return partial, false -} - -func (t *tScreen) parseRune(buf *bytes.Buffer, evs *[]Event) (bool, bool) { - b := buf.Bytes() - if b[0] >= ' ' && b[0] <= 0x7F { - // printable ASCII easy to deal with -- no encodings - mod := ModNone - if t.escaped { - mod = ModAlt - t.escaped = false - } - *evs = append(*evs, NewEventKey(KeyRune, rune(b[0]), mod)) - buf.ReadByte() - return true, true - } - - if b[0] < 0x80 { - // Low numbered values are control keys, not runes. - return false, false - } - - utfb := make([]byte, 12) - for l := 1; l <= len(b); l++ { - t.decoder.Reset() - nout, nin, e := t.decoder.Transform(utfb, b[:l], true) - if e == transform.ErrShortSrc { - continue - } - if nout != 0 { - r, _ := utf8.DecodeRune(utfb[:nout]) - if r != utf8.RuneError { - mod := ModNone - if t.escaped { - mod = ModAlt - t.escaped = false - } - *evs = append(*evs, NewEventKey(KeyRune, r, mod)) - } - for nin > 0 { - buf.ReadByte() - nin-- - } - return true, true - } - } - // Looks like potential escape - return true, false -} - -func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) { - evs := t.collectEventsFromInput(buf, expire) - - for _, ev := range evs { - t.PostEventWait(ev) - } -} - -// Return an array of Events extracted from the supplied buffer. This is done -// while holding the screen's lock - the events can then be queued for -// application processing with the lock released. -func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event { - - res := make([]Event, 0, 20) - - t.Lock() - defer t.Unlock() - - for { - b := buf.Bytes() - if len(b) == 0 { - buf.Reset() - return res - } - - partials := 0 - - if part, comp := t.parseRune(buf, &res); comp { - continue - } else if part { - partials++ - } - - if part, comp := t.parseFunctionKey(buf, &res); comp { - continue - } else if part { - partials++ - } - - // Only parse mouse records if this term claims to have - // mouse support - - if t.ti.Mouse != "" { - if part, comp := t.parseXtermMouse(buf, &res); comp { - continue - } else if part { - partials++ - } - - if part, comp := t.parseSgrMouse(buf, &res); comp { - continue - } else if part { - partials++ - } - } - - if partials == 0 || expire { - if b[0] == '\x1b' { - if len(b) == 1 { - res = append(res, NewEventKey(KeyEsc, 0, ModNone)) - t.escaped = false - } else { - t.escaped = true - } - buf.ReadByte() - continue - } - // Nothing was going to match, or we timed out - // waiting for more data -- just deliver the characters - // to the app & let them sort it out. Possibly we - // should only do this for control characters like ESC. - by, _ := buf.ReadByte() - mod := ModNone - if t.escaped { - t.escaped = false - mod = ModAlt - } - res = append(res, NewEventKey(KeyRune, rune(by), mod)) - continue - } - - // well we have some partial data, wait until we get - // some more - break - } - - return res -} - -func (t *tScreen) mainLoop() { - buf := &bytes.Buffer{} - for { - select { - case <-t.quit: - close(t.indoneq) - return - case <-t.sigwinch: - t.Lock() - t.cx = -1 - t.cy = -1 - t.resize() - t.cells.Invalidate() - t.draw() - t.Unlock() - continue - case <-t.keytimer.C: - // If the timer fired, and the current time - // is after the expiration of the escape sequence, - // then we assume the escape sequence reached it's - // conclusion, and process the chunk independently. - // This lets us detect conflicts such as a lone ESC. - if buf.Len() > 0 { - if time.Now().After(t.keyexpire) { - t.scanInput(buf, true) - } - } - if buf.Len() > 0 { - if !t.keytimer.Stop() { - select { - case <-t.keytimer.C: - default: - } - } - t.keytimer.Reset(time.Millisecond * 50) - } - case chunk := <-t.keychan: - buf.Write(chunk) - t.keyexpire = time.Now().Add(time.Millisecond * 50) - t.scanInput(buf, false) - if !t.keytimer.Stop() { - select { - case <-t.keytimer.C: - default: - } - } - if buf.Len() > 0 { - t.keytimer.Reset(time.Millisecond * 50) - } - } - } -} - -func (t *tScreen) inputLoop() { - - for { - chunk := make([]byte, 128) - n, e := t.in.Read(chunk) - switch e { - case io.EOF: - case nil: - default: - t.PostEvent(NewEventError(e)) - return - } - t.keychan <- chunk[:n] - } -} - -func (t *tScreen) Sync() { - t.Lock() - t.cx = -1 - t.cy = -1 - if !t.fini { - t.resize() - t.clear = true - t.cells.Invalidate() - t.draw() - } - t.Unlock() -} - -func (t *tScreen) CharacterSet() string { - return t.charset -} - -func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) { - t.Lock() - t.fallback[orig] = fallback - t.Unlock() -} - -func (t *tScreen) UnregisterRuneFallback(orig rune) { - t.Lock() - delete(t.fallback, orig) - t.Unlock() -} - -func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool { - - if enc := t.encoder; enc != nil { - nb := make([]byte, 6) - ob := make([]byte, 6) - num := utf8.EncodeRune(ob, r) - - enc.Reset() - dst, _, err := enc.Transform(nb, ob[:num], true) - if dst != 0 && err == nil && nb[0] != '\x1A' { - return true - } - } - // Terminal fallbacks always permitted, since we assume they are - // basically nearly perfect renditions. - if _, ok := t.acs[r]; ok { - return true - } - if !checkFallbacks { - return false - } - if _, ok := t.fallback[r]; ok { - return true - } - return false -} - -func (t *tScreen) HasMouse() bool { - return len(t.mouse) != 0 -} - -func (t *tScreen) HasKey(k Key) bool { - if k == KeyRune { - return true - } - return t.keyexist[k] -} - -func (t *tScreen) Resize(int, int, int, int) {} diff --git a/vendor/github.com/gdamore/tcell/tscreen_bsd.go b/vendor/github.com/gdamore/tcell/tscreen_bsd.go deleted file mode 100644 index 25137165..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_bsd.go +++ /dev/null @@ -1,116 +0,0 @@ -// +build freebsd netbsd openbsd dragonfly - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "os" - "os/signal" - "syscall" - "unsafe" -) - -type termiosPrivate syscall.Termios - -func (t *tScreen) termioInit() error { - var e error - var newtios termiosPrivate - var fd uintptr - var tios uintptr - var ioc uintptr - t.tiosp = &termiosPrivate{} - - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { - goto failed - } - - tios = uintptr(unsafe.Pointer(t.tiosp)) - ioc = uintptr(syscall.TIOCGETA) - fd = uintptr(t.out.Fd()) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - newtios = *t.tiosp - newtios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | - syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | - syscall.ICRNL | syscall.IXON - newtios.Oflag &^= syscall.OPOST - newtios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | - syscall.ISIG | syscall.IEXTEN - newtios.Cflag &^= syscall.CSIZE | syscall.PARENB - newtios.Cflag |= syscall.CS8 - - tios = uintptr(unsafe.Pointer(&newtios)) - - ioc = uintptr(syscall.TIOCSETA) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - signal.Notify(t.sigwinch, syscall.SIGWINCH) - - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { - t.cells.Resize(w, h) - } - - return nil - -failed: - if t.in != nil { - t.in.Close() - } - if t.out != nil { - t.out.Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil { - fd := uintptr(t.out.Fd()) - ioc := uintptr(syscall.TIOCSETAF) - tios := uintptr(unsafe.Pointer(t.tiosp)) - syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0) - t.out.Close() - } - if t.in != nil { - t.in.Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - - fd := uintptr(t.out.Fd()) - dim := [4]uint16{} - dimp := uintptr(unsafe.Pointer(&dim)) - ioc := uintptr(syscall.TIOCGWINSZ) - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, - fd, ioc, dimp, 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dim[1]), int(dim[0]), nil -} diff --git a/vendor/github.com/gdamore/tcell/tscreen_darwin.go b/vendor/github.com/gdamore/tcell/tscreen_darwin.go deleted file mode 100644 index 6ee5fd5e..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_darwin.go +++ /dev/null @@ -1,137 +0,0 @@ -// +build darwin - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// The Darwin system is *almost* a real BSD system, but it suffers from -// a brain damaged TTY driver. This TTY driver does not actually -// wake up in poll() or similar calls, which means that we cannot reliably -// shut down the terminal without resorting to obscene custom C code -// and a dedicated poller thread. -// -// So instead, we do a best effort, and simply try to do the close in the -// background. Probably this will cause a leak of two goroutines and -// maybe also the file descriptor, meaning that applications on Darwin -// can't reinitialize the screen, but that's probably a very rare behavior, -// and accepting that is the best of some very poor alternative options. -// -// Maybe someday Apple will fix there tty driver, but its been broken for -// a long time (probably forever) so holding one's breath is contraindicated. - -import ( - "os" - "os/signal" - "syscall" - "unsafe" -) - -type termiosPrivate syscall.Termios - -func (t *tScreen) termioInit() error { - var e error - var newtios termiosPrivate - var fd uintptr - var tios uintptr - var ioc uintptr - t.tiosp = &termiosPrivate{} - - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { - goto failed - } - - tios = uintptr(unsafe.Pointer(t.tiosp)) - ioc = uintptr(syscall.TIOCGETA) - fd = uintptr(t.out.Fd()) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - newtios = *t.tiosp - newtios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | - syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | - syscall.ICRNL | syscall.IXON - newtios.Oflag &^= syscall.OPOST - newtios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | - syscall.ISIG | syscall.IEXTEN - newtios.Cflag &^= syscall.CSIZE | syscall.PARENB - newtios.Cflag |= syscall.CS8 - - tios = uintptr(unsafe.Pointer(&newtios)) - - ioc = uintptr(syscall.TIOCSETA) - if _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0); e1 != 0 { - e = e1 - goto failed - } - - signal.Notify(t.sigwinch, syscall.SIGWINCH) - - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { - t.cells.Resize(w, h) - } - - return nil - -failed: - if t.in != nil { - t.in.Close() - } - if t.out != nil { - t.out.Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil { - fd := uintptr(t.out.Fd()) - ioc := uintptr(syscall.TIOCSETAF) - tios := uintptr(unsafe.Pointer(t.tiosp)) - syscall.Syscall6(syscall.SYS_IOCTL, fd, ioc, tios, 0, 0, 0) - t.out.Close() - } - - // See above -- we background this call which might help, but - // really the tty is probably open. - - go func() { - if t.in != nil { - t.in.Close() - } - }() -} - -func (t *tScreen) getWinSize() (int, int, error) { - - fd := uintptr(t.out.Fd()) - dim := [4]uint16{} - dimp := uintptr(unsafe.Pointer(&dim)) - ioc := uintptr(syscall.TIOCGWINSZ) - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, - fd, ioc, dimp, 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dim[1]), int(dim[0]), nil -} diff --git a/vendor/github.com/gdamore/tcell/tscreen_linux.go b/vendor/github.com/gdamore/tcell/tscreen_linux.go deleted file mode 100644 index 19e58d60..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_linux.go +++ /dev/null @@ -1,119 +0,0 @@ -// +build linux - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "os" - "os/signal" - "syscall" - - "golang.org/x/sys/unix" -) - -type termiosPrivate struct { - tio *unix.Termios -} - -func (t *tScreen) termioInit() error { - var e error - var raw *unix.Termios - var tio *unix.Termios - - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { - goto failed - } - - tio, e = unix.IoctlGetTermios(int(t.out.Fd()), unix.TCGETS) - if e != nil { - goto failed - } - - t.tiosp = &termiosPrivate{tio: tio} - - // make a local copy, to make it raw - raw = &unix.Termios{ - Cflag: tio.Cflag, - Oflag: tio.Oflag, - Iflag: tio.Iflag, - Lflag: tio.Lflag, - Cc: tio.Cc, - } - raw.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | - unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) - raw.Oflag &^= unix.OPOST - raw.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | - unix.IEXTEN) - raw.Cflag &^= (unix.CSIZE | unix.PARENB) - raw.Cflag |= unix.CS8 - - // This is setup for blocking reads. In the past we attempted to - // use non-blocking reads, but now a separate input loop and timer - // copes with the problems we had on some systems (BSD/Darwin) - // where close hung forever. - raw.Cc[unix.VMIN] = 1 - raw.Cc[unix.VTIME] = 0 - - e = unix.IoctlSetTermios(int(t.out.Fd()), unix.TCSETS, raw) - if e != nil { - goto failed - } - - signal.Notify(t.sigwinch, syscall.SIGWINCH) - - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { - t.cells.Resize(w, h) - } - - return nil - -failed: - if t.in != nil { - t.in.Close() - } - if t.out != nil { - t.out.Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil && t.tiosp != nil { - unix.IoctlSetTermios(int(t.out.Fd()), unix.TCSETSF, t.tiosp.tio) - t.out.Close() - } - - if t.in != nil { - t.in.Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - - wsz, err := unix.IoctlGetWinsize(int(t.out.Fd()), unix.TIOCGWINSZ) - if err != nil { - return -1, -1, err - } - return int(wsz.Col), int(wsz.Row), nil -} diff --git a/vendor/github.com/gdamore/tcell/tscreen_solaris.go b/vendor/github.com/gdamore/tcell/tscreen_solaris.go deleted file mode 100644 index 9fa48b85..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_solaris.go +++ /dev/null @@ -1,117 +0,0 @@ -// +build solaris - -// Copyright 2019 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -import ( - "os" - "os/signal" - "syscall" - - "golang.org/x/sys/unix" -) - -type termiosPrivate struct { - tio *unix.Termios -} - -func (t *tScreen) termioInit() error { - var e error - var raw *unix.Termios - var tio *unix.Termios - - if t.in, e = os.OpenFile("/dev/tty", os.O_RDONLY, 0); e != nil { - goto failed - } - if t.out, e = os.OpenFile("/dev/tty", os.O_WRONLY, 0); e != nil { - goto failed - } - - tio, e = unix.IoctlGetTermios(int(t.out.Fd()), unix.TCGETS) - if e != nil { - goto failed - } - - t.tiosp = &termiosPrivate{tio: tio} - - // make a local copy, to make it raw - raw = &unix.Termios{ - Cflag: tio.Cflag, - Oflag: tio.Oflag, - Iflag: tio.Iflag, - Lflag: tio.Lflag, - Cc: tio.Cc, - } - - raw.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.INLCR | - unix.IGNCR | unix.ICRNL | unix.IXON) - raw.Oflag &^= unix.OPOST - raw.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) - raw.Cflag &^= (unix.CSIZE | unix.PARENB) - raw.Cflag |= unix.CS8 - - // This is setup for blocking reads. In the past we attempted to - // use non-blocking reads, but now a separate input loop and timer - // copes with the problems we had on some systems (BSD/Darwin) - // where close hung forever. - raw.Cc[unix.VMIN] = 1 - raw.Cc[unix.VTIME] = 0 - - e = unix.IoctlSetTermios(int(t.out.Fd()), unix.TCSETS, raw) - if e != nil { - goto failed - } - - signal.Notify(t.sigwinch, syscall.SIGWINCH) - - if w, h, e := t.getWinSize(); e == nil && w != 0 && h != 0 { - t.cells.Resize(w, h) - } - - return nil - -failed: - if t.in != nil { - t.in.Close() - } - if t.out != nil { - t.out.Close() - } - return e -} - -func (t *tScreen) termioFini() { - - signal.Stop(t.sigwinch) - - <-t.indoneq - - if t.out != nil && t.tiosp != nil { - unix.IoctlSetTermios(int(t.out.Fd()), unix.TCSETSF, t.tiosp.tio) - t.out.Close() - } - if t.in != nil { - t.in.Close() - } -} - -func (t *tScreen) getWinSize() (int, int, error) { - wsz, err := unix.IoctlGetWinsize(int(t.out.Fd()), unix.TIOCGWINSZ) - if err != nil { - return -1, -1, err - } - return int(wsz.Col), int(wsz.Row), nil -} diff --git a/vendor/github.com/gdamore/tcell/tscreen_stub.go b/vendor/github.com/gdamore/tcell/tscreen_stub.go deleted file mode 100644 index 91de26e1..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_stub.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build nacl plan9 - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// This stub file is for systems that have no termios. - -type termiosPrivate struct{} - -func (t *tScreen) termioInit() error { - return ErrNoScreen -} - -func (t *tScreen) termioFini() { -} - -func (t *tScreen) getWinSize() (int, int, error) { - return 0, 0, ErrNoScreen -} diff --git a/vendor/github.com/gdamore/tcell/tscreen_windows.go b/vendor/github.com/gdamore/tcell/tscreen_windows.go deleted file mode 100644 index daac0976..00000000 --- a/vendor/github.com/gdamore/tcell/tscreen_windows.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build windows - -// Copyright 2015 The TCell Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use 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 tcell - -// On Windows we don't have support for termios. We probably could, and -// may should, in a cygwin type environment. Its not clear how to make -// this all work nicely with both cygwin and Windows console, so we -// decline to do so here. - -func (t *tScreen) termioInit() error { - return ErrNoScreen -} - -func (t *tScreen) termioFini() { - return -} - -func (t *tScreen) getWinSize() (int, int, error) { - return 0, 0, ErrNoScreen -} - -func (t *tScreen) getCharset() string { - return "UTF-16LE" -} - -type termiosPrivate struct{} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/.gitignore b/vendor/github.com/lucasb-eyer/go-colorful/.gitignore deleted file mode 100644 index 47fda8ee..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Vim swap files -.*.sw? - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -# Code coverage stuff -coverage.out diff --git a/vendor/github.com/lucasb-eyer/go-colorful/.travis.yml b/vendor/github.com/lucasb-eyer/go-colorful/.travis.yml deleted file mode 100644 index 5bb2a827..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls -script: - - go test -v -covermode=count -coverprofile=coverage.out - - if [[ "$TRAVIS_PULL_REQUEST" = "false" ]]; then $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN; fi diff --git a/vendor/github.com/lucasb-eyer/go-colorful/LICENSE b/vendor/github.com/lucasb-eyer/go-colorful/LICENSE deleted file mode 100644 index 4e402a00..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2013 Lucas Beyer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/lucasb-eyer/go-colorful/README.md b/vendor/github.com/lucasb-eyer/go-colorful/README.md deleted file mode 100644 index 2cb0c424..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/README.md +++ /dev/null @@ -1,492 +0,0 @@ -go-colorful -=========== -A library for playing with colors in go (golang). - -[![Build Status](https://travis-ci.org/lucasb-eyer/go-colorful.svg?branch=master)](https://travis-ci.org/lucasb-eyer/go-colorful) -[![Coverage Status](https://coveralls.io/repos/github/lucasb-eyer/go-colorful/badge.svg?branch=master)](https://coveralls.io/github/lucasb-eyer/go-colorful?branch=master) - -Why? -==== -I love games. I make games. I love detail and I get lost in detail. -One such detail popped up during the development of [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/), -when we wanted the server to assign the players random colors. Sometimes -two players got very similar colors, which bugged me. The very same evening, -[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) was the top post -on HackerNews' frontpage and showed me how to Do It Right™. Last but not -least, there was no library for handling color spaces available in go. Colorful -does just that and implements Go's `color.Color` interface. - -What? -===== -Go-Colorful stores colors in RGB and provides methods from converting these to various color-spaces. Currently supported colorspaces are: - -- **RGB:** All three of Red, Green and Blue in [0..1]. -- **HSL:** Hue in [0..360], Saturation and Luminance in [0..1]. For legacy reasons; please forget that it exists. -- **HSV:** Hue in [0..360], Saturation and Value in [0..1]. You're better off using HCL, see below. -- **Hex RGB:** The "internet" color format, as in #FF00FF. -- **Linear RGB:** See [gamma correct rendering](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). -- **CIE-XYZ:** CIE's standard color space, almost in [0..1]. -- **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1] -- **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1]. -- **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better". -- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*a\*b\*. - -For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the -[D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white -by default but methods for using your own reference white are provided. - -A coordinate being *almost in* a range means that generally it is, but for very -bright colors and depending on the reference white, it might overflow this -range slightly. For example, C\* of #0000ff is 1.338. - -Unit-tests are provided. - -Nice, but what's it useful for? -------------------------------- - -- Converting color spaces. Some people like to do that. -- Blending (interpolating) between colors in a "natural" look by using the right colorspace. -- Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.) -- Generating gorgeous random palettes with distinct colors of a same temperature. - -What not (yet)? -=============== -There are a few features which are currently missing and might be useful. -I just haven't implemented them yet because I didn't have the need for it. -Pull requests welcome. - -- Sorting colors (potentially using above mentioned distances) - -So which colorspace should I use? -================================= -It depends on what you want to do. I think the folks from *I want hue* are -on-spot when they say that RGB fits to how *screens produce* color, CIE L\*a\*b\* -fits how *humans perceive* color and HCL fits how *humans think* colors. - -Whenever you'd use HSV, rather go for CIE-L\*C\*h°. for fixed lightness L\* and -chroma C\* values, the hue angle h° rotates through colors of the same -perceived brightness and intensity. - -How? -==== - -### Installing -Installing the library is as easy as - -```bash -$ go get github.com/lucasb-eyer/go-colorful -``` - -The package can then be used through an - -```go -import "github.com/lucasb-eyer/go-colorful" -``` - -### Basic usage - -Create a beautiful blue color using different source space: - -```go -// Any of the following should be the same -c := colorful.Color{0.313725, 0.478431, 0.721569} -c, err := colorful.Hex("#517AB8") -if err != nil { - log.Fatal(err) -} -c = colorful.Hsv(216.0, 0.56, 0.722) -c = colorful.Xyz(0.189165, 0.190837, 0.480248) -c = colorful.Xyy(0.219895, 0.221839, 0.190837) -c = colorful.Lab(0.507850, 0.040585,-0.370945) -c = colorful.Luv(0.507849,-0.194172,-0.567924) -c = colorful.Hcl(276.2440, 0.373160, 0.507849) -fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B) -``` - -And then converting this color back into various color spaces: - -```go -hex := c.Hex() -h, s, v := c.Hsv() -x, y, z := c.Xyz() -x, y, Y := c.Xyy() -l, a, b := c.Lab() -l, u, v := c.Luv() -h, c, l := c.Hcl() -``` - -Note that, because of Go's unfortunate choice of requiring an initial uppercase, -the name of the functions relating to the xyY space are just off. If you have -any good suggestion, please open an issue. (I don't consider XyY good.) - -### The `color.Color` interface -Because a `colorful.Color` implements Go's `color.Color` interface (found in the -`image/color` package), it can be used anywhere that expects a `color.Color`. - -Furthermore, you can convert anything that implements the `color.Color` interface -into a `colorful.Color` using the `MakeColor` function: - -```go -c, ok := colorful.MakeColor(color.Gray16{12345}) -``` - -**Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a -corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied -alpha colors, this means the RGB values are lost (set to 0) and it's impossible -to recover them. In such a case `MakeColor` will return `false` as its second value. - -### Comparing colors -In the RGB color space, the Euclidian distance between colors *doesn't* correspond -to visual/perceptual distance. This means that two pairs of colors which have the -same distance in RGB space can look much further apart. This is fixed by the -CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces. -Thus you should only compare colors in any of these space. -(Note that the distance in CIE-L\*a\*b\* and CIE-L\*C\*h° are the same, since it's the same space but in cylindrical coordinates) - -![Color distance comparison](doc/colordist/colordist.png) - -The two colors shown on the top look much more different than the two shown on -the bottom. Still, in RGB space, their distance is the same. -Here is a little example program which shows the distances between the top two -and bottom two colors in RGB, CIE-L\*a\*b\* and CIE-L\*u\*v\* space. You can find it in `doc/colordist/colordist.go`. - -```go -package main - -import "fmt" -import "github.com/lucasb-eyer/go-colorful" - -func main() { - c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0} - c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0} - c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0} - c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0} - - fmt.Printf("DistanceRgb: c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b)) - fmt.Printf("DistanceLab: c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b)) - fmt.Printf("DistanceLuv: c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b)) - fmt.Printf("DistanceCIE76: c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b)) - fmt.Printf("DistanceCIE94: c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b)) - fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b)) -} -``` - -Running the above program shows that you should always prefer any of the CIE distances: - -```bash -$ go run colordist.go -DistanceRgb: c1: 0.3803921568627451 and c2: 0.3858713931171159 -DistanceLab: c1: 0.32048458312798056 and c2: 0.24397151758565272 -DistanceLuv: c1: 0.5134369614199698 and c2: 0.2568692839860636 -DistanceCIE76: c1: 0.32048458312798056 and c2: 0.24397151758565272 -DistanceCIE94: c1: 0.19799168128511324 and c2: 0.12207136371167401 -DistanceCIEDE2000: c1: 0.17274551120971166 and c2: 0.10665210031428465 -``` - -It also shows that `DistanceLab` is more formally known as `DistanceCIE76` and -has been superseded by the slightly more accurate, but much more expensive -`DistanceCIE94` and `DistanceCIEDE2000`. - -Note that `AlmostEqualRgb` is provided mainly for (unit-)testing purposes. Use -it only if you really know what you're doing. It will eat your cat. - -### Blending colors -Blending is highly connected to distance, since it basically "walks through" the -colorspace thus, if the colorspace maps distances well, the walk is "smooth". - -Colorful comes with blending functions in RGB, HSV and any of the LAB spaces. -Of course, you'd rather want to use the blending functions of the LAB spaces since -these spaces map distances well but, just in case, here is an example showing -you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces: - -![Blending colors in different spaces.](doc/colorblend/colorblend.png) - -What you see is that HSV is really bad: it adds some green, which is not present -in the original colors at all! RGB is much better, but it stays light a little -too long. LUV and LAB both hit the right lightness but LAB has a little more -color. HCL works in the same vein as HSV (both cylindrical interpolations) but -it does it right in that there is no green appearing and the lighthness changes -in a linear manner. - -While this seems all good, you need to know one thing: When interpolating in any -of the CIE color spaces, you might get invalid RGB colors! This is important if -the starting and ending colors are user-input or random. An example of where this -happens is when blending between `#eeef61` and `#1e3140`: - -![Invalid RGB colors may crop up when blending in CIE spaces.](doc/colorblend/invalid.png) - -You can test whether a color is a valid RGB color by calling the `IsValid` method -and indeed, calling IsValid will return false for the redish colors on the bottom. -One way to "fix" this is to get a valid color close to the invalid one by calling -`Clamped`, which always returns a nearby valid color. Doing this, we get the -following result, which is satisfactory: - -![Fixing invalid RGB colors by clamping them to the valid range.](doc/colorblend/clamped.png) - -The following is the code creating the above three images; it can be found in `doc/colorblend/colorblend.go` - -```go -package main - -import "fmt" -import "github.com/lucasb-eyer/go-colorful" -import "image" -import "image/draw" -import "image/png" -import "os" - -func main() { - blocks := 10 - blockw := 40 - img := image.NewRGBA(image.Rect(0,0,blocks*blockw,200)) - - c1, _ := colorful.Hex("#fdffcc") - c2, _ := colorful.Hex("#242a42") - - // Use these colors to get invalid RGB in the gradient. - //c1, _ := colorful.Hex("#EEEF61") - //c2, _ := colorful.Hex("#1E3140") - - for i := 0 ; i < blocks ; i++ { - draw.Draw(img, image.Rect(i*blockw, 0,(i+1)*blockw, 40), &image.Uniform{c1.BlendHsv(c2, float64(i)/float64(blocks-1))}, image.ZP, draw.Src) - draw.Draw(img, image.Rect(i*blockw, 40,(i+1)*blockw, 80), &image.Uniform{c1.BlendLuv(c2, float64(i)/float64(blocks-1))}, image.ZP, draw.Src) - draw.Draw(img, image.Rect(i*blockw, 80,(i+1)*blockw,120), &image.Uniform{c1.BlendRgb(c2, float64(i)/float64(blocks-1))}, image.ZP, draw.Src) - draw.Draw(img, image.Rect(i*blockw,120,(i+1)*blockw,160), &image.Uniform{c1.BlendLab(c2, float64(i)/float64(blocks-1))}, image.ZP, draw.Src) - draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1))}, image.ZP, draw.Src) - - // This can be used to "fix" invalid colors in the gradient. - //draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1)).Clamped()}, image.ZP, draw.Src) - } - - toimg, err := os.Create("colorblend.png") - if err != nil { - fmt.Printf("Error: %v", err) - return - } - defer toimg.Close() - - png.Encode(toimg, img) -} -``` - -#### Generating color gradients -A very common reason to blend colors is creating gradients. There is an example -program in [doc/gradientgen.go](doc/gradientgen/gradientgen.go); it doesn't use any API -which hasn't been used in the previous example code, so I won't bother pasting -the code in here. Just look at that gorgeous gradient it generated in HCL space: - -!["Spectral" colorbrewer gradient in HCL space.](doc/gradientgen/gradientgen.png) - -### Getting random colors -It is sometimes necessary to generate random colors. You could simply do this -on your own by generating colors with random values. By restricting the random -values to a range smaller than [0..1] and using a space such as CIE-H\*C\*l° or -HSV, you can generate both random shades of a color or random colors of a -lightness: - -```go -random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7) -random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4) -random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4) -``` - -Since getting random "warm" and "happy" colors is quite a common task, there -are some helper functions: - -```go -colorful.WarmColor() -colorful.HappyColor() -colorful.FastWarmColor() -colorful.FastHappyColor() -``` - -The ones prefixed by `Fast` are faster but less coherent since they use the HSV -space as opposed to the regular ones which use CIE-L\*C\*h° space. The -following picture shows the warm colors in the top two rows and happy colors -in the bottom two rows. Within these, the first is the regular one and the -second is the fast one. - -![Warm, fast warm, happy and fast happy random colors, respectively.](doc/colorgens/colorgens.png) - -Don't forget to initialize the random seed! You can see the code used for -generating this picture in `doc/colorgens/colorgens.go`. - -### Getting random palettes -As soon as you need to generate more than one random color, you probably want -them to be distinguishible. Playing against an opponent which has almost the -same blue as I do is not fun. This is where random palettes can help. - -These palettes are generated using an algorithm which ensures that all colors -on the palette are as distinguishible as possible. Again, there is a `Fast` -method which works in HSV and is less perceptually uniform and a non-`Fast` -method which works in CIE spaces. For more theory on `SoftPalette`, check out -[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet -again, there is a `Happy` and a `Warm` version, which do what you expect, but -now there is an additional `Soft` version, which is more configurable: you can -give a constraint on the color space in order to get colors within a certain *feel*. - -Let's start with the simple methods first, all they take is the amount of -colors to generate, which could, for example, be the player count. They return -an array of `colorful.Color` objects: - -```go -pal1, err1 := colorful.WarmPalette(10) -pal2 := colorful.FastWarmPalette(10) -pal3, err3 := colorful.HappyPalette(10) -pal4 := colorful.FastHappyPalette(10) -pal5, err5 := colorful.SoftPalette(10) -``` - -Note that the non-fast methods *may* fail if you ask for way too many colors. -Let's move on to the advanced one, namely `SoftPaletteEx`. Besides the color -count, this function takes a `SoftPaletteSettings` object as argument. The -interesting part here is its `CheckColor` member, which is a boolean function -taking three floating points as arguments: `l`, `a` and `b`. This function -should return `true` for colors which lie within the region you want and `false` -otherwise. The other members are `Iteration`, which should be within [5..100] -where higher means slower but more exact palette, and `ManySamples` which you -should set to `true` in case your `CheckColor` constraint rejects a large part -of the color space. - -For example, to create a palette of 10 brownish colors, you'd call it like this: - -```go -func isbrowny(l, a, b float64) bool { - h, c, L := colorful.LabToHcl(l, a, b) - return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5 -} -// Since the above function is pretty restrictive, we set ManySamples to true. -brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true}) -``` - -The following picture shows the palettes generated by all of these methods -(sourcecode in `doc/palettegens/palettegens.go`), in the order they were presented, i.e. -from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`, -`SoftEx(isbrowny)`. All of them contain some randomness, so YMMV. - -![All example palettes](doc/palettegens/palettegens.png) - -Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go). - -### Sorting colors -TODO: Sort using dist fn. - -### Using linear RGB for computations -There are two methods for transforming RGB<->Linear RGB: a fast and almost precise one, -and a slow and precise one. - -```go -r, g, b := colorful.Hex("#FF0000").FastLinearRgb() -``` - -TODO: describe some more. - -### Want to use some other reference point? - -```go -c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50) -l, a, b := c.LabWhiteRef(colorful.D50) -``` - -### Reading and writing colors from databases - -The type `HexColor` makes it easy to store colors as strings in a database. It -implements the [https://godoc.org/database/sql#Scanner](database/sql.Scanner) -and [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value) -interfaces which provide automatic type conversion. - -Example: - -```go -var hc HexColor -_, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc) -// hc == HexColor{R: 1, G: 0, B: 0}; err == nil -``` - -FAQ -=== - -### Q: I get all f!@#ed up values! Your library sucks! -A: You probably provided values in the wrong range. For example, RGB values are -expected to reside between 0 and 1, *not* between 0 and 255. Normalize your colors. - -### Q: Lab/Luv/HCl seem broken! Your library sucks! -They look like this: - - - -A: You're likely trying to generate and display colors that can't be represented by RGB, -and thus monitors. When you're trying to convert, say, `HCL(190.0, 1.0, 1.0).RGB255()`, -you're asking for RGB values of `(-2105.254 300.680 286.185)`, which clearly don't exist, -and the `RGB255` function just casts these numbers to `uint8`, creating wrap-around and -what looks like a completely broken gradient. What you want to do, is either use more -reasonable values of colors which actually exist in RGB, or just `Clamp()` the resulting -color to its nearest existing one, living with the consequences: -`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`. It will look something like this: - - - -[Here's an issue going in-depth about this](https://github.com/lucasb-eyer/go-colorful/issues/14), -as well as [my answer](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385), -both with code and pretty pictures. Also note that this was somewhat covered above in the -["Blending colors" section](https://github.com/lucasb-eyer/go-colorful#blending-colors). - -### Q: In a tight loop, conversion to Lab/Luv/HCl/... are slooooow! -A: Yes, they are. -This library aims for correctness, readability, and modularity; it wasn't written with speed in mind. -A large part of the slowness comes from these conversions going through `LinearRgb` which uses powers. -I implemented a fast approximation to `LinearRgb` called `FastLinearRgb` by using Taylor approximations. -The approximation is roughly 5x faster and precise up to roughly 0.5%, -the major caveat being that if the input values are outside the range 0-1, accuracy drops dramatically. -You can use these in your conversions as follows: - -```go -col := // Get your color somehow -l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb())) -``` - -If you need faster versions of `Distance*` and `Blend*` that make use of this fast approximation, -feel free to implement them and open a pull-request, I'll happily accept. - -The derivation of these functions can be followed in [this Jupyter notebook](doc/LinearRGB Approximations.ipynb). -Here's the main figure showing the approximation quality: - -![approximation quality](doc/approx-quality.png) - -More speed could be gained by using SIMD instructions in many places. -You can also get more speed for specific conversions by approximating the full conversion function, -but that is outside the scope of this library. -Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation, -see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details. - -### Q: Why would `MakeColor` ever fail!? -A: `MakeColor` fails when the alpha channel is zero. In that case, the -conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21) -as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface) -section above. - -Who? -==== - -This library has been developed by Lucas Beyer with contributions from -Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli). - -Release Notes -============= - -### Version 1.0 -- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](https://github.com/lucasb-eyer/go-colorful#the-colorcolor-interface) section and [this FAQ entry](https://github.com/lucasb-eyer/go-colorful#q-why-would-makecolor-ever-fail) for details. - -### Version 0.9 -- Initial version number after having ignored versioning for a long time :) - -License: MIT -============ -Copyright (c) 2013 Lucas Beyer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go b/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go deleted file mode 100644 index 2e2e49e1..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/colorgens.go +++ /dev/null @@ -1,55 +0,0 @@ -// Various ways to generate single random colors - -package colorful - -import ( - "math/rand" -) - -// Creates a random dark, "warm" color through a restricted HSV space. -func FastWarmColor() Color { - return Hsv( - rand.Float64()*360.0, - 0.5+rand.Float64()*0.3, - 0.3+rand.Float64()*0.3) -} - -// Creates a random dark, "warm" color through restricted HCL space. -// This is slower than FastWarmColor but will likely give you colors which have -// the same "warmness" if you run it many times. -func WarmColor() (c Color) { - for c = randomWarm(); !c.IsValid(); c = randomWarm() { - } - return -} - -func randomWarm() Color { - return Hcl( - rand.Float64()*360.0, - 0.1+rand.Float64()*0.3, - 0.2+rand.Float64()*0.3) -} - -// Creates a random bright, "pimpy" color through a restricted HSV space. -func FastHappyColor() Color { - return Hsv( - rand.Float64()*360.0, - 0.7+rand.Float64()*0.3, - 0.6+rand.Float64()*0.3) -} - -// Creates a random bright, "pimpy" color through restricted HCL space. -// This is slower than FastHappyColor but will likely give you colors which -// have the same "brightness" if you run it many times. -func HappyColor() (c Color) { - for c = randomPimp(); !c.IsValid(); c = randomPimp() { - } - return -} - -func randomPimp() Color { - return Hcl( - rand.Float64()*360.0, - 0.5+rand.Float64()*0.3, - 0.5+rand.Float64()*0.3) -} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/colors.go b/vendor/github.com/lucasb-eyer/go-colorful/colors.go deleted file mode 100644 index 29870df0..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/colors.go +++ /dev/null @@ -1,903 +0,0 @@ -// The colorful package provides all kinds of functions for working with colors. -package colorful - -import ( - "fmt" - "image/color" - "math" -) - -// A color is stored internally using sRGB (standard RGB) values in the range 0-1 -type Color struct { - R, G, B float64 -} - -// Implement the Go color.Color interface. -func (col Color) RGBA() (r, g, b, a uint32) { - r = uint32(col.R*65535.0 + 0.5) - g = uint32(col.G*65535.0 + 0.5) - b = uint32(col.B*65535.0 + 0.5) - a = 0xFFFF - return -} - -// Constructs a colorful.Color from something implementing color.Color -func MakeColor(col color.Color) (Color, bool) { - r, g, b, a := col.RGBA() - if a == 0 { - return Color{0, 0, 0}, false - } - - // Since color.Color is alpha pre-multiplied, we need to divide the - // RGB values by alpha again in order to get back the original RGB. - r *= 0xffff - r /= a - g *= 0xffff - g /= a - b *= 0xffff - b /= a - - return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true -} - -// Might come in handy sometimes to reduce boilerplate code. -func (col Color) RGB255() (r, g, b uint8) { - r = uint8(col.R*255.0 + 0.5) - g = uint8(col.G*255.0 + 0.5) - b = uint8(col.B*255.0 + 0.5) - return -} - -// This is the tolerance used when comparing colors using AlmostEqualRgb. -const Delta = 1.0 / 255.0 - -// This is the default reference white point. -var D65 = [3]float64{0.95047, 1.00000, 1.08883} - -// And another one. -var D50 = [3]float64{0.96422, 1.00000, 0.82521} - -// Checks whether the color exists in RGB space, i.e. all values are in [0..1] -func (c Color) IsValid() bool { - return 0.0 <= c.R && c.R <= 1.0 && - 0.0 <= c.G && c.G <= 1.0 && - 0.0 <= c.B && c.B <= 1.0 -} - -func clamp01(v float64) float64 { - return math.Max(0.0, math.Min(v, 1.0)) -} - -// Returns Clamps the color into valid range, clamping each value to [0..1] -// If the color is valid already, this is a no-op. -func (c Color) Clamped() Color { - return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)} -} - -func sq(v float64) float64 { - return v * v -} - -func cub(v float64) float64 { - return v * v * v -} - -// DistanceRgb computes the distance between two colors in RGB space. -// This is not a good measure! Rather do it in Lab space. -func (c1 Color) DistanceRgb(c2 Color) float64 { - return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B)) -} - -// Check for equality between colors within the tolerance Delta (1/255). -func (c1 Color) AlmostEqualRgb(c2 Color) bool { - return math.Abs(c1.R-c2.R)+ - math.Abs(c1.G-c2.G)+ - math.Abs(c1.B-c2.B) < 3.0*Delta -} - -// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl. -func (c1 Color) BlendRgb(c2 Color, t float64) Color { - return Color{c1.R + t*(c2.R-c1.R), - c1.G + t*(c2.G-c1.G), - c1.B + t*(c2.B-c1.B)} -} - -// Utility used by Hxx color-spaces for interpolating between two angles in [0,360]. -func interp_angle(a0, a1, t float64) float64 { - // Based on the answer here: http://stackoverflow.com/a/14498790/2366315 - // With potential proof that it works here: http://math.stackexchange.com/a/2144499 - delta := math.Mod(math.Mod(a1-a0, 360.0)+540, 360.0) - 180.0 - return math.Mod(a0+t*delta+360.0, 360.0) -} - -/// HSV /// -/////////// -// From http://en.wikipedia.org/wiki/HSL_and_HSV -// Note that h is in [0..360] and s,v in [0..1] - -// Hsv returns the Hue [0..360], Saturation and Value [0..1] of the color. -func (col Color) Hsv() (h, s, v float64) { - min := math.Min(math.Min(col.R, col.G), col.B) - v = math.Max(math.Max(col.R, col.G), col.B) - C := v - min - - s = 0.0 - if v != 0.0 { - s = C / v - } - - h = 0.0 // We use 0 instead of undefined as in wp. - if min != v { - if v == col.R { - h = math.Mod((col.G-col.B)/C, 6.0) - } - if v == col.G { - h = (col.B-col.R)/C + 2.0 - } - if v == col.B { - h = (col.R-col.G)/C + 4.0 - } - h *= 60.0 - if h < 0.0 { - h += 360.0 - } - } - return -} - -// Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1] -func Hsv(H, S, V float64) Color { - Hp := H / 60.0 - C := V * S - X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0)) - - m := V - C - r, g, b := 0.0, 0.0, 0.0 - - switch { - case 0.0 <= Hp && Hp < 1.0: - r = C - g = X - case 1.0 <= Hp && Hp < 2.0: - r = X - g = C - case 2.0 <= Hp && Hp < 3.0: - g = C - b = X - case 3.0 <= Hp && Hp < 4.0: - g = X - b = C - case 4.0 <= Hp && Hp < 5.0: - r = X - b = C - case 5.0 <= Hp && Hp < 6.0: - r = C - b = X - } - - return Color{m + r, m + g, m + b} -} - -// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl. -func (c1 Color) BlendHsv(c2 Color, t float64) Color { - h1, s1, v1 := c1.Hsv() - h2, s2, v2 := c2.Hsv() - - // We know that h are both in [0..360] - return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1)) -} - -/// HSL /// -/////////// - -// Hsl returns the Hue [0..360], Saturation [0..1], and Luminance (lightness) [0..1] of the color. -func (col Color) Hsl() (h, s, l float64) { - min := math.Min(math.Min(col.R, col.G), col.B) - max := math.Max(math.Max(col.R, col.G), col.B) - - l = (max + min) / 2 - - if min == max { - s = 0 - h = 0 - } else { - if l < 0.5 { - s = (max - min) / (max + min) - } else { - s = (max - min) / (2.0 - max - min) - } - - if max == col.R { - h = (col.G - col.B) / (max - min) - } else if max == col.G { - h = 2.0 + (col.B-col.R)/(max-min) - } else { - h = 4.0 + (col.R-col.G)/(max-min) - } - - h *= 60 - - if h < 0 { - h += 360 - } - } - - return -} - -// Hsl creates a new Color given a Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1] -func Hsl(h, s, l float64) Color { - if s == 0 { - return Color{l, l, l} - } - - var r, g, b float64 - var t1 float64 - var t2 float64 - var tr float64 - var tg float64 - var tb float64 - - if l < 0.5 { - t1 = l * (1.0 + s) - } else { - t1 = l + s - l*s - } - - t2 = 2*l - t1 - h /= 360 - tr = h + 1.0/3.0 - tg = h - tb = h - 1.0/3.0 - - if tr < 0 { - tr++ - } - if tr > 1 { - tr-- - } - if tg < 0 { - tg++ - } - if tg > 1 { - tg-- - } - if tb < 0 { - tb++ - } - if tb > 1 { - tb-- - } - - // Red - if 6*tr < 1 { - r = t2 + (t1-t2)*6*tr - } else if 2*tr < 1 { - r = t1 - } else if 3*tr < 2 { - r = t2 + (t1-t2)*(2.0/3.0-tr)*6 - } else { - r = t2 - } - - // Green - if 6*tg < 1 { - g = t2 + (t1-t2)*6*tg - } else if 2*tg < 1 { - g = t1 - } else if 3*tg < 2 { - g = t2 + (t1-t2)*(2.0/3.0-tg)*6 - } else { - g = t2 - } - - // Blue - if 6*tb < 1 { - b = t2 + (t1-t2)*6*tb - } else if 2*tb < 1 { - b = t1 - } else if 3*tb < 2 { - b = t2 + (t1-t2)*(2.0/3.0-tb)*6 - } else { - b = t2 - } - - return Color{r, g, b} -} - -/// Hex /// -/////////// - -// Hex returns the hex "html" representation of the color, as in #ff0080. -func (col Color) Hex() string { - // Add 0.5 for rounding - return fmt.Sprintf("#%02x%02x%02x", uint8(col.R*255.0+0.5), uint8(col.G*255.0+0.5), uint8(col.B*255.0+0.5)) -} - -// Hex parses a "html" hex color-string, either in the 3 "#f0c" or 6 "#ff1034" digits form. -func Hex(scol string) (Color, error) { - format := "#%02x%02x%02x" - factor := 1.0 / 255.0 - if len(scol) == 4 { - format = "#%1x%1x%1x" - factor = 1.0 / 15.0 - } - - var r, g, b uint8 - n, err := fmt.Sscanf(scol, format, &r, &g, &b) - if err != nil { - return Color{}, err - } - if n != 3 { - return Color{}, fmt.Errorf("color: %v is not a hex-color", scol) - } - - return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil -} - -/// Linear /// -////////////// -// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ -// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html - -func linearize(v float64) float64 { - if v <= 0.04045 { - return v / 12.92 - } - return math.Pow((v+0.055)/1.055, 2.4) -} - -// LinearRgb converts the color into the linear RGB space (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). -func (col Color) LinearRgb() (r, g, b float64) { - r = linearize(col.R) - g = linearize(col.G) - b = linearize(col.B) - return -} - -// A much faster and still quite precise linearization using a 6th-order Taylor approximation. -// See the accompanying Jupyter notebook for derivation of the constants. -func linearize_fast(v float64) float64 { - v1 := v - 0.5 - v2 := v1 * v1 - v3 := v2 * v1 - v4 := v2 * v2 - //v5 := v3*v2 - return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5 -} - -// FastLinearRgb is much faster than and almost as accurate as LinearRgb. -// BUT it is important to NOTE that they only produce good results for valid colors r,g,b in [0,1]. -func (col Color) FastLinearRgb() (r, g, b float64) { - r = linearize_fast(col.R) - g = linearize_fast(col.G) - b = linearize_fast(col.B) - return -} - -func delinearize(v float64) float64 { - if v <= 0.0031308 { - return 12.92 * v - } - return 1.055*math.Pow(v, 1.0/2.4) - 0.055 -} - -// LinearRgb creates an sRGB color out of the given linear RGB color (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/). -func LinearRgb(r, g, b float64) Color { - return Color{delinearize(r), delinearize(g), delinearize(b)} -} - -func delinearize_fast(v float64) float64 { - // This function (fractional root) is much harder to linearize, so we need to split. - if v > 0.2 { - v1 := v - 0.6 - v2 := v1 * v1 - v3 := v2 * v1 - v4 := v2 * v2 - v5 := v3 * v2 - return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5 - } else if v > 0.03 { - v1 := v - 0.115 - v2 := v1 * v1 - v3 := v2 * v1 - v4 := v2 * v2 - v5 := v3 * v2 - return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5 - } else { - v1 := v - 0.015 - v2 := v1 * v1 - v3 := v2 * v1 - v4 := v2 * v2 - v5 := v3 * v2 - // You can clearly see from the involved constants that the low-end is highly nonlinear. - return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5 - } -} - -// FastLinearRgb is much faster than and almost as accurate as LinearRgb. -// BUT it is important to NOTE that they only produce good results for valid inputs r,g,b in [0,1]. -func FastLinearRgb(r, g, b float64) Color { - return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)} -} - -// XyzToLinearRgb converts from CIE XYZ-space to Linear RGB space. -func XyzToLinearRgb(x, y, z float64) (r, g, b float64) { - r = 3.2404542*x - 1.5371385*y - 0.4985314*z - g = -0.9692660*x + 1.8760108*y + 0.0415560*z - b = 0.0556434*x - 0.2040259*y + 1.0572252*z - return -} - -func LinearRgbToXyz(r, g, b float64) (x, y, z float64) { - x = 0.4124564*r + 0.3575761*g + 0.1804375*b - y = 0.2126729*r + 0.7151522*g + 0.0721750*b - z = 0.0193339*r + 0.1191920*g + 0.9503041*b - return -} - -/// XYZ /// -/////////// -// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/ - -func (col Color) Xyz() (x, y, z float64) { - return LinearRgbToXyz(col.LinearRgb()) -} - -func Xyz(x, y, z float64) Color { - return LinearRgb(XyzToLinearRgb(x, y, z)) -} - -/// xyY /// -/////////// -// http://www.brucelindbloom.com/Eqn_XYZ_to_xyY.html - -// Well, the name is bad, since it's xyY but Golang needs me to start with a -// capital letter to make the method public. -func XyzToXyy(X, Y, Z float64) (x, y, Yout float64) { - return XyzToXyyWhiteRef(X, Y, Z, D65) -} - -func XyzToXyyWhiteRef(X, Y, Z float64, wref [3]float64) (x, y, Yout float64) { - Yout = Y - N := X + Y + Z - if math.Abs(N) < 1e-14 { - // When we have black, Bruce Lindbloom recommends to use - // the reference white's chromacity for x and y. - x = wref[0] / (wref[0] + wref[1] + wref[2]) - y = wref[1] / (wref[0] + wref[1] + wref[2]) - } else { - x = X / N - y = Y / N - } - return -} - -func XyyToXyz(x, y, Y float64) (X, Yout, Z float64) { - Yout = Y - - if -1e-14 < y && y < 1e-14 { - X = 0.0 - Z = 0.0 - } else { - X = Y / y * x - Z = Y / y * (1.0 - x - y) - } - - return -} - -// Converts the given color to CIE xyY space using D65 as reference white. -// (Note that the reference white is only used for black input.) -// x, y and Y are in [0..1] -func (col Color) Xyy() (x, y, Y float64) { - return XyzToXyy(col.Xyz()) -} - -// Converts the given color to CIE xyY space, taking into account -// a given reference white. (i.e. the monitor's white) -// (Note that the reference white is only used for black input.) -// x, y and Y are in [0..1] -func (col Color) XyyWhiteRef(wref [3]float64) (x, y, Y float64) { - X, Y2, Z := col.Xyz() - return XyzToXyyWhiteRef(X, Y2, Z, wref) -} - -// Generates a color by using data given in CIE xyY space. -// x, y and Y are in [0..1] -func Xyy(x, y, Y float64) Color { - return Xyz(XyyToXyz(x, y, Y)) -} - -/// L*a*b* /// -////////////// -// http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions -// For L*a*b*, we need to L*a*b*<->XYZ->RGB and the first one is device dependent. - -func lab_f(t float64) float64 { - if t > 6.0/29.0*6.0/29.0*6.0/29.0 { - return math.Cbrt(t) - } - return t/3.0*29.0/6.0*29.0/6.0 + 4.0/29.0 -} - -func XyzToLab(x, y, z float64) (l, a, b float64) { - // Use D65 white as reference point by default. - // http://www.fredmiranda.com/forum/topic/1035332 - // http://en.wikipedia.org/wiki/Standard_illuminant - return XyzToLabWhiteRef(x, y, z, D65) -} - -func XyzToLabWhiteRef(x, y, z float64, wref [3]float64) (l, a, b float64) { - fy := lab_f(y / wref[1]) - l = 1.16*fy - 0.16 - a = 5.0 * (lab_f(x/wref[0]) - fy) - b = 2.0 * (fy - lab_f(z/wref[2])) - return -} - -func lab_finv(t float64) float64 { - if t > 6.0/29.0 { - return t * t * t - } - return 3.0 * 6.0 / 29.0 * 6.0 / 29.0 * (t - 4.0/29.0) -} - -func LabToXyz(l, a, b float64) (x, y, z float64) { - // D65 white (see above). - return LabToXyzWhiteRef(l, a, b, D65) -} - -func LabToXyzWhiteRef(l, a, b float64, wref [3]float64) (x, y, z float64) { - l2 := (l + 0.16) / 1.16 - x = wref[0] * lab_finv(l2+a/5.0) - y = wref[1] * lab_finv(l2) - z = wref[2] * lab_finv(l2-b/2.0) - return -} - -// Converts the given color to CIE L*a*b* space using D65 as reference white. -func (col Color) Lab() (l, a, b float64) { - return XyzToLab(col.Xyz()) -} - -// Converts the given color to CIE L*a*b* space, taking into account -// a given reference white. (i.e. the monitor's white) -func (col Color) LabWhiteRef(wref [3]float64) (l, a, b float64) { - x, y, z := col.Xyz() - return XyzToLabWhiteRef(x, y, z, wref) -} - -// Generates a color by using data given in CIE L*a*b* space using D65 as reference white. -// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding -// valid RGB values, check the FAQ in the README if you're unsure. -func Lab(l, a, b float64) Color { - return Xyz(LabToXyz(l, a, b)) -} - -// Generates a color by using data given in CIE L*a*b* space, taking -// into account a given reference white. (i.e. the monitor's white) -func LabWhiteRef(l, a, b float64, wref [3]float64) Color { - return Xyz(LabToXyzWhiteRef(l, a, b, wref)) -} - -// DistanceLab is a good measure of visual similarity between two colors! -// A result of 0 would mean identical colors, while a result of 1 or higher -// means the colors differ a lot. -func (c1 Color) DistanceLab(c2 Color) float64 { - l1, a1, b1 := c1.Lab() - l2, a2, b2 := c2.Lab() - return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2)) -} - -// That's actually the same, but I don't want to break code. -func (c1 Color) DistanceCIE76(c2 Color) float64 { - return c1.DistanceLab(c2) -} - -// Uses the CIE94 formula to calculate color distance. More accurate than -// DistanceLab, but also more work. -func (cl Color) DistanceCIE94(cr Color) float64 { - l1, a1, b1 := cl.Lab() - l2, a2, b2 := cr.Lab() - - // NOTE: Since all those formulas expect L,a,b values 100x larger than we - // have them in this library, we either need to adjust all constants - // in the formula, or convert the ranges of L,a,b before, and then - // scale the distances down again. The latter is less error-prone. - l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0 - l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0 - - kl := 1.0 // 2.0 for textiles - kc := 1.0 - kh := 1.0 - k1 := 0.045 // 0.048 for textiles - k2 := 0.015 // 0.014 for textiles. - - deltaL := l1 - l2 - c1 := math.Sqrt(sq(a1) + sq(b1)) - c2 := math.Sqrt(sq(a2) + sq(b2)) - deltaCab := c1 - c2 - - // Not taking Sqrt here for stability, and it's unnecessary. - deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab) - sl := 1.0 - sc := 1.0 + k1*c1 - sh := 1.0 + k2*c1 - - vL2 := sq(deltaL / (kl * sl)) - vC2 := sq(deltaCab / (kc * sc)) - vH2 := deltaHab2 / sq(kh*sh) - - return math.Sqrt(vL2+vC2+vH2) * 0.01 // See above. -} - -// DistanceCIEDE2000 uses the Delta E 2000 formula to calculate color -// distance. It is more expensive but more accurate than both DistanceLab -// and DistanceCIE94. -func (cl Color) DistanceCIEDE2000(cr Color) float64 { - return cl.DistanceCIEDE2000klch(cr, 1.0, 1.0, 1.0) -} - -// DistanceCIEDE2000klch uses the Delta E 2000 formula with custom values -// for the weighting factors kL, kC, and kH. -func (cl Color) DistanceCIEDE2000klch(cr Color, kl, kc, kh float64) float64 { - l1, a1, b1 := cl.Lab() - l2, a2, b2 := cr.Lab() - - // As with CIE94, we scale up the ranges of L,a,b beforehand and scale - // them down again afterwards. - l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0 - l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0 - - cab1 := math.Sqrt(sq(a1) + sq(b1)) - cab2 := math.Sqrt(sq(a2) + sq(b2)) - cabmean := (cab1 + cab2) / 2 - - g := 0.5 * (1 - math.Sqrt(math.Pow(cabmean, 7)/(math.Pow(cabmean, 7)+math.Pow(25, 7)))) - ap1 := (1 + g) * a1 - ap2 := (1 + g) * a2 - cp1 := math.Sqrt(sq(ap1) + sq(b1)) - cp2 := math.Sqrt(sq(ap2) + sq(b2)) - - hp1 := 0.0 - if b1 != ap1 || ap1 != 0 { - hp1 = math.Atan2(b1, ap1) - if hp1 < 0 { - hp1 += math.Pi * 2 - } - hp1 *= 180 / math.Pi - } - hp2 := 0.0 - if b2 != ap2 || ap2 != 0 { - hp2 = math.Atan2(b2, ap2) - if hp2 < 0 { - hp2 += math.Pi * 2 - } - hp2 *= 180 / math.Pi - } - - deltaLp := l2 - l1 - deltaCp := cp2 - cp1 - dhp := 0.0 - cpProduct := cp1 * cp2 - if cpProduct != 0 { - dhp = hp2 - hp1 - if dhp > 180 { - dhp -= 360 - } else if dhp < -180 { - dhp += 360 - } - } - deltaHp := 2 * math.Sqrt(cpProduct) * math.Sin(dhp/2*math.Pi/180) - - lpmean := (l1 + l2) / 2 - cpmean := (cp1 + cp2) / 2 - hpmean := hp1 + hp2 - if cpProduct != 0 { - hpmean /= 2 - if math.Abs(hp1-hp2) > 180 { - if hp1+hp2 < 360 { - hpmean += 180 - } else { - hpmean -= 180 - } - } - } - - t := 1 - 0.17*math.Cos((hpmean-30)*math.Pi/180) + 0.24*math.Cos(2*hpmean*math.Pi/180) + 0.32*math.Cos((3*hpmean+6)*math.Pi/180) - 0.2*math.Cos((4*hpmean-63)*math.Pi/180) - deltaTheta := 30 * math.Exp(-sq((hpmean-275)/25)) - rc := 2 * math.Sqrt(math.Pow(cpmean, 7)/(math.Pow(cpmean, 7)+math.Pow(25, 7))) - sl := 1 + (0.015*sq(lpmean-50))/math.Sqrt(20+sq(lpmean-50)) - sc := 1 + 0.045*cpmean - sh := 1 + 0.015*cpmean*t - rt := -math.Sin(2*deltaTheta*math.Pi/180) * rc - - return math.Sqrt(sq(deltaLp/(kl*sl))+sq(deltaCp/(kc*sc))+sq(deltaHp/(kh*sh))+rt*(deltaCp/(kc*sc))*(deltaHp/(kh*sh))) * 0.01 -} - -// BlendLab blends two colors in the L*a*b* color-space, which should result in a smoother blend. -// t == 0 results in c1, t == 1 results in c2 -func (c1 Color) BlendLab(c2 Color, t float64) Color { - l1, a1, b1 := c1.Lab() - l2, a2, b2 := c2.Lab() - return Lab(l1+t*(l2-l1), - a1+t*(a2-a1), - b1+t*(b2-b1)) -} - -/// L*u*v* /// -////////////// -// http://en.wikipedia.org/wiki/CIELUV#XYZ_.E2.86.92_CIELUV_and_CIELUV_.E2.86.92_XYZ_conversions -// For L*u*v*, we need to L*u*v*<->XYZ<->RGB and the first one is device dependent. - -func XyzToLuv(x, y, z float64) (l, a, b float64) { - // Use D65 white as reference point by default. - // http://www.fredmiranda.com/forum/topic/1035332 - // http://en.wikipedia.org/wiki/Standard_illuminant - return XyzToLuvWhiteRef(x, y, z, D65) -} - -func XyzToLuvWhiteRef(x, y, z float64, wref [3]float64) (l, u, v float64) { - if y/wref[1] <= 6.0/29.0*6.0/29.0*6.0/29.0 { - l = y / wref[1] * 29.0 / 3.0 * 29.0 / 3.0 * 29.0 / 3.0 - } else { - l = 1.16*math.Cbrt(y/wref[1]) - 0.16 - } - ubis, vbis := xyz_to_uv(x, y, z) - un, vn := xyz_to_uv(wref[0], wref[1], wref[2]) - u = 13.0 * l * (ubis - un) - v = 13.0 * l * (vbis - vn) - return -} - -// For this part, we do as R's graphics.hcl does, not as wikipedia does. -// Or is it the same? -func xyz_to_uv(x, y, z float64) (u, v float64) { - denom := x + 15.0*y + 3.0*z - if denom == 0.0 { - u, v = 0.0, 0.0 - } else { - u = 4.0 * x / denom - v = 9.0 * y / denom - } - return -} - -func LuvToXyz(l, u, v float64) (x, y, z float64) { - // D65 white (see above). - return LuvToXyzWhiteRef(l, u, v, D65) -} - -func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) { - //y = wref[1] * lab_finv((l + 0.16) / 1.16) - if l <= 0.08 { - y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0 - } else { - y = wref[1] * cub((l+0.16)/1.16) - } - un, vn := xyz_to_uv(wref[0], wref[1], wref[2]) - if l != 0.0 { - ubis := u/(13.0*l) + un - vbis := v/(13.0*l) + vn - x = y * 9.0 * ubis / (4.0 * vbis) - z = y * (12.0 - 3.0*ubis - 20.0*vbis) / (4.0 * vbis) - } else { - x, y = 0.0, 0.0 - } - return -} - -// Converts the given color to CIE L*u*v* space using D65 as reference white. -// L* is in [0..1] and both u* and v* are in about [-1..1] -func (col Color) Luv() (l, u, v float64) { - return XyzToLuv(col.Xyz()) -} - -// Converts the given color to CIE L*u*v* space, taking into account -// a given reference white. (i.e. the monitor's white) -// L* is in [0..1] and both u* and v* are in about [-1..1] -func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) { - x, y, z := col.Xyz() - return XyzToLuvWhiteRef(x, y, z, wref) -} - -// Generates a color by using data given in CIE L*u*v* space using D65 as reference white. -// L* is in [0..1] and both u* and v* are in about [-1..1] -// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding -// valid RGB values, check the FAQ in the README if you're unsure. -func Luv(l, u, v float64) Color { - return Xyz(LuvToXyz(l, u, v)) -} - -// Generates a color by using data given in CIE L*u*v* space, taking -// into account a given reference white. (i.e. the monitor's white) -// L* is in [0..1] and both u* and v* are in about [-1..1] -func LuvWhiteRef(l, u, v float64, wref [3]float64) Color { - return Xyz(LuvToXyzWhiteRef(l, u, v, wref)) -} - -// DistanceLuv is a good measure of visual similarity between two colors! -// A result of 0 would mean identical colors, while a result of 1 or higher -// means the colors differ a lot. -func (c1 Color) DistanceLuv(c2 Color) float64 { - l1, u1, v1 := c1.Luv() - l2, u2, v2 := c2.Luv() - return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2)) -} - -// BlendLuv blends two colors in the CIE-L*u*v* color-space, which should result in a smoother blend. -// t == 0 results in c1, t == 1 results in c2 -func (c1 Color) BlendLuv(c2 Color, t float64) Color { - l1, u1, v1 := c1.Luv() - l2, u2, v2 := c2.Luv() - return Luv(l1+t*(l2-l1), - u1+t*(u2-u1), - v1+t*(v2-v1)) -} - -/// HCL /// -/////////// -// HCL is nothing else than L*a*b* in cylindrical coordinates! -// (this was wrong on English wikipedia, I fixed it, let's hope the fix stays.) -// But it is widely popular since it is a "correct HSV" -// http://www.hunterlab.com/appnotes/an09_96a.pdf - -// Converts the given color to HCL space using D65 as reference white. -// H values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0 -func (col Color) Hcl() (h, c, l float64) { - return col.HclWhiteRef(D65) -} - -func LabToHcl(L, a, b float64) (h, c, l float64) { - // Oops, floating point workaround necessary if a ~= b and both are very small (i.e. almost zero). - if math.Abs(b-a) > 1e-4 && math.Abs(a) > 1e-4 { - h = math.Mod(57.29577951308232087721*math.Atan2(b, a)+360.0, 360.0) // Rad2Deg - } else { - h = 0.0 - } - c = math.Sqrt(sq(a) + sq(b)) - l = L - return -} - -// Converts the given color to HCL space, taking into account -// a given reference white. (i.e. the monitor's white) -// H values are in [0..360], C and L values are in [0..1] -func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) { - L, a, b := col.LabWhiteRef(wref) - return LabToHcl(L, a, b) -} - -// Generates a color by using data given in HCL space using D65 as reference white. -// H values are in [0..360], C and L values are in [0..1] -// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding -// valid RGB values, check the FAQ in the README if you're unsure. -func Hcl(h, c, l float64) Color { - return HclWhiteRef(h, c, l, D65) -} - -func HclToLab(h, c, l float64) (L, a, b float64) { - H := 0.01745329251994329576 * h // Deg2Rad - a = c * math.Cos(H) - b = c * math.Sin(H) - L = l - return -} - -// Generates a color by using data given in HCL space, taking -// into account a given reference white. (i.e. the monitor's white) -// H values are in [0..360], C and L values are in [0..1] -func HclWhiteRef(h, c, l float64, wref [3]float64) Color { - L, a, b := HclToLab(h, c, l) - return LabWhiteRef(L, a, b, wref) -} - -// BlendHcl blends two colors in the CIE-L*C*h° color-space, which should result in a smoother blend. -// t == 0 results in c1, t == 1 results in c2 -func (col1 Color) BlendHcl(col2 Color, t float64) Color { - h1, c1, l1 := col1.Hcl() - h2, c2, l2 := col2.Hcl() - - // We know that h are both in [0..360] - return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1)) -} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go deleted file mode 100644 index bb66dfa4..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go +++ /dev/null @@ -1,25 +0,0 @@ -package colorful - -import ( - "math/rand" -) - -// Uses the HSV color space to generate colors with similar S,V but distributed -// evenly along their Hue. This is fast but not always pretty. -// If you've got time to spare, use Lab (the non-fast below). -func FastHappyPalette(colorsCount int) (colors []Color) { - colors = make([]Color, colorsCount) - - for i := 0; i < colorsCount; i++ { - colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.8+rand.Float64()*0.2, 0.65+rand.Float64()*0.2) - } - return -} - -func HappyPalette(colorsCount int) ([]Color, error) { - pimpy := func(l, a, b float64) bool { - _, c, _ := LabToHcl(l, a, b) - return 0.3 <= c && 0.4 <= l && l <= 0.8 - } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{pimpy, 50, true}) -} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go b/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go deleted file mode 100644 index 86a5ed98..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go +++ /dev/null @@ -1,37 +0,0 @@ -package colorful - -import ( - "database/sql/driver" - "fmt" - "reflect" -) - -// A HexColor is a Color stored as a hex string "#rrggbb". It implements the -// database/sql.Scanner and database/sql/driver.Value interfaces. -type HexColor Color - -type errUnsupportedType struct { - got interface{} - want reflect.Type -} - -func (hc *HexColor) Scan(value interface{}) error { - s, ok := value.(string) - if !ok { - return errUnsupportedType{got: reflect.TypeOf(value), want: reflect.TypeOf("")} - } - c, err := Hex(s) - if err != nil { - return err - } - *hc = HexColor(c) - return nil -} - -func (hc *HexColor) Value() (driver.Value, error) { - return Color(*hc).Hex(), nil -} - -func (e errUnsupportedType) Error() string { - return fmt.Sprintf("unsupported type: got %v, want a %s", e.got, e.want) -} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go deleted file mode 100644 index 0154ac9b..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go +++ /dev/null @@ -1,185 +0,0 @@ -// Largely inspired by the descriptions in http://lab.medialab.sciences-po.fr/iwanthue/ -// but written from scratch. - -package colorful - -import ( - "fmt" - "math" - "math/rand" -) - -// The algorithm works in L*a*b* color space and converts to RGB in the end. -// L* in [0..1], a* and b* in [-1..1] -type lab_t struct { - L, A, B float64 -} - -type SoftPaletteSettings struct { - // A function which can be used to restrict the allowed color-space. - CheckColor func(l, a, b float64) bool - - // The higher, the better quality but the slower. Usually two figures. - Iterations int - - // Use up to 160000 or 8000 samples of the L*a*b* space (and thus calls to CheckColor). - // Set this to true only if your CheckColor shapes the Lab space weirdly. - ManySamples bool -} - -// Yeah, windows-stype Foo, FooEx, screw you golang... -// Uses K-means to cluster the color-space and return the means of the clusters -// as a new palette of distinctive colors. Falls back to K-medoid if the mean -// happens to fall outside of the color-space, which can only happen if you -// specify a CheckColor function. -func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) { - - // Checks whether it's a valid RGB and also fulfills the potentially provided constraint. - check := func(col lab_t) bool { - c := Lab(col.L, col.A, col.B) - return c.IsValid() && (settings.CheckColor == nil || settings.CheckColor(col.L, col.A, col.B)) - } - - // Sample the color space. These will be the points k-means is run on. - dl := 0.05 - dab := 0.1 - if settings.ManySamples { - dl = 0.01 - dab = 0.05 - } - - samples := make([]lab_t, 0, int(1.0/dl*2.0/dab*2.0/dab)) - for l := 0.0; l <= 1.0; l += dl { - for a := -1.0; a <= 1.0; a += dab { - for b := -1.0; b <= 1.0; b += dab { - if check(lab_t{l, a, b}) { - samples = append(samples, lab_t{l, a, b}) - } - } - } - } - - // That would cause some infinite loops down there... - if len(samples) < colorsCount { - return nil, fmt.Errorf("palettegen: more colors requested (%v) than samples available (%v). Your requested color count may be wrong, you might want to use many samples or your constraint function makes the valid color space too small.", colorsCount, len(samples)) - } else if len(samples) == colorsCount { - return labs2cols(samples), nil // Oops? - } - - // We take the initial means out of the samples, so they are in fact medoids. - // This helps us avoid infinite loops or arbitrary cutoffs with too restrictive constraints. - means := make([]lab_t, colorsCount) - for i := 0; i < colorsCount; i++ { - for means[i] = samples[rand.Intn(len(samples))]; in(means, i, means[i]); means[i] = samples[rand.Intn(len(samples))] { - } - } - - clusters := make([]int, len(samples)) - samples_used := make([]bool, len(samples)) - - // The actual k-means/medoid iterations - for i := 0; i < settings.Iterations; i++ { - // Reassing the samples to clusters, i.e. to their closest mean. - // By the way, also check if any sample is used as a medoid and if so, mark that. - for isample, sample := range samples { - samples_used[isample] = false - mindist := math.Inf(+1) - for imean, mean := range means { - dist := lab_dist(sample, mean) - if dist < mindist { - mindist = dist - clusters[isample] = imean - } - - // Mark samples which are used as a medoid. - if lab_eq(sample, mean) { - samples_used[isample] = true - } - } - } - - // Compute new means according to the samples. - for imean := range means { - // The new mean is the average of all samples belonging to it.. - nsamples := 0 - newmean := lab_t{0.0, 0.0, 0.0} - for isample, sample := range samples { - if clusters[isample] == imean { - nsamples++ - newmean.L += sample.L - newmean.A += sample.A - newmean.B += sample.B - } - } - if nsamples > 0 { - newmean.L /= float64(nsamples) - newmean.A /= float64(nsamples) - newmean.B /= float64(nsamples) - } else { - // That mean doesn't have any samples? Get a new mean from the sample list! - var inewmean int - for inewmean = rand.Intn(len(samples_used)); samples_used[inewmean]; inewmean = rand.Intn(len(samples_used)) { - } - newmean = samples[inewmean] - samples_used[inewmean] = true - } - - // But now we still need to check whether the new mean is an allowed color. - if nsamples > 0 && check(newmean) { - // It does, life's good (TM) - means[imean] = newmean - } else { - // New mean isn't an allowed color or doesn't have any samples! - // Switch to medoid mode and pick the closest (unused) sample. - // This should always find something thanks to len(samples) >= colorsCount - mindist := math.Inf(+1) - for isample, sample := range samples { - if !samples_used[isample] { - dist := lab_dist(sample, newmean) - if dist < mindist { - mindist = dist - newmean = sample - } - } - } - } - } - } - return labs2cols(means), nil -} - -// A wrapper which uses common parameters. -func SoftPalette(colorsCount int) ([]Color, error) { - return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false}) -} - -func in(haystack []lab_t, upto int, needle lab_t) bool { - for i := 0; i < upto && i < len(haystack); i++ { - if haystack[i] == needle { - return true - } - } - return false -} - -const LAB_DELTA = 1e-6 - -func lab_eq(lab1, lab2 lab_t) bool { - return math.Abs(lab1.L-lab2.L) < LAB_DELTA && - math.Abs(lab1.A-lab2.A) < LAB_DELTA && - math.Abs(lab1.B-lab2.B) < LAB_DELTA -} - -// That's faster than using colorful's DistanceLab since we would have to -// convert back and forth for that. Here is no conversion. -func lab_dist(lab1, lab2 lab_t) float64 { - return math.Sqrt(sq(lab1.L-lab2.L) + sq(lab1.A-lab2.A) + sq(lab1.B-lab2.B)) -} - -func labs2cols(labs []lab_t) (cols []Color) { - cols = make([]Color, len(labs)) - for k, v := range labs { - cols[k] = Lab(v.L, v.A, v.B) - } - return cols -} diff --git a/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go b/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go deleted file mode 100644 index 00f42a5c..00000000 --- a/vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go +++ /dev/null @@ -1,25 +0,0 @@ -package colorful - -import ( - "math/rand" -) - -// Uses the HSV color space to generate colors with similar S,V but distributed -// evenly along their Hue. This is fast but not always pretty. -// If you've got time to spare, use Lab (the non-fast below). -func FastWarmPalette(colorsCount int) (colors []Color) { - colors = make([]Color, colorsCount) - - for i := 0; i < colorsCount; i++ { - colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.55+rand.Float64()*0.2, 0.35+rand.Float64()*0.2) - } - return -} - -func WarmPalette(colorsCount int) ([]Color, error) { - warmy := func(l, a, b float64) bool { - _, c, _ := LabToHcl(l, a, b) - return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 - } - return SoftPaletteEx(colorsCount, SoftPaletteSettings{warmy, 50, true}) -} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml deleted file mode 100644 index 6a21813a..00000000 --- a/vendor/github.com/mattn/go-runewidth/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -sudo: false -go: - - 1.13.x - - tip - -before_install: - - go get -t -v ./... - -script: - - go generate - - git diff --cached --exit-code - - ./go.test.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE deleted file mode 100644 index 91b5cef3..00000000 --- a/vendor/github.com/mattn/go-runewidth/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/mattn/go-runewidth/README.md b/vendor/github.com/mattn/go-runewidth/README.md deleted file mode 100644 index aa56ab96..00000000 --- a/vendor/github.com/mattn/go-runewidth/README.md +++ /dev/null @@ -1,27 +0,0 @@ -go-runewidth -============ - -[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) -[![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) -[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) -[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) - -Provides functions to get fixed width of the character or string. - -Usage ------ - -```go -runewidth.StringWidth("つのだ☆HIRO") == 12 -``` - - -Author ------- - -Yasuhiro Matsumoto - -License -------- - -under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/go.test.sh b/vendor/github.com/mattn/go-runewidth/go.test.sh deleted file mode 100644 index 012162b0..00000000 --- a/vendor/github.com/mattn/go-runewidth/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go deleted file mode 100644 index 19f8e044..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth.go +++ /dev/null @@ -1,257 +0,0 @@ -package runewidth - -import ( - "os" -) - -//go:generate go run script/generate.go - -var ( - // EastAsianWidth will be set true if the current locale is CJK - EastAsianWidth bool - - // ZeroWidthJoiner is flag to set to use UTR#51 ZWJ - ZeroWidthJoiner bool - - // DefaultCondition is a condition in current locale - DefaultCondition = &Condition{} -) - -func init() { - handleEnv() -} - -func handleEnv() { - env := os.Getenv("RUNEWIDTH_EASTASIAN") - if env == "" { - EastAsianWidth = IsEastAsian() - } else { - EastAsianWidth = env == "1" - } - // update DefaultCondition - DefaultCondition.EastAsianWidth = EastAsianWidth - DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner -} - -type interval struct { - first rune - last rune -} - -type table []interval - -func inTables(r rune, ts ...table) bool { - for _, t := range ts { - if inTable(r, t) { - return true - } - } - return false -} - -func inTable(r rune, t table) bool { - if r < t[0].first { - return false - } - - bot := 0 - top := len(t) - 1 - for top >= bot { - mid := (bot + top) >> 1 - - switch { - case t[mid].last < r: - bot = mid + 1 - case t[mid].first > r: - top = mid - 1 - default: - return true - } - } - - return false -} - -var private = table{ - {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, -} - -var nonprint = table{ - {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, - {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, - {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, - {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, -} - -// Condition have flag EastAsianWidth whether the current locale is CJK or not. -type Condition struct { - EastAsianWidth bool - ZeroWidthJoiner bool -} - -// NewCondition return new instance of Condition which is current locale. -func NewCondition() *Condition { - return &Condition{ - EastAsianWidth: EastAsianWidth, - ZeroWidthJoiner: ZeroWidthJoiner, - } -} - -// RuneWidth returns the number of cells in r. -// See http://www.unicode.org/reports/tr11/ -func (c *Condition) RuneWidth(r rune) int { - switch { - case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining, notassigned): - return 0 - case (c.EastAsianWidth && IsAmbiguousWidth(r)) || inTables(r, doublewidth): - return 2 - default: - return 1 - } -} - -func (c *Condition) stringWidth(s string) (width int) { - for _, r := range []rune(s) { - width += c.RuneWidth(r) - } - return width -} - -func (c *Condition) stringWidthZeroJoiner(s string) (width int) { - r1, r2 := rune(0), rune(0) - for _, r := range []rune(s) { - if r == 0xFE0E || r == 0xFE0F { - continue - } - w := c.RuneWidth(r) - if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) { - if width < w { - width = w - } - } else { - width += w - } - r1, r2 = r2, r - } - return width -} - -// StringWidth return width as you can see -func (c *Condition) StringWidth(s string) (width int) { - if c.ZeroWidthJoiner { - return c.stringWidthZeroJoiner(s) - } - return c.stringWidth(s) -} - -// Truncate return string truncated with w cells -func (c *Condition) Truncate(s string, w int, tail string) string { - if c.StringWidth(s) <= w { - return s - } - r := []rune(s) - tw := c.StringWidth(tail) - w -= tw - width := 0 - i := 0 - for ; i < len(r); i++ { - cw := c.RuneWidth(r[i]) - if width+cw > w { - break - } - width += cw - } - return string(r[0:i]) + tail -} - -// Wrap return string wrapped with w cells -func (c *Condition) Wrap(s string, w int) string { - width := 0 - out := "" - for _, r := range []rune(s) { - cw := RuneWidth(r) - if r == '\n' { - out += string(r) - width = 0 - continue - } else if width+cw > w { - out += "\n" - width = 0 - out += string(r) - width += cw - continue - } - out += string(r) - width += cw - } - return out -} - -// FillLeft return string filled in left by spaces in w cells -func (c *Condition) FillLeft(s string, w int) string { - width := c.StringWidth(s) - count := w - width - if count > 0 { - b := make([]byte, count) - for i := range b { - b[i] = ' ' - } - return string(b) + s - } - return s -} - -// FillRight return string filled in left by spaces in w cells -func (c *Condition) FillRight(s string, w int) string { - width := c.StringWidth(s) - count := w - width - if count > 0 { - b := make([]byte, count) - for i := range b { - b[i] = ' ' - } - return s + string(b) - } - return s -} - -// RuneWidth returns the number of cells in r. -// See http://www.unicode.org/reports/tr11/ -func RuneWidth(r rune) int { - return DefaultCondition.RuneWidth(r) -} - -// IsAmbiguousWidth returns whether is ambiguous width or not. -func IsAmbiguousWidth(r rune) bool { - return inTables(r, private, ambiguous) -} - -// IsNeutralWidth returns whether is neutral width or not. -func IsNeutralWidth(r rune) bool { - return inTable(r, neutral) -} - -// StringWidth return width as you can see -func StringWidth(s string) (width int) { - return DefaultCondition.StringWidth(s) -} - -// Truncate return string truncated with w cells -func Truncate(s string, w int, tail string) string { - return DefaultCondition.Truncate(s, w, tail) -} - -// Wrap return string wrapped with w cells -func Wrap(s string, w int) string { - return DefaultCondition.Wrap(s, w) -} - -// FillLeft return string filled in left by spaces in w cells -func FillLeft(s string, w int) string { - return DefaultCondition.FillLeft(s, w) -} - -// FillRight return string filled in left by spaces in w cells -func FillRight(s string, w int) string { - return DefaultCondition.FillRight(s, w) -} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go deleted file mode 100644 index 7d99f6e5..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build appengine - -package runewidth - -// IsEastAsian return true if the current locale is CJK -func IsEastAsian() bool { - return false -} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go deleted file mode 100644 index c5fdf40b..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth_js.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build js -// +build !appengine - -package runewidth - -func IsEastAsian() bool { - // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. - return false -} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go deleted file mode 100644 index 66a58b5d..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go +++ /dev/null @@ -1,79 +0,0 @@ -// +build !windows -// +build !js -// +build !appengine - -package runewidth - -import ( - "os" - "regexp" - "strings" -) - -var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) - -var mblenTable = map[string]int{ - "utf-8": 6, - "utf8": 6, - "jis": 8, - "eucjp": 3, - "euckr": 2, - "euccn": 2, - "sjis": 2, - "cp932": 2, - "cp51932": 2, - "cp936": 2, - "cp949": 2, - "cp950": 2, - "big5": 2, - "gbk": 2, - "gb2312": 2, -} - -func isEastAsian(locale string) bool { - charset := strings.ToLower(locale) - r := reLoc.FindStringSubmatch(locale) - if len(r) == 2 { - charset = strings.ToLower(r[1]) - } - - if strings.HasSuffix(charset, "@cjk_narrow") { - return false - } - - for pos, b := range []byte(charset) { - if b == '@' { - charset = charset[:pos] - break - } - } - max := 1 - if m, ok := mblenTable[charset]; ok { - max = m - } - if max > 1 && (charset[0] != 'u' || - strings.HasPrefix(locale, "ja") || - strings.HasPrefix(locale, "ko") || - strings.HasPrefix(locale, "zh")) { - return true - } - return false -} - -// IsEastAsian return true if the current locale is CJK -func IsEastAsian() bool { - locale := os.Getenv("LC_CTYPE") - if locale == "" { - locale = os.Getenv("LANG") - } - - // ignore C locale - if locale == "POSIX" || locale == "C" { - return false - } - if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { - return false - } - - return isEastAsian(locale) -} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go deleted file mode 100644 index a8ccee5b..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth_table.go +++ /dev/null @@ -1,429 +0,0 @@ -// Code generated by script/generate.go. DO NOT EDIT. - -package runewidth - -var combining = table{ - {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, - {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, - {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1ABE}, - {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, - {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, - {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, - {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, - {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, - {0x10F46, 0x10F50}, {0x11300, 0x11301}, {0x1133B, 0x1133C}, - {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, - {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, - {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, - {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, - {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, -} - -var doublewidth = table{ - {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, - {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, - {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, - {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, - {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, - {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, - {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, - {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, - {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, - {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, - {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, - {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, - {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, - {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31BA}, - {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, - {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, - {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, - {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, - {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, - {0x16FE0, 0x16FE3}, {0x17000, 0x187F7}, {0x18800, 0x18AF2}, - {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, - {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, - {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, - {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, - {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, - {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, - {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, - {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, - {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, - {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, - {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, - {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D5}, {0x1F6EB, 0x1F6EC}, - {0x1F6F4, 0x1F6FA}, {0x1F7E0, 0x1F7EB}, {0x1F90D, 0x1F971}, - {0x1F973, 0x1F976}, {0x1F97A, 0x1F9A2}, {0x1F9A5, 0x1F9AA}, - {0x1F9AE, 0x1F9CA}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA73}, - {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA82}, {0x1FA90, 0x1FA95}, - {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, -} - -var ambiguous = table{ - {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, - {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, - {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, - {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, - {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, - {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, - {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, - {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, - {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, - {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, - {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, - {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, - {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, - {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, - {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, - {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, - {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, - {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, - {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, - {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, - {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, - {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, - {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, - {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, - {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, - {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, - {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, - {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, - {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, - {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, - {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, - {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, - {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, - {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, - {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, - {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, - {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, - {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, - {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, - {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, - {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, - {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, - {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, - {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, - {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, - {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, - {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, - {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, - {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, - {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, - {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, - {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, - {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, - {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, - {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, - {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, - {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, - {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, - {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, - {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, -} -var notassigned = table{ - {0x27E6, 0x27ED}, {0x2985, 0x2986}, -} - -var neutral = table{ - {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, - {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, - {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, - {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, - {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, - {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, - {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, - {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, - {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, - {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, - {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, - {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, - {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, - {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, - {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, - {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, - {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, - {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, - {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, - {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, - {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, - {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, - {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, - {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, - {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, - {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, - {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, - {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, - {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, - {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, - {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, - {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, - {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, - {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, - {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, - {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, - {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, - {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, - {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, - {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, - {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, - {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, - {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, - {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, - {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, - {0x0B56, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, - {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, - {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, - {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, - {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, - {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, - {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, - {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, - {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, - {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, - {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, - {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, - {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, - {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3}, - {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D03}, - {0x0D05, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, - {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, - {0x0D66, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, - {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, - {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, - {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, - {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, - {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, - {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, - {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, - {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, - {0x0F49, 0x0F6C}, {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, - {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, - {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, - {0x1160, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, - {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, - {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, - {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, - {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, - {0x1318, 0x135A}, {0x135D, 0x137C}, {0x1380, 0x1399}, - {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x169C}, - {0x16A0, 0x16F8}, {0x1700, 0x170C}, {0x170E, 0x1714}, - {0x1720, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, - {0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD}, - {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180E}, - {0x1810, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, - {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B}, - {0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D}, - {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, - {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, - {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, - {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ABE}, {0x1B00, 0x1B4B}, - {0x1B50, 0x1B7C}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, - {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, - {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, - {0x1DFB, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, - {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, - {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, - {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, - {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, - {0x1FF6, 0x1FFE}, {0x2000, 0x200F}, {0x2011, 0x2012}, - {0x2017, 0x2017}, {0x201A, 0x201B}, {0x201E, 0x201F}, - {0x2023, 0x2023}, {0x2028, 0x202F}, {0x2031, 0x2031}, - {0x2034, 0x2034}, {0x2036, 0x203A}, {0x203C, 0x203D}, - {0x203F, 0x2064}, {0x2066, 0x2071}, {0x2075, 0x207E}, - {0x2080, 0x2080}, {0x2085, 0x208E}, {0x2090, 0x209C}, - {0x20A0, 0x20A8}, {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, - {0x20D0, 0x20F0}, {0x2100, 0x2102}, {0x2104, 0x2104}, - {0x2106, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2115}, - {0x2117, 0x2120}, {0x2123, 0x2125}, {0x2127, 0x212A}, - {0x212C, 0x2152}, {0x2155, 0x215A}, {0x215F, 0x215F}, - {0x216C, 0x216F}, {0x217A, 0x2188}, {0x218A, 0x218B}, - {0x219A, 0x21B7}, {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, - {0x21D5, 0x21E6}, {0x21E8, 0x21FF}, {0x2201, 0x2201}, - {0x2204, 0x2206}, {0x2209, 0x220A}, {0x220C, 0x220E}, - {0x2210, 0x2210}, {0x2212, 0x2214}, {0x2216, 0x2219}, - {0x221B, 0x221C}, {0x2221, 0x2222}, {0x2224, 0x2224}, - {0x2226, 0x2226}, {0x222D, 0x222D}, {0x222F, 0x2233}, - {0x2238, 0x223B}, {0x223E, 0x2247}, {0x2249, 0x224B}, - {0x224D, 0x2251}, {0x2253, 0x225F}, {0x2262, 0x2263}, - {0x2268, 0x2269}, {0x226C, 0x226D}, {0x2270, 0x2281}, - {0x2284, 0x2285}, {0x2288, 0x2294}, {0x2296, 0x2298}, - {0x229A, 0x22A4}, {0x22A6, 0x22BE}, {0x22C0, 0x2311}, - {0x2313, 0x2319}, {0x231C, 0x2328}, {0x232B, 0x23E8}, - {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x2426}, - {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F}, - {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F}, - {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, - {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, - {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, - {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604}, - {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, - {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, - {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, - {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, - {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, - {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, - {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, - {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, - {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, - {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, - {0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF}, - {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984}, - {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, - {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2C2E}, - {0x2C30, 0x2C5E}, {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, - {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, - {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, - {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, - {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, - {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E4F}, {0x303F, 0x303F}, - {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, - {0xA700, 0xA7BF}, {0xA7C2, 0xA7C6}, {0xA7F7, 0xA82B}, - {0xA830, 0xA839}, {0xA840, 0xA877}, {0xA880, 0xA8C5}, - {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, - {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, - {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, - {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, - {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, - {0xAB28, 0xAB2E}, {0xAB30, 0xAB67}, {0xAB70, 0xABED}, - {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, - {0xD800, 0xDFFF}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, - {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, - {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, - {0xFBD3, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, - {0xFDF0, 0xFDFD}, {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, - {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, - {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, - {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, - {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, - {0x10137, 0x1018E}, {0x10190, 0x1019B}, {0x101A0, 0x101A0}, - {0x101D0, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, - {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A}, - {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, - {0x103C8, 0x103D5}, {0x10400, 0x1049D}, {0x104A0, 0x104A9}, - {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, - {0x10530, 0x10563}, {0x1056F, 0x1056F}, {0x10600, 0x10736}, - {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10800, 0x10805}, - {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, - {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E}, - {0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, - {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F}, - {0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, - {0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, - {0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, - {0x10A50, 0x10A58}, {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, - {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, - {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, - {0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, - {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, - {0x10E60, 0x10E7E}, {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, - {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, - {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, - {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11146}, - {0x11150, 0x11176}, {0x11180, 0x111CD}, {0x111D0, 0x111DF}, - {0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x1123E}, - {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, - {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, - {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C}, - {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, - {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344}, - {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, - {0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C}, - {0x11370, 0x11374}, {0x11400, 0x11459}, {0x1145B, 0x1145B}, - {0x1145D, 0x1145F}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, - {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, - {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B8}, - {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, - {0x11730, 0x1173F}, {0x11800, 0x1183B}, {0x118A0, 0x118F2}, - {0x118FF, 0x118FF}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, - {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, - {0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, - {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, - {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, - {0x11D08, 0x11D09}, {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, - {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, - {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, - {0x11D90, 0x11D91}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, - {0x11EE0, 0x11EF8}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, - {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, - {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, - {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, - {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, - {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, - {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, - {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, - {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, - {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, - {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, - {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, - {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, - {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, - {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, - {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, - {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, - {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, - {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, - {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, - {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, - {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, - {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, - {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, - {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, - {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, - {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, - {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, - {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, - {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, - {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, - {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, - {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, - {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, - {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, - {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, - {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, - {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, - {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, - {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, {0x1F12E, 0x1F12F}, - {0x1F16A, 0x1F16C}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C}, - {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F}, - {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3}, - {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, - {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F}, - {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3}, - {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB}, - {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA}, - {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D8}, - {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, - {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F900, 0x1F90B}, - {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0xE0001, 0xE0001}, - {0xE0020, 0xE007F}, -} - -var emoji = table{ - {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, - {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, - {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, - {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, - {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, - {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, - {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, - {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, - {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, - {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, - {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, - {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, - {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, - {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, - {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, - {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, - {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, - {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, - {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, - {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, - {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, - {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FFFD}, -} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go deleted file mode 100644 index d6a61777..00000000 --- a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -// +build windows -// +build !appengine - -package runewidth - -import ( - "syscall" -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32") - procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") -) - -// IsEastAsian return true if the current locale is CJK -func IsEastAsian() bool { - r1, _, _ := procGetConsoleOutputCP.Call() - if r1 == 0 { - return false - } - - switch int(r1) { - case 932, 51932, 936, 949, 950: - return true - } - - return false -} diff --git a/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md b/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md deleted file mode 100644 index 601e63b8..00000000 --- a/vendor/github.com/rivo/tview/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,73 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -education, socio-economic status, nationality, personal appearance, race, -religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at https://rentafounder.com/page/about-me/. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org diff --git a/vendor/github.com/rivo/tview/CONTRIBUTING.md b/vendor/github.com/rivo/tview/CONTRIBUTING.md deleted file mode 100644 index 92f6886d..00000000 --- a/vendor/github.com/rivo/tview/CONTRIBUTING.md +++ /dev/null @@ -1,35 +0,0 @@ -# Contributing to tview - -First of all, thank you for taking the time to contribute. - -The following provides you with some guidance on how to contribute to this project. Mainly, it is meant to save us all some time so please read it, it's not long. - -Please note that this document is work in progress so I might add to it in the future. - -## Issues - -- Please include enough information so everybody understands your request. -- Screenshots or code that illustrates your point always helps. -- It's fine to ask for help. But you should have checked out the [documentation](https://godoc.org/github.com/rivo/tview) first in any case. -- If you request a new feature, state your motivation and share a use case that you faced where you needed that new feature. It should be something that others will also need. - -## Pull Requests - -In my limited time I can spend on this project, I will always go through issues first before looking at pull requests. It takes a _lot_ of time to look at code that you submitted and I may not have that time. So be prepared to have your pull requests lying around for a long time. - -Therefore, if you have a feature request, open an issue first before sending me a pull request, and allow for some discussion. It may save you from writing code that will get rejected. If your case is strong, there is a good chance that I will add the feature for you. - -I'm very picky about the code that goes into this repo. So if you violate any of the following guidelines, there is a good chance I won't merge your pull request. - -- There must be a strong case for your additions/changes, such as: - - Bug fixes - - Features that are needed (see "Issues" above; state your motivation) - - Improvements in stability or performance (if readability does not suffer) -- Your code must follow the structure of the existing code. Don't just patch something on. Try to understand how `tview` is currently designed and follow that design. Your code needs to be consistent with existing code. -- If you're adding code that increases the work required to maintain the project, you must be willing to take responsibility for that extra work. I will ask you to maintain your part of the code in the long run. -- Function/type/variable/constant names must be as descriptive as they are right now. Follow the conventions of the package. -- All functions/types/variables/constants, even private ones, must have comments in good English. These comments must be elaborate enough so that new users of the package understand them and can follow them. Provide examples if you have to. Start all sentences upper-case, as is common in English, and end them with a period. -- A new function should be located close to related functions in the file. For example, `GetColor()` should come after (or before) `SetColor()`. -- Your changes must not decrease the project's [Go Report](https://goreportcard.com/report/github.com/rivo/tview) rating. -- No breaking changes unless there is absolutely no other way. -- If an issue accompanies your pull request, reference it in the PR's comments, e.g. "Fixes #123", so it is closed automatically when the PR is closed. diff --git a/vendor/github.com/rivo/tview/LICENSE.txt b/vendor/github.com/rivo/tview/LICENSE.txt deleted file mode 100644 index 9d694307..00000000 --- a/vendor/github.com/rivo/tview/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Oliver Kuederle - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/rivo/tview/README.md b/vendor/github.com/rivo/tview/README.md deleted file mode 100644 index 6e016ad2..00000000 --- a/vendor/github.com/rivo/tview/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Rich Interactive Widgets for Terminal UIs - -[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/rivo/tview) -[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/tview) - -This Go package provides commonly needed components for terminal based user interfaces. - -![Screenshot](tview.gif) - -Among these components are: - -- __Input forms__ (include __input/password fields__, __drop-down selections__, __checkboxes__, and __buttons__) -- Navigable multi-color __text views__ -- Sophisticated navigable __table views__ -- Flexible __tree views__ -- Selectable __lists__ -- __Grid__, __Flexbox__ and __page layouts__ -- Modal __message windows__ -- An __application__ wrapper - -They come with lots of customization options and can be easily extended to fit your needs. - -## Installation - -```bash -go get github.com/rivo/tview -``` - -## Hello World - -This basic example creates a box titled "Hello, World!" and displays it in your terminal: - -```go -package main - -import ( - "github.com/rivo/tview" -) - -func main() { - box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!") - if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil { - panic(err) - } -} -``` - -Check out the [GitHub Wiki](https://github.com/rivo/tview/wiki) for more examples along with screenshots. Or try the examples in the "demos" subdirectory. - -For a presentation highlighting this package, compile and run the program found in the "demos/presentation" subdirectory. - -## Documentation - -Refer to https://pkg.go.dev/github.com/rivo/tview for the package's documentation. - -## Dependencies - -This package is based on [github.com/gdamore/tcell](https://github.com/gdamore/tcell) (and its dependencies) as well as on [github.com/rivo/uniseg](https://github.com/rivo/uniseg). - -## Your Feedback - -Add your issue here on GitHub. Feel free to get in touch if you have any questions. diff --git a/vendor/github.com/rivo/tview/ansi.go b/vendor/github.com/rivo/tview/ansi.go deleted file mode 100644 index 49b2e92f..00000000 --- a/vendor/github.com/rivo/tview/ansi.go +++ /dev/null @@ -1,258 +0,0 @@ -package tview - -import ( - "bytes" - "fmt" - "io" - "strconv" - "strings" -) - -// The states of the ANSI escape code parser. -const ( - ansiText = iota - ansiEscape - ansiSubstring - ansiControlSequence -) - -// ansi is a io.Writer which translates ANSI escape codes into tview color -// tags. -type ansi struct { - io.Writer - - // Reusable buffers. - buffer *bytes.Buffer // The entire output text of one Write(). - csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings. - attributes string // The buffer's current text attributes (a tview attribute string). - - // The current state of the parser. One of the ansi constants. - state int -} - -// ANSIWriter returns an io.Writer which translates any ANSI escape codes -// written to it into tview color tags. Other escape codes don't have an effect -// and are simply removed. The translated text is written to the provided -// writer. -func ANSIWriter(writer io.Writer) io.Writer { - return &ansi{ - Writer: writer, - buffer: new(bytes.Buffer), - csiParameter: new(bytes.Buffer), - csiIntermediate: new(bytes.Buffer), - state: ansiText, - } -} - -// Write parses the given text as a string of runes, translates ANSI escape -// codes to color tags and writes them to the output writer. -func (a *ansi) Write(text []byte) (int, error) { - defer func() { - a.buffer.Reset() - }() - - for _, r := range string(text) { - switch a.state { - - // We just entered an escape sequence. - case ansiEscape: - switch r { - case '[': // Control Sequence Introducer. - a.csiParameter.Reset() - a.csiIntermediate.Reset() - a.state = ansiControlSequence - case 'c': // Reset. - fmt.Fprint(a.buffer, "[-:-:-]") - a.state = ansiText - case 'P', ']', 'X', '^', '_': // Substrings and commands. - a.state = ansiSubstring - default: // Ignore. - a.state = ansiText - } - - // CSI Sequences. - case ansiControlSequence: - switch { - case r >= 0x30 && r <= 0x3f: // Parameter bytes. - if _, err := a.csiParameter.WriteRune(r); err != nil { - return 0, err - } - case r >= 0x20 && r <= 0x2f: // Intermediate bytes. - if _, err := a.csiIntermediate.WriteRune(r); err != nil { - return 0, err - } - case r >= 0x40 && r <= 0x7e: // Final byte. - switch r { - case 'E': // Next line. - count, _ := strconv.Atoi(a.csiParameter.String()) - if count == 0 { - count = 1 - } - fmt.Fprint(a.buffer, strings.Repeat("\n", count)) - case 'm': // Select Graphic Rendition. - var background, foreground string - params := a.csiParameter.String() - fields := strings.Split(params, ";") - if len(params) == 0 || len(fields) == 1 && fields[0] == "0" { - // Reset. - a.attributes = "" - if _, err := a.buffer.WriteString("[-:-:-]"); err != nil { - return 0, err - } - break - } - lookupColor := func(colorNumber int) string { - if colorNumber < 0 || colorNumber > 15 { - return "black" - } - return []string{ - "black", - "maroon", - "green", - "olive", - "navy", - "purple", - "teal", - "silver", - "gray", - "red", - "lime", - "yellow", - "blue", - "fuchsia", - "aqua", - "white", - }[colorNumber] - } - FieldLoop: - for index, field := range fields { - switch field { - case "1", "01": - if strings.IndexRune(a.attributes, 'b') < 0 { - a.attributes += "b" - } - case "2", "02": - if strings.IndexRune(a.attributes, 'd') < 0 { - a.attributes += "d" - } - case "4", "04": - if strings.IndexRune(a.attributes, 'u') < 0 { - a.attributes += "u" - } - case "5", "05": - if strings.IndexRune(a.attributes, 'l') < 0 { - a.attributes += "l" - } - case "22": - if i := strings.IndexRune(a.attributes, 'b'); i >= 0 { - a.attributes = a.attributes[:i] + a.attributes[i+1:] - } - if i := strings.IndexRune(a.attributes, 'd'); i >= 0 { - a.attributes = a.attributes[:i] + a.attributes[i+1:] - } - case "24": - if i := strings.IndexRune(a.attributes, 'u'); i >= 0 { - a.attributes = a.attributes[:i] + a.attributes[i+1:] - } - case "25": - if i := strings.IndexRune(a.attributes, 'l'); i >= 0 { - a.attributes = a.attributes[:i] + a.attributes[i+1:] - } - case "30", "31", "32", "33", "34", "35", "36", "37": - colorNumber, _ := strconv.Atoi(field) - foreground = lookupColor(colorNumber - 30) - case "39": - foreground = "-" - case "40", "41", "42", "43", "44", "45", "46", "47": - colorNumber, _ := strconv.Atoi(field) - background = lookupColor(colorNumber - 40) - case "49": - background = "-" - case "90", "91", "92", "93", "94", "95", "96", "97": - colorNumber, _ := strconv.Atoi(field) - foreground = lookupColor(colorNumber - 82) - case "100", "101", "102", "103", "104", "105", "106", "107": - colorNumber, _ := strconv.Atoi(field) - background = lookupColor(colorNumber - 92) - case "38", "48": - var color string - if len(fields) > index+1 { - if fields[index+1] == "5" && len(fields) > index+2 { // 8-bit colors. - colorNumber, _ := strconv.Atoi(fields[index+2]) - if colorNumber <= 15 { - color = lookupColor(colorNumber) - } else if colorNumber <= 231 { - red := (colorNumber - 16) / 36 - green := ((colorNumber - 16) / 6) % 6 - blue := (colorNumber - 16) % 6 - color = fmt.Sprintf("#%02x%02x%02x", 255*red/5, 255*green/5, 255*blue/5) - } else if colorNumber <= 255 { - grey := 255 * (colorNumber - 232) / 23 - color = fmt.Sprintf("#%02x%02x%02x", grey, grey, grey) - } - } else if fields[index+1] == "2" && len(fields) > index+4 { // 24-bit colors. - red, _ := strconv.Atoi(fields[index+2]) - green, _ := strconv.Atoi(fields[index+3]) - blue, _ := strconv.Atoi(fields[index+4]) - color = fmt.Sprintf("#%02x%02x%02x", red, green, blue) - } - } - if len(color) > 0 { - if field == "38" { - foreground = color - } else { - background = color - } - } - break FieldLoop - } - } - var colon string - if len(a.attributes) > 0 { - colon = ":" - } - if len(foreground) > 0 || len(background) > 0 || len(a.attributes) > 0 { - fmt.Fprintf(a.buffer, "[%s:%s%s%s]", foreground, background, colon, a.attributes) - } - } - a.state = ansiText - default: // Undefined byte. - a.state = ansiText // Abort CSI. - } - - // We just entered a substring/command sequence. - case ansiSubstring: - if r == 27 { // Most likely the end of the substring. - a.state = ansiEscape - } // Ignore all other characters. - - // "ansiText" and all others. - default: - if r == 27 { - // This is the start of an escape sequence. - a.state = ansiEscape - } else { - // Just a regular rune. Send to buffer. - if _, err := a.buffer.WriteRune(r); err != nil { - return 0, err - } - } - } - } - - // Write buffer to target writer. - n, err := a.buffer.WriteTo(a.Writer) - if err != nil { - return int(n), err - } - return len(text), nil -} - -// TranslateANSI replaces ANSI escape sequences found in the provided string -// with tview's color tags and returns the resulting string. -func TranslateANSI(text string) string { - var buffer bytes.Buffer - writer := ANSIWriter(&buffer) - writer.Write([]byte(text)) - return buffer.String() -} diff --git a/vendor/github.com/rivo/tview/application.go b/vendor/github.com/rivo/tview/application.go deleted file mode 100644 index 67ef6dd8..00000000 --- a/vendor/github.com/rivo/tview/application.go +++ /dev/null @@ -1,728 +0,0 @@ -package tview - -import ( - "sync" - "time" - - "github.com/gdamore/tcell" -) - -const ( - // The size of the event/update/redraw channels. - queueSize = 100 - - // The minimum time between two consecutive redraws. - redrawPause = 50 * time.Millisecond -) - -// DoubleClickInterval specifies the maximum time between clicks to register a -// double click rather than click. -var DoubleClickInterval = 500 * time.Millisecond - -// MouseAction indicates one of the actions the mouse is logically doing. -type MouseAction int16 - -// Available mouse actions. -const ( - MouseMove MouseAction = iota - MouseLeftDown - MouseLeftUp - MouseLeftClick - MouseLeftDoubleClick - MouseMiddleDown - MouseMiddleUp - MouseMiddleClick - MouseMiddleDoubleClick - MouseRightDown - MouseRightUp - MouseRightClick - MouseRightDoubleClick - MouseScrollUp - MouseScrollDown - MouseScrollLeft - MouseScrollRight -) - -// queuedUpdate represented the execution of f queued by -// Application.QueueUpdate(). The "done" channel receives exactly one element -// after f has executed. -type queuedUpdate struct { - f func() - done chan struct{} -} - -// Application represents the top node of an application. -// -// It is not strictly required to use this class as none of the other classes -// depend on it. However, it provides useful tools to set up an application and -// plays nicely with all widgets. -// -// The following command displays a primitive p on the screen until Ctrl-C is -// pressed: -// -// if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil { -// panic(err) -// } -type Application struct { - sync.RWMutex - - // The application's screen. Apart from Run(), this variable should never be - // set directly. Always use the screenReplacement channel after calling - // Fini(), to set a new screen (or nil to stop the application). - screen tcell.Screen - - // The primitive which currently has the keyboard focus. - focus Primitive - - // The root primitive to be seen on the screen. - root Primitive - - // Whether or not the application resizes the root primitive. - rootFullscreen bool - - // Set to true if mouse events are enabled. - enableMouse bool - - // An optional capture function which receives a key event and returns the - // event to be forwarded to the default input handler (nil if nothing should - // be forwarded). - inputCapture func(event *tcell.EventKey) *tcell.EventKey - - // An optional callback function which is invoked just before the root - // primitive is drawn. - beforeDraw func(screen tcell.Screen) bool - - // An optional callback function which is invoked after the root primitive - // was drawn. - afterDraw func(screen tcell.Screen) - - // Used to send screen events from separate goroutine to main event loop - events chan tcell.Event - - // Functions queued from goroutines, used to serialize updates to primitives. - updates chan queuedUpdate - - // An object that the screen variable will be set to after Fini() was called. - // Use this channel to set a new screen object for the application - // (screen.Init() and draw() will be called implicitly). A value of nil will - // stop the application. - screenReplacement chan tcell.Screen - - // An optional capture function which receives a mouse event and returns the - // event to be forwarded to the default mouse handler (nil if nothing should - // be forwarded). - mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) - - mouseCapturingPrimitive Primitive // A Primitive returned by a MouseHandler which will capture future mouse events. - lastMouseX, lastMouseY int // The last position of the mouse. - mouseDownX, mouseDownY int // The position of the mouse when its button was last pressed. - lastMouseClick time.Time // The time when a mouse button was last clicked. - lastMouseButtons tcell.ButtonMask // The last mouse button state. -} - -// NewApplication creates and returns a new application. -func NewApplication() *Application { - return &Application{ - events: make(chan tcell.Event, queueSize), - updates: make(chan queuedUpdate, queueSize), - screenReplacement: make(chan tcell.Screen, 1), - } -} - -// SetInputCapture sets a function which captures all key events before they are -// forwarded to the key event handler of the primitive which currently has -// focus. This function can then choose to forward that key event (or a -// different one) by returning it or stop the key event processing by returning -// nil. -// -// Note that this also affects the default event handling of the application -// itself: Such a handler can intercept the Ctrl-C event which closes the -// application. -func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application { - a.inputCapture = capture - return a -} - -// GetInputCapture returns the function installed with SetInputCapture() or nil -// if no such function has been installed. -func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey { - return a.inputCapture -} - -// SetMouseCapture sets a function which captures mouse events (consisting of -// the original tcell mouse event and the semantic mouse action) before they are -// forwarded to the appropriate mouse event handler. This function can then -// choose to forward that event (or a different one) by returning it or stop -// the event processing by returning a nil mouse event. -func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application { - a.mouseCapture = capture - return a -} - -// GetMouseCapture returns the function installed with SetMouseCapture() or nil -// if no such function has been installed. -func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) { - return a.mouseCapture -} - -// SetScreen allows you to provide your own tcell.Screen object. For most -// applications, this is not needed and you should be familiar with -// tcell.Screen when using this function. -// -// This function is typically called before the first call to Run(). Init() need -// not be called on the screen. -func (a *Application) SetScreen(screen tcell.Screen) *Application { - if screen == nil { - return a // Invalid input. Do nothing. - } - - a.Lock() - if a.screen == nil { - // Run() has not been called yet. - a.screen = screen - a.Unlock() - return a - } - - // Run() is already in progress. Exchange screen. - oldScreen := a.screen - a.Unlock() - oldScreen.Fini() - a.screenReplacement <- screen - - return a -} - -// EnableMouse enables mouse events. -func (a *Application) EnableMouse(enable bool) *Application { - a.Lock() - defer a.Unlock() - if enable != a.enableMouse && a.screen != nil { - if enable { - a.screen.EnableMouse() - } else { - a.screen.DisableMouse() - } - } - a.enableMouse = enable - return a -} - -// Run starts the application and thus the event loop. This function returns -// when Stop() was called. -func (a *Application) Run() error { - var ( - err error - width, height int // The current size of the screen. - lastRedraw time.Time // The time the screen was last redrawn. - redrawTimer *time.Timer // A timer to schedule the next redraw. - ) - a.Lock() - - // Make a screen if there is none yet. - if a.screen == nil { - a.screen, err = tcell.NewScreen() - if err != nil { - a.Unlock() - return err - } - if err = a.screen.Init(); err != nil { - a.Unlock() - return err - } - if a.enableMouse { - a.screen.EnableMouse() - } - } - - // We catch panics to clean up because they mess up the terminal. - defer func() { - if p := recover(); p != nil { - if a.screen != nil { - a.screen.Fini() - } - panic(p) - } - }() - - // Draw the screen for the first time. - a.Unlock() - a.draw() - - // Separate loop to wait for screen events. - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - for { - a.RLock() - screen := a.screen - a.RUnlock() - if screen == nil { - // We have no screen. Let's stop. - a.QueueEvent(nil) - break - } - - // Wait for next event and queue it. - event := screen.PollEvent() - if event != nil { - // Regular event. Queue. - a.QueueEvent(event) - continue - } - - // A screen was finalized (event is nil). Wait for a new scren. - screen = <-a.screenReplacement - if screen == nil { - // No new screen. We're done. - a.QueueEvent(nil) - return - } - - // We have a new screen. Keep going. - a.Lock() - a.screen = screen - a.Unlock() - - // Initialize and draw this screen. - if err := screen.Init(); err != nil { - panic(err) - } - a.draw() - } - }() - - // Start event loop. -EventLoop: - for { - select { - case event := <-a.events: - if event == nil { - break EventLoop - } - - switch event := event.(type) { - case *tcell.EventKey: - a.RLock() - p := a.focus - inputCapture := a.inputCapture - a.RUnlock() - - // Intercept keys. - if inputCapture != nil { - event = inputCapture(event) - if event == nil { - a.draw() - continue // Don't forward event. - } - } - - // Ctrl-C closes the application. - if event.Key() == tcell.KeyCtrlC { - a.Stop() - } - - // Pass other key events to the currently focused primitive. - if p != nil { - if handler := p.InputHandler(); handler != nil { - handler(event, func(p Primitive) { - a.SetFocus(p) - }) - a.draw() - } - } - case *tcell.EventResize: - if time.Since(lastRedraw) < redrawPause { - if redrawTimer != nil { - redrawTimer.Stop() - } - redrawTimer = time.AfterFunc(redrawPause, func() { - a.events <- event - }) - } - a.RLock() - screen := a.screen - a.RUnlock() - if screen == nil { - continue - } - newWidth, newHeight := screen.Size() - if newWidth == width && newHeight == height { - continue - } - width, height = newWidth, newHeight - lastRedraw = time.Now() - screen.Clear() - a.draw() - case *tcell.EventMouse: - consumed, isMouseDownAction := a.fireMouseActions(event) - if consumed { - a.draw() - } - a.lastMouseButtons = event.Buttons() - if isMouseDownAction { - a.mouseDownX, a.mouseDownY = event.Position() - } - } - - // If we have updates, now is the time to execute them. - case update := <-a.updates: - update.f() - update.done <- struct{}{} - } - } - - // Wait for the event loop to finish. - wg.Wait() - a.screen = nil - - return nil -} - -// fireMouseActions analyzes the provided mouse event, derives mouse actions -// from it and then forwards them to the corresponding primitives. -func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) { - // We want to relay follow-up events to the same target primitive. - var targetPrimitive Primitive - - // Helper function to fire a mouse action. - fire := func(action MouseAction) { - switch action { - case MouseLeftDown, MouseMiddleDown, MouseRightDown: - isMouseDownAction = true - } - - // Intercept event. - if a.mouseCapture != nil { - event, action = a.mouseCapture(event, action) - if event == nil { - consumed = true - return // Don't forward event. - } - } - - // Determine the target primitive. - var primitive, capturingPrimitive Primitive - if a.mouseCapturingPrimitive != nil { - primitive = a.mouseCapturingPrimitive - targetPrimitive = a.mouseCapturingPrimitive - } else if targetPrimitive != nil { - primitive = targetPrimitive - } else { - primitive = a.root - } - if primitive != nil { - if handler := primitive.MouseHandler(); handler != nil { - var wasConsumed bool - wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) { - a.SetFocus(p) - }) - if wasConsumed { - consumed = true - } - } - } - a.mouseCapturingPrimitive = capturingPrimitive - } - - x, y := event.Position() - buttons := event.Buttons() - clickMoved := x != a.mouseDownX || y != a.mouseDownY - buttonChanges := buttons ^ a.lastMouseButtons - - if x != a.lastMouseX || y != a.lastMouseY { - fire(MouseMove) - a.lastMouseX = x - a.lastMouseY = y - } - - for _, buttonEvent := range []struct { - button tcell.ButtonMask - down, up, click, dclick MouseAction - }{ - {tcell.Button1, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick}, - {tcell.Button2, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick}, - {tcell.Button3, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick}, - } { - if buttonChanges&buttonEvent.button != 0 { - if buttons&buttonEvent.button != 0 { - fire(buttonEvent.down) - } else { - fire(buttonEvent.up) - if !clickMoved { - if a.lastMouseClick.Add(DoubleClickInterval).Before(time.Now()) { - fire(buttonEvent.click) - a.lastMouseClick = time.Now() - } else { - fire(buttonEvent.dclick) - a.lastMouseClick = time.Time{} // reset - } - } - } - } - } - - for _, wheelEvent := range []struct { - button tcell.ButtonMask - action MouseAction - }{ - {tcell.WheelUp, MouseScrollUp}, - {tcell.WheelDown, MouseScrollDown}, - {tcell.WheelLeft, MouseScrollLeft}, - {tcell.WheelRight, MouseScrollRight}} { - if buttons&wheelEvent.button != 0 { - fire(wheelEvent.action) - } - } - - return consumed, isMouseDownAction -} - -// Stop stops the application, causing Run() to return. -func (a *Application) Stop() { - a.Lock() - defer a.Unlock() - screen := a.screen - if screen == nil { - return - } - a.screen = nil - screen.Fini() - a.screenReplacement <- nil -} - -// Suspend temporarily suspends the application by exiting terminal UI mode and -// invoking the provided function "f". When "f" returns, terminal UI mode is -// entered again and the application resumes. -// -// A return value of true indicates that the application was suspended and "f" -// was called. If false is returned, the application was already suspended, -// terminal UI mode was not exited, and "f" was not called. -func (a *Application) Suspend(f func()) bool { - a.RLock() - screen := a.screen - a.RUnlock() - if screen == nil { - return false // Screen has not yet been initialized. - } - - // Enter suspended mode. - screen.Fini() - - // Wait for "f" to return. - f() - - // Make a new screen. - var err error - screen, err = tcell.NewScreen() - if err != nil { - panic(err) - } - a.screenReplacement <- screen - // One key event will get lost, see https://github.com/gdamore/tcell/issues/194 - - // Continue application loop. - return true -} - -// Draw refreshes the screen (during the next update cycle). It calls the Draw() -// function of the application's root primitive and then syncs the screen -// buffer. It is almost never necessary to call this function. Please see -// https://github.com/rivo/tview/wiki/Concurrency for details. -func (a *Application) Draw() *Application { - a.QueueUpdate(func() { - a.draw() - }) - return a -} - -// ForceDraw refreshes the screen immediately. Use this function with caution as -// it may lead to race conditions with updates to primitives in other -// goroutines. It is always preferrable to use Draw() instead. Never call this -// function from a goroutine. -// -// It is safe to call this function during queued updates and direct event -// handling. -func (a *Application) ForceDraw() *Application { - return a.draw() -} - -// draw actually does what Draw() promises to do. -func (a *Application) draw() *Application { - a.Lock() - defer a.Unlock() - - screen := a.screen - root := a.root - fullscreen := a.rootFullscreen - before := a.beforeDraw - after := a.afterDraw - - // Maybe we're not ready yet or not anymore. - if screen == nil || root == nil { - return a - } - - // Resize if requested. - if fullscreen && root != nil { - width, height := screen.Size() - root.SetRect(0, 0, width, height) - } - - // Call before handler if there is one. - if before != nil { - if before(screen) { - screen.Show() - return a - } - } - - // Draw all primitives. - root.Draw(screen) - - // Call after handler if there is one. - if after != nil { - after(screen) - } - - // Sync screen. - screen.Show() - - return a -} - -// SetBeforeDrawFunc installs a callback function which is invoked just before -// the root primitive is drawn during screen updates. If the function returns -// true, drawing will not continue, i.e. the root primitive will not be drawn -// (and an after-draw-handler will not be called). -// -// Note that the screen is not cleared by the application. To clear the screen, -// you may call screen.Clear(). -// -// Provide nil to uninstall the callback function. -func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application { - a.beforeDraw = handler - return a -} - -// GetBeforeDrawFunc returns the callback function installed with -// SetBeforeDrawFunc() or nil if none has been installed. -func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool { - return a.beforeDraw -} - -// SetAfterDrawFunc installs a callback function which is invoked after the root -// primitive was drawn during screen updates. -// -// Provide nil to uninstall the callback function. -func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application { - a.afterDraw = handler - return a -} - -// GetAfterDrawFunc returns the callback function installed with -// SetAfterDrawFunc() or nil if none has been installed. -func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) { - return a.afterDraw -} - -// SetRoot sets the root primitive for this application. If "fullscreen" is set -// to true, the root primitive's position will be changed to fill the screen. -// -// This function must be called at least once or nothing will be displayed when -// the application starts. -// -// It also calls SetFocus() on the primitive. -func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application { - a.Lock() - a.root = root - a.rootFullscreen = fullscreen - if a.screen != nil { - a.screen.Clear() - } - a.Unlock() - - a.SetFocus(root) - - return a -} - -// ResizeToFullScreen resizes the given primitive such that it fills the entire -// screen. -func (a *Application) ResizeToFullScreen(p Primitive) *Application { - a.RLock() - width, height := a.screen.Size() - a.RUnlock() - p.SetRect(0, 0, width, height) - return a -} - -// SetFocus sets the focus on a new primitive. All key events will be redirected -// to that primitive. Callers must ensure that the primitive will handle key -// events. -// -// Blur() will be called on the previously focused primitive. Focus() will be -// called on the new primitive. -func (a *Application) SetFocus(p Primitive) *Application { - a.Lock() - if a.focus != nil { - a.focus.Blur() - } - a.focus = p - if a.screen != nil { - a.screen.HideCursor() - } - a.Unlock() - if p != nil { - p.Focus(func(p Primitive) { - a.SetFocus(p) - }) - } - - return a -} - -// GetFocus returns the primitive which has the current focus. If none has it, -// nil is returned. -func (a *Application) GetFocus() Primitive { - a.RLock() - defer a.RUnlock() - return a.focus -} - -// QueueUpdate is used to synchronize access to primitives from non-main -// goroutines. The provided function will be executed as part of the event loop -// and thus will not cause race conditions with other such update functions or -// the Draw() function. -// -// Note that Draw() is not implicitly called after the execution of f as that -// may not be desirable. You can call Draw() from f if the screen should be -// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow -// up with an immediate refresh of the screen. -// -// This function returns after f has executed. -func (a *Application) QueueUpdate(f func()) *Application { - ch := make(chan struct{}) - a.updates <- queuedUpdate{f: f, done: ch} - <-ch - return a -} - -// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen -// immediately after executing f. -func (a *Application) QueueUpdateDraw(f func()) *Application { - a.QueueUpdate(func() { - f() - a.draw() - }) - return a -} - -// QueueEvent sends an event to the Application event loop. -// -// It is not recommended for event to be nil. -func (a *Application) QueueEvent(event tcell.Event) *Application { - a.events <- event - return a -} diff --git a/vendor/github.com/rivo/tview/borders.go b/vendor/github.com/rivo/tview/borders.go deleted file mode 100644 index 946c8783..00000000 --- a/vendor/github.com/rivo/tview/borders.go +++ /dev/null @@ -1,45 +0,0 @@ -package tview - -// Borders defines various borders used when primitives are drawn. -// These may be changed to accommodate a different look and feel. -var Borders = struct { - Horizontal rune - Vertical rune - TopLeft rune - TopRight rune - BottomLeft rune - BottomRight rune - - LeftT rune - RightT rune - TopT rune - BottomT rune - Cross rune - - HorizontalFocus rune - VerticalFocus rune - TopLeftFocus rune - TopRightFocus rune - BottomLeftFocus rune - BottomRightFocus rune -}{ - Horizontal: BoxDrawingsLightHorizontal, - Vertical: BoxDrawingsLightVertical, - TopLeft: BoxDrawingsLightDownAndRight, - TopRight: BoxDrawingsLightDownAndLeft, - BottomLeft: BoxDrawingsLightUpAndRight, - BottomRight: BoxDrawingsLightUpAndLeft, - - LeftT: BoxDrawingsLightVerticalAndRight, - RightT: BoxDrawingsLightVerticalAndLeft, - TopT: BoxDrawingsLightDownAndHorizontal, - BottomT: BoxDrawingsLightUpAndHorizontal, - Cross: BoxDrawingsLightVerticalAndHorizontal, - - HorizontalFocus: BoxDrawingsDoubleHorizontal, - VerticalFocus: BoxDrawingsDoubleVertical, - TopLeftFocus: BoxDrawingsDoubleDownAndRight, - TopRightFocus: BoxDrawingsDoubleDownAndLeft, - BottomLeftFocus: BoxDrawingsDoubleUpAndRight, - BottomRightFocus: BoxDrawingsDoubleUpAndLeft, -} diff --git a/vendor/github.com/rivo/tview/box.go b/vendor/github.com/rivo/tview/box.go deleted file mode 100644 index 6cc34b78..00000000 --- a/vendor/github.com/rivo/tview/box.go +++ /dev/null @@ -1,412 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Box implements the Primitive interface with an empty background and optional -// elements such as a border and a title. Box itself does not hold any content -// but serves as the superclass of all other primitives. Subclasses add their -// own content, typically (but not necessarily) keeping their content within the -// box's rectangle. -// -// Box provides a number of utility functions available to all primitives. -// -// See https://github.com/rivo/tview/wiki/Box for an example. -type Box struct { - // The position of the rect. - x, y, width, height int - - // The inner rect reserved for the box's content. - innerX, innerY, innerWidth, innerHeight int - - // Border padding. - paddingTop, paddingBottom, paddingLeft, paddingRight int - - // The box's background color. - backgroundColor tcell.Color - - // Whether or not a border is drawn, reducing the box's space for content by - // two in width and height. - border bool - - // The color of the border. - borderColor tcell.Color - - // The style attributes of the border. - borderAttributes tcell.AttrMask - - // The title. Only visible if there is a border, too. - title string - - // The color of the title. - titleColor tcell.Color - - // The alignment of the title. - titleAlign int - - // Provides a way to find out if this box has focus. We always go through - // this interface because it may be overridden by implementing classes. - focus Focusable - - // Whether or not this box has focus. - hasFocus bool - - // An optional capture function which receives a key event and returns the - // event to be forwarded to the primitive's default input handler (nil if - // nothing should be forwarded). - inputCapture func(event *tcell.EventKey) *tcell.EventKey - - // An optional function which is called before the box is drawn. - draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) - - // An optional capture function which receives a mouse event and returns the - // event to be forwarded to the primitive's default mouse event handler (at - // least one nil if nothing should be forwarded). - mouseCapture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) -} - -// NewBox returns a Box without a border. -func NewBox() *Box { - b := &Box{ - width: 15, - height: 10, - innerX: -1, // Mark as uninitialized. - backgroundColor: Styles.PrimitiveBackgroundColor, - borderColor: Styles.BorderColor, - titleColor: Styles.TitleColor, - titleAlign: AlignCenter, - } - b.focus = b - return b -} - -// SetBorderPadding sets the size of the borders around the box content. -func (b *Box) SetBorderPadding(top, bottom, left, right int) *Box { - b.paddingTop, b.paddingBottom, b.paddingLeft, b.paddingRight = top, bottom, left, right - return b -} - -// GetRect returns the current position of the rectangle, x, y, width, and -// height. -func (b *Box) GetRect() (int, int, int, int) { - return b.x, b.y, b.width, b.height -} - -// GetInnerRect returns the position of the inner rectangle (x, y, width, -// height), without the border and without any padding. Width and height values -// will clamp to 0 and thus never be negative. -func (b *Box) GetInnerRect() (int, int, int, int) { - if b.innerX >= 0 { - return b.innerX, b.innerY, b.innerWidth, b.innerHeight - } - x, y, width, height := b.GetRect() - if b.border { - x++ - y++ - width -= 2 - height -= 2 - } - x, y, width, height = x+b.paddingLeft, - y+b.paddingTop, - width-b.paddingLeft-b.paddingRight, - height-b.paddingTop-b.paddingBottom - if width < 0 { - width = 0 - } - if height < 0 { - height = 0 - } - return x, y, width, height -} - -// SetRect sets a new position of the primitive. Note that this has no effect -// if this primitive is part of a layout (e.g. Flex, Grid) or if it was added -// like this: -// -// application.SetRoot(b, true) -func (b *Box) SetRect(x, y, width, height int) { - b.x = x - b.y = y - b.width = width - b.height = height - b.innerX = -1 // Mark inner rect as uninitialized. -} - -// SetDrawFunc sets a callback function which is invoked after the box primitive -// has been drawn. This allows you to add a more individual style to the box -// (and all primitives which extend it). -// -// The function is provided with the box's dimensions (set via SetRect()). It -// must return the box's inner dimensions (x, y, width, height) which will be -// returned by GetInnerRect(), used by descendent primitives to draw their own -// content. -func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) *Box { - b.draw = handler - return b -} - -// GetDrawFunc returns the callback function which was installed with -// SetDrawFunc() or nil if no such function has been installed. -func (b *Box) GetDrawFunc() func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { - return b.draw -} - -// WrapInputHandler wraps an input handler (see InputHandler()) with the -// functionality to capture input (see SetInputCapture()) before passing it -// on to the provided (default) input handler. -// -// This is only meant to be used by subclassing primitives. -func (b *Box) WrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primitive))) func(*tcell.EventKey, func(p Primitive)) { - return func(event *tcell.EventKey, setFocus func(p Primitive)) { - if b.inputCapture != nil { - event = b.inputCapture(event) - } - if event != nil && inputHandler != nil { - inputHandler(event, setFocus) - } - } -} - -// InputHandler returns nil. -func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return b.WrapInputHandler(nil) -} - -// SetInputCapture installs a function which captures key events before they are -// forwarded to the primitive's default key event handler. This function can -// then choose to forward that key event (or a different one) to the default -// handler by returning it. If nil is returned, the default handler will not -// be called. -// -// Providing a nil handler will remove a previously existing handler. -// -// Note that this function will not have an effect on primitives composed of -// other primitives, such as Form, Flex, or Grid. Key events are only captured -// by the primitives that have focus (e.g. InputField) and only one primitive -// can have focus at a time. Composing primitives such as Form pass the focus on -// to their contained primitives and thus never receive any key events -// themselves. Therefore, they cannot intercept key events. -func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box { - b.inputCapture = capture - return b -} - -// GetInputCapture returns the function installed with SetInputCapture() or nil -// if no such function has been installed. -func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey { - return b.inputCapture -} - -// WrapMouseHandler wraps a mouse event handler (see MouseHandler()) with the -// functionality to capture mouse events (see SetMouseCapture()) before passing -// them on to the provided (default) event handler. -// -// This is only meant to be used by subclassing primitives. -func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse, func(p Primitive)) (bool, Primitive)) func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if b.mouseCapture != nil { - action, event = b.mouseCapture(action, event) - } - if event != nil && mouseHandler != nil { - consumed, capture = mouseHandler(action, event, setFocus) - } - return - } -} - -// MouseHandler returns nil. -func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if action == MouseLeftClick && b.InRect(event.Position()) { - setFocus(b) - consumed = true - } - return - }) -} - -// SetMouseCapture sets a function which captures mouse events (consisting of -// the original tcell mouse event and the semantic mouse action) before they are -// forwarded to the primitive's default mouse event handler. This function can -// then choose to forward that event (or a different one) by returning it or -// returning a nil mouse event, in which case the default handler will not be -// called. -// -// Providing a nil handler will remove a previously existing handler. -func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) *Box { - b.mouseCapture = capture - return b -} - -// InRect returns true if the given coordinate is within the bounds of the box's -// rectangle. -func (b *Box) InRect(x, y int) bool { - rectX, rectY, width, height := b.GetRect() - return x >= rectX && x < rectX+width && y >= rectY && y < rectY+height -} - -// GetMouseCapture returns the function installed with SetMouseCapture() or nil -// if no such function has been installed. -func (b *Box) GetMouseCapture() func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) { - return b.mouseCapture -} - -// SetBackgroundColor sets the box's background color. -func (b *Box) SetBackgroundColor(color tcell.Color) *Box { - b.backgroundColor = color - return b -} - -// SetBorder sets the flag indicating whether or not the box should have a -// border. -func (b *Box) SetBorder(show bool) *Box { - b.border = show - return b -} - -// SetBorderColor sets the box's border color. -func (b *Box) SetBorderColor(color tcell.Color) *Box { - b.borderColor = color - return b -} - -// SetBorderAttributes sets the border's style attributes. You can combine -// different attributes using bitmask operations: -// -// box.SetBorderAttributes(tcell.AttrUnderline | tcell.AttrBold) -func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box { - b.borderAttributes = attr - return b -} - -// GetBorderAttributes returns the border's style attributes. -func (b *Box) GetBorderAttributes() tcell.AttrMask { - return b.borderAttributes -} - -// GetBorderColor returns the box's border color. -func (b *Box) GetBorderColor() tcell.Color { - return b.borderColor -} - -// GetBackgroundColor returns the box's background color. -func (b *Box) GetBackgroundColor() tcell.Color { - return b.backgroundColor -} - -// SetTitle sets the box's title. -func (b *Box) SetTitle(title string) *Box { - b.title = title - return b -} - -// GetTitle returns the box's current title. -func (b *Box) GetTitle() string { - return b.title -} - -// SetTitleColor sets the box's title color. -func (b *Box) SetTitleColor(color tcell.Color) *Box { - b.titleColor = color - return b -} - -// SetTitleAlign sets the alignment of the title, one of AlignLeft, AlignCenter, -// or AlignRight. -func (b *Box) SetTitleAlign(align int) *Box { - b.titleAlign = align - return b -} - -// Draw draws this primitive onto the screen. -func (b *Box) Draw(screen tcell.Screen) { - // Don't draw anything if there is no space. - if b.width <= 0 || b.height <= 0 { - return - } - - def := tcell.StyleDefault - - // Fill background. - background := def.Background(b.backgroundColor) - if b.backgroundColor != tcell.ColorDefault { - for y := b.y; y < b.y+b.height; y++ { - for x := b.x; x < b.x+b.width; x++ { - screen.SetContent(x, y, ' ', nil, background) - } - } - } - - // Draw border. - if b.border && b.width >= 2 && b.height >= 2 { - border := background.Foreground(b.borderColor) | tcell.Style(b.borderAttributes) - var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune - if b.focus.HasFocus() { - horizontal = Borders.HorizontalFocus - vertical = Borders.VerticalFocus - topLeft = Borders.TopLeftFocus - topRight = Borders.TopRightFocus - bottomLeft = Borders.BottomLeftFocus - bottomRight = Borders.BottomRightFocus - } else { - horizontal = Borders.Horizontal - vertical = Borders.Vertical - topLeft = Borders.TopLeft - topRight = Borders.TopRight - bottomLeft = Borders.BottomLeft - bottomRight = Borders.BottomRight - } - for x := b.x + 1; x < b.x+b.width-1; x++ { - screen.SetContent(x, b.y, horizontal, nil, border) - screen.SetContent(x, b.y+b.height-1, horizontal, nil, border) - } - for y := b.y + 1; y < b.y+b.height-1; y++ { - screen.SetContent(b.x, y, vertical, nil, border) - screen.SetContent(b.x+b.width-1, y, vertical, nil, border) - } - screen.SetContent(b.x, b.y, topLeft, nil, border) - screen.SetContent(b.x+b.width-1, b.y, topRight, nil, border) - screen.SetContent(b.x, b.y+b.height-1, bottomLeft, nil, border) - screen.SetContent(b.x+b.width-1, b.y+b.height-1, bottomRight, nil, border) - - // Draw title. - if b.title != "" && b.width >= 4 { - printed, _ := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor) - if len(b.title)-printed > 0 && printed > 0 { - _, _, style, _ := screen.GetContent(b.x+b.width-2, b.y) - fg, _, _ := style.Decompose() - Print(screen, string(SemigraphicsHorizontalEllipsis), b.x+b.width-2, b.y, 1, AlignLeft, fg) - } - } - } - - // Call custom draw function. - if b.draw != nil { - b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.draw(screen, b.x, b.y, b.width, b.height) - } else { - // Remember the inner rect. - b.innerX = -1 - b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.GetInnerRect() - } -} - -// Focus is called when this primitive receives focus. -func (b *Box) Focus(delegate func(p Primitive)) { - b.hasFocus = true -} - -// Blur is called when this primitive loses focus. -func (b *Box) Blur() { - b.hasFocus = false -} - -// HasFocus returns whether or not this primitive has focus. -func (b *Box) HasFocus() bool { - return b.hasFocus -} - -// GetFocusable returns the item's Focusable. -func (b *Box) GetFocusable() Focusable { - return b.focus -} diff --git a/vendor/github.com/rivo/tview/button.go b/vendor/github.com/rivo/tview/button.go deleted file mode 100644 index fd7c2347..00000000 --- a/vendor/github.com/rivo/tview/button.go +++ /dev/null @@ -1,157 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Button is labeled box that triggers an action when selected. -// -// See https://github.com/rivo/tview/wiki/Button for an example. -type Button struct { - *Box - - // The text to be displayed before the input area. - label string - - // The label color. - labelColor tcell.Color - - // The label color when the button is in focus. - labelColorActivated tcell.Color - - // The background color when the button is in focus. - backgroundColorActivated tcell.Color - - // An optional function which is called when the button was selected. - selected func() - - // An optional function which is called when the user leaves the button. A - // key is provided indicating which key was pressed to leave (tab or backtab). - blur func(tcell.Key) -} - -// NewButton returns a new input field. -func NewButton(label string) *Button { - box := NewBox().SetBackgroundColor(Styles.ContrastBackgroundColor) - box.SetRect(0, 0, TaggedStringWidth(label)+4, 1) - return &Button{ - Box: box, - label: label, - labelColor: Styles.PrimaryTextColor, - labelColorActivated: Styles.InverseTextColor, - backgroundColorActivated: Styles.PrimaryTextColor, - } -} - -// SetLabel sets the button text. -func (b *Button) SetLabel(label string) *Button { - b.label = label - return b -} - -// GetLabel returns the button text. -func (b *Button) GetLabel() string { - return b.label -} - -// SetLabelColor sets the color of the button text. -func (b *Button) SetLabelColor(color tcell.Color) *Button { - b.labelColor = color - return b -} - -// SetLabelColorActivated sets the color of the button text when the button is -// in focus. -func (b *Button) SetLabelColorActivated(color tcell.Color) *Button { - b.labelColorActivated = color - return b -} - -// SetBackgroundColorActivated sets the background color of the button text when -// the button is in focus. -func (b *Button) SetBackgroundColorActivated(color tcell.Color) *Button { - b.backgroundColorActivated = color - return b -} - -// SetSelectedFunc sets a handler which is called when the button was selected. -func (b *Button) SetSelectedFunc(handler func()) *Button { - b.selected = handler - return b -} - -// SetBlurFunc sets a handler which is called when the user leaves the button. -// The callback function is provided with the key that was pressed, which is one -// of the following: -// -// - KeyEscape: Leaving the button with no specific direction. -// - KeyTab: Move to the next field. -// - KeyBacktab: Move to the previous field. -func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button { - b.blur = handler - return b -} - -// Draw draws this primitive onto the screen. -func (b *Button) Draw(screen tcell.Screen) { - // Draw the box. - borderColor := b.borderColor - backgroundColor := b.backgroundColor - if b.focus.HasFocus() { - b.backgroundColor = b.backgroundColorActivated - b.borderColor = b.labelColorActivated - defer func() { - b.borderColor = borderColor - }() - } - b.Box.Draw(screen) - b.backgroundColor = backgroundColor - - // Draw label. - x, y, width, height := b.GetInnerRect() - if width > 0 && height > 0 { - y = y + height/2 - labelColor := b.labelColor - if b.focus.HasFocus() { - labelColor = b.labelColorActivated - } - Print(screen, b.label, x, y, width, AlignCenter, labelColor) - } -} - -// InputHandler returns the handler for this primitive. -func (b *Button) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return b.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - // Process key event. - switch key := event.Key(); key { - case tcell.KeyEnter: // Selected. - if b.selected != nil { - b.selected() - } - case tcell.KeyBacktab, tcell.KeyTab, tcell.KeyEscape: // Leave. No action. - if b.blur != nil { - b.blur(key) - } - } - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (b *Button) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !b.InRect(event.Position()) { - return false, nil - } - - // Process mouse event. - if action == MouseLeftClick { - setFocus(b) - if b.selected != nil { - b.selected() - } - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/checkbox.go b/vendor/github.com/rivo/tview/checkbox.go deleted file mode 100644 index 7c4b5052..00000000 --- a/vendor/github.com/rivo/tview/checkbox.go +++ /dev/null @@ -1,226 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Checkbox implements a simple box for boolean values which can be checked and -// unchecked. -// -// See https://github.com/rivo/tview/wiki/Checkbox for an example. -type Checkbox struct { - *Box - - // Whether or not this box is checked. - checked bool - - // The text to be displayed before the input area. - label string - - // The screen width of the label area. A value of 0 means use the width of - // the label text. - labelWidth int - - // The label color. - labelColor tcell.Color - - // The background color of the input area. - fieldBackgroundColor tcell.Color - - // The text color of the input area. - fieldTextColor tcell.Color - - // An optional function which is called when the user changes the checked - // state of this checkbox. - changed func(checked bool) - - // An optional function which is called when the user indicated that they - // are done entering text. The key which was pressed is provided (tab, - // shift-tab, or escape). - done func(tcell.Key) - - // A callback function set by the Form class and called when the user leaves - // this form item. - finished func(tcell.Key) -} - -// NewCheckbox returns a new input field. -func NewCheckbox() *Checkbox { - return &Checkbox{ - Box: NewBox(), - labelColor: Styles.SecondaryTextColor, - fieldBackgroundColor: Styles.ContrastBackgroundColor, - fieldTextColor: Styles.PrimaryTextColor, - } -} - -// SetChecked sets the state of the checkbox. -func (c *Checkbox) SetChecked(checked bool) *Checkbox { - c.checked = checked - return c -} - -// IsChecked returns whether or not the box is checked. -func (c *Checkbox) IsChecked() bool { - return c.checked -} - -// SetLabel sets the text to be displayed before the input area. -func (c *Checkbox) SetLabel(label string) *Checkbox { - c.label = label - return c -} - -// GetLabel returns the text to be displayed before the input area. -func (c *Checkbox) GetLabel() string { - return c.label -} - -// SetLabelWidth sets the screen width of the label. A value of 0 will cause the -// primitive to use the width of the label string. -func (c *Checkbox) SetLabelWidth(width int) *Checkbox { - c.labelWidth = width - return c -} - -// SetLabelColor sets the color of the label. -func (c *Checkbox) SetLabelColor(color tcell.Color) *Checkbox { - c.labelColor = color - return c -} - -// SetFieldBackgroundColor sets the background color of the input area. -func (c *Checkbox) SetFieldBackgroundColor(color tcell.Color) *Checkbox { - c.fieldBackgroundColor = color - return c -} - -// SetFieldTextColor sets the text color of the input area. -func (c *Checkbox) SetFieldTextColor(color tcell.Color) *Checkbox { - c.fieldTextColor = color - return c -} - -// SetFormAttributes sets attributes shared by all form items. -func (c *Checkbox) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { - c.labelWidth = labelWidth - c.labelColor = labelColor - c.backgroundColor = bgColor - c.fieldTextColor = fieldTextColor - c.fieldBackgroundColor = fieldBgColor - return c -} - -// GetFieldWidth returns this primitive's field width. -func (c *Checkbox) GetFieldWidth() int { - return 1 -} - -// SetChangedFunc sets a handler which is called when the checked state of this -// checkbox was changed by the user. The handler function receives the new -// state. -func (c *Checkbox) SetChangedFunc(handler func(checked bool)) *Checkbox { - c.changed = handler - return c -} - -// SetDoneFunc sets a handler which is called when the user is done using the -// checkbox. The callback function is provided with the key that was pressed, -// which is one of the following: -// -// - KeyEscape: Abort text input. -// - KeyTab: Move to the next field. -// - KeyBacktab: Move to the previous field. -func (c *Checkbox) SetDoneFunc(handler func(key tcell.Key)) *Checkbox { - c.done = handler - return c -} - -// SetFinishedFunc sets a callback invoked when the user leaves this form item. -func (c *Checkbox) SetFinishedFunc(handler func(key tcell.Key)) FormItem { - c.finished = handler - return c -} - -// Draw draws this primitive onto the screen. -func (c *Checkbox) Draw(screen tcell.Screen) { - c.Box.Draw(screen) - - // Prepare - x, y, width, height := c.GetInnerRect() - rightLimit := x + width - if height < 1 || rightLimit <= x { - return - } - - // Draw label. - if c.labelWidth > 0 { - labelWidth := c.labelWidth - if labelWidth > rightLimit-x { - labelWidth = rightLimit - x - } - Print(screen, c.label, x, y, labelWidth, AlignLeft, c.labelColor) - x += labelWidth - } else { - _, drawnWidth := Print(screen, c.label, x, y, rightLimit-x, AlignLeft, c.labelColor) - x += drawnWidth - } - - // Draw checkbox. - fieldStyle := tcell.StyleDefault.Background(c.fieldBackgroundColor).Foreground(c.fieldTextColor) - if c.focus.HasFocus() { - fieldStyle = fieldStyle.Background(c.fieldTextColor).Foreground(c.fieldBackgroundColor) - } - checkedRune := 'X' - if !c.checked { - checkedRune = ' ' - } - screen.SetContent(x, y, checkedRune, nil, fieldStyle) -} - -// InputHandler returns the handler for this primitive. -func (c *Checkbox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - // Process key event. - switch key := event.Key(); key { - case tcell.KeyRune, tcell.KeyEnter: // Check. - if key == tcell.KeyRune && event.Rune() != ' ' { - break - } - c.checked = !c.checked - if c.changed != nil { - c.changed(c.checked) - } - case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done. - if c.done != nil { - c.done(key) - } - if c.finished != nil { - c.finished(key) - } - } - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (c *Checkbox) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return c.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - x, y := event.Position() - _, rectY, _, _ := c.GetInnerRect() - if !c.InRect(x, y) { - return false, nil - } - - // Process mouse event. - if action == MouseLeftClick && y == rectY { - setFocus(c) - c.checked = !c.checked - if c.changed != nil { - c.changed(c.checked) - } - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/doc.go b/vendor/github.com/rivo/tview/doc.go deleted file mode 100644 index 4bceea2a..00000000 --- a/vendor/github.com/rivo/tview/doc.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Package tview implements rich widgets for terminal based user interfaces. The -widgets provided with this package are useful for data exploration and data -entry. - -Widgets - -The package implements the following widgets: - - - TextView: A scrollable window that display multi-colored text. Text may also - be highlighted. - - Table: A scrollable display of tabular data. Table cells, rows, or columns - may also be highlighted. - - TreeView: A scrollable display for hierarchical data. Tree nodes can be - highlighted, collapsed, expanded, and more. - - List: A navigable text list with optional keyboard shortcuts. - - InputField: One-line input fields to enter text. - - DropDown: Drop-down selection fields. - - Checkbox: Selectable checkbox for boolean values. - - Button: Buttons which get activated when the user selects them. - - Form: Forms composed of input fields, drop down selections, checkboxes, and - buttons. - - Modal: A centered window with a text message and one or more buttons. - - Grid: A grid based layout manager. - - Flex: A Flexbox based layout manager. - - Pages: A page based layout manager. - -The package also provides Application which is used to poll the event queue and -draw widgets on screen. - -Hello World - -The following is a very basic example showing a box with the title "Hello, -world!": - - package main - - import ( - "github.com/rivo/tview" - ) - - func main() { - box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!") - if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil { - panic(err) - } - } - -First, we create a box primitive with a border and a title. Then we create an -application, set the box as its root primitive, and run the event loop. The -application exits when the application's Stop() function is called or when -Ctrl-C is pressed. - -If we have a primitive which consumes key presses, we call the application's -SetFocus() function to redirect all key presses to that primitive. Most -primitives then offer ways to install handlers that allow you to react to any -actions performed on them. - -More Demos - -You will find more demos in the "demos" subdirectory. It also contains a -presentation (written using tview) which gives an overview of the different -widgets and how they can be used. - -Colors - -Throughout this package, colors are specified using the tcell.Color type. -Functions such as tcell.GetColor(), tcell.NewHexColor(), and tcell.NewRGBColor() -can be used to create colors from W3C color names or RGB values. - -Almost all strings which are displayed can contain color tags. Color tags are -W3C color names or six hexadecimal digits following a hash tag, wrapped in -square brackets. Examples: - - This is a [red]warning[white]! - The sky is [#8080ff]blue[#ffffff]. - -A color tag changes the color of the characters following that color tag. This -applies to almost everything from box titles, list text, form item labels, to -table cells. In a TextView, this functionality has to be switched on explicitly. -See the TextView documentation for more information. - -Color tags may contain not just the foreground (text) color but also the -background color and additional flags. In fact, the full definition of a color -tag is as follows: - - [::] - -Each of the three fields can be left blank and trailing fields can be omitted. -(Empty square brackets "[]", however, are not considered color tags.) Colors -that are not specified will be left unchanged. A field with just a dash ("-") -means "reset to default". - -You can specify the following flags (some flags may not be supported by your -terminal): - - l: blink - b: bold - d: dim - r: reverse (switch foreground and background color) - u: underline - -Examples: - - [yellow]Yellow text - [yellow:red]Yellow text on red background - [:red]Red background, text color unchanged - [yellow::u]Yellow text underlined - [::bl]Bold, blinking text - [::-]Colors unchanged, flags reset - [-]Reset foreground color - [-:-:-]Reset everything - [:]No effect - []Not a valid color tag, will print square brackets as they are - -In the rare event that you want to display a string such as "[red]" or -"[#00ff1a]" without applying its effect, you need to put an opening square -bracket before the closing square bracket. Note that the text inside the -brackets will be matched less strictly than region or colors tags. I.e. any -character that may be used in color or region tags will be recognized. Examples: - - [red[] will be output as [red] - ["123"[] will be output as ["123"] - [#6aff00[[] will be output as [#6aff00[] - [a#"[[[] will be output as [a#"[[] - [] will be output as [] (see color tags above) - [[] will be output as [[] (not an escaped tag) - -You can use the Escape() function to insert brackets automatically where needed. - -Styles - -When primitives are instantiated, they are initialized with colors taken from -the global Styles variable. You may change this variable to adapt the look and -feel of the primitives to your preferred style. - -Unicode Support - -This package supports unicode characters including wide characters. - -Concurrency - -Many functions in this package are not thread-safe. For many applications, this -may not be an issue: If your code makes changes in response to key events, it -will execute in the main goroutine and thus will not cause any race conditions. - -If you access your primitives from other goroutines, however, you will need to -synchronize execution. The easiest way to do this is to call -Application.QueueUpdate() or Application.QueueUpdateDraw() (see the function -documentation for details): - - go func() { - app.QueueUpdateDraw(func() { - table.SetCellSimple(0, 0, "Foo bar") - }) - }() - -One exception to this is the io.Writer interface implemented by TextView. You -can safely write to a TextView from any goroutine. See the TextView -documentation for details. - -You can also call Application.Draw() from any goroutine without having to wrap -it in QueueUpdate(). And, as mentioned above, key event callbacks are executed -in the main goroutine and thus should not use QueueUpdate() as that may lead to -deadlocks. - -Type Hierarchy - -All widgets listed above contain the Box type. All of Box's functions are -therefore available for all widgets, too. - -All widgets also implement the Primitive interface. There is also the Focusable -interface which is used to override functions in subclassing types. - -The tview package is based on https://github.com/gdamore/tcell. It uses types -and constants from that package (e.g. colors and keyboard values). - -This package does not process mouse input (yet). -*/ -package tview diff --git a/vendor/github.com/rivo/tview/dropdown.go b/vendor/github.com/rivo/tview/dropdown.go deleted file mode 100644 index 89d50c13..00000000 --- a/vendor/github.com/rivo/tview/dropdown.go +++ /dev/null @@ -1,547 +0,0 @@ -package tview - -import ( - "strings" - - "github.com/gdamore/tcell" -) - -// dropDownOption is one option that can be selected in a drop-down primitive. -type dropDownOption struct { - Text string // The text to be displayed in the drop-down. - Selected func() // The (optional) callback for when this option was selected. -} - -// DropDown implements a selection widget whose options become visible in a -// drop-down list when activated. -// -// See https://github.com/rivo/tview/wiki/DropDown for an example. -type DropDown struct { - *Box - - // The options from which the user can choose. - options []*dropDownOption - - // Strings to be placed before and after each drop-down option. - optionPrefix, optionSuffix string - - // The index of the currently selected option. Negative if no option is - // currently selected. - currentOption int - - // Strings to be placed beefore and after the current option. - currentOptionPrefix, currentOptionSuffix string - - // The text to be displayed when no option has yet been selected. - noSelection string - - // Set to true if the options are visible and selectable. - open bool - - // The runes typed so far to directly access one of the list items. - prefix string - - // The list element for the options. - list *List - - // The text to be displayed before the input area. - label string - - // The label color. - labelColor tcell.Color - - // The background color of the input area. - fieldBackgroundColor tcell.Color - - // The text color of the input area. - fieldTextColor tcell.Color - - // The color for prefixes. - prefixTextColor tcell.Color - - // The screen width of the label area. A value of 0 means use the width of - // the label text. - labelWidth int - - // The screen width of the input area. A value of 0 means extend as much as - // possible. - fieldWidth int - - // An optional function which is called when the user indicated that they - // are done selecting options. The key which was pressed is provided (tab, - // shift-tab, or escape). - done func(tcell.Key) - - // A callback function set by the Form class and called when the user leaves - // this form item. - finished func(tcell.Key) - - // A callback function which is called when the user changes the drop-down's - // selection. - selected func(text string, index int) - - dragging bool // Set to true when mouse dragging is in progress. -} - -// NewDropDown returns a new drop-down. -func NewDropDown() *DropDown { - list := NewList() - list.ShowSecondaryText(false). - SetMainTextColor(Styles.PrimitiveBackgroundColor). - SetSelectedTextColor(Styles.PrimitiveBackgroundColor). - SetSelectedBackgroundColor(Styles.PrimaryTextColor). - SetHighlightFullLine(true). - SetBackgroundColor(Styles.MoreContrastBackgroundColor) - - d := &DropDown{ - Box: NewBox(), - currentOption: -1, - list: list, - labelColor: Styles.SecondaryTextColor, - fieldBackgroundColor: Styles.ContrastBackgroundColor, - fieldTextColor: Styles.PrimaryTextColor, - prefixTextColor: Styles.ContrastSecondaryTextColor, - } - - d.focus = d - - return d -} - -// SetCurrentOption sets the index of the currently selected option. This may -// be a negative value to indicate that no option is currently selected. Calling -// this function will also trigger the "selected" callback (if there is one). -func (d *DropDown) SetCurrentOption(index int) *DropDown { - if index >= 0 && index < len(d.options) { - d.currentOption = index - d.list.SetCurrentItem(index) - if d.selected != nil { - d.selected(d.options[index].Text, index) - } - if d.options[index].Selected != nil { - d.options[index].Selected() - } - } else { - d.currentOption = -1 - d.list.SetCurrentItem(0) // Set to 0 because -1 means "last item". - if d.selected != nil { - d.selected("", -1) - } - } - return d -} - -// GetCurrentOption returns the index of the currently selected option as well -// as its text. If no option was selected, -1 and an empty string is returned. -func (d *DropDown) GetCurrentOption() (int, string) { - var text string - if d.currentOption >= 0 && d.currentOption < len(d.options) { - text = d.options[d.currentOption].Text - } - return d.currentOption, text -} - -// SetTextOptions sets the text to be placed before and after each drop-down -// option (prefix/suffix), the text placed before and after the currently -// selected option (currentPrefix/currentSuffix) as well as the text to be -// displayed when no option is currently selected. Per default, all of these -// strings are empty. -func (d *DropDown) SetTextOptions(prefix, suffix, currentPrefix, currentSuffix, noSelection string) *DropDown { - d.currentOptionPrefix = currentPrefix - d.currentOptionSuffix = currentSuffix - d.noSelection = noSelection - d.optionPrefix = prefix - d.optionSuffix = suffix - for index := 0; index < d.list.GetItemCount(); index++ { - d.list.SetItemText(index, prefix+d.options[index].Text+suffix, "") - } - return d -} - -// SetLabel sets the text to be displayed before the input area. -func (d *DropDown) SetLabel(label string) *DropDown { - d.label = label - return d -} - -// GetLabel returns the text to be displayed before the input area. -func (d *DropDown) GetLabel() string { - return d.label -} - -// SetLabelWidth sets the screen width of the label. A value of 0 will cause the -// primitive to use the width of the label string. -func (d *DropDown) SetLabelWidth(width int) *DropDown { - d.labelWidth = width - return d -} - -// SetLabelColor sets the color of the label. -func (d *DropDown) SetLabelColor(color tcell.Color) *DropDown { - d.labelColor = color - return d -} - -// SetFieldBackgroundColor sets the background color of the options area. -func (d *DropDown) SetFieldBackgroundColor(color tcell.Color) *DropDown { - d.fieldBackgroundColor = color - return d -} - -// SetFieldTextColor sets the text color of the options area. -func (d *DropDown) SetFieldTextColor(color tcell.Color) *DropDown { - d.fieldTextColor = color - return d -} - -// SetPrefixTextColor sets the color of the prefix string. The prefix string is -// shown when the user starts typing text, which directly selects the first -// option that starts with the typed string. -func (d *DropDown) SetPrefixTextColor(color tcell.Color) *DropDown { - d.prefixTextColor = color - return d -} - -// SetFormAttributes sets attributes shared by all form items. -func (d *DropDown) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { - d.labelWidth = labelWidth - d.labelColor = labelColor - d.backgroundColor = bgColor - d.fieldTextColor = fieldTextColor - d.fieldBackgroundColor = fieldBgColor - return d -} - -// SetFieldWidth sets the screen width of the options area. A value of 0 means -// extend to as long as the longest option text. -func (d *DropDown) SetFieldWidth(width int) *DropDown { - d.fieldWidth = width - return d -} - -// GetFieldWidth returns this primitive's field screen width. -func (d *DropDown) GetFieldWidth() int { - if d.fieldWidth > 0 { - return d.fieldWidth - } - fieldWidth := 0 - for _, option := range d.options { - width := TaggedStringWidth(option.Text) - if width > fieldWidth { - fieldWidth = width - } - } - return fieldWidth -} - -// AddOption adds a new selectable option to this drop-down. The "selected" -// callback is called when this option was selected. It may be nil. -func (d *DropDown) AddOption(text string, selected func()) *DropDown { - d.options = append(d.options, &dropDownOption{Text: text, Selected: selected}) - d.list.AddItem(d.optionPrefix+text+d.optionSuffix, "", 0, nil) - return d -} - -// SetOptions replaces all current options with the ones provided and installs -// one callback function which is called when one of the options is selected. -// It will be called with the option's text and its index into the options -// slice. The "selected" parameter may be nil. -func (d *DropDown) SetOptions(texts []string, selected func(text string, index int)) *DropDown { - d.list.Clear() - d.options = nil - for index, text := range texts { - func(t string, i int) { - d.AddOption(text, nil) - }(text, index) - } - d.selected = selected - return d -} - -// SetSelectedFunc sets a handler which is called when the user changes the -// drop-down's option. This handler will be called in addition and prior to -// an option's optional individual handler. The handler is provided with the -// selected option's text and index. If "no option" was selected, these values -// are an empty string and -1. -func (d *DropDown) SetSelectedFunc(handler func(text string, index int)) *DropDown { - d.selected = handler - return d -} - -// SetDoneFunc sets a handler which is called when the user is done selecting -// options. The callback function is provided with the key that was pressed, -// which is one of the following: -// -// - KeyEscape: Abort selection. -// - KeyTab: Move to the next field. -// - KeyBacktab: Move to the previous field. -func (d *DropDown) SetDoneFunc(handler func(key tcell.Key)) *DropDown { - d.done = handler - return d -} - -// SetFinishedFunc sets a callback invoked when the user leaves this form item. -func (d *DropDown) SetFinishedFunc(handler func(key tcell.Key)) FormItem { - d.finished = handler - return d -} - -// Draw draws this primitive onto the screen. -func (d *DropDown) Draw(screen tcell.Screen) { - d.Box.Draw(screen) - - // Prepare. - x, y, width, height := d.GetInnerRect() - rightLimit := x + width - if height < 1 || rightLimit <= x { - return - } - - // Draw label. - if d.labelWidth > 0 { - labelWidth := d.labelWidth - if labelWidth > rightLimit-x { - labelWidth = rightLimit - x - } - Print(screen, d.label, x, y, labelWidth, AlignLeft, d.labelColor) - x += labelWidth - } else { - _, drawnWidth := Print(screen, d.label, x, y, rightLimit-x, AlignLeft, d.labelColor) - x += drawnWidth - } - - // What's the longest option text? - maxWidth := 0 - optionWrapWidth := TaggedStringWidth(d.optionPrefix + d.optionSuffix) - for _, option := range d.options { - strWidth := TaggedStringWidth(option.Text) + optionWrapWidth - if strWidth > maxWidth { - maxWidth = strWidth - } - } - - // Draw selection area. - fieldWidth := d.fieldWidth - if fieldWidth == 0 { - fieldWidth = maxWidth - if d.currentOption < 0 { - noSelectionWidth := TaggedStringWidth(d.noSelection) - if noSelectionWidth > fieldWidth { - fieldWidth = noSelectionWidth - } - } else if d.currentOption < len(d.options) { - currentOptionWidth := TaggedStringWidth(d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix) - if currentOptionWidth > fieldWidth { - fieldWidth = currentOptionWidth - } - } - } - if rightLimit-x < fieldWidth { - fieldWidth = rightLimit - x - } - fieldStyle := tcell.StyleDefault.Background(d.fieldBackgroundColor) - if d.GetFocusable().HasFocus() && !d.open { - fieldStyle = fieldStyle.Background(d.fieldTextColor) - } - for index := 0; index < fieldWidth; index++ { - screen.SetContent(x+index, y, ' ', nil, fieldStyle) - } - - // Draw selected text. - if d.open && len(d.prefix) > 0 { - // Show the prefix. - currentOptionPrefixWidth := TaggedStringWidth(d.currentOptionPrefix) - prefixWidth := stringWidth(d.prefix) - listItemText := d.options[d.list.GetCurrentItem()].Text - Print(screen, d.currentOptionPrefix, x, y, fieldWidth, AlignLeft, d.fieldTextColor) - Print(screen, d.prefix, x+currentOptionPrefixWidth, y, fieldWidth-currentOptionPrefixWidth, AlignLeft, d.prefixTextColor) - if len(d.prefix) < len(listItemText) { - Print(screen, listItemText[len(d.prefix):]+d.currentOptionSuffix, x+prefixWidth+currentOptionPrefixWidth, y, fieldWidth-prefixWidth-currentOptionPrefixWidth, AlignLeft, d.fieldTextColor) - } - } else { - color := d.fieldTextColor - text := d.noSelection - if d.currentOption >= 0 && d.currentOption < len(d.options) { - text = d.currentOptionPrefix + d.options[d.currentOption].Text + d.currentOptionSuffix - } - // Just show the current selection. - if d.GetFocusable().HasFocus() && !d.open { - color = d.fieldBackgroundColor - } - Print(screen, text, x, y, fieldWidth, AlignLeft, color) - } - - // Draw options list. - if d.HasFocus() && d.open { - // We prefer to drop down but if there is no space, maybe drop up? - lx := x - ly := y + 1 - lwidth := maxWidth - lheight := len(d.options) - _, sheight := screen.Size() - if ly+lheight >= sheight && ly-2 > lheight-ly { - ly = y - lheight - if ly < 0 { - ly = 0 - } - } - if ly+lheight >= sheight { - lheight = sheight - ly - } - d.list.SetRect(lx, ly, lwidth, lheight) - d.list.Draw(screen) - } -} - -// InputHandler returns the handler for this primitive. -func (d *DropDown) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - // Process key event. - switch key := event.Key(); key { - case tcell.KeyEnter, tcell.KeyRune, tcell.KeyDown: - d.prefix = "" - - // If the first key was a letter already, it becomes part of the prefix. - if r := event.Rune(); key == tcell.KeyRune && r != ' ' { - d.prefix += string(r) - d.evalPrefix() - } - - d.openList(setFocus) - case tcell.KeyEscape, tcell.KeyTab, tcell.KeyBacktab: - if d.done != nil { - d.done(key) - } - if d.finished != nil { - d.finished(key) - } - } - }) -} - -// evalPrefix selects an item in the drop-down list based on the current prefix. -func (d *DropDown) evalPrefix() { - if len(d.prefix) > 0 { - for index, option := range d.options { - if strings.HasPrefix(strings.ToLower(option.Text), d.prefix) { - d.list.SetCurrentItem(index) - return - } - } - - // Prefix does not match any item. Remove last rune. - r := []rune(d.prefix) - d.prefix = string(r[:len(r)-1]) - } -} - -// openList hands control over to the embedded List primitive. -func (d *DropDown) openList(setFocus func(Primitive)) { - d.open = true - optionBefore := d.currentOption - - d.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { - if d.dragging { - return // If we're dragging the mouse, we don't want to trigger any events. - } - - // An option was selected. Close the list again. - d.currentOption = index - d.closeList(setFocus) - - // Trigger "selected" event. - if d.selected != nil { - d.selected(d.options[d.currentOption].Text, d.currentOption) - } - if d.options[d.currentOption].Selected != nil { - d.options[d.currentOption].Selected() - } - }).SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - if event.Key() == tcell.KeyRune { - d.prefix += string(event.Rune()) - d.evalPrefix() - } else if event.Key() == tcell.KeyBackspace || event.Key() == tcell.KeyBackspace2 { - if len(d.prefix) > 0 { - r := []rune(d.prefix) - d.prefix = string(r[:len(r)-1]) - } - d.evalPrefix() - } else if event.Key() == tcell.KeyEscape { - d.currentOption = optionBefore - d.closeList(setFocus) - } else { - d.prefix = "" - } - - return event - }) - - setFocus(d.list) -} - -// closeList closes the embedded List element by hiding it and removing focus -// from it. -func (d *DropDown) closeList(setFocus func(Primitive)) { - d.open = false - if d.list.HasFocus() { - setFocus(d) - } -} - -// Focus is called by the application when the primitive receives focus. -func (d *DropDown) Focus(delegate func(p Primitive)) { - d.Box.Focus(delegate) - if d.open { - delegate(d.list) - } -} - -// HasFocus returns whether or not this primitive has focus. -func (d *DropDown) HasFocus() bool { - if d.open { - return d.list.HasFocus() - } - return d.hasFocus -} - -// MouseHandler returns the mouse handler for this primitive. -func (d *DropDown) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return d.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - // Was the mouse event in the drop-down box itself (or on its label)? - x, y := event.Position() - _, rectY, _, _ := d.GetInnerRect() - inRect := y == rectY - if !d.open && !inRect { - return d.InRect(x, y), nil // No, and it's not expanded either. Ignore. - } - - // Handle dragging. Clicks are implicitly handled by this logic. - switch action { - case MouseLeftDown: - consumed = d.open || inRect - capture = d - if !d.open { - d.openList(setFocus) - d.dragging = true - } else if consumed, _ := d.list.MouseHandler()(MouseLeftClick, event, setFocus); !consumed { - d.closeList(setFocus) // Close drop-down if clicked outside of it. - } - case MouseMove: - if d.dragging { - // We pretend it's a left click so we can see the selection during - // dragging. Because we don't act upon it, it's not a problem. - d.list.MouseHandler()(MouseLeftClick, event, setFocus) - consumed = true - capture = d - } - case MouseLeftUp: - if d.dragging { - d.dragging = false - d.list.MouseHandler()(MouseLeftClick, event, setFocus) - consumed = true - } - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/flex.go b/vendor/github.com/rivo/tview/flex.go deleted file mode 100644 index 3343e863..00000000 --- a/vendor/github.com/rivo/tview/flex.go +++ /dev/null @@ -1,225 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Configuration values. -const ( - FlexRow = iota - FlexColumn -) - -// flexItem holds layout options for one item. -type flexItem struct { - Item Primitive // The item to be positioned. May be nil for an empty item. - FixedSize int // The item's fixed size which may not be changed, 0 if it has no fixed size. - Proportion int // The item's proportion. - Focus bool // Whether or not this item attracts the layout's focus. -} - -// Flex is a basic implementation of the Flexbox layout. The contained -// primitives are arranged horizontally or vertically. The way they are -// distributed along that dimension depends on their layout settings, which is -// either a fixed length or a proportional length. See AddItem() for details. -// -// See https://github.com/rivo/tview/wiki/Flex for an example. -type Flex struct { - *Box - - // The items to be positioned. - items []*flexItem - - // FlexRow or FlexColumn. - direction int - - // If set to true, Flex will use the entire screen as its available space - // instead its box dimensions. - fullScreen bool -} - -// NewFlex returns a new flexbox layout container with no primitives and its -// direction set to FlexColumn. To add primitives to this layout, see AddItem(). -// To change the direction, see SetDirection(). -// -// Note that Box, the superclass of Flex, will have its background color set to -// transparent so that any nil flex items will leave their background unchanged. -// To clear a Flex's background before any items are drawn, set it to the -// desired color: -// -// flex.SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor) -func NewFlex() *Flex { - f := &Flex{ - Box: NewBox().SetBackgroundColor(tcell.ColorDefault), - direction: FlexColumn, - } - f.focus = f - return f -} - -// SetDirection sets the direction in which the contained primitives are -// distributed. This can be either FlexColumn (default) or FlexRow. -func (f *Flex) SetDirection(direction int) *Flex { - f.direction = direction - return f -} - -// SetFullScreen sets the flag which, when true, causes the flex layout to use -// the entire screen space instead of whatever size it is currently assigned to. -func (f *Flex) SetFullScreen(fullScreen bool) *Flex { - f.fullScreen = fullScreen - return f -} - -// AddItem adds a new item to the container. The "fixedSize" argument is a width -// or height that may not be changed by the layout algorithm. A value of 0 means -// that its size is flexible and may be changed. The "proportion" argument -// defines the relative size of the item compared to other flexible-size items. -// For example, items with a proportion of 2 will be twice as large as items -// with a proportion of 1. The proportion must be at least 1 if fixedSize == 0 -// (ignored otherwise). -// -// If "focus" is set to true, the item will receive focus when the Flex -// primitive receives focus. If multiple items have the "focus" flag set to -// true, the first one will receive focus. -// -// You can provide a nil value for the primitive. This will still consume screen -// space but nothing will be drawn. -func (f *Flex) AddItem(item Primitive, fixedSize, proportion int, focus bool) *Flex { - f.items = append(f.items, &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus}) - return f -} - -// RemoveItem removes all items for the given primitive from the container, -// keeping the order of the remaining items intact. -func (f *Flex) RemoveItem(p Primitive) *Flex { - for index := len(f.items) - 1; index >= 0; index-- { - if f.items[index].Item == p { - f.items = append(f.items[:index], f.items[index+1:]...) - } - } - return f -} - -// Clear removes all items from the container. -func (f *Flex) Clear() *Flex { - f.items = nil - return f -} - -// ResizeItem sets a new size for the item(s) with the given primitive. If there -// are multiple Flex items with the same primitive, they will all receive the -// same size. For details regarding the size parameters, see AddItem(). -func (f *Flex) ResizeItem(p Primitive, fixedSize, proportion int) *Flex { - for _, item := range f.items { - if item.Item == p { - item.FixedSize = fixedSize - item.Proportion = proportion - } - } - return f -} - -// Draw draws this primitive onto the screen. -func (f *Flex) Draw(screen tcell.Screen) { - f.Box.Draw(screen) - - // Calculate size and position of the items. - - // Do we use the entire screen? - if f.fullScreen { - width, height := screen.Size() - f.SetRect(0, 0, width, height) - } - - // How much space can we distribute? - x, y, width, height := f.GetInnerRect() - var proportionSum int - distSize := width - if f.direction == FlexRow { - distSize = height - } - for _, item := range f.items { - if item.FixedSize > 0 { - distSize -= item.FixedSize - } else { - proportionSum += item.Proportion - } - } - - // Calculate positions and draw items. - pos := x - if f.direction == FlexRow { - pos = y - } - for _, item := range f.items { - size := item.FixedSize - if size <= 0 { - if proportionSum > 0 { - size = distSize * item.Proportion / proportionSum - distSize -= size - proportionSum -= item.Proportion - } else { - size = 0 - } - } - if item.Item != nil { - if f.direction == FlexColumn { - item.Item.SetRect(pos, y, size, height) - } else { - item.Item.SetRect(x, pos, width, size) - } - } - pos += size - - if item.Item != nil { - if item.Item.GetFocusable().HasFocus() { - defer item.Item.Draw(screen) - } else { - item.Item.Draw(screen) - } - } - } -} - -// Focus is called when this primitive receives focus. -func (f *Flex) Focus(delegate func(p Primitive)) { - for _, item := range f.items { - if item.Item != nil && item.Focus { - delegate(item.Item) - return - } - } -} - -// HasFocus returns whether or not this primitive has focus. -func (f *Flex) HasFocus() bool { - for _, item := range f.items { - if item.Item != nil && item.Item.GetFocusable().HasFocus() { - return true - } - } - return false -} - -// MouseHandler returns the mouse handler for this primitive. -func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !f.InRect(event.Position()) { - return false, nil - } - - // Pass mouse events along to the first child item that takes it. - for _, item := range f.items { - if item.Item == nil { - continue - } - consumed, capture = item.Item.MouseHandler()(action, event, setFocus) - if consumed { - return - } - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/focusable.go b/vendor/github.com/rivo/tview/focusable.go deleted file mode 100644 index 99fdaaf4..00000000 --- a/vendor/github.com/rivo/tview/focusable.go +++ /dev/null @@ -1,8 +0,0 @@ -package tview - -// Focusable provides a method which determines if a primitive has focus. -// Composed primitives may be focused based on the focused state of their -// contained primitives. -type Focusable interface { - HasFocus() bool -} diff --git a/vendor/github.com/rivo/tview/form.go b/vendor/github.com/rivo/tview/form.go deleted file mode 100644 index d022fab2..00000000 --- a/vendor/github.com/rivo/tview/form.go +++ /dev/null @@ -1,663 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// DefaultFormFieldWidth is the default field screen width of form elements -// whose field width is flexible (0). This is used in the Form class for -// horizontal layouts. -var DefaultFormFieldWidth = 10 - -// FormItem is the interface all form items must implement to be able to be -// included in a form. -type FormItem interface { - Primitive - - // GetLabel returns the item's label text. - GetLabel() string - - // SetFormAttributes sets a number of item attributes at once. - SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem - - // GetFieldWidth returns the width of the form item's field (the area which - // is manipulated by the user) in number of screen cells. A value of 0 - // indicates the the field width is flexible and may use as much space as - // required. - GetFieldWidth() int - - // SetFinishedFunc sets the handler function for when the user finished - // entering data into the item. The handler may receive events for the - // Enter key (we're done), the Escape key (cancel input), the Tab key (move to - // next field), and the Backtab key (move to previous field). - SetFinishedFunc(handler func(key tcell.Key)) FormItem -} - -// Form allows you to combine multiple one-line form elements into a vertical -// or horizontal layout. Form elements include types such as InputField or -// Checkbox. These elements can be optionally followed by one or more buttons -// for which you can define form-wide actions (e.g. Save, Clear, Cancel). -// -// See https://github.com/rivo/tview/wiki/Form for an example. -type Form struct { - *Box - - // The items of the form (one row per item). - items []FormItem - - // The buttons of the form. - buttons []*Button - - // If set to true, instead of position items and buttons from top to bottom, - // they are positioned from left to right. - horizontal bool - - // The alignment of the buttons. - buttonsAlign int - - // The number of empty rows between items. - itemPadding int - - // The index of the item or button which has focus. (Items are counted first, - // buttons are counted last.) This is only used when the form itself receives - // focus so that the last element that had focus keeps it. - focusedElement int - - // The label color. - labelColor tcell.Color - - // The background color of the input area. - fieldBackgroundColor tcell.Color - - // The text color of the input area. - fieldTextColor tcell.Color - - // The background color of the buttons. - buttonBackgroundColor tcell.Color - - // The color of the button text. - buttonTextColor tcell.Color - - // An optional function which is called when the user hits Escape. - cancel func() -} - -// NewForm returns a new form. -func NewForm() *Form { - box := NewBox().SetBorderPadding(1, 1, 1, 1) - - f := &Form{ - Box: box, - itemPadding: 1, - labelColor: Styles.SecondaryTextColor, - fieldBackgroundColor: Styles.ContrastBackgroundColor, - fieldTextColor: Styles.PrimaryTextColor, - buttonBackgroundColor: Styles.ContrastBackgroundColor, - buttonTextColor: Styles.PrimaryTextColor, - } - - f.focus = f - - return f -} - -// SetItemPadding sets the number of empty rows between form items for vertical -// layouts and the number of empty cells between form items for horizontal -// layouts. -func (f *Form) SetItemPadding(padding int) *Form { - f.itemPadding = padding - return f -} - -// SetHorizontal sets the direction the form elements are laid out. If set to -// true, instead of positioning them from top to bottom (the default), they are -// positioned from left to right, moving into the next row if there is not -// enough space. -func (f *Form) SetHorizontal(horizontal bool) *Form { - f.horizontal = horizontal - return f -} - -// SetLabelColor sets the color of the labels. -func (f *Form) SetLabelColor(color tcell.Color) *Form { - f.labelColor = color - return f -} - -// SetFieldBackgroundColor sets the background color of the input areas. -func (f *Form) SetFieldBackgroundColor(color tcell.Color) *Form { - f.fieldBackgroundColor = color - return f -} - -// SetFieldTextColor sets the text color of the input areas. -func (f *Form) SetFieldTextColor(color tcell.Color) *Form { - f.fieldTextColor = color - return f -} - -// SetButtonsAlign sets how the buttons align horizontally, one of AlignLeft -// (the default), AlignCenter, and AlignRight. This is only -func (f *Form) SetButtonsAlign(align int) *Form { - f.buttonsAlign = align - return f -} - -// SetButtonBackgroundColor sets the background color of the buttons. -func (f *Form) SetButtonBackgroundColor(color tcell.Color) *Form { - f.buttonBackgroundColor = color - return f -} - -// SetButtonTextColor sets the color of the button texts. -func (f *Form) SetButtonTextColor(color tcell.Color) *Form { - f.buttonTextColor = color - return f -} - -// SetFocus shifts the focus to the form element with the given index, counting -// non-button items first and buttons last. Note that this index is only used -// when the form itself receives focus. -func (f *Form) SetFocus(index int) *Form { - if index < 0 { - f.focusedElement = 0 - } else if index >= len(f.items)+len(f.buttons) { - f.focusedElement = len(f.items) + len(f.buttons) - } else { - f.focusedElement = index - } - return f -} - -// AddInputField adds an input field to the form. It has a label, an optional -// initial value, a field width (a value of 0 extends it as far as possible), -// an optional accept function to validate the item's value (set to nil to -// accept any text), and an (optional) callback function which is invoked when -// the input field's text has changed. -func (f *Form) AddInputField(label, value string, fieldWidth int, accept func(textToCheck string, lastChar rune) bool, changed func(text string)) *Form { - f.items = append(f.items, NewInputField(). - SetLabel(label). - SetText(value). - SetFieldWidth(fieldWidth). - SetAcceptanceFunc(accept). - SetChangedFunc(changed)) - return f -} - -// AddPasswordField adds a password field to the form. This is similar to an -// input field except that the user's input not shown. Instead, a "mask" -// character is displayed. The password field has a label, an optional initial -// value, a field width (a value of 0 extends it as far as possible), and an -// (optional) callback function which is invoked when the input field's text has -// changed. -func (f *Form) AddPasswordField(label, value string, fieldWidth int, mask rune, changed func(text string)) *Form { - if mask == 0 { - mask = '*' - } - f.items = append(f.items, NewInputField(). - SetLabel(label). - SetText(value). - SetFieldWidth(fieldWidth). - SetMaskCharacter(mask). - SetChangedFunc(changed)) - return f -} - -// AddDropDown adds a drop-down element to the form. It has a label, options, -// and an (optional) callback function which is invoked when an option was -// selected. The initial option may be a negative value to indicate that no -// option is currently selected. -func (f *Form) AddDropDown(label string, options []string, initialOption int, selected func(option string, optionIndex int)) *Form { - f.items = append(f.items, NewDropDown(). - SetLabel(label). - SetOptions(options, selected). - SetCurrentOption(initialOption)) - return f -} - -// AddCheckbox adds a checkbox to the form. It has a label, an initial state, -// and an (optional) callback function which is invoked when the state of the -// checkbox was changed by the user. -func (f *Form) AddCheckbox(label string, checked bool, changed func(checked bool)) *Form { - f.items = append(f.items, NewCheckbox(). - SetLabel(label). - SetChecked(checked). - SetChangedFunc(changed)) - return f -} - -// AddButton adds a new button to the form. The "selected" function is called -// when the user selects this button. It may be nil. -func (f *Form) AddButton(label string, selected func()) *Form { - f.buttons = append(f.buttons, NewButton(label).SetSelectedFunc(selected)) - return f -} - -// GetButton returns the button at the specified 0-based index. Note that -// buttons have been specially prepared for this form and modifying some of -// their attributes may have unintended side effects. -func (f *Form) GetButton(index int) *Button { - return f.buttons[index] -} - -// RemoveButton removes the button at the specified position, starting with 0 -// for the button that was added first. -func (f *Form) RemoveButton(index int) *Form { - f.buttons = append(f.buttons[:index], f.buttons[index+1:]...) - return f -} - -// GetButtonCount returns the number of buttons in this form. -func (f *Form) GetButtonCount() int { - return len(f.buttons) -} - -// GetButtonIndex returns the index of the button with the given label, starting -// with 0 for the button that was added first. If no such label was found, -1 -// is returned. -func (f *Form) GetButtonIndex(label string) int { - for index, button := range f.buttons { - if button.GetLabel() == label { - return index - } - } - return -1 -} - -// Clear removes all input elements from the form, including the buttons if -// specified. -func (f *Form) Clear(includeButtons bool) *Form { - f.items = nil - if includeButtons { - f.ClearButtons() - } - f.focusedElement = 0 - return f -} - -// ClearButtons removes all buttons from the form. -func (f *Form) ClearButtons() *Form { - f.buttons = nil - return f -} - -// AddFormItem adds a new item to the form. This can be used to add your own -// objects to the form. Note, however, that the Form class will override some -// of its attributes to make it work in the form context. Specifically, these -// are: -// -// - The label width -// - The label color -// - The background color -// - The field text color -// - The field background color -func (f *Form) AddFormItem(item FormItem) *Form { - f.items = append(f.items, item) - return f -} - -// GetFormItemCount returns the number of items in the form (not including the -// buttons). -func (f *Form) GetFormItemCount() int { - return len(f.items) -} - -// GetFormItem returns the form item at the given position, starting with index -// 0. Elements are referenced in the order they were added. Buttons are not -// included. -func (f *Form) GetFormItem(index int) FormItem { - return f.items[index] -} - -// RemoveFormItem removes the form element at the given position, starting with -// index 0. Elements are referenced in the order they were added. Buttons are -// not included. -func (f *Form) RemoveFormItem(index int) *Form { - f.items = append(f.items[:index], f.items[index+1:]...) - return f -} - -// GetFormItemByLabel returns the first form element with the given label. If -// no such element is found, nil is returned. Buttons are not searched and will -// therefore not be returned. -func (f *Form) GetFormItemByLabel(label string) FormItem { - for _, item := range f.items { - if item.GetLabel() == label { - return item - } - } - return nil -} - -// GetFormItemIndex returns the index of the first form element with the given -// label. If no such element is found, -1 is returned. Buttons are not searched -// and will therefore not be returned. -func (f *Form) GetFormItemIndex(label string) int { - for index, item := range f.items { - if item.GetLabel() == label { - return index - } - } - return -1 -} - -// GetFocusedItemIndex returns the indices of the form element or button which -// currently has focus. If they don't, -1 is returned resepectively. -func (f *Form) GetFocusedItemIndex() (formItem, button int) { - index := f.focusIndex() - if index < 0 { - return -1, -1 - } - if index < len(f.items) { - return index, -1 - } - return -1, index - len(f.items) -} - -// SetCancelFunc sets a handler which is called when the user hits the Escape -// key. -func (f *Form) SetCancelFunc(callback func()) *Form { - f.cancel = callback - return f -} - -// Draw draws this primitive onto the screen. -func (f *Form) Draw(screen tcell.Screen) { - f.Box.Draw(screen) - - // Determine the actual item that has focus. - if index := f.focusIndex(); index >= 0 { - f.focusedElement = index - } - - // Determine the dimensions. - x, y, width, height := f.GetInnerRect() - topLimit := y - bottomLimit := y + height - rightLimit := x + width - startX := x - - // Find the longest label. - var maxLabelWidth int - for _, item := range f.items { - labelWidth := TaggedStringWidth(item.GetLabel()) - if labelWidth > maxLabelWidth { - maxLabelWidth = labelWidth - } - } - maxLabelWidth++ // Add one space. - - // Calculate positions of form items. - positions := make([]struct{ x, y, width, height int }, len(f.items)+len(f.buttons)) - var focusedPosition struct{ x, y, width, height int } - for index, item := range f.items { - // Calculate the space needed. - labelWidth := TaggedStringWidth(item.GetLabel()) - var itemWidth int - if f.horizontal { - fieldWidth := item.GetFieldWidth() - if fieldWidth == 0 { - fieldWidth = DefaultFormFieldWidth - } - labelWidth++ - itemWidth = labelWidth + fieldWidth - } else { - // We want all fields to align vertically. - labelWidth = maxLabelWidth - itemWidth = width - } - - // Advance to next line if there is no space. - if f.horizontal && x+labelWidth+1 >= rightLimit { - x = startX - y += 2 - } - - // Adjust the item's attributes. - if x+itemWidth >= rightLimit { - itemWidth = rightLimit - x - } - item.SetFormAttributes( - labelWidth, - f.labelColor, - f.backgroundColor, - f.fieldTextColor, - f.fieldBackgroundColor, - ) - - // Save position. - positions[index].x = x - positions[index].y = y - positions[index].width = itemWidth - positions[index].height = 1 - if item.GetFocusable().HasFocus() { - focusedPosition = positions[index] - } - - // Advance to next item. - if f.horizontal { - x += itemWidth + f.itemPadding - } else { - y += 1 + f.itemPadding - } - } - - // How wide are the buttons? - buttonWidths := make([]int, len(f.buttons)) - buttonsWidth := 0 - for index, button := range f.buttons { - w := TaggedStringWidth(button.GetLabel()) + 4 - buttonWidths[index] = w - buttonsWidth += w + 1 - } - buttonsWidth-- - - // Where do we place them? - if !f.horizontal && x+buttonsWidth < rightLimit { - if f.buttonsAlign == AlignRight { - x = rightLimit - buttonsWidth - } else if f.buttonsAlign == AlignCenter { - x = (x + rightLimit - buttonsWidth) / 2 - } - - // In vertical layouts, buttons always appear after an empty line. - if f.itemPadding == 0 { - y++ - } - } - - // Calculate positions of buttons. - for index, button := range f.buttons { - space := rightLimit - x - buttonWidth := buttonWidths[index] - if f.horizontal { - if space < buttonWidth-4 { - x = startX - y += 2 - space = width - } - } else { - if space < 1 { - break // No space for this button anymore. - } - } - if buttonWidth > space { - buttonWidth = space - } - button.SetLabelColor(f.buttonTextColor). - SetLabelColorActivated(f.buttonBackgroundColor). - SetBackgroundColorActivated(f.buttonTextColor). - SetBackgroundColor(f.buttonBackgroundColor) - - buttonIndex := index + len(f.items) - positions[buttonIndex].x = x - positions[buttonIndex].y = y - positions[buttonIndex].width = buttonWidth - positions[buttonIndex].height = 1 - - if button.HasFocus() { - focusedPosition = positions[buttonIndex] - } - - x += buttonWidth + 1 - } - - // Determine vertical offset based on the position of the focused item. - var offset int - if focusedPosition.y+focusedPosition.height > bottomLimit { - offset = focusedPosition.y + focusedPosition.height - bottomLimit - if focusedPosition.y-offset < topLimit { - offset = focusedPosition.y - topLimit - } - } - - // Draw items. - for index, item := range f.items { - // Set position. - y := positions[index].y - offset - height := positions[index].height - item.SetRect(positions[index].x, y, positions[index].width, height) - - // Is this item visible? - if y+height <= topLimit || y >= bottomLimit { - continue - } - - // Draw items with focus last (in case of overlaps). - if item.GetFocusable().HasFocus() { - defer item.Draw(screen) - } else { - item.Draw(screen) - } - } - - // Draw buttons. - for index, button := range f.buttons { - // Set position. - buttonIndex := index + len(f.items) - y := positions[buttonIndex].y - offset - height := positions[buttonIndex].height - button.SetRect(positions[buttonIndex].x, y, positions[buttonIndex].width, height) - - // Is this button visible? - if y+height <= topLimit || y >= bottomLimit { - continue - } - - // Draw button. - button.Draw(screen) - } -} - -// Focus is called by the application when the primitive receives focus. -func (f *Form) Focus(delegate func(p Primitive)) { - if len(f.items)+len(f.buttons) == 0 { - f.hasFocus = true - return - } - f.hasFocus = false - - // Hand on the focus to one of our child elements. - if f.focusedElement < 0 || f.focusedElement >= len(f.items)+len(f.buttons) { - f.focusedElement = 0 - } - handler := func(key tcell.Key) { - switch key { - case tcell.KeyTab, tcell.KeyEnter: - f.focusedElement++ - f.Focus(delegate) - case tcell.KeyBacktab: - f.focusedElement-- - if f.focusedElement < 0 { - f.focusedElement = len(f.items) + len(f.buttons) - 1 - } - f.Focus(delegate) - case tcell.KeyEscape: - if f.cancel != nil { - f.cancel() - } else { - f.focusedElement = 0 - f.Focus(delegate) - } - } - } - - if f.focusedElement < len(f.items) { - // We're selecting an item. - item := f.items[f.focusedElement] - item.SetFinishedFunc(handler) - delegate(item) - } else { - // We're selecting a button. - button := f.buttons[f.focusedElement-len(f.items)] - button.SetBlurFunc(handler) - delegate(button) - } -} - -// HasFocus returns whether or not this primitive has focus. -func (f *Form) HasFocus() bool { - if f.hasFocus { - return true - } - return f.focusIndex() >= 0 -} - -// focusIndex returns the index of the currently focused item, counting form -// items first, then buttons. A negative value indicates that no containeed item -// has focus. -func (f *Form) focusIndex() int { - for index, item := range f.items { - if item.GetFocusable().HasFocus() { - return index - } - } - for index, button := range f.buttons { - if button.focus.HasFocus() { - return len(f.items) + index - } - } - return -1 -} - -// MouseHandler returns the mouse handler for this primitive. -func (f *Form) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !f.InRect(event.Position()) { - return false, nil - } - - // At the end, update f.focusedElement and prepare current item/button. - defer func() { - if consumed { - index := f.focusIndex() - if index >= 0 { - f.focusedElement = index - } - f.Focus(setFocus) - } - }() - - // Determine items to pass mouse events to. - for _, item := range f.items { - consumed, capture = item.MouseHandler()(action, event, setFocus) - if consumed { - return - } - } - for _, button := range f.buttons { - consumed, capture = button.MouseHandler()(action, event, setFocus) - if consumed { - return - } - } - - // A mouse click anywhere else will return the focus to the last selected - // element. - if action == MouseLeftClick { - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/frame.go b/vendor/github.com/rivo/tview/frame.go deleted file mode 100644 index 1ea9d6ee..00000000 --- a/vendor/github.com/rivo/tview/frame.go +++ /dev/null @@ -1,169 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// frameText holds information about a line of text shown in the frame. -type frameText struct { - Text string // The text to be displayed. - Header bool // true = place in header, false = place in footer. - Align int // One of the Align constants. - Color tcell.Color // The text color. -} - -// Frame is a wrapper which adds space around another primitive. In addition, -// the top area (header) and the bottom area (footer) may also contain text. -// -// See https://github.com/rivo/tview/wiki/Frame for an example. -type Frame struct { - *Box - - // The contained primitive. - primitive Primitive - - // The lines of text to be displayed. - text []*frameText - - // Border spacing. - top, bottom, header, footer, left, right int -} - -// NewFrame returns a new frame around the given primitive. The primitive's -// size will be changed to fit within this frame. -func NewFrame(primitive Primitive) *Frame { - box := NewBox() - - f := &Frame{ - Box: box, - primitive: primitive, - top: 1, - bottom: 1, - header: 1, - footer: 1, - left: 1, - right: 1, - } - - f.focus = f - - return f -} - -// AddText adds text to the frame. Set "header" to true if the text is to appear -// in the header, above the contained primitive. Set it to false for it to -// appear in the footer, below the contained primitive. "align" must be one of -// the Align constants. Rows in the header are printed top to bottom, rows in -// the footer are printed bottom to top. Note that long text can overlap as -// different alignments will be placed on the same row. -func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame { - f.text = append(f.text, &frameText{ - Text: text, - Header: header, - Align: align, - Color: color, - }) - return f -} - -// Clear removes all text from the frame. -func (f *Frame) Clear() *Frame { - f.text = nil - return f -} - -// SetBorders sets the width of the frame borders as well as "header" and -// "footer", the vertical space between the header and footer text and the -// contained primitive (does not apply if there is no text). -func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame { - f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right - return f -} - -// Draw draws this primitive onto the screen. -func (f *Frame) Draw(screen tcell.Screen) { - f.Box.Draw(screen) - - // Calculate start positions. - x, top, width, height := f.GetInnerRect() - bottom := top + height - 1 - x += f.left - top += f.top - bottom -= f.bottom - width -= f.left + f.right - if width <= 0 || top >= bottom { - return // No space left. - } - - // Draw text. - var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. - topMax := top - bottomMin := bottom - for _, text := range f.text { - // Where do we place this text? - var y int - if text.Header { - y = top + rows[text.Align] - rows[text.Align]++ - if y >= bottomMin { - continue - } - if y+1 > topMax { - topMax = y + 1 - } - } else { - y = bottom - rows[3+text.Align] - rows[3+text.Align]++ - if y <= topMax { - continue - } - if y-1 < bottomMin { - bottomMin = y - 1 - } - } - - // Draw text. - Print(screen, text.Text, x, y, width, text.Align, text.Color) - } - - // Set the size of the contained primitive. - if topMax > top { - top = topMax + f.header - } - if bottomMin < bottom { - bottom = bottomMin - f.footer - } - if top > bottom { - return // No space for the primitive. - } - f.primitive.SetRect(x, top, width, bottom+1-top) - - // Finally, draw the contained primitive. - f.primitive.Draw(screen) -} - -// Focus is called when this primitive receives focus. -func (f *Frame) Focus(delegate func(p Primitive)) { - delegate(f.primitive) -} - -// HasFocus returns whether or not this primitive has focus. -func (f *Frame) HasFocus() bool { - focusable, ok := f.primitive.(Focusable) - if ok { - return focusable.HasFocus() - } - return false -} - -// MouseHandler returns the mouse handler for this primitive. -func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !f.InRect(event.Position()) { - return false, nil - } - - // Pass mouse events on to contained primitive. - return f.primitive.MouseHandler()(action, event, setFocus) - }) -} diff --git a/vendor/github.com/rivo/tview/grid.go b/vendor/github.com/rivo/tview/grid.go deleted file mode 100644 index 5b247bfd..00000000 --- a/vendor/github.com/rivo/tview/grid.go +++ /dev/null @@ -1,684 +0,0 @@ -package tview - -import ( - "math" - - "github.com/gdamore/tcell" -) - -// gridItem represents one primitive and its possible position on a grid. -type gridItem struct { - Item Primitive // The item to be positioned. May be nil for an empty item. - Row, Column int // The top-left grid cell where the item is placed. - Width, Height int // The number of rows and columns the item occupies. - MinGridWidth, MinGridHeight int // The minimum grid width/height for which this item is visible. - Focus bool // Whether or not this item attracts the layout's focus. - - visible bool // Whether or not this item was visible the last time the grid was drawn. - x, y, w, h int // The last position of the item relative to the top-left corner of the grid. Undefined if visible is false. -} - -// Grid is an implementation of a grid-based layout. It works by defining the -// size of the rows and columns, then placing primitives into the grid. -// -// Some settings can lead to the grid exceeding its available space. SetOffset() -// can then be used to scroll in steps of rows and columns. These offset values -// can also be controlled with the arrow keys (or the "g","G", "j", "k", "h", -// and "l" keys) while the grid has focus and none of its contained primitives -// do. -// -// See https://github.com/rivo/tview/wiki/Grid for an example. -type Grid struct { - *Box - - // The items to be positioned. - items []*gridItem - - // The definition of the rows and columns of the grid. See - // SetRows()/SetColumns() for details. - rows, columns []int - - // The minimum sizes for rows and columns. - minWidth, minHeight int - - // The size of the gaps between neighboring primitives. This is automatically - // set to 1 if borders is true. - gapRows, gapColumns int - - // The number of rows and columns skipped before drawing the top-left corner - // of the grid. - rowOffset, columnOffset int - - // Whether or not borders are drawn around grid items. If this is set to true, - // a gap size of 1 is automatically assumed (which is filled with the border - // graphics). - borders bool - - // The color of the borders around grid items. - bordersColor tcell.Color -} - -// NewGrid returns a new grid-based layout container with no initial primitives. -// -// Note that Box, the superclass of Grid, will have its background color set to -// transparent so that any grid areas not covered by any primitives will leave -// their background unchanged. To clear a Grid's background before any items are -// drawn, set it to the desired color: -// -// grid.SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor) -func NewGrid() *Grid { - g := &Grid{ - Box: NewBox().SetBackgroundColor(tcell.ColorDefault), - bordersColor: Styles.GraphicsColor, - } - g.focus = g - return g -} - -// SetColumns defines how the columns of the grid are distributed. Each value -// defines the size of one column, starting with the leftmost column. Values -// greater 0 represent absolute column widths (gaps not included). Values less -// or equal 0 represent proportional column widths or fractions of the remaining -// free space, where 0 is treated the same as -1. That is, a column with a value -// of -3 will have three times the width of a column with a value of -1 (or 0). -// The minimum width set with SetMinSize() is always observed. -// -// Primitives may extend beyond the columns defined explicitly with this -// function. A value of 0 is assumed for any undefined column. In fact, if you -// never call this function, all columns occupied by primitives will have the -// same width. On the other hand, unoccupied columns defined with this function -// will always take their place. -// -// Assuming a total width of the grid of 100 cells and a minimum width of 0, the -// following call will result in columns with widths of 30, 10, 15, 15, and 30 -// cells: -// -// grid.Setcolumns(30, 10, -1, -1, -2) -// -// If a primitive were then placed in the 6th and 7th column, the resulting -// widths would be: 30, 10, 10, 10, 20, 10, and 10 cells. -// -// If you then called SetMinSize() as follows: -// -// grid.SetMinSize(15, 20) -// -// The resulting widths would be: 30, 15, 15, 15, 20, 15, and 15 cells, a total -// of 125 cells, 25 cells wider than the available grid width. -func (g *Grid) SetColumns(columns ...int) *Grid { - g.columns = columns - return g -} - -// SetRows defines how the rows of the grid are distributed. These values behave -// the same as the column values provided with SetColumns(), see there for a -// definition and examples. -// -// The provided values correspond to row heights, the first value defining -// the height of the topmost row. -func (g *Grid) SetRows(rows ...int) *Grid { - g.rows = rows - return g -} - -// SetSize is a shortcut for SetRows() and SetColumns() where all row and column -// values are set to the given size values. See SetColumns() for details on sizes. -func (g *Grid) SetSize(numRows, numColumns, rowSize, columnSize int) *Grid { - g.rows = make([]int, numRows) - for index := range g.rows { - g.rows[index] = rowSize - } - g.columns = make([]int, numColumns) - for index := range g.columns { - g.columns[index] = columnSize - } - return g -} - -// SetMinSize sets an absolute minimum width for rows and an absolute minimum -// height for columns. Panics if negative values are provided. -func (g *Grid) SetMinSize(row, column int) *Grid { - if row < 0 || column < 0 { - panic("Invalid minimum row/column size") - } - g.minHeight, g.minWidth = row, column - return g -} - -// SetGap sets the size of the gaps between neighboring primitives on the grid. -// If borders are drawn (see SetBorders()), these values are ignored and a gap -// of 1 is assumed. Panics if negative values are provided. -func (g *Grid) SetGap(row, column int) *Grid { - if row < 0 || column < 0 { - panic("Invalid gap size") - } - g.gapRows, g.gapColumns = row, column - return g -} - -// SetBorders sets whether or not borders are drawn around grid items. Setting -// this value to true will cause the gap values (see SetGap()) to be ignored and -// automatically assumed to be 1 where the border graphics are drawn. -func (g *Grid) SetBorders(borders bool) *Grid { - g.borders = borders - return g -} - -// SetBordersColor sets the color of the item borders. -func (g *Grid) SetBordersColor(color tcell.Color) *Grid { - g.bordersColor = color - return g -} - -// AddItem adds a primitive and its position to the grid. The top-left corner -// of the primitive will be located in the top-left corner of the grid cell at -// the given row and column and will span "rowSpan" rows and "colSpan" columns. -// For example, for a primitive to occupy rows 2, 3, and 4 and columns 5 and 6: -// -// grid.AddItem(p, 2, 5, 3, 2, 0, 0, true) -// -// If rowSpan or colSpan is 0, the primitive will not be drawn. -// -// You can add the same primitive multiple times with different grid positions. -// The minGridWidth and minGridHeight values will then determine which of those -// positions will be used. This is similar to CSS media queries. These minimum -// values refer to the overall size of the grid. If multiple items for the same -// primitive apply, the one that has at least one highest minimum value will be -// used, or the primitive added last if those values are the same. Example: -// -// grid.AddItem(p, 0, 0, 0, 0, 0, 0, true). // Hide in small grids. -// AddItem(p, 0, 0, 1, 2, 100, 0, true). // One-column layout for medium grids. -// AddItem(p, 1, 1, 3, 2, 300, 0, true) // Multi-column layout for large grids. -// -// To use the same grid layout for all sizes, simply set minGridWidth and -// minGridHeight to 0. -// -// If the item's focus is set to true, it will receive focus when the grid -// receives focus. If there are multiple items with a true focus flag, the last -// visible one that was added will receive focus. -func (g *Grid) AddItem(p Primitive, row, column, rowSpan, colSpan, minGridHeight, minGridWidth int, focus bool) *Grid { - g.items = append(g.items, &gridItem{ - Item: p, - Row: row, - Column: column, - Height: rowSpan, - Width: colSpan, - MinGridHeight: minGridHeight, - MinGridWidth: minGridWidth, - Focus: focus, - }) - return g -} - -// RemoveItem removes all items for the given primitive from the grid, keeping -// the order of the remaining items intact. -func (g *Grid) RemoveItem(p Primitive) *Grid { - for index := len(g.items) - 1; index >= 0; index-- { - if g.items[index].Item == p { - g.items = append(g.items[:index], g.items[index+1:]...) - } - } - return g -} - -// Clear removes all items from the grid. -func (g *Grid) Clear() *Grid { - g.items = nil - return g -} - -// SetOffset sets the number of rows and columns which are skipped before -// drawing the first grid cell in the top-left corner. As the grid will never -// completely move off the screen, these values may be adjusted the next time -// the grid is drawn. The actual position of the grid may also be adjusted such -// that contained primitives that have focus remain visible. -func (g *Grid) SetOffset(rows, columns int) *Grid { - g.rowOffset, g.columnOffset = rows, columns - return g -} - -// GetOffset returns the current row and column offset (see SetOffset() for -// details). -func (g *Grid) GetOffset() (rows, columns int) { - return g.rowOffset, g.columnOffset -} - -// Focus is called when this primitive receives focus. -func (g *Grid) Focus(delegate func(p Primitive)) { - for _, item := range g.items { - if item.Focus { - delegate(item.Item) - return - } - } - g.hasFocus = true -} - -// Blur is called when this primitive loses focus. -func (g *Grid) Blur() { - g.hasFocus = false -} - -// HasFocus returns whether or not this primitive has focus. -func (g *Grid) HasFocus() bool { - for _, item := range g.items { - if item.visible && item.Item.GetFocusable().HasFocus() { - return true - } - } - return g.hasFocus -} - -// InputHandler returns the handler for this primitive. -func (g *Grid) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return g.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - switch event.Key() { - case tcell.KeyRune: - switch event.Rune() { - case 'g': - g.rowOffset, g.columnOffset = 0, 0 - case 'G': - g.rowOffset = math.MaxInt32 - case 'j': - g.rowOffset++ - case 'k': - g.rowOffset-- - case 'h': - g.columnOffset-- - case 'l': - g.columnOffset++ - } - case tcell.KeyHome: - g.rowOffset, g.columnOffset = 0, 0 - case tcell.KeyEnd: - g.rowOffset = math.MaxInt32 - case tcell.KeyUp: - g.rowOffset-- - case tcell.KeyDown: - g.rowOffset++ - case tcell.KeyLeft: - g.columnOffset-- - case tcell.KeyRight: - g.columnOffset++ - } - }) -} - -// Draw draws this primitive onto the screen. -func (g *Grid) Draw(screen tcell.Screen) { - g.Box.Draw(screen) - x, y, width, height := g.GetInnerRect() - screenWidth, screenHeight := screen.Size() - - // Make a list of items which apply. - items := make(map[Primitive]*gridItem) - for _, item := range g.items { - item.visible = false - if item.Width <= 0 || item.Height <= 0 || width < item.MinGridWidth || height < item.MinGridHeight { - continue - } - previousItem, ok := items[item.Item] - if ok && item.MinGridWidth < previousItem.MinGridWidth && item.MinGridHeight < previousItem.MinGridHeight { - continue - } - items[item.Item] = item - } - - // How many rows and columns do we have? - rows := len(g.rows) - columns := len(g.columns) - for _, item := range items { - rowEnd := item.Row + item.Height - if rowEnd > rows { - rows = rowEnd - } - columnEnd := item.Column + item.Width - if columnEnd > columns { - columns = columnEnd - } - } - if rows == 0 || columns == 0 { - return // No content. - } - - // Where are they located? - rowPos := make([]int, rows) - rowHeight := make([]int, rows) - columnPos := make([]int, columns) - columnWidth := make([]int, columns) - - // How much space do we distribute? - remainingWidth := width - remainingHeight := height - proportionalWidth := 0 - proportionalHeight := 0 - for index, row := range g.rows { - if row > 0 { - if row < g.minHeight { - row = g.minHeight - } - remainingHeight -= row - rowHeight[index] = row - } else if row == 0 { - proportionalHeight++ - } else { - proportionalHeight += -row - } - } - for index, column := range g.columns { - if column > 0 { - if column < g.minWidth { - column = g.minWidth - } - remainingWidth -= column - columnWidth[index] = column - } else if column == 0 { - proportionalWidth++ - } else { - proportionalWidth += -column - } - } - if g.borders { - remainingHeight -= rows + 1 - remainingWidth -= columns + 1 - } else { - remainingHeight -= (rows - 1) * g.gapRows - remainingWidth -= (columns - 1) * g.gapColumns - } - if rows > len(g.rows) { - proportionalHeight += rows - len(g.rows) - } - if columns > len(g.columns) { - proportionalWidth += columns - len(g.columns) - } - - // Distribute proportional rows/columns. - for index := 0; index < rows; index++ { - row := 0 - if index < len(g.rows) { - row = g.rows[index] - } - if row > 0 { - if row < g.minHeight { - row = g.minHeight - } - continue // Not proportional. We already know the width. - } else if row == 0 { - row = 1 - } else { - row = -row - } - rowAbs := row * remainingHeight / proportionalHeight - remainingHeight -= rowAbs - proportionalHeight -= row - if rowAbs < g.minHeight { - rowAbs = g.minHeight - } - rowHeight[index] = rowAbs - } - for index := 0; index < columns; index++ { - column := 0 - if index < len(g.columns) { - column = g.columns[index] - } - if column > 0 { - if column < g.minWidth { - column = g.minWidth - } - continue // Not proportional. We already know the height. - } else if column == 0 { - column = 1 - } else { - column = -column - } - columnAbs := column * remainingWidth / proportionalWidth - remainingWidth -= columnAbs - proportionalWidth -= column - if columnAbs < g.minWidth { - columnAbs = g.minWidth - } - columnWidth[index] = columnAbs - } - - // Calculate row/column positions. - var columnX, rowY int - if g.borders { - columnX++ - rowY++ - } - for index, row := range rowHeight { - rowPos[index] = rowY - gap := g.gapRows - if g.borders { - gap = 1 - } - rowY += row + gap - } - for index, column := range columnWidth { - columnPos[index] = columnX - gap := g.gapColumns - if g.borders { - gap = 1 - } - columnX += column + gap - } - - // Calculate primitive positions. - var focus *gridItem // The item which has focus. - for primitive, item := range items { - px := columnPos[item.Column] - py := rowPos[item.Row] - var pw, ph int - for index := 0; index < item.Height; index++ { - ph += rowHeight[item.Row+index] - } - for index := 0; index < item.Width; index++ { - pw += columnWidth[item.Column+index] - } - if g.borders { - pw += item.Width - 1 - ph += item.Height - 1 - } else { - pw += (item.Width - 1) * g.gapColumns - ph += (item.Height - 1) * g.gapRows - } - item.x, item.y, item.w, item.h = px, py, pw, ph - item.visible = true - if primitive.GetFocusable().HasFocus() { - focus = item - } - } - - // Calculate screen offsets. - var offsetX, offsetY int - add := 1 - if !g.borders { - add = g.gapRows - } - for index, height := range rowHeight { - if index >= g.rowOffset { - break - } - offsetY += height + add - } - if !g.borders { - add = g.gapColumns - } - for index, width := range columnWidth { - if index >= g.columnOffset { - break - } - offsetX += width + add - } - - // Line up the last row/column with the end of the available area. - var border int - if g.borders { - border = 1 - } - last := len(rowPos) - 1 - if rowPos[last]+rowHeight[last]+border-offsetY < height { - offsetY = rowPos[last] - height + rowHeight[last] + border - } - last = len(columnPos) - 1 - if columnPos[last]+columnWidth[last]+border-offsetX < width { - offsetX = columnPos[last] - width + columnWidth[last] + border - } - - // The focused item must be within the visible area. - if focus != nil { - if focus.y+focus.h-offsetY >= height { - offsetY = focus.y - height + focus.h - } - if focus.y-offsetY < 0 { - offsetY = focus.y - } - if focus.x+focus.w-offsetX >= width { - offsetX = focus.x - width + focus.w - } - if focus.x-offsetX < 0 { - offsetX = focus.x - } - } - - // Adjust row/column offsets based on this value. - var from, to int - for index, pos := range rowPos { - if pos-offsetY < 0 { - from = index + 1 - } - if pos-offsetY < height { - to = index - } - } - if g.rowOffset < from { - g.rowOffset = from - } - if g.rowOffset > to { - g.rowOffset = to - } - from, to = 0, 0 - for index, pos := range columnPos { - if pos-offsetX < 0 { - from = index + 1 - } - if pos-offsetX < width { - to = index - } - } - if g.columnOffset < from { - g.columnOffset = from - } - if g.columnOffset > to { - g.columnOffset = to - } - - // Draw primitives and borders. - for primitive, item := range items { - // Final primitive position. - if !item.visible { - continue - } - item.x -= offsetX - item.y -= offsetY - if item.x >= width || item.x+item.w <= 0 || item.y >= height || item.y+item.h <= 0 { - item.visible = false - continue - } - if item.x+item.w > width { - item.w = width - item.x - } - if item.y+item.h > height { - item.h = height - item.y - } - if item.x < 0 { - item.w += item.x - item.x = 0 - } - if item.y < 0 { - item.h += item.y - item.y = 0 - } - if item.w <= 0 || item.h <= 0 { - item.visible = false - continue - } - item.x += x - item.y += y - primitive.SetRect(item.x, item.y, item.w, item.h) - - // Draw primitive. - if item == focus { - defer primitive.Draw(screen) - } else { - primitive.Draw(screen) - } - - // Draw border around primitive. - if g.borders { - for bx := item.x; bx < item.x+item.w; bx++ { // Top/bottom lines. - if bx < 0 || bx >= screenWidth { - continue - } - by := item.y - 1 - if by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, g.bordersColor) - } - by = item.y + item.h - if by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.Horizontal, g.bordersColor) - } - } - for by := item.y; by < item.y+item.h; by++ { // Left/right lines. - if by < 0 || by >= screenHeight { - continue - } - bx := item.x - 1 - if bx >= 0 && bx < screenWidth { - PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, g.bordersColor) - } - bx = item.x + item.w - if bx >= 0 && bx < screenWidth { - PrintJoinedSemigraphics(screen, bx, by, Borders.Vertical, g.bordersColor) - } - } - bx, by := item.x-1, item.y-1 // Top-left corner. - if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.TopLeft, g.bordersColor) - } - bx, by = item.x+item.w, item.y-1 // Top-right corner. - if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.TopRight, g.bordersColor) - } - bx, by = item.x-1, item.y+item.h // Bottom-left corner. - if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.BottomLeft, g.bordersColor) - } - bx, by = item.x+item.w, item.y+item.h // Bottom-right corner. - if bx >= 0 && bx < screenWidth && by >= 0 && by < screenHeight { - PrintJoinedSemigraphics(screen, bx, by, Borders.BottomRight, g.bordersColor) - } - } - } -} - -// MouseHandler returns the mouse handler for this primitive. -func (g *Grid) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return g.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !g.InRect(event.Position()) { - return false, nil - } - - // Pass mouse events along to the first child item that takes it. - for _, item := range g.items { - if item.Item == nil { - continue - } - consumed, capture = item.Item.MouseHandler()(action, event, setFocus) - if consumed { - return - } - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/inputfield.go b/vendor/github.com/rivo/tview/inputfield.go deleted file mode 100644 index d497bf9c..00000000 --- a/vendor/github.com/rivo/tview/inputfield.go +++ /dev/null @@ -1,625 +0,0 @@ -package tview - -import ( - "math" - "regexp" - "strings" - "sync" - "unicode/utf8" - - "github.com/gdamore/tcell" -) - -// InputField is a one-line box (three lines if there is a title) where the -// user can enter text. Use SetAcceptanceFunc() to accept or reject input, -// SetChangedFunc() to listen for changes, and SetMaskCharacter() to hide input -// from onlookers (e.g. for password input). -// -// The following keys can be used for navigation and editing: -// -// - Left arrow: Move left by one character. -// - Right arrow: Move right by one character. -// - Home, Ctrl-A, Alt-a: Move to the beginning of the line. -// - End, Ctrl-E, Alt-e: Move to the end of the line. -// - Alt-left, Alt-b: Move left by one word. -// - Alt-right, Alt-f: Move right by one word. -// - Backspace: Delete the character before the cursor. -// - Delete: Delete the character after the cursor. -// - Ctrl-K: Delete from the cursor to the end of the line. -// - Ctrl-W: Delete the last word before the cursor. -// - Ctrl-U: Delete the entire line. -// -// See https://github.com/rivo/tview/wiki/InputField for an example. -type InputField struct { - *Box - - // The text that was entered. - text string - - // The text to be displayed before the input area. - label string - - // The text to be displayed in the input area when "text" is empty. - placeholder string - - // The label color. - labelColor tcell.Color - - // The background color of the input area. - fieldBackgroundColor tcell.Color - - // The text color of the input area. - fieldTextColor tcell.Color - - // The text color of the placeholder. - placeholderTextColor tcell.Color - - // The screen width of the label area. A value of 0 means use the width of - // the label text. - labelWidth int - - // The screen width of the input area. A value of 0 means extend as much as - // possible. - fieldWidth int - - // A character to mask entered text (useful for password fields). A value of 0 - // disables masking. - maskCharacter rune - - // The cursor position as a byte index into the text string. - cursorPos int - - // An optional autocomplete function which receives the current text of the - // input field and returns a slice of strings to be displayed in a drop-down - // selection. - autocomplete func(text string) []string - - // The List object which shows the selectable autocomplete entries. If not - // nil, the list's main texts represent the current autocomplete entries. - autocompleteList *List - autocompleteListMutex sync.Mutex - - // An optional function which may reject the last character that was entered. - accept func(text string, ch rune) bool - - // An optional function which is called when the input has changed. - changed func(text string) - - // An optional function which is called when the user indicated that they - // are done entering text. The key which was pressed is provided (tab, - // shift-tab, enter, or escape). - done func(tcell.Key) - - // A callback function set by the Form class and called when the user leaves - // this form item. - finished func(tcell.Key) - - fieldX int // The x-coordinate of the input field as determined during the last call to Draw(). - offset int // The number of bytes of the text string skipped ahead while drawing. -} - -// NewInputField returns a new input field. -func NewInputField() *InputField { - return &InputField{ - Box: NewBox(), - labelColor: Styles.SecondaryTextColor, - fieldBackgroundColor: Styles.ContrastBackgroundColor, - fieldTextColor: Styles.PrimaryTextColor, - placeholderTextColor: Styles.ContrastSecondaryTextColor, - } -} - -// SetText sets the current text of the input field. -func (i *InputField) SetText(text string) *InputField { - i.text = text - i.cursorPos = len(text) - if i.changed != nil { - i.changed(text) - } - return i -} - -// GetText returns the current text of the input field. -func (i *InputField) GetText() string { - return i.text -} - -// SetLabel sets the text to be displayed before the input area. -func (i *InputField) SetLabel(label string) *InputField { - i.label = label - return i -} - -// GetLabel returns the text to be displayed before the input area. -func (i *InputField) GetLabel() string { - return i.label -} - -// SetLabelWidth sets the screen width of the label. A value of 0 will cause the -// primitive to use the width of the label string. -func (i *InputField) SetLabelWidth(width int) *InputField { - i.labelWidth = width - return i -} - -// SetPlaceholder sets the text to be displayed when the input text is empty. -func (i *InputField) SetPlaceholder(text string) *InputField { - i.placeholder = text - return i -} - -// SetLabelColor sets the color of the label. -func (i *InputField) SetLabelColor(color tcell.Color) *InputField { - i.labelColor = color - return i -} - -// SetFieldBackgroundColor sets the background color of the input area. -func (i *InputField) SetFieldBackgroundColor(color tcell.Color) *InputField { - i.fieldBackgroundColor = color - return i -} - -// SetFieldTextColor sets the text color of the input area. -func (i *InputField) SetFieldTextColor(color tcell.Color) *InputField { - i.fieldTextColor = color - return i -} - -// SetPlaceholderTextColor sets the text color of placeholder text. -func (i *InputField) SetPlaceholderTextColor(color tcell.Color) *InputField { - i.placeholderTextColor = color - return i -} - -// SetFormAttributes sets attributes shared by all form items. -func (i *InputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem { - i.labelWidth = labelWidth - i.labelColor = labelColor - i.backgroundColor = bgColor - i.fieldTextColor = fieldTextColor - i.fieldBackgroundColor = fieldBgColor - return i -} - -// SetFieldWidth sets the screen width of the input area. A value of 0 means -// extend as much as possible. -func (i *InputField) SetFieldWidth(width int) *InputField { - i.fieldWidth = width - return i -} - -// GetFieldWidth returns this primitive's field width. -func (i *InputField) GetFieldWidth() int { - return i.fieldWidth -} - -// SetMaskCharacter sets a character that masks user input on a screen. A value -// of 0 disables masking. -func (i *InputField) SetMaskCharacter(mask rune) *InputField { - i.maskCharacter = mask - return i -} - -// SetAutocompleteFunc sets an autocomplete callback function which may return -// strings to be selected from a drop-down based on the current text of the -// input field. The drop-down appears only if len(entries) > 0. The callback is -// invoked in this function and whenever the current text changes or when -// Autocomplete() is called. Entries are cleared when the user selects an entry -// or presses Escape. -func (i *InputField) SetAutocompleteFunc(callback func(currentText string) (entries []string)) *InputField { - i.autocomplete = callback - i.Autocomplete() - return i -} - -// Autocomplete invokes the autocomplete callback (if there is one). If the -// length of the returned autocomplete entries slice is greater than 0, the -// input field will present the user with a corresponding drop-down list the -// next time the input field is drawn. -// -// It is safe to call this function from any goroutine. Note that the input -// field is not redrawn automatically unless called from the main goroutine -// (e.g. in response to events). -func (i *InputField) Autocomplete() *InputField { - i.autocompleteListMutex.Lock() - defer i.autocompleteListMutex.Unlock() - if i.autocomplete == nil { - return i - } - - // Do we have any autocomplete entries? - entries := i.autocomplete(i.text) - if len(entries) == 0 { - // No entries, no list. - i.autocompleteList = nil - return i - } - - // Make a list if we have none. - if i.autocompleteList == nil { - i.autocompleteList = NewList() - i.autocompleteList.ShowSecondaryText(false). - SetMainTextColor(Styles.PrimitiveBackgroundColor). - SetSelectedTextColor(Styles.PrimitiveBackgroundColor). - SetSelectedBackgroundColor(Styles.PrimaryTextColor). - SetHighlightFullLine(true). - SetBackgroundColor(Styles.MoreContrastBackgroundColor) - } - - // Fill it with the entries. - currentEntry := -1 - i.autocompleteList.Clear() - for index, entry := range entries { - i.autocompleteList.AddItem(entry, "", 0, nil) - if currentEntry < 0 && entry == i.text { - currentEntry = index - } - } - - // Set the selection if we have one. - if currentEntry >= 0 { - i.autocompleteList.SetCurrentItem(currentEntry) - } - - return i -} - -// SetAcceptanceFunc sets a handler which may reject the last character that was -// entered (by returning false). -// -// This package defines a number of variables prefixed with InputField which may -// be used for common input (e.g. numbers, maximum text length). -func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *InputField { - i.accept = handler - return i -} - -// SetChangedFunc sets a handler which is called whenever the text of the input -// field has changed. It receives the current text (after the change). -func (i *InputField) SetChangedFunc(handler func(text string)) *InputField { - i.changed = handler - return i -} - -// SetDoneFunc sets a handler which is called when the user is done entering -// text. The callback function is provided with the key that was pressed, which -// is one of the following: -// -// - KeyEnter: Done entering text. -// - KeyEscape: Abort text input. -// - KeyTab: Move to the next field. -// - KeyBacktab: Move to the previous field. -func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField { - i.done = handler - return i -} - -// SetFinishedFunc sets a callback invoked when the user leaves this form item. -func (i *InputField) SetFinishedFunc(handler func(key tcell.Key)) FormItem { - i.finished = handler - return i -} - -// Draw draws this primitive onto the screen. -func (i *InputField) Draw(screen tcell.Screen) { - i.Box.Draw(screen) - - // Prepare - x, y, width, height := i.GetInnerRect() - rightLimit := x + width - if height < 1 || rightLimit <= x { - return - } - - // Draw label. - if i.labelWidth > 0 { - labelWidth := i.labelWidth - if labelWidth > rightLimit-x { - labelWidth = rightLimit - x - } - Print(screen, i.label, x, y, labelWidth, AlignLeft, i.labelColor) - x += labelWidth - } else { - _, drawnWidth := Print(screen, i.label, x, y, rightLimit-x, AlignLeft, i.labelColor) - x += drawnWidth - } - - // Draw input area. - i.fieldX = x - fieldWidth := i.fieldWidth - if fieldWidth == 0 { - fieldWidth = math.MaxInt32 - } - if rightLimit-x < fieldWidth { - fieldWidth = rightLimit - x - } - fieldStyle := tcell.StyleDefault.Background(i.fieldBackgroundColor) - for index := 0; index < fieldWidth; index++ { - screen.SetContent(x+index, y, ' ', nil, fieldStyle) - } - - // Text. - var cursorScreenPos int - text := i.text - if text == "" && i.placeholder != "" { - // Draw placeholder text. - Print(screen, Escape(i.placeholder), x, y, fieldWidth, AlignLeft, i.placeholderTextColor) - i.offset = 0 - } else { - // Draw entered text. - if i.maskCharacter > 0 { - text = strings.Repeat(string(i.maskCharacter), utf8.RuneCountInString(i.text)) - } - if fieldWidth >= stringWidth(text) { - // We have enough space for the full text. - Print(screen, Escape(text), x, y, fieldWidth, AlignLeft, i.fieldTextColor) - i.offset = 0 - iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - if textPos >= i.cursorPos { - return true - } - cursorScreenPos += screenWidth - return false - }) - } else { - // The text doesn't fit. Where is the cursor? - if i.cursorPos < 0 { - i.cursorPos = 0 - } else if i.cursorPos > len(text) { - i.cursorPos = len(text) - } - // Shift the text so the cursor is inside the field. - var shiftLeft int - if i.offset > i.cursorPos { - i.offset = i.cursorPos - } else if subWidth := stringWidth(text[i.offset:i.cursorPos]); subWidth > fieldWidth-1 { - shiftLeft = subWidth - fieldWidth + 1 - } - currentOffset := i.offset - iterateString(text, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - if textPos >= currentOffset { - if shiftLeft > 0 { - i.offset = textPos + textWidth - shiftLeft -= screenWidth - } else { - if textPos+textWidth > i.cursorPos { - return true - } - cursorScreenPos += screenWidth - } - } - return false - }) - Print(screen, Escape(text[i.offset:]), x, y, fieldWidth, AlignLeft, i.fieldTextColor) - } - } - - // Draw autocomplete list. - i.autocompleteListMutex.Lock() - defer i.autocompleteListMutex.Unlock() - if i.autocompleteList != nil { - // How much space do we need? - lheight := i.autocompleteList.GetItemCount() - lwidth := 0 - for index := 0; index < lheight; index++ { - entry, _ := i.autocompleteList.GetItemText(index) - width := TaggedStringWidth(entry) - if width > lwidth { - lwidth = width - } - } - - // We prefer to drop down but if there is no space, maybe drop up? - lx := x - ly := y + 1 - _, sheight := screen.Size() - if ly+lheight >= sheight && ly-2 > lheight-ly { - ly = y - lheight - if ly < 0 { - ly = 0 - } - } - if ly+lheight >= sheight { - lheight = sheight - ly - } - i.autocompleteList.SetRect(lx, ly, lwidth, lheight) - i.autocompleteList.Draw(screen) - } - - // Set cursor. - if i.focus.HasFocus() { - screen.ShowCursor(x+cursorScreenPos, y) - } -} - -// InputHandler returns the handler for this primitive. -func (i *InputField) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return i.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - // Trigger changed events. - currentText := i.text - defer func() { - if i.text != currentText { - i.Autocomplete() - if i.changed != nil { - i.changed(i.text) - } - } - }() - - // Movement functions. - home := func() { i.cursorPos = 0 } - end := func() { i.cursorPos = len(i.text) } - moveLeft := func() { - iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - i.cursorPos -= textWidth - return true - }) - } - moveRight := func() { - iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - i.cursorPos += textWidth - return true - }) - } - moveWordLeft := func() { - i.cursorPos = len(regexp.MustCompile(`\S+\s*$`).ReplaceAllString(i.text[:i.cursorPos], "")) - } - moveWordRight := func() { - i.cursorPos = len(i.text) - len(regexp.MustCompile(`^\s*\S+\s*`).ReplaceAllString(i.text[i.cursorPos:], "")) - } - - // Add character function. Returns whether or not the rune character is - // accepted. - add := func(r rune) bool { - newText := i.text[:i.cursorPos] + string(r) + i.text[i.cursorPos:] - if i.accept != nil && !i.accept(newText, r) { - return false - } - i.text = newText - i.cursorPos += len(string(r)) - return true - } - - // Finish up. - finish := func(key tcell.Key) { - if i.done != nil { - i.done(key) - } - if i.finished != nil { - i.finished(key) - } - } - - // Process key event. - i.autocompleteListMutex.Lock() - defer i.autocompleteListMutex.Unlock() - switch key := event.Key(); key { - case tcell.KeyRune: // Regular character. - if event.Modifiers()&tcell.ModAlt > 0 { - // We accept some Alt- key combinations. - switch event.Rune() { - case 'a': // Home. - home() - case 'e': // End. - end() - case 'b': // Move word left. - moveWordLeft() - case 'f': // Move word right. - moveWordRight() - default: - if !add(event.Rune()) { - return - } - } - } else { - // Other keys are simply accepted as regular characters. - if !add(event.Rune()) { - return - } - } - case tcell.KeyCtrlU: // Delete all. - i.text = "" - i.cursorPos = 0 - case tcell.KeyCtrlK: // Delete until the end of the line. - i.text = i.text[:i.cursorPos] - case tcell.KeyCtrlW: // Delete last word. - lastWord := regexp.MustCompile(`\S+\s*$`) - newText := lastWord.ReplaceAllString(i.text[:i.cursorPos], "") + i.text[i.cursorPos:] - i.cursorPos -= len(i.text) - len(newText) - i.text = newText - case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete character before the cursor. - iterateStringReverse(i.text[:i.cursorPos], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - i.text = i.text[:textPos] + i.text[textPos+textWidth:] - i.cursorPos -= textWidth - return true - }) - if i.offset >= i.cursorPos { - i.offset = 0 - } - case tcell.KeyDelete: // Delete character after the cursor. - iterateString(i.text[i.cursorPos:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - i.text = i.text[:i.cursorPos] + i.text[i.cursorPos+textWidth:] - return true - }) - case tcell.KeyLeft: - if event.Modifiers()&tcell.ModAlt > 0 { - moveWordLeft() - } else { - moveLeft() - } - case tcell.KeyRight: - if event.Modifiers()&tcell.ModAlt > 0 { - moveWordRight() - } else { - moveRight() - } - case tcell.KeyHome, tcell.KeyCtrlA: - home() - case tcell.KeyEnd, tcell.KeyCtrlE: - end() - case tcell.KeyEnter, tcell.KeyEscape: // We might be done. - if i.autocompleteList != nil { - i.autocompleteList = nil - } else { - finish(key) - } - case tcell.KeyDown, tcell.KeyTab: // Autocomplete selection. - if i.autocompleteList != nil { - count := i.autocompleteList.GetItemCount() - newEntry := i.autocompleteList.GetCurrentItem() + 1 - if newEntry >= count { - newEntry = 0 - } - i.autocompleteList.SetCurrentItem(newEntry) - currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice. - i.SetText(currentText) - } else { - finish(key) - } - case tcell.KeyUp, tcell.KeyBacktab: // Autocomplete selection. - if i.autocompleteList != nil { - newEntry := i.autocompleteList.GetCurrentItem() - 1 - if newEntry < 0 { - newEntry = i.autocompleteList.GetItemCount() - 1 - } - i.autocompleteList.SetCurrentItem(newEntry) - currentText, _ = i.autocompleteList.GetItemText(newEntry) // Don't trigger changed function twice. - i.SetText(currentText) - } else { - finish(key) - } - } - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (i *InputField) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return i.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - x, y := event.Position() - _, rectY, _, _ := i.GetInnerRect() - if !i.InRect(x, y) { - return false, nil - } - - // Process mouse event. - if action == MouseLeftClick && y == rectY { - // Determine where to place the cursor. - if x >= i.fieldX { - if !iterateString(i.text[i.offset:], func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool { - if x-i.fieldX < screenPos+screenWidth { - i.cursorPos = textPos + i.offset - return true - } - return false - }) { - i.cursorPos = len(i.text) - } - } - setFocus(i) - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/list.go b/vendor/github.com/rivo/tview/list.go deleted file mode 100644 index 692eb117..00000000 --- a/vendor/github.com/rivo/tview/list.go +++ /dev/null @@ -1,628 +0,0 @@ -package tview - -import ( - "fmt" - "strings" - - "github.com/gdamore/tcell" -) - -// listItem represents one item in a List. -type listItem struct { - MainText string // The main text of the list item. - SecondaryText string // A secondary text to be shown underneath the main text. - Shortcut rune // The key to select the list item directly, 0 if there is no shortcut. - Selected func() // The optional function which is called when the item is selected. -} - -// List displays rows of items, each of which can be selected. -// -// See https://github.com/rivo/tview/wiki/List for an example. -type List struct { - *Box - - // The items of the list. - items []*listItem - - // The index of the currently selected item. - currentItem int - - // Whether or not to show the secondary item texts. - showSecondaryText bool - - // The item main text color. - mainTextColor tcell.Color - - // The item secondary text color. - secondaryTextColor tcell.Color - - // The item shortcut text color. - shortcutColor tcell.Color - - // The text color for selected items. - selectedTextColor tcell.Color - - // The background color for selected items. - selectedBackgroundColor tcell.Color - - // If true, the selection is only shown when the list has focus. - selectedFocusOnly bool - - // If true, the entire row is highlighted when selected. - highlightFullLine bool - - // Whether or not navigating the list will wrap around. - wrapAround bool - - // The number of list items skipped at the top before the first item is drawn. - offset int - - // An optional function which is called when the user has navigated to a list - // item. - changed func(index int, mainText, secondaryText string, shortcut rune) - - // An optional function which is called when a list item was selected. This - // function will be called even if the list item defines its own callback. - selected func(index int, mainText, secondaryText string, shortcut rune) - - // An optional function which is called when the user presses the Escape key. - done func() -} - -// NewList returns a new form. -func NewList() *List { - return &List{ - Box: NewBox(), - showSecondaryText: true, - wrapAround: true, - mainTextColor: Styles.PrimaryTextColor, - secondaryTextColor: Styles.TertiaryTextColor, - shortcutColor: Styles.SecondaryTextColor, - selectedTextColor: Styles.PrimitiveBackgroundColor, - selectedBackgroundColor: Styles.PrimaryTextColor, - } -} - -// SetCurrentItem sets the currently selected item by its index, starting at 0 -// for the first item. If a negative index is provided, items are referred to -// from the back (-1 = last item, -2 = second-to-last item, and so on). Out of -// range indices are clamped to the beginning/end. -// -// Calling this function triggers a "changed" event if the selection changes. -func (l *List) SetCurrentItem(index int) *List { - if index < 0 { - index = len(l.items) + index - } - if index >= len(l.items) { - index = len(l.items) - 1 - } - if index < 0 { - index = 0 - } - - if index != l.currentItem && l.changed != nil { - item := l.items[index] - l.changed(index, item.MainText, item.SecondaryText, item.Shortcut) - } - - l.currentItem = index - - return l -} - -// GetCurrentItem returns the index of the currently selected list item, -// starting at 0 for the first item. -func (l *List) GetCurrentItem() int { - return l.currentItem -} - -// RemoveItem removes the item with the given index (starting at 0) from the -// list. If a negative index is provided, items are referred to from the back -// (-1 = last item, -2 = second-to-last item, and so on). Out of range indices -// are clamped to the beginning/end, i.e. unless the list is empty, an item is -// always removed. -// -// The currently selected item is shifted accordingly. If it is the one that is -// removed, a "changed" event is fired. -func (l *List) RemoveItem(index int) *List { - if len(l.items) == 0 { - return l - } - - // Adjust index. - if index < 0 { - index = len(l.items) + index - } - if index >= len(l.items) { - index = len(l.items) - 1 - } - if index < 0 { - index = 0 - } - - // Remove item. - l.items = append(l.items[:index], l.items[index+1:]...) - - // If there is nothing left, we're done. - if len(l.items) == 0 { - return l - } - - // Shift current item. - previousCurrentItem := l.currentItem - if l.currentItem >= index { - l.currentItem-- - } - - // Fire "changed" event for removed items. - if previousCurrentItem == index && l.changed != nil { - item := l.items[l.currentItem] - l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) - } - - return l -} - -// SetMainTextColor sets the color of the items' main text. -func (l *List) SetMainTextColor(color tcell.Color) *List { - l.mainTextColor = color - return l -} - -// SetSecondaryTextColor sets the color of the items' secondary text. -func (l *List) SetSecondaryTextColor(color tcell.Color) *List { - l.secondaryTextColor = color - return l -} - -// SetShortcutColor sets the color of the items' shortcut. -func (l *List) SetShortcutColor(color tcell.Color) *List { - l.shortcutColor = color - return l -} - -// SetSelectedTextColor sets the text color of selected items. -func (l *List) SetSelectedTextColor(color tcell.Color) *List { - l.selectedTextColor = color - return l -} - -// SetSelectedBackgroundColor sets the background color of selected items. -func (l *List) SetSelectedBackgroundColor(color tcell.Color) *List { - l.selectedBackgroundColor = color - return l -} - -// SetSelectedFocusOnly sets a flag which determines when the currently selected -// list item is highlighted. If set to true, selected items are only highlighted -// when the list has focus. If set to false, they are always highlighted. -func (l *List) SetSelectedFocusOnly(focusOnly bool) *List { - l.selectedFocusOnly = focusOnly - return l -} - -// SetHighlightFullLine sets a flag which determines whether the colored -// background of selected items spans the entire width of the view. If set to -// true, the highlight spans the entire view. If set to false, only the text of -// the selected item from beginning to end is highlighted. -func (l *List) SetHighlightFullLine(highlight bool) *List { - l.highlightFullLine = highlight - return l -} - -// ShowSecondaryText determines whether or not to show secondary item texts. -func (l *List) ShowSecondaryText(show bool) *List { - l.showSecondaryText = show - return l -} - -// SetWrapAround sets the flag that determines whether navigating the list will -// wrap around. That is, navigating downwards on the last item will move the -// selection to the first item (similarly in the other direction). If set to -// false, the selection won't change when navigating downwards on the last item -// or navigating upwards on the first item. -func (l *List) SetWrapAround(wrapAround bool) *List { - l.wrapAround = wrapAround - return l -} - -// SetChangedFunc sets the function which is called when the user navigates to -// a list item. The function receives the item's index in the list of items -// (starting with 0), its main text, secondary text, and its shortcut rune. -// -// This function is also called when the first item is added or when -// SetCurrentItem() is called. -func (l *List) SetChangedFunc(handler func(index int, mainText string, secondaryText string, shortcut rune)) *List { - l.changed = handler - return l -} - -// SetSelectedFunc sets the function which is called when the user selects a -// list item by pressing Enter on the current selection. The function receives -// the item's index in the list of items (starting with 0), its main text, -// secondary text, and its shortcut rune. -func (l *List) SetSelectedFunc(handler func(int, string, string, rune)) *List { - l.selected = handler - return l -} - -// SetDoneFunc sets a function which is called when the user presses the Escape -// key. -func (l *List) SetDoneFunc(handler func()) *List { - l.done = handler - return l -} - -// AddItem calls InsertItem() with an index of -1. -func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List { - l.InsertItem(-1, mainText, secondaryText, shortcut, selected) - return l -} - -// InsertItem adds a new item to the list at the specified index. An index of 0 -// will insert the item at the beginning, an index of 1 before the second item, -// and so on. An index of GetItemCount() or higher will insert the item at the -// end of the list. Negative indices are also allowed: An index of -1 will -// insert the item at the end of the list, an index of -2 before the last item, -// and so on. An index of -GetItemCount()-1 or lower will insert the item at the -// beginning. -// -// An item has a main text which will be highlighted when selected. It also has -// a secondary text which is shown underneath the main text (if it is set to -// visible) but which may remain empty. -// -// The shortcut is a key binding. If the specified rune is entered, the item -// is selected immediately. Set to 0 for no binding. -// -// The "selected" callback will be invoked when the user selects the item. You -// may provide nil if no such callback is needed or if all events are handled -// through the selected callback set with SetSelectedFunc(). -// -// The currently selected item will shift its position accordingly. If the list -// was previously empty, a "changed" event is fired because the new item becomes -// selected. -func (l *List) InsertItem(index int, mainText, secondaryText string, shortcut rune, selected func()) *List { - item := &listItem{ - MainText: mainText, - SecondaryText: secondaryText, - Shortcut: shortcut, - Selected: selected, - } - - // Shift index to range. - if index < 0 { - index = len(l.items) + index + 1 - } - if index < 0 { - index = 0 - } else if index > len(l.items) { - index = len(l.items) - } - - // Shift current item. - if l.currentItem < len(l.items) && l.currentItem >= index { - l.currentItem++ - } - - // Insert item (make space for the new item, then shift and insert). - l.items = append(l.items, nil) - if index < len(l.items)-1 { // -1 because l.items has already grown by one item. - copy(l.items[index+1:], l.items[index:]) - } - l.items[index] = item - - // Fire a "change" event for the first item in the list. - if len(l.items) == 1 && l.changed != nil { - item := l.items[0] - l.changed(0, item.MainText, item.SecondaryText, item.Shortcut) - } - - return l -} - -// GetItemCount returns the number of items in the list. -func (l *List) GetItemCount() int { - return len(l.items) -} - -// GetItemText returns an item's texts (main and secondary). Panics if the index -// is out of range. -func (l *List) GetItemText(index int) (main, secondary string) { - return l.items[index].MainText, l.items[index].SecondaryText -} - -// SetItemText sets an item's main and secondary text. Panics if the index is -// out of range. -func (l *List) SetItemText(index int, main, secondary string) *List { - item := l.items[index] - item.MainText = main - item.SecondaryText = secondary - return l -} - -// FindItems searches the main and secondary texts for the given strings and -// returns a list of item indices in which those strings are found. One of the -// two search strings may be empty, it will then be ignored. Indices are always -// returned in ascending order. -// -// If mustContainBoth is set to true, mainSearch must be contained in the main -// text AND secondarySearch must be contained in the secondary text. If it is -// false, only one of the two search strings must be contained. -// -// Set ignoreCase to true for case-insensitive search. -func (l *List) FindItems(mainSearch, secondarySearch string, mustContainBoth, ignoreCase bool) (indices []int) { - if mainSearch == "" && secondarySearch == "" { - return - } - - if ignoreCase { - mainSearch = strings.ToLower(mainSearch) - secondarySearch = strings.ToLower(secondarySearch) - } - - for index, item := range l.items { - mainText := item.MainText - secondaryText := item.SecondaryText - if ignoreCase { - mainText = strings.ToLower(mainText) - secondaryText = strings.ToLower(secondaryText) - } - - // strings.Contains() always returns true for a "" search. - mainContained := strings.Contains(mainText, mainSearch) - secondaryContained := strings.Contains(secondaryText, secondarySearch) - if mustContainBoth && mainContained && secondaryContained || - !mustContainBoth && (mainText != "" && mainContained || secondaryText != "" && secondaryContained) { - indices = append(indices, index) - } - } - - return -} - -// Clear removes all items from the list. -func (l *List) Clear() *List { - l.items = nil - l.currentItem = 0 - return l -} - -// Draw draws this primitive onto the screen. -func (l *List) Draw(screen tcell.Screen) { - l.Box.Draw(screen) - - // Determine the dimensions. - x, y, width, height := l.GetInnerRect() - bottomLimit := y + height - _, totalHeight := screen.Size() - if bottomLimit > totalHeight { - bottomLimit = totalHeight - } - - // Do we show any shortcuts? - var showShortcuts bool - for _, item := range l.items { - if item.Shortcut != 0 { - showShortcuts = true - x += 4 - width -= 4 - break - } - } - - // Adjust offset to keep the current selection in view. - if l.currentItem < l.offset { - l.offset = l.currentItem - } else if l.showSecondaryText { - if 2*(l.currentItem-l.offset) >= height-1 { - l.offset = (2*l.currentItem + 3 - height) / 2 - } - } else { - if l.currentItem-l.offset >= height { - l.offset = l.currentItem + 1 - height - } - } - - // Draw the list items. - for index, item := range l.items { - if index < l.offset { - continue - } - - if y >= bottomLimit { - break - } - - // Shortcuts. - if showShortcuts && item.Shortcut != 0 { - Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 4, AlignRight, l.shortcutColor) - } - - // Main text. - Print(screen, item.MainText, x, y, width, AlignLeft, l.mainTextColor) - - // Background color of selected text. - if index == l.currentItem && (!l.selectedFocusOnly || l.HasFocus()) { - textWidth := width - if !l.highlightFullLine { - if w := TaggedStringWidth(item.MainText); w < textWidth { - textWidth = w - } - } - - for bx := 0; bx < textWidth; bx++ { - m, c, style, _ := screen.GetContent(x+bx, y) - fg, _, _ := style.Decompose() - if fg == l.mainTextColor { - fg = l.selectedTextColor - } - style = style.Background(l.selectedBackgroundColor).Foreground(fg) - screen.SetContent(x+bx, y, m, c, style) - } - } - - y++ - - if y >= bottomLimit { - break - } - - // Secondary text. - if l.showSecondaryText { - Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor) - y++ - } - } -} - -// InputHandler returns the handler for this primitive. -func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return l.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - if event.Key() == tcell.KeyEscape { - if l.done != nil { - l.done() - } - return - } else if len(l.items) == 0 { - return - } - - previousItem := l.currentItem - - switch key := event.Key(); key { - case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight: - l.currentItem++ - case tcell.KeyBacktab, tcell.KeyUp, tcell.KeyLeft: - l.currentItem-- - case tcell.KeyHome: - l.currentItem = 0 - case tcell.KeyEnd: - l.currentItem = len(l.items) - 1 - case tcell.KeyPgDn: - _, _, _, height := l.GetInnerRect() - l.currentItem += height - case tcell.KeyPgUp: - _, _, _, height := l.GetInnerRect() - l.currentItem -= height - case tcell.KeyEnter: - if l.currentItem >= 0 && l.currentItem < len(l.items) { - item := l.items[l.currentItem] - if item.Selected != nil { - item.Selected() - } - if l.selected != nil { - l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) - } - } - case tcell.KeyRune: - ch := event.Rune() - if ch != ' ' { - // It's not a space bar. Is it a shortcut? - var found bool - for index, item := range l.items { - if item.Shortcut == ch { - // We have a shortcut. - found = true - l.currentItem = index - break - } - } - if !found { - break - } - } - item := l.items[l.currentItem] - if item.Selected != nil { - item.Selected() - } - if l.selected != nil { - l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) - } - } - - if l.currentItem < 0 { - if l.wrapAround { - l.currentItem = len(l.items) - 1 - } else { - l.currentItem = 0 - } - } else if l.currentItem >= len(l.items) { - if l.wrapAround { - l.currentItem = 0 - } else { - l.currentItem = len(l.items) - 1 - } - } - - if l.currentItem != previousItem && l.currentItem < len(l.items) && l.changed != nil { - item := l.items[l.currentItem] - l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut) - } - }) -} - -// indexAtPoint returns the index of the list item found at the given position -// or a negative value if there is no such list item. -func (l *List) indexAtPoint(x, y int) int { - rectX, rectY, width, height := l.GetInnerRect() - if rectX < 0 || rectX >= rectX+width || y < rectY || y >= rectY+height { - return -1 - } - - index := y - rectY - if l.showSecondaryText { - index /= 2 - } - index += l.offset - - if index >= len(l.items) { - return -1 - } - return index -} - -// MouseHandler returns the mouse handler for this primitive. -func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return l.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !l.InRect(event.Position()) { - return false, nil - } - - // Process mouse event. - switch action { - case MouseLeftClick: - setFocus(l) - index := l.indexAtPoint(event.Position()) - if index != -1 { - item := l.items[index] - if item.Selected != nil { - item.Selected() - } - if l.selected != nil { - l.selected(index, item.MainText, item.SecondaryText, item.Shortcut) - } - if index != l.currentItem && l.changed != nil { - l.changed(index, item.MainText, item.SecondaryText, item.Shortcut) - } - l.currentItem = index - } - consumed = true - case MouseScrollUp: - if l.offset > 0 { - l.offset-- - } - consumed = true - case MouseScrollDown: - lines := len(l.items) - l.offset - if l.showSecondaryText { - lines *= 2 - } - if _, _, _, height := l.GetInnerRect(); lines > height { - l.offset++ - } - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/modal.go b/vendor/github.com/rivo/tview/modal.go deleted file mode 100644 index f5341132..00000000 --- a/vendor/github.com/rivo/tview/modal.go +++ /dev/null @@ -1,190 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Modal is a centered message window used to inform the user or prompt them -// for an immediate decision. It needs to have at least one button (added via -// AddButtons()) or it will never disappear. -// -// See https://github.com/rivo/tview/wiki/Modal for an example. -type Modal struct { - *Box - - // The frame embedded in the modal. - frame *Frame - - // The form embedded in the modal's frame. - form *Form - - // The message text (original, not word-wrapped). - text string - - // The text color. - textColor tcell.Color - - // The optional callback for when the user clicked one of the buttons. It - // receives the index of the clicked button and the button's label. - done func(buttonIndex int, buttonLabel string) -} - -// NewModal returns a new modal message window. -func NewModal() *Modal { - m := &Modal{ - Box: NewBox(), - textColor: Styles.PrimaryTextColor, - } - m.form = NewForm(). - SetButtonsAlign(AlignCenter). - SetButtonBackgroundColor(Styles.PrimitiveBackgroundColor). - SetButtonTextColor(Styles.PrimaryTextColor) - m.form.SetBackgroundColor(Styles.ContrastBackgroundColor).SetBorderPadding(0, 0, 0, 0) - m.form.SetCancelFunc(func() { - if m.done != nil { - m.done(-1, "") - } - }) - m.frame = NewFrame(m.form).SetBorders(0, 0, 1, 0, 0, 0) - m.frame.SetBorder(true). - SetBackgroundColor(Styles.ContrastBackgroundColor). - SetBorderPadding(1, 1, 1, 1) - m.focus = m - return m -} - -// SetBackgroundColor sets the color of the modal frame background. -func (m *Modal) SetBackgroundColor(color tcell.Color) *Modal { - m.form.SetBackgroundColor(color) - m.frame.SetBackgroundColor(color) - return m -} - -// SetTextColor sets the color of the message text. -func (m *Modal) SetTextColor(color tcell.Color) *Modal { - m.textColor = color - return m -} - -// SetButtonBackgroundColor sets the background color of the buttons. -func (m *Modal) SetButtonBackgroundColor(color tcell.Color) *Modal { - m.form.SetButtonBackgroundColor(color) - return m -} - -// SetButtonTextColor sets the color of the button texts. -func (m *Modal) SetButtonTextColor(color tcell.Color) *Modal { - m.form.SetButtonTextColor(color) - return m -} - -// SetDoneFunc sets a handler which is called when one of the buttons was -// pressed. It receives the index of the button as well as its label text. The -// handler is also called when the user presses the Escape key. The index will -// then be negative and the label text an emptry string. -func (m *Modal) SetDoneFunc(handler func(buttonIndex int, buttonLabel string)) *Modal { - m.done = handler - return m -} - -// SetText sets the message text of the window. The text may contain line -// breaks. Note that words are wrapped, too, based on the final size of the -// window. -func (m *Modal) SetText(text string) *Modal { - m.text = text - return m -} - -// AddButtons adds buttons to the window. There must be at least one button and -// a "done" handler so the window can be closed again. -func (m *Modal) AddButtons(labels []string) *Modal { - for index, label := range labels { - func(i int, l string) { - m.form.AddButton(label, func() { - if m.done != nil { - m.done(i, l) - } - }) - button := m.form.GetButton(m.form.GetButtonCount() - 1) - button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - switch event.Key() { - case tcell.KeyDown, tcell.KeyRight: - return tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone) - case tcell.KeyUp, tcell.KeyLeft: - return tcell.NewEventKey(tcell.KeyBacktab, 0, tcell.ModNone) - } - return event - }) - }(index, label) - } - return m -} - -// ClearButtons removes all buttons from the window. -func (m *Modal) ClearButtons() *Modal { - m.form.ClearButtons() - return m -} - -// SetFocus shifts the focus to the button with the given index. -func (m *Modal) SetFocus(index int) *Modal { - m.form.SetFocus(index) - return m -} - -// Focus is called when this primitive receives focus. -func (m *Modal) Focus(delegate func(p Primitive)) { - delegate(m.form) -} - -// HasFocus returns whether or not this primitive has focus. -func (m *Modal) HasFocus() bool { - return m.form.HasFocus() -} - -// Draw draws this primitive onto the screen. -func (m *Modal) Draw(screen tcell.Screen) { - // Calculate the width of this modal. - buttonsWidth := 0 - for _, button := range m.form.buttons { - buttonsWidth += TaggedStringWidth(button.label) + 4 + 2 - } - buttonsWidth -= 2 - screenWidth, screenHeight := screen.Size() - width := screenWidth / 3 - if width < buttonsWidth { - width = buttonsWidth - } - // width is now without the box border. - - // Reset the text and find out how wide it is. - m.frame.Clear() - lines := WordWrap(m.text, width) - for _, line := range lines { - m.frame.AddText(line, true, AlignCenter, m.textColor) - } - - // Set the modal's position and size. - height := len(lines) + 6 - width += 4 - x := (screenWidth - width) / 2 - y := (screenHeight - height) / 2 - m.SetRect(x, y, width, height) - - // Draw the frame. - m.frame.SetRect(x, y, width, height) - m.frame.Draw(screen) -} - -// MouseHandler returns the mouse handler for this primitive. -func (m *Modal) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return m.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - // Pass mouse events on to the form. - consumed, capture = m.form.MouseHandler()(action, event, setFocus) - if !consumed && action == MouseLeftClick && m.InRect(event.Position()) { - setFocus(m) - consumed = true - } - return - }) -} diff --git a/vendor/github.com/rivo/tview/pages.go b/vendor/github.com/rivo/tview/pages.go deleted file mode 100644 index 09550453..00000000 --- a/vendor/github.com/rivo/tview/pages.go +++ /dev/null @@ -1,302 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// page represents one page of a Pages object. -type page struct { - Name string // The page's name. - Item Primitive // The page's primitive. - Resize bool // Whether or not to resize the page when it is drawn. - Visible bool // Whether or not this page is visible. -} - -// Pages is a container for other primitives often used as the application's -// root primitive. It allows to easily switch the visibility of the contained -// primitives. -// -// See https://github.com/rivo/tview/wiki/Pages for an example. -type Pages struct { - *Box - - // The contained pages. (Visible) pages are drawn from back to front. - pages []*page - - // We keep a reference to the function which allows us to set the focus to - // a newly visible page. - setFocus func(p Primitive) - - // An optional handler which is called whenever the visibility or the order of - // pages changes. - changed func() -} - -// NewPages returns a new Pages object. -func NewPages() *Pages { - p := &Pages{ - Box: NewBox(), - } - p.focus = p - return p -} - -// SetChangedFunc sets a handler which is called whenever the visibility or the -// order of any visible pages changes. This can be used to redraw the pages. -func (p *Pages) SetChangedFunc(handler func()) *Pages { - p.changed = handler - return p -} - -// GetPageCount returns the number of pages currently stored in this object. -func (p *Pages) GetPageCount() int { - return len(p.pages) -} - -// AddPage adds a new page with the given name and primitive. If there was -// previously a page with the same name, it is overwritten. Leaving the name -// empty may cause conflicts in other functions so always specify a non-empty -// name. -// -// Visible pages will be drawn in the order they were added (unless that order -// was changed in one of the other functions). If "resize" is set to true, the -// primitive will be set to the size available to the Pages primitive whenever -// the pages are drawn. -func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) *Pages { - hasFocus := p.HasFocus() - for index, pg := range p.pages { - if pg.Name == name { - p.pages = append(p.pages[:index], p.pages[index+1:]...) - break - } - } - p.pages = append(p.pages, &page{Item: item, Name: name, Resize: resize, Visible: visible}) - if p.changed != nil { - p.changed() - } - if hasFocus { - p.Focus(p.setFocus) - } - return p -} - -// AddAndSwitchToPage calls AddPage(), then SwitchToPage() on that newly added -// page. -func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) *Pages { - p.AddPage(name, item, resize, true) - p.SwitchToPage(name) - return p -} - -// RemovePage removes the page with the given name. If that page was the only -// visible page, visibility is assigned to the last page. -func (p *Pages) RemovePage(name string) *Pages { - var isVisible bool - hasFocus := p.HasFocus() - for index, page := range p.pages { - if page.Name == name { - isVisible = page.Visible - p.pages = append(p.pages[:index], p.pages[index+1:]...) - if page.Visible && p.changed != nil { - p.changed() - } - break - } - } - if isVisible { - for index, page := range p.pages { - if index < len(p.pages)-1 { - if page.Visible { - break // There is a remaining visible page. - } - } else { - page.Visible = true // We need at least one visible page. - } - } - } - if hasFocus { - p.Focus(p.setFocus) - } - return p -} - -// HasPage returns true if a page with the given name exists in this object. -func (p *Pages) HasPage(name string) bool { - for _, page := range p.pages { - if page.Name == name { - return true - } - } - return false -} - -// ShowPage sets a page's visibility to "true" (in addition to any other pages -// which are already visible). -func (p *Pages) ShowPage(name string) *Pages { - for _, page := range p.pages { - if page.Name == name { - page.Visible = true - if p.changed != nil { - p.changed() - } - break - } - } - if p.HasFocus() { - p.Focus(p.setFocus) - } - return p -} - -// HidePage sets a page's visibility to "false". -func (p *Pages) HidePage(name string) *Pages { - for _, page := range p.pages { - if page.Name == name { - page.Visible = false - if p.changed != nil { - p.changed() - } - break - } - } - if p.HasFocus() { - p.Focus(p.setFocus) - } - return p -} - -// SwitchToPage sets a page's visibility to "true" and all other pages' -// visibility to "false". -func (p *Pages) SwitchToPage(name string) *Pages { - for _, page := range p.pages { - if page.Name == name { - page.Visible = true - } else { - page.Visible = false - } - } - if p.changed != nil { - p.changed() - } - if p.HasFocus() { - p.Focus(p.setFocus) - } - return p -} - -// SendToFront changes the order of the pages such that the page with the given -// name comes last, causing it to be drawn last with the next update (if -// visible). -func (p *Pages) SendToFront(name string) *Pages { - for index, page := range p.pages { - if page.Name == name { - if index < len(p.pages)-1 { - p.pages = append(append(p.pages[:index], p.pages[index+1:]...), page) - } - if page.Visible && p.changed != nil { - p.changed() - } - break - } - } - if p.HasFocus() { - p.Focus(p.setFocus) - } - return p -} - -// SendToBack changes the order of the pages such that the page with the given -// name comes first, causing it to be drawn first with the next update (if -// visible). -func (p *Pages) SendToBack(name string) *Pages { - for index, pg := range p.pages { - if pg.Name == name { - if index > 0 { - p.pages = append(append([]*page{pg}, p.pages[:index]...), p.pages[index+1:]...) - } - if pg.Visible && p.changed != nil { - p.changed() - } - break - } - } - if p.HasFocus() { - p.Focus(p.setFocus) - } - return p -} - -// GetFrontPage returns the front-most visible page. If there are no visible -// pages, ("", nil) is returned. -func (p *Pages) GetFrontPage() (name string, item Primitive) { - for index := len(p.pages) - 1; index >= 0; index-- { - if p.pages[index].Visible { - return p.pages[index].Name, p.pages[index].Item - } - } - return -} - -// HasFocus returns whether or not this primitive has focus. -func (p *Pages) HasFocus() bool { - for _, page := range p.pages { - if page.Item.GetFocusable().HasFocus() { - return true - } - } - return false -} - -// Focus is called by the application when the primitive receives focus. -func (p *Pages) Focus(delegate func(p Primitive)) { - if delegate == nil { - return // We cannot delegate so we cannot focus. - } - p.setFocus = delegate - var topItem Primitive - for _, page := range p.pages { - if page.Visible { - topItem = page.Item - } - } - if topItem != nil { - delegate(topItem) - } -} - -// Draw draws this primitive onto the screen. -func (p *Pages) Draw(screen tcell.Screen) { - p.Box.Draw(screen) - for _, page := range p.pages { - if !page.Visible { - continue - } - if page.Resize { - x, y, width, height := p.GetInnerRect() - page.Item.SetRect(x, y, width, height) - } - page.Item.Draw(screen) - } -} - -// MouseHandler returns the mouse handler for this primitive. -func (p *Pages) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return p.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - if !p.InRect(event.Position()) { - return false, nil - } - - // Pass mouse events along to the last visible page item that takes it. - for index := len(p.pages) - 1; index >= 0; index-- { - page := p.pages[index] - if page.Visible { - consumed, capture = page.Item.MouseHandler()(action, event, setFocus) - if consumed { - return - } - } - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/primitive.go b/vendor/github.com/rivo/tview/primitive.go deleted file mode 100644 index 416d7082..00000000 --- a/vendor/github.com/rivo/tview/primitive.go +++ /dev/null @@ -1,57 +0,0 @@ -package tview - -import "github.com/gdamore/tcell" - -// Primitive is the top-most interface for all graphical primitives. -type Primitive interface { - // Draw draws this primitive onto the screen. Implementers can call the - // screen's ShowCursor() function but should only do so when they have focus. - // (They will need to keep track of this themselves.) - Draw(screen tcell.Screen) - - // GetRect returns the current position of the primitive, x, y, width, and - // height. - GetRect() (int, int, int, int) - - // SetRect sets a new position of the primitive. - SetRect(x, y, width, height int) - - // InputHandler returns a handler which receives key events when it has focus. - // It is called by the Application class. - // - // A value of nil may also be returned, in which case this primitive cannot - // receive focus and will not process any key events. - // - // The handler will receive the key event and a function that allows it to - // set the focus to a different primitive, so that future key events are sent - // to that primitive. - // - // The Application's Draw() function will be called automatically after the - // handler returns. - // - // The Box class provides functionality to intercept keyboard input. If you - // subclass from Box, it is recommended that you wrap your handler using - // Box.WrapInputHandler() so you inherit that functionality. - InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) - - // Focus is called by the application when the primitive receives focus. - // Implementers may call delegate() to pass the focus on to another primitive. - Focus(delegate func(p Primitive)) - - // Blur is called by the application when the primitive loses focus. - Blur() - - // GetFocusable returns the item's Focusable. - GetFocusable() Focusable - - // MouseHandler returns a handler which receives mouse events. - // It is called by the Application class. - // - // A value of nil may also be returned to stop the downward propagation of - // mouse events. - // - // The Box class provides functionality to intercept mouse events. If you - // subclass from Box, it is recommended that you wrap your handler using - // Box.WrapMouseHandler() so you inherit that functionality. - MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) -} diff --git a/vendor/github.com/rivo/tview/semigraphics.go b/vendor/github.com/rivo/tview/semigraphics.go deleted file mode 100644 index 3b66c5fc..00000000 --- a/vendor/github.com/rivo/tview/semigraphics.go +++ /dev/null @@ -1,296 +0,0 @@ -package tview - -import "github.com/gdamore/tcell" - -// Semigraphics provides an easy way to access unicode characters for drawing. -// -// Named like the unicode characters, 'Semigraphics'-prefix used if unicode block -// isn't prefixed itself. -const ( - // Block: General Punctation U+2000-U+206F (http://unicode.org/charts/PDF/U2000.pdf) - SemigraphicsHorizontalEllipsis rune = '\u2026' // … - - // Block: Box Drawing U+2500-U+257F (http://unicode.org/charts/PDF/U2500.pdf) - BoxDrawingsLightHorizontal rune = '\u2500' // ─ - BoxDrawingsHeavyHorizontal rune = '\u2501' // ━ - BoxDrawingsLightVertical rune = '\u2502' // │ - BoxDrawingsHeavyVertical rune = '\u2503' // ┃ - BoxDrawingsLightTripleDashHorizontal rune = '\u2504' // ┄ - BoxDrawingsHeavyTripleDashHorizontal rune = '\u2505' // ┅ - BoxDrawingsLightTripleDashVertical rune = '\u2506' // ┆ - BoxDrawingsHeavyTripleDashVertical rune = '\u2507' // ┇ - BoxDrawingsLightQuadrupleDashHorizontal rune = '\u2508' // ┈ - BoxDrawingsHeavyQuadrupleDashHorizontal rune = '\u2509' // ┉ - BoxDrawingsLightQuadrupleDashVertical rune = '\u250a' // ┊ - BoxDrawingsHeavyQuadrupleDashVertical rune = '\u250b' // ┋ - BoxDrawingsLightDownAndRight rune = '\u250c' // ┌ - BoxDrawingsDownLighAndRightHeavy rune = '\u250d' // ┍ - BoxDrawingsDownHeavyAndRightLight rune = '\u250e' // ┎ - BoxDrawingsHeavyDownAndRight rune = '\u250f' // ┏ - BoxDrawingsLightDownAndLeft rune = '\u2510' // ┐ - BoxDrawingsDownLighAndLeftHeavy rune = '\u2511' // ┑ - BoxDrawingsDownHeavyAndLeftLight rune = '\u2512' // ┒ - BoxDrawingsHeavyDownAndLeft rune = '\u2513' // ┓ - BoxDrawingsLightUpAndRight rune = '\u2514' // └ - BoxDrawingsUpLightAndRightHeavy rune = '\u2515' // ┕ - BoxDrawingsUpHeavyAndRightLight rune = '\u2516' // ┖ - BoxDrawingsHeavyUpAndRight rune = '\u2517' // ┗ - BoxDrawingsLightUpAndLeft rune = '\u2518' // ┘ - BoxDrawingsUpLightAndLeftHeavy rune = '\u2519' // ┙ - BoxDrawingsUpHeavyAndLeftLight rune = '\u251a' // ┚ - BoxDrawingsHeavyUpAndLeft rune = '\u251b' // ┛ - BoxDrawingsLightVerticalAndRight rune = '\u251c' // ├ - BoxDrawingsVerticalLightAndRightHeavy rune = '\u251d' // ┝ - BoxDrawingsUpHeavyAndRightDownLight rune = '\u251e' // ┞ - BoxDrawingsDownHeacyAndRightUpLight rune = '\u251f' // ┟ - BoxDrawingsVerticalHeavyAndRightLight rune = '\u2520' // ┠ - BoxDrawingsDownLightAnbdRightUpHeavy rune = '\u2521' // ┡ - BoxDrawingsUpLightAndRightDownHeavy rune = '\u2522' // ┢ - BoxDrawingsHeavyVerticalAndRight rune = '\u2523' // ┣ - BoxDrawingsLightVerticalAndLeft rune = '\u2524' // ┤ - BoxDrawingsVerticalLightAndLeftHeavy rune = '\u2525' // ┥ - BoxDrawingsUpHeavyAndLeftDownLight rune = '\u2526' // ┦ - BoxDrawingsDownHeavyAndLeftUpLight rune = '\u2527' // ┧ - BoxDrawingsVerticalheavyAndLeftLight rune = '\u2528' // ┨ - BoxDrawingsDownLightAndLeftUpHeavy rune = '\u2529' // ┨ - BoxDrawingsUpLightAndLeftDownHeavy rune = '\u252a' // ┪ - BoxDrawingsHeavyVerticalAndLeft rune = '\u252b' // ┫ - BoxDrawingsLightDownAndHorizontal rune = '\u252c' // ┬ - BoxDrawingsLeftHeavyAndRightDownLight rune = '\u252d' // ┭ - BoxDrawingsRightHeavyAndLeftDownLight rune = '\u252e' // ┮ - BoxDrawingsDownLightAndHorizontalHeavy rune = '\u252f' // ┯ - BoxDrawingsDownHeavyAndHorizontalLight rune = '\u2530' // ┰ - BoxDrawingsRightLightAndLeftDownHeavy rune = '\u2531' // ┱ - BoxDrawingsLeftLightAndRightDownHeavy rune = '\u2532' // ┲ - BoxDrawingsHeavyDownAndHorizontal rune = '\u2533' // ┳ - BoxDrawingsLightUpAndHorizontal rune = '\u2534' // ┴ - BoxDrawingsLeftHeavyAndRightUpLight rune = '\u2535' // ┵ - BoxDrawingsRightHeavyAndLeftUpLight rune = '\u2536' // ┶ - BoxDrawingsUpLightAndHorizontalHeavy rune = '\u2537' // ┷ - BoxDrawingsUpHeavyAndHorizontalLight rune = '\u2538' // ┸ - BoxDrawingsRightLightAndLeftUpHeavy rune = '\u2539' // ┹ - BoxDrawingsLeftLightAndRightUpHeavy rune = '\u253a' // ┺ - BoxDrawingsHeavyUpAndHorizontal rune = '\u253b' // ┻ - BoxDrawingsLightVerticalAndHorizontal rune = '\u253c' // ┼ - BoxDrawingsLeftHeavyAndRightVerticalLight rune = '\u253d' // ┽ - BoxDrawingsRightHeavyAndLeftVerticalLight rune = '\u253e' // ┾ - BoxDrawingsVerticalLightAndHorizontalHeavy rune = '\u253f' // ┿ - BoxDrawingsUpHeavyAndDownHorizontalLight rune = '\u2540' // ╀ - BoxDrawingsDownHeavyAndUpHorizontalLight rune = '\u2541' // ╁ - BoxDrawingsVerticalHeavyAndHorizontalLight rune = '\u2542' // ╂ - BoxDrawingsLeftUpHeavyAndRightDownLight rune = '\u2543' // ╃ - BoxDrawingsRightUpHeavyAndLeftDownLight rune = '\u2544' // ╄ - BoxDrawingsLeftDownHeavyAndRightUpLight rune = '\u2545' // ╅ - BoxDrawingsRightDownHeavyAndLeftUpLight rune = '\u2546' // ╆ - BoxDrawingsDownLightAndUpHorizontalHeavy rune = '\u2547' // ╇ - BoxDrawingsUpLightAndDownHorizontalHeavy rune = '\u2548' // ╈ - BoxDrawingsRightLightAndLeftVerticalHeavy rune = '\u2549' // ╉ - BoxDrawingsLeftLightAndRightVerticalHeavy rune = '\u254a' // ╊ - BoxDrawingsHeavyVerticalAndHorizontal rune = '\u254b' // ╋ - BoxDrawingsLightDoubleDashHorizontal rune = '\u254c' // ╌ - BoxDrawingsHeavyDoubleDashHorizontal rune = '\u254d' // ╍ - BoxDrawingsLightDoubleDashVertical rune = '\u254e' // ╎ - BoxDrawingsHeavyDoubleDashVertical rune = '\u254f' // ╏ - BoxDrawingsDoubleHorizontal rune = '\u2550' // ═ - BoxDrawingsDoubleVertical rune = '\u2551' // ║ - BoxDrawingsDownSingleAndRightDouble rune = '\u2552' // ╒ - BoxDrawingsDownDoubleAndRightSingle rune = '\u2553' // ╓ - BoxDrawingsDoubleDownAndRight rune = '\u2554' // ╔ - BoxDrawingsDownSingleAndLeftDouble rune = '\u2555' // ╕ - BoxDrawingsDownDoubleAndLeftSingle rune = '\u2556' // ╖ - BoxDrawingsDoubleDownAndLeft rune = '\u2557' // ╗ - BoxDrawingsUpSingleAndRightDouble rune = '\u2558' // ╘ - BoxDrawingsUpDoubleAndRightSingle rune = '\u2559' // ╙ - BoxDrawingsDoubleUpAndRight rune = '\u255a' // ╚ - BoxDrawingsUpSingleAndLeftDouble rune = '\u255b' // ╛ - BoxDrawingsUpDobuleAndLeftSingle rune = '\u255c' // ╜ - BoxDrawingsDoubleUpAndLeft rune = '\u255d' // ╝ - BoxDrawingsVerticalSingleAndRightDouble rune = '\u255e' // ╞ - BoxDrawingsVerticalDoubleAndRightSingle rune = '\u255f' // ╟ - BoxDrawingsDoubleVerticalAndRight rune = '\u2560' // ╠ - BoxDrawingsVerticalSingleAndLeftDouble rune = '\u2561' // ╡ - BoxDrawingsVerticalDoubleAndLeftSingle rune = '\u2562' // ╢ - BoxDrawingsDoubleVerticalAndLeft rune = '\u2563' // ╣ - BoxDrawingsDownSingleAndHorizontalDouble rune = '\u2564' // ╤ - BoxDrawingsDownDoubleAndHorizontalSingle rune = '\u2565' // ╥ - BoxDrawingsDoubleDownAndHorizontal rune = '\u2566' // ╦ - BoxDrawingsUpSingleAndHorizontalDouble rune = '\u2567' // ╧ - BoxDrawingsUpDoubleAndHorizontalSingle rune = '\u2568' // ╨ - BoxDrawingsDoubleUpAndHorizontal rune = '\u2569' // ╩ - BoxDrawingsVerticalSingleAndHorizontalDouble rune = '\u256a' // ╪ - BoxDrawingsVerticalDoubleAndHorizontalSingle rune = '\u256b' // ╫ - BoxDrawingsDoubleVerticalAndHorizontal rune = '\u256c' // ╬ - BoxDrawingsLightArcDownAndRight rune = '\u256d' // ╭ - BoxDrawingsLightArcDownAndLeft rune = '\u256e' // ╮ - BoxDrawingsLightArcUpAndLeft rune = '\u256f' // ╯ - BoxDrawingsLightArcUpAndRight rune = '\u2570' // ╰ - BoxDrawingsLightDiagonalUpperRightToLowerLeft rune = '\u2571' // ╱ - BoxDrawingsLightDiagonalUpperLeftToLowerRight rune = '\u2572' // ╲ - BoxDrawingsLightDiagonalCross rune = '\u2573' // ╳ - BoxDrawingsLightLeft rune = '\u2574' // ╴ - BoxDrawingsLightUp rune = '\u2575' // ╵ - BoxDrawingsLightRight rune = '\u2576' // ╶ - BoxDrawingsLightDown rune = '\u2577' // ╷ - BoxDrawingsHeavyLeft rune = '\u2578' // ╸ - BoxDrawingsHeavyUp rune = '\u2579' // ╹ - BoxDrawingsHeavyRight rune = '\u257a' // ╺ - BoxDrawingsHeavyDown rune = '\u257b' // ╻ - BoxDrawingsLightLeftAndHeavyRight rune = '\u257c' // ╼ - BoxDrawingsLightUpAndHeavyDown rune = '\u257d' // ╽ - BoxDrawingsHeavyLeftAndLightRight rune = '\u257e' // ╾ - BoxDrawingsHeavyUpAndLightDown rune = '\u257f' // ╿ -) - -// SemigraphicJoints is a map for joining semigraphic (or otherwise) runes. -// So far only light lines are supported but if you want to change the border -// styling you need to provide the joints, too. -// The matching will be sorted ascending by rune value, so you don't need to -// provide all rune combinations, -// e.g. (─) + (│) = (┼) will also match (│) + (─) = (┼) -var SemigraphicJoints = map[string]rune{ - // (─) + (│) = (┼) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVertical}): BoxDrawingsLightVerticalAndHorizontal, - // (─) + (┌) = (┬) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndRight}): BoxDrawingsLightDownAndHorizontal, - // (─) + (┐) = (┬) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightDownAndHorizontal, - // (─) + (└) = (┴) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndRight}): BoxDrawingsLightUpAndHorizontal, - // (─) + (┘) = (┴) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightUpAndHorizontal, - // (─) + (├) = (┼) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, - // (─) + (┤) = (┼) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, - // (─) + (┬) = (┬) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, - // (─) + (┴) = (┴) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, - // (─) + (┼) = (┼) - string([]rune{BoxDrawingsLightHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (│) + (┌) = (├) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndRight}): BoxDrawingsLightVerticalAndRight, - // (│) + (┐) = (┤) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (│) + (└) = (├) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndRight, - // (│) + (┘) = (┤) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (│) + (├) = (├) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, - // (│) + (┤) = (┤) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (│) + (┬) = (┼) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (│) + (┴) = (┼) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (│) + (┼) = (┼) - string([]rune{BoxDrawingsLightVertical, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┌) + (┐) = (┬) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightDownAndLeft}): BoxDrawingsLightDownAndHorizontal, - // (┌) + (└) = (├) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndRight, - // (┌) + (┘) = (┼) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndHorizontal, - // (┌) + (├) = (├) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, - // (┌) + (┤) = (┼) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, - // (┌) + (┬) = (┬) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, - // (┌) + (┴) = (┼) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┌) + (┴) = (┼) - string([]rune{BoxDrawingsLightDownAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┐) + (└) = (┼) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndRight}): BoxDrawingsLightVerticalAndHorizontal, - // (┐) + (┘) = (┤) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (┐) + (├) = (┼) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, - // (┐) + (┤) = (┤) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (┐) + (┬) = (┬) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightDownAndHorizontal, - // (┐) + (┴) = (┼) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┐) + (┼) = (┼) - string([]rune{BoxDrawingsLightDownAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (└) + (┘) = (┴) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightUpAndLeft}): BoxDrawingsLightUpAndHorizontal, - // (└) + (├) = (├) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndRight, - // (└) + (┤) = (┼) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, - // (└) + (┬) = (┼) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (└) + (┴) = (┴) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, - // (└) + (┼) = (┼) - string([]rune{BoxDrawingsLightUpAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┘) + (├) = (┼) - string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndRight}): BoxDrawingsLightVerticalAndHorizontal, - // (┘) + (┤) = (┤) - string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndLeft, - // (┘) + (┬) = (┼) - string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┘) + (┴) = (┴) - string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightUpAndHorizontal, - // (┘) + (┼) = (┼) - string([]rune{BoxDrawingsLightUpAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (├) + (┤) = (┼) - string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightVerticalAndLeft}): BoxDrawingsLightVerticalAndHorizontal, - // (├) + (┬) = (┼) - string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (├) + (┴) = (┼) - string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (├) + (┼) = (┼) - string([]rune{BoxDrawingsLightVerticalAndRight, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┤) + (┬) = (┼) - string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightDownAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┤) + (┴) = (┼) - string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┤) + (┼) = (┼) - string([]rune{BoxDrawingsLightVerticalAndLeft, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┬) + (┴) = (┼) - string([]rune{BoxDrawingsLightDownAndHorizontal, BoxDrawingsLightUpAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - // (┬) + (┼) = (┼) - string([]rune{BoxDrawingsLightDownAndHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, - - // (┴) + (┼) = (┼) - string([]rune{BoxDrawingsLightUpAndHorizontal, BoxDrawingsLightVerticalAndHorizontal}): BoxDrawingsLightVerticalAndHorizontal, -} - -// PrintJoinedSemigraphics prints a semigraphics rune into the screen at the given -// position with the given color, joining it with any existing semigraphics -// rune. Background colors are preserved. At this point, only regular single -// line borders are supported. -func PrintJoinedSemigraphics(screen tcell.Screen, x, y int, ch rune, color tcell.Color) { - previous, _, style, _ := screen.GetContent(x, y) - style = style.Foreground(color) - - // What's the resulting rune? - var result rune - if ch == previous { - result = ch - } else { - if ch < previous { - previous, ch = ch, previous - } - result = SemigraphicJoints[string([]rune{previous, ch})] - } - if result == 0 { - result = ch - } - - // We only print something if we have something. - screen.SetContent(x, y, result, nil, style) -} diff --git a/vendor/github.com/rivo/tview/styles.go b/vendor/github.com/rivo/tview/styles.go deleted file mode 100644 index 4f0448f1..00000000 --- a/vendor/github.com/rivo/tview/styles.go +++ /dev/null @@ -1,35 +0,0 @@ -package tview - -import "github.com/gdamore/tcell" - -// Theme defines the colors used when primitives are initialized. -type Theme struct { - PrimitiveBackgroundColor tcell.Color // Main background color for primitives. - ContrastBackgroundColor tcell.Color // Background color for contrasting elements. - MoreContrastBackgroundColor tcell.Color // Background color for even more contrasting elements. - BorderColor tcell.Color // Box borders. - TitleColor tcell.Color // Box titles. - GraphicsColor tcell.Color // Graphics. - PrimaryTextColor tcell.Color // Primary text. - SecondaryTextColor tcell.Color // Secondary text (e.g. labels). - TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes). - InverseTextColor tcell.Color // Text on primary-colored backgrounds. - ContrastSecondaryTextColor tcell.Color // Secondary text on ContrastBackgroundColor-colored backgrounds. -} - -// Styles defines the theme for applications. The default is for a black -// background and some basic colors: black, white, yellow, green, cyan, and -// blue. -var Styles = Theme{ - PrimitiveBackgroundColor: tcell.ColorBlack, - ContrastBackgroundColor: tcell.ColorBlue, - MoreContrastBackgroundColor: tcell.ColorGreen, - BorderColor: tcell.ColorWhite, - TitleColor: tcell.ColorWhite, - GraphicsColor: tcell.ColorWhite, - PrimaryTextColor: tcell.ColorWhite, - SecondaryTextColor: tcell.ColorYellow, - TertiaryTextColor: tcell.ColorGreen, - InverseTextColor: tcell.ColorBlue, - ContrastSecondaryTextColor: tcell.ColorDarkCyan, -} diff --git a/vendor/github.com/rivo/tview/table.go b/vendor/github.com/rivo/tview/table.go deleted file mode 100644 index 992ccdaa..00000000 --- a/vendor/github.com/rivo/tview/table.go +++ /dev/null @@ -1,1266 +0,0 @@ -package tview - -import ( - "sort" - - "github.com/gdamore/tcell" - colorful "github.com/lucasb-eyer/go-colorful" -) - -// TableCell represents one cell inside a Table. You can instantiate this type -// directly but all colors (background and text) will be set to their default -// which is black. -type TableCell struct { - // The reference object. - Reference interface{} - - // The text to be displayed in the table cell. - Text string - - // The alignment of the cell text. One of AlignLeft (default), AlignCenter, - // or AlignRight. - Align int - - // The maximum width of the cell in screen space. This is used to give a - // column a maximum width. Any cell text whose screen width exceeds this width - // is cut off. Set to 0 if there is no maximum width. - MaxWidth int - - // If the total table width is less than the available width, this value is - // used to add extra width to a column. See SetExpansion() for details. - Expansion int - - // The color of the cell text. - Color tcell.Color - - // The background color of the cell. - BackgroundColor tcell.Color - - // The style attributes of the cell. - Attributes tcell.AttrMask - - // If set to true, this cell cannot be selected. - NotSelectable bool - - // The position and width of the cell the last time table was drawn. - x, y, width int -} - -// NewTableCell returns a new table cell with sensible defaults. That is, left -// aligned text with the primary text color (see Styles) and a transparent -// background (using the background of the Table). -func NewTableCell(text string) *TableCell { - return &TableCell{ - Text: text, - Align: AlignLeft, - Color: Styles.PrimaryTextColor, - BackgroundColor: tcell.ColorDefault, - } -} - -// SetText sets the cell's text. -func (c *TableCell) SetText(text string) *TableCell { - c.Text = text - return c -} - -// SetAlign sets the cell's text alignment, one of AlignLeft, AlignCenter, or -// AlignRight. -func (c *TableCell) SetAlign(align int) *TableCell { - c.Align = align - return c -} - -// SetMaxWidth sets maximum width of the cell in screen space. This is used to -// give a column a maximum width. Any cell text whose screen width exceeds this -// width is cut off. Set to 0 if there is no maximum width. -func (c *TableCell) SetMaxWidth(maxWidth int) *TableCell { - c.MaxWidth = maxWidth - return c -} - -// SetExpansion sets the value by which the column of this cell expands if the -// available width for the table is more than the table width (prior to applying -// this expansion value). This is a proportional value. The amount of unused -// horizontal space is divided into widths to be added to each column. How much -// extra width a column receives depends on the expansion value: A value of 0 -// (the default) will not cause the column to increase in width. Other values -// are proportional, e.g. a value of 2 will cause a column to grow by twice -// the amount of a column with a value of 1. -// -// Since this value affects an entire column, the maximum over all visible cells -// in that column is used. -// -// This function panics if a negative value is provided. -func (c *TableCell) SetExpansion(expansion int) *TableCell { - if expansion < 0 { - panic("Table cell expansion values may not be negative") - } - c.Expansion = expansion - return c -} - -// SetTextColor sets the cell's text color. -func (c *TableCell) SetTextColor(color tcell.Color) *TableCell { - c.Color = color - return c -} - -// SetBackgroundColor sets the cell's background color. Set to -// tcell.ColorDefault to use the table's background color. -func (c *TableCell) SetBackgroundColor(color tcell.Color) *TableCell { - c.BackgroundColor = color - return c -} - -// SetAttributes sets the cell's text attributes. You can combine different -// attributes using bitmask operations: -// -// cell.SetAttributes(tcell.AttrUnderline | tcell.AttrBold) -func (c *TableCell) SetAttributes(attr tcell.AttrMask) *TableCell { - c.Attributes = attr - return c -} - -// SetStyle sets the cell's style (foreground color, background color, and -// attributes) all at once. -func (c *TableCell) SetStyle(style tcell.Style) *TableCell { - c.Color, c.BackgroundColor, c.Attributes = style.Decompose() - return c -} - -// SetSelectable sets whether or not this cell can be selected by the user. -func (c *TableCell) SetSelectable(selectable bool) *TableCell { - c.NotSelectable = !selectable - return c -} - -// SetReference allows you to store a reference of any type in this cell. This -// will allow you to establish a mapping between the cell and your -// actual data. -func (c *TableCell) SetReference(reference interface{}) *TableCell { - c.Reference = reference - return c -} - -// GetReference returns this cell's reference object. -func (c *TableCell) GetReference() interface{} { - return c.Reference -} - -// GetLastPosition returns the position of the table cell the last time it was -// drawn on screen. If the cell is not on screen, the return values are -// undefined. -// -// Because the Table class will attempt to keep selected cells on screen, this -// function is most useful in response to a "selected" event (see -// SetSelectedFunc()) or a "selectionChanged" event (see -// SetSelectionChangedFunc()). -func (c *TableCell) GetLastPosition() (x, y, width int) { - return c.x, c.y, c.width -} - -// Table visualizes two-dimensional data consisting of rows and columns. Each -// Table cell is defined via SetCell() by the TableCell type. They can be added -// dynamically to the table and changed any time. -// -// The most compact display of a table is without borders. Each row will then -// occupy one row on screen and columns are separated by the rune defined via -// SetSeparator() (a space character by default). -// -// When borders are turned on (via SetBorders()), each table cell is surrounded -// by lines. Therefore one table row will require two rows on screen. -// -// Columns will use as much horizontal space as they need. You can constrain -// their size with the MaxWidth parameter of the TableCell type. -// -// Fixed Columns -// -// You can define fixed rows and rolumns via SetFixed(). They will always stay -// in their place, even when the table is scrolled. Fixed rows are always the -// top rows. Fixed columns are always the leftmost columns. -// -// Selections -// -// You can call SetSelectable() to set columns and/or rows to "selectable". If -// the flag is set only for columns, entire columns can be selected by the user. -// If it is set only for rows, entire rows can be selected. If both flags are -// set, individual cells can be selected. The "selected" handler set via -// SetSelectedFunc() is invoked when the user presses Enter on a selection. -// -// Navigation -// -// If the table extends beyond the available space, it can be navigated with -// key bindings similar to Vim: -// -// - h, left arrow: Move left by one column. -// - l, right arrow: Move right by one column. -// - j, down arrow: Move down by one row. -// - k, up arrow: Move up by one row. -// - g, home: Move to the top. -// - G, end: Move to the bottom. -// - Ctrl-F, page down: Move down by one page. -// - Ctrl-B, page up: Move up by one page. -// -// When there is no selection, this affects the entire table (except for fixed -// rows and columns). When there is a selection, the user moves the selection. -// The class will attempt to keep the selection from moving out of the screen. -// -// Use SetInputCapture() to override or modify keyboard input. -// -// See https://github.com/rivo/tview/wiki/Table for an example. -type Table struct { - *Box - - // Whether or not this table has borders around each cell. - borders bool - - // The color of the borders or the separator. - bordersColor tcell.Color - - // If there are no borders, the column separator. - separator rune - - // The cells of the table. Rows first, then columns. - cells [][]*TableCell - - // The rightmost column in the data set. - lastColumn int - - // If true, when calculating the widths of the columns, all rows are evaluated - // instead of only the visible ones. - evaluateAllRows bool - - // The number of fixed rows / columns. - fixedRows, fixedColumns int - - // Whether or not rows or columns can be selected. If both are set to true, - // cells can be selected. - rowsSelectable, columnsSelectable bool - - // The currently selected row and column. - selectedRow, selectedColumn int - - // The number of rows/columns by which the table is scrolled down/to the - // right. - rowOffset, columnOffset int - - // If set to true, the table's last row will always be visible. - trackEnd bool - - // The number of visible rows the last time the table was drawn. - visibleRows int - - // The indices of the visible columns as of the last time the table was drawn. - visibleColumnIndices []int - - // The net widths of the visible columns as of the last time the table was - // drawn. - visibleColumnWidths []int - - // The style of the selected rows. If this value is 0, selected rows are - // simply inverted. - selectedStyle tcell.Style - - // An optional function which gets called when the user presses Enter on a - // selected cell. If entire rows selected, the column value is undefined. - // Likewise for entire columns. - selected func(row, column int) - - // An optional function which gets called when the user changes the selection. - // If entire rows selected, the column value is undefined. - // Likewise for entire columns. - selectionChanged func(row, column int) - - // An optional function which gets called when the user presses Escape, Tab, - // or Backtab. Also when the user presses Enter if nothing is selectable. - done func(key tcell.Key) -} - -// NewTable returns a new table. -func NewTable() *Table { - return &Table{ - Box: NewBox(), - bordersColor: Styles.GraphicsColor, - separator: ' ', - lastColumn: -1, - } -} - -// Clear removes all table data. -func (t *Table) Clear() *Table { - t.cells = nil - t.lastColumn = -1 - return t -} - -// SetBorders sets whether or not each cell in the table is surrounded by a -// border. -func (t *Table) SetBorders(show bool) *Table { - t.borders = show - return t -} - -// SetBordersColor sets the color of the cell borders. -func (t *Table) SetBordersColor(color tcell.Color) *Table { - t.bordersColor = color - return t -} - -// SetSelectedStyle sets a specific style for selected cells. If no such style -// is set, per default, selected cells are inverted (i.e. their foreground and -// background colors are swapped). -// -// To reset a previous setting to its default, make the following call: -// -// table.SetSelectedStyle(tcell.ColorDefault, tcell.ColorDefault, 0) -func (t *Table) SetSelectedStyle(foregroundColor, backgroundColor tcell.Color, attributes tcell.AttrMask) *Table { - t.selectedStyle = tcell.StyleDefault.Foreground(foregroundColor).Background(backgroundColor) | tcell.Style(attributes) - return t -} - -// SetSeparator sets the character used to fill the space between two -// neighboring cells. This is a space character ' ' per default but you may -// want to set it to Borders.Vertical (or any other rune) if the column -// separation should be more visible. If cell borders are activated, this is -// ignored. -// -// Separators have the same color as borders. -func (t *Table) SetSeparator(separator rune) *Table { - t.separator = separator - return t -} - -// SetFixed sets the number of fixed rows and columns which are always visible -// even when the rest of the cells are scrolled out of view. Rows are always the -// top-most ones. Columns are always the left-most ones. -func (t *Table) SetFixed(rows, columns int) *Table { - t.fixedRows, t.fixedColumns = rows, columns - return t -} - -// SetSelectable sets the flags which determine what can be selected in a table. -// There are three selection modi: -// -// - rows = false, columns = false: Nothing can be selected. -// - rows = true, columns = false: Rows can be selected. -// - rows = false, columns = true: Columns can be selected. -// - rows = true, columns = true: Individual cells can be selected. -func (t *Table) SetSelectable(rows, columns bool) *Table { - t.rowsSelectable, t.columnsSelectable = rows, columns - return t -} - -// GetSelectable returns what can be selected in a table. Refer to -// SetSelectable() for details. -func (t *Table) GetSelectable() (rows, columns bool) { - return t.rowsSelectable, t.columnsSelectable -} - -// GetSelection returns the position of the current selection. -// If entire rows are selected, the column index is undefined. -// Likewise for entire columns. -func (t *Table) GetSelection() (row, column int) { - return t.selectedRow, t.selectedColumn -} - -// Select sets the selected cell. Depending on the selection settings -// specified via SetSelectable(), this may be an entire row or column, or even -// ignored completely. The "selection changed" event is fired if such a callback -// is available (even if the selection ends up being the same as before and even -// if cells are not selectable). -func (t *Table) Select(row, column int) *Table { - t.selectedRow, t.selectedColumn = row, column - if t.selectionChanged != nil { - t.selectionChanged(row, column) - } - return t -} - -// SetOffset sets how many rows and columns should be skipped when drawing the -// table. This is useful for large tables that do not fit on the screen. -// Navigating a selection can change these values. -// -// Fixed rows and columns are never skipped. -func (t *Table) SetOffset(row, column int) *Table { - t.rowOffset, t.columnOffset = row, column - t.trackEnd = false - return t -} - -// GetOffset returns the current row and column offset. This indicates how many -// rows and columns the table is scrolled down and to the right. -func (t *Table) GetOffset() (row, column int) { - return t.rowOffset, t.columnOffset -} - -// SetEvaluateAllRows sets a flag which determines the rows to be evaluated when -// calculating the widths of the table's columns. When false, only visible rows -// are evaluated. When true, all rows in the table are evaluated. -// -// Set this flag to true to avoid shifting column widths when the table is -// scrolled. (May be slower for large tables.) -func (t *Table) SetEvaluateAllRows(all bool) *Table { - t.evaluateAllRows = all - return t -} - -// SetSelectedFunc sets a handler which is called whenever the user presses the -// Enter key on a selected cell/row/column. The handler receives the position of -// the selection and its cell contents. If entire rows are selected, the column -// index is undefined. Likewise for entire columns. -func (t *Table) SetSelectedFunc(handler func(row, column int)) *Table { - t.selected = handler - return t -} - -// SetSelectionChangedFunc sets a handler which is called whenever the current -// selection changes. The handler receives the position of the new selection. -// If entire rows are selected, the column index is undefined. Likewise for -// entire columns. -func (t *Table) SetSelectionChangedFunc(handler func(row, column int)) *Table { - t.selectionChanged = handler - return t -} - -// SetDoneFunc sets a handler which is called whenever the user presses the -// Escape, Tab, or Backtab key. If nothing is selected, it is also called when -// user presses the Enter key (because pressing Enter on a selection triggers -// the "selected" handler set via SetSelectedFunc()). -func (t *Table) SetDoneFunc(handler func(key tcell.Key)) *Table { - t.done = handler - return t -} - -// SetCell sets the content of a cell the specified position. It is ok to -// directly instantiate a TableCell object. If the cell has content, at least -// the Text and Color fields should be set. -// -// Note that setting cells in previously unknown rows and columns will -// automatically extend the internal table representation, e.g. starting with -// a row of 100,000 will immediately create 100,000 empty rows. -// -// To avoid unnecessary garbage collection, fill columns from left to right. -func (t *Table) SetCell(row, column int, cell *TableCell) *Table { - if row >= len(t.cells) { - t.cells = append(t.cells, make([][]*TableCell, row-len(t.cells)+1)...) - } - rowLen := len(t.cells[row]) - if column >= rowLen { - t.cells[row] = append(t.cells[row], make([]*TableCell, column-rowLen+1)...) - for c := rowLen; c < column; c++ { - t.cells[row][c] = &TableCell{} - } - } - t.cells[row][column] = cell - if column > t.lastColumn { - t.lastColumn = column - } - return t -} - -// SetCellSimple calls SetCell() with the given text, left-aligned, in white. -func (t *Table) SetCellSimple(row, column int, text string) *Table { - t.SetCell(row, column, NewTableCell(text)) - return t -} - -// GetCell returns the contents of the cell at the specified position. A valid -// TableCell object is always returned but it will be uninitialized if the cell -// was not previously set. Such an uninitialized object will not automatically -// be inserted. Therefore, repeated calls to this function may return different -// pointers for uninitialized cells. -func (t *Table) GetCell(row, column int) *TableCell { - if row >= len(t.cells) || column >= len(t.cells[row]) { - return &TableCell{} - } - return t.cells[row][column] -} - -// RemoveRow removes the row at the given position from the table. If there is -// no such row, this has no effect. -func (t *Table) RemoveRow(row int) *Table { - if row < 0 || row >= len(t.cells) { - return t - } - - t.cells = append(t.cells[:row], t.cells[row+1:]...) - - return t -} - -// RemoveColumn removes the column at the given position from the table. If -// there is no such column, this has no effect. -func (t *Table) RemoveColumn(column int) *Table { - for row := range t.cells { - if column < 0 || column >= len(t.cells[row]) { - continue - } - t.cells[row] = append(t.cells[row][:column], t.cells[row][column+1:]...) - } - - return t -} - -// InsertRow inserts a row before the row with the given index. Cells on the -// given row and below will be shifted to the bottom by one row. If "row" is -// equal or larger than the current number of rows, this function has no effect. -func (t *Table) InsertRow(row int) *Table { - if row >= len(t.cells) { - return t - } - t.cells = append(t.cells, nil) // Extend by one. - copy(t.cells[row+1:], t.cells[row:]) // Shift down. - t.cells[row] = nil // New row is uninitialized. - return t -} - -// InsertColumn inserts a column before the column with the given index. Cells -// in the given column and to its right will be shifted to the right by one -// column. Rows that have fewer initialized cells than "column" will remain -// unchanged. -func (t *Table) InsertColumn(column int) *Table { - for row := range t.cells { - if column >= len(t.cells[row]) { - continue - } - t.cells[row] = append(t.cells[row], nil) // Extend by one. - copy(t.cells[row][column+1:], t.cells[row][column:]) // Shift to the right. - t.cells[row][column] = &TableCell{} // New element is an uninitialized table cell. - } - return t -} - -// GetRowCount returns the number of rows in the table. -func (t *Table) GetRowCount() int { - return len(t.cells) -} - -// GetColumnCount returns the (maximum) number of columns in the table. -func (t *Table) GetColumnCount() int { - if len(t.cells) == 0 { - return 0 - } - return t.lastColumn + 1 -} - -// cellAt returns the row and column located at the given screen coordinates. -// Each returned value may be negative if there is no row and/or cell. This -// function will also process coordinates outside the table's inner rectangle so -// callers will need to check for bounds themselves. -func (t *Table) cellAt(x, y int) (row, column int) { - rectX, rectY, _, _ := t.GetInnerRect() - - // Determine row as seen on screen. - if t.borders { - row = (y - rectY - 1) / 2 - } else { - row = y - rectY - } - - // Respect fixed rows and row offset. - if row >= 0 { - if row >= t.fixedRows { - row += t.rowOffset - } - if row >= len(t.cells) { - row = -1 - } - } - - // Saerch for the clicked column. - column = -1 - if x >= rectX { - columnX := rectX - if t.borders { - columnX++ - } - for index, width := range t.visibleColumnWidths { - columnX += width + 1 - if x < columnX { - column = t.visibleColumnIndices[index] - break - } - } - } - - return -} - -// ScrollToBeginning scrolls the table to the beginning to that the top left -// corner of the table is shown. Note that this position may be corrected if -// there is a selection. -func (t *Table) ScrollToBeginning() *Table { - t.trackEnd = false - t.columnOffset = 0 - t.rowOffset = 0 - return t -} - -// ScrollToEnd scrolls the table to the beginning to that the bottom left corner -// of the table is shown. Adding more rows to the table will cause it to -// automatically scroll with the new data. Note that this position may be -// corrected if there is a selection. -func (t *Table) ScrollToEnd() *Table { - t.trackEnd = true - t.columnOffset = 0 - t.rowOffset = len(t.cells) - return t -} - -// Draw draws this primitive onto the screen. -func (t *Table) Draw(screen tcell.Screen) { - t.Box.Draw(screen) - - // What's our available screen space? - _, totalHeight := screen.Size() - x, y, width, height := t.GetInnerRect() - if t.borders { - t.visibleRows = height / 2 - } else { - t.visibleRows = height - } - - // Return the cell at the specified position (nil if it doesn't exist). - getCell := func(row, column int) *TableCell { - if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) { - return nil - } - return t.cells[row][column] - } - - // If this cell is not selectable, find the next one. - if t.rowsSelectable || t.columnsSelectable { - if t.selectedColumn < 0 { - t.selectedColumn = 0 - } - if t.selectedRow < 0 { - t.selectedRow = 0 - } - for t.selectedRow < len(t.cells) { - cell := getCell(t.selectedRow, t.selectedColumn) - if cell == nil || !cell.NotSelectable { - break - } - t.selectedColumn++ - if t.selectedColumn > t.lastColumn { - t.selectedColumn = 0 - t.selectedRow++ - } - } - } - - // Clamp row offsets. - if t.rowsSelectable { - if t.selectedRow >= t.fixedRows && t.selectedRow < t.fixedRows+t.rowOffset { - t.rowOffset = t.selectedRow - t.fixedRows - t.trackEnd = false - } - if t.borders { - if 2*(t.selectedRow+1-t.rowOffset) >= height { - t.rowOffset = t.selectedRow + 1 - height/2 - t.trackEnd = false - } - } else { - if t.selectedRow+1-t.rowOffset >= height { - t.rowOffset = t.selectedRow + 1 - height - t.trackEnd = false - } - } - } - if t.borders { - if 2*(len(t.cells)-t.rowOffset) < height { - t.trackEnd = true - } - } else { - if len(t.cells)-t.rowOffset < height { - t.trackEnd = true - } - } - if t.trackEnd { - if t.borders { - t.rowOffset = len(t.cells) - height/2 - } else { - t.rowOffset = len(t.cells) - height - } - } - if t.rowOffset < 0 { - t.rowOffset = 0 - } - - // Clamp column offset. (Only left side here. The right side is more - // difficult and we'll do it below.) - if t.columnsSelectable && t.selectedColumn >= t.fixedColumns && t.selectedColumn < t.fixedColumns+t.columnOffset { - t.columnOffset = t.selectedColumn - t.fixedColumns - } - if t.columnOffset < 0 { - t.columnOffset = 0 - } - if t.selectedColumn < 0 { - t.selectedColumn = 0 - } - - // Determine the indices and widths of the columns and rows which fit on the - // screen. - var ( - columns, rows, allRows, widths []int - tableHeight, tableWidth int - ) - rowStep := 1 - if t.borders { - rowStep = 2 // With borders, every table row takes two screen rows. - tableWidth = 1 // We start at the second character because of the left table border. - } - if t.evaluateAllRows { - allRows = make([]int, len(t.cells)) - for row := range t.cells { - allRows[row] = row - } - } - indexRow := func(row int) bool { // Determine if this row is visible, store its index. - if tableHeight >= height { - return false - } - rows = append(rows, row) - tableHeight += rowStep - return true - } - for row := 0; row < t.fixedRows && row < len(t.cells); row++ { // Do the fixed rows first. - if !indexRow(row) { - break - } - } - for row := t.fixedRows + t.rowOffset; row < len(t.cells); row++ { // Then the remaining rows. - if !indexRow(row) { - break - } - } - var ( - skipped, lastTableWidth, expansionTotal int - expansions []int - ) -ColumnLoop: - for column := 0; ; column++ { - // If we've moved beyond the right border, we stop or skip a column. - for tableWidth-1 >= width { // -1 because we include one extra column if the separator falls on the right end of the box. - // We've moved beyond the available space. - if column < t.fixedColumns { - break ColumnLoop // We're in the fixed area. We're done. - } - if !t.columnsSelectable && skipped >= t.columnOffset { - break ColumnLoop // There is no selection and we've already reached the offset. - } - if t.columnsSelectable && t.selectedColumn-skipped == t.fixedColumns { - break ColumnLoop // The selected column reached the leftmost point before disappearing. - } - if t.columnsSelectable && skipped >= t.columnOffset && - (t.selectedColumn < column && lastTableWidth < width-1 && tableWidth < width-1 || t.selectedColumn < column-1) { - break ColumnLoop // We've skipped as many as requested and the selection is visible. - } - if len(columns) <= t.fixedColumns { - break // Nothing to skip. - } - - // We need to skip a column. - skipped++ - lastTableWidth -= widths[t.fixedColumns] + 1 - tableWidth -= widths[t.fixedColumns] + 1 - columns = append(columns[:t.fixedColumns], columns[t.fixedColumns+1:]...) - widths = append(widths[:t.fixedColumns], widths[t.fixedColumns+1:]...) - expansions = append(expansions[:t.fixedColumns], expansions[t.fixedColumns+1:]...) - } - - // What's this column's width (without expansion)? - maxWidth := -1 - expansion := 0 - evaluationRows := rows - if t.evaluateAllRows { - evaluationRows = allRows - } - for _, row := range evaluationRows { - if cell := getCell(row, column); cell != nil { - _, _, _, _, _, _, cellWidth := decomposeString(cell.Text, true, false) - if cell.MaxWidth > 0 && cell.MaxWidth < cellWidth { - cellWidth = cell.MaxWidth - } - if cellWidth > maxWidth { - maxWidth = cellWidth - } - if cell.Expansion > expansion { - expansion = cell.Expansion - } - } - } - if maxWidth < 0 { - break // No more cells found in this column. - } - - // Store new column info at the end. - columns = append(columns, column) - widths = append(widths, maxWidth) - lastTableWidth = tableWidth - tableWidth += maxWidth + 1 - expansions = append(expansions, expansion) - expansionTotal += expansion - } - t.columnOffset = skipped - - // If we have space left, distribute it. - if tableWidth < width { - toDistribute := width - tableWidth - for index, expansion := range expansions { - if expansionTotal <= 0 { - break - } - expWidth := toDistribute * expansion / expansionTotal - widths[index] += expWidth - toDistribute -= expWidth - expansionTotal -= expansion - } - } - - // Helper function which draws border runes. - borderStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.bordersColor) - drawBorder := func(colX, rowY int, ch rune) { - screen.SetContent(x+colX, y+rowY, ch, nil, borderStyle) - } - - // Draw the cells (and borders). - var columnX int - if !t.borders { - columnX-- - } - for columnIndex, column := range columns { - columnWidth := widths[columnIndex] - for rowY, row := range rows { - if t.borders { - // Draw borders. - rowY *= 2 - for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ { - drawBorder(columnX+pos+1, rowY, Borders.Horizontal) - } - ch := Borders.Cross - if columnIndex == 0 { - if rowY == 0 { - ch = Borders.TopLeft - } else { - ch = Borders.LeftT - } - } else if rowY == 0 { - ch = Borders.TopT - } - drawBorder(columnX, rowY, ch) - rowY++ - if rowY >= height || y+rowY >= totalHeight { - break // No space for the text anymore. - } - drawBorder(columnX, rowY, Borders.Vertical) - } else if columnIndex > 0 { - // Draw separator. - drawBorder(columnX, rowY, t.separator) - } - - // Get the cell. - cell := getCell(row, column) - if cell == nil { - continue - } - - // Draw text. - finalWidth := columnWidth - if columnX+1+columnWidth >= width { - finalWidth = width - columnX - 1 - } - cell.x, cell.y, cell.width = x+columnX+1, y+rowY, finalWidth - _, printed := printWithStyle(screen, cell.Text, x+columnX+1, y+rowY, finalWidth, cell.Align, tcell.StyleDefault.Foreground(cell.Color)|tcell.Style(cell.Attributes)) - if TaggedStringWidth(cell.Text)-printed > 0 && printed > 0 { - _, _, style, _ := screen.GetContent(x+columnX+finalWidth, y+rowY) - printWithStyle(screen, string(SemigraphicsHorizontalEllipsis), x+columnX+finalWidth, y+rowY, 1, AlignLeft, style) - } - } - - // Draw bottom border. - if rowY := 2 * len(rows); t.borders && rowY < height { - for pos := 0; pos < columnWidth && columnX+1+pos < width; pos++ { - drawBorder(columnX+pos+1, rowY, Borders.Horizontal) - } - ch := Borders.BottomT - if columnIndex == 0 { - ch = Borders.BottomLeft - } - drawBorder(columnX, rowY, ch) - } - - columnX += columnWidth + 1 - } - - // Draw right border. - if t.borders && len(t.cells) > 0 && columnX < width { - for rowY := range rows { - rowY *= 2 - if rowY+1 < height { - drawBorder(columnX, rowY+1, Borders.Vertical) - } - ch := Borders.RightT - if rowY == 0 { - ch = Borders.TopRight - } - drawBorder(columnX, rowY, ch) - } - if rowY := 2 * len(rows); rowY < height { - drawBorder(columnX, rowY, Borders.BottomRight) - } - } - - // Helper function which colors the background of a box. - // backgroundColor == tcell.ColorDefault => Don't color the background. - // textColor == tcell.ColorDefault => Don't change the text color. - // attr == 0 => Don't change attributes. - // invert == true => Ignore attr, set text to backgroundColor or t.backgroundColor; - // set background to textColor. - colorBackground := func(fromX, fromY, w, h int, backgroundColor, textColor tcell.Color, attr tcell.AttrMask, invert bool) { - for by := 0; by < h && fromY+by < y+height; by++ { - for bx := 0; bx < w && fromX+bx < x+width; bx++ { - m, c, style, _ := screen.GetContent(fromX+bx, fromY+by) - fg, bg, a := style.Decompose() - if invert { - if fg == textColor || fg == t.bordersColor { - fg = backgroundColor - } - if fg == tcell.ColorDefault { - fg = t.backgroundColor - } - style = style.Background(textColor).Foreground(fg) - } else { - if backgroundColor != tcell.ColorDefault { - bg = backgroundColor - } - if textColor != tcell.ColorDefault { - fg = textColor - } - if attr != 0 { - a = attr - } - style = style.Background(bg).Foreground(fg) | tcell.Style(a) - } - screen.SetContent(fromX+bx, fromY+by, m, c, style) - } - } - } - - // Color the cell backgrounds. To avoid undesirable artefacts, we combine - // the drawing of a cell by background color, selected cells last. - type cellInfo struct { - x, y, w, h int - text tcell.Color - selected bool - } - cellsByBackgroundColor := make(map[tcell.Color][]*cellInfo) - var backgroundColors []tcell.Color - for rowY, row := range rows { - columnX := 0 - rowSelected := t.rowsSelectable && !t.columnsSelectable && row == t.selectedRow - for columnIndex, column := range columns { - columnWidth := widths[columnIndex] - cell := getCell(row, column) - if cell == nil { - continue - } - bx, by, bw, bh := x+columnX, y+rowY, columnWidth+1, 1 - if t.borders { - by = y + rowY*2 - bw++ - bh = 3 - } - columnSelected := t.columnsSelectable && !t.rowsSelectable && column == t.selectedColumn - cellSelected := !cell.NotSelectable && (columnSelected || rowSelected || t.rowsSelectable && t.columnsSelectable && column == t.selectedColumn && row == t.selectedRow) - entries, ok := cellsByBackgroundColor[cell.BackgroundColor] - cellsByBackgroundColor[cell.BackgroundColor] = append(entries, &cellInfo{ - x: bx, - y: by, - w: bw, - h: bh, - text: cell.Color, - selected: cellSelected, - }) - if !ok { - backgroundColors = append(backgroundColors, cell.BackgroundColor) - } - columnX += columnWidth + 1 - } - } - sort.Slice(backgroundColors, func(i int, j int) bool { - // Draw brightest colors last (i.e. on top). - r, g, b := backgroundColors[i].RGB() - c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} - _, _, li := c.Hcl() - r, g, b = backgroundColors[j].RGB() - c = colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} - _, _, lj := c.Hcl() - return li < lj - }) - selFg, selBg, selAttr := t.selectedStyle.Decompose() - for _, bgColor := range backgroundColors { - entries := cellsByBackgroundColor[bgColor] - for _, cell := range entries { - if cell.selected { - if t.selectedStyle != 0 { - defer colorBackground(cell.x, cell.y, cell.w, cell.h, selBg, selFg, selAttr, false) - } else { - defer colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, cell.text, 0, true) - } - } else { - colorBackground(cell.x, cell.y, cell.w, cell.h, bgColor, tcell.ColorDefault, 0, false) - } - } - } - - // Remember column infos. - t.visibleColumnIndices, t.visibleColumnWidths = columns, widths -} - -// InputHandler returns the handler for this primitive. -func (t *Table) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - key := event.Key() - - if (!t.rowsSelectable && !t.columnsSelectable && key == tcell.KeyEnter) || - key == tcell.KeyEscape || - key == tcell.KeyTab || - key == tcell.KeyBacktab { - if t.done != nil { - t.done(key) - } - return - } - - // Movement functions. - previouslySelectedRow, previouslySelectedColumn := t.selectedRow, t.selectedColumn - var ( - getCell = func(row, column int) *TableCell { - if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) { - return nil - } - return t.cells[row][column] - } - - previous = func() { - for t.selectedRow >= 0 { - cell := getCell(t.selectedRow, t.selectedColumn) - if cell == nil || !cell.NotSelectable { - return - } - t.selectedColumn-- - if t.selectedColumn < 0 { - t.selectedColumn = t.lastColumn - t.selectedRow-- - } - } - } - - next = func() { - if t.selectedColumn > t.lastColumn { - t.selectedColumn = 0 - t.selectedRow++ - if t.selectedRow >= len(t.cells) { - t.selectedRow = len(t.cells) - 1 - } - } - for t.selectedRow < len(t.cells) { - cell := getCell(t.selectedRow, t.selectedColumn) - if cell == nil || !cell.NotSelectable { - return - } - t.selectedColumn++ - if t.selectedColumn > t.lastColumn { - t.selectedColumn = 0 - t.selectedRow++ - } - } - t.selectedColumn = t.lastColumn - t.selectedRow = len(t.cells) - 1 - previous() - } - - home = func() { - if t.rowsSelectable { - t.selectedRow = 0 - t.selectedColumn = 0 - next() - } else { - t.trackEnd = false - t.rowOffset = 0 - t.columnOffset = 0 - } - } - - end = func() { - if t.rowsSelectable { - t.selectedRow = len(t.cells) - 1 - t.selectedColumn = t.lastColumn - previous() - } else { - t.trackEnd = true - t.columnOffset = 0 - } - } - - down = func() { - if t.rowsSelectable { - t.selectedRow++ - if t.selectedRow >= len(t.cells) { - t.selectedRow = len(t.cells) - 1 - } - next() - } else { - t.rowOffset++ - } - } - - up = func() { - if t.rowsSelectable { - t.selectedRow-- - if t.selectedRow < 0 { - t.selectedRow = 0 - } - previous() - } else { - t.trackEnd = false - t.rowOffset-- - } - } - - left = func() { - if t.columnsSelectable { - t.selectedColumn-- - if t.selectedColumn < 0 { - t.selectedColumn = 0 - } - previous() - } else { - t.columnOffset-- - } - } - - right = func() { - if t.columnsSelectable { - t.selectedColumn++ - if t.selectedColumn > t.lastColumn { - t.selectedColumn = t.lastColumn - } - next() - } else { - t.columnOffset++ - } - } - - pageDown = func() { - offsetAmount := t.visibleRows - t.fixedRows - if offsetAmount < 0 { - offsetAmount = 0 - } - - if t.rowsSelectable { - t.selectedRow += offsetAmount - if t.selectedRow >= len(t.cells) { - t.selectedRow = len(t.cells) - 1 - } - next() - } else { - t.rowOffset += offsetAmount - } - } - - pageUp = func() { - offsetAmount := t.visibleRows - t.fixedRows - if offsetAmount < 0 { - offsetAmount = 0 - } - - if t.rowsSelectable { - t.selectedRow -= offsetAmount - if t.selectedRow < 0 { - t.selectedRow = 0 - } - previous() - } else { - t.trackEnd = false - t.rowOffset -= offsetAmount - } - } - ) - - switch key { - case tcell.KeyRune: - switch event.Rune() { - case 'g': - home() - case 'G': - end() - case 'j': - down() - case 'k': - up() - case 'h': - left() - case 'l': - right() - } - case tcell.KeyHome: - home() - case tcell.KeyEnd: - end() - case tcell.KeyUp: - up() - case tcell.KeyDown: - down() - case tcell.KeyLeft: - left() - case tcell.KeyRight: - right() - case tcell.KeyPgDn, tcell.KeyCtrlF: - pageDown() - case tcell.KeyPgUp, tcell.KeyCtrlB: - pageUp() - case tcell.KeyEnter: - if (t.rowsSelectable || t.columnsSelectable) && t.selected != nil { - t.selected(t.selectedRow, t.selectedColumn) - } - } - - // If the selection has changed, notify the handler. - if t.selectionChanged != nil && - (t.rowsSelectable && previouslySelectedRow != t.selectedRow || - t.columnsSelectable && previouslySelectedColumn != t.selectedColumn) { - t.selectionChanged(t.selectedRow, t.selectedColumn) - } - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (t *Table) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - x, y := event.Position() - if !t.InRect(x, y) { - return false, nil - } - - switch action { - case MouseLeftClick: - if t.rowsSelectable || t.columnsSelectable { - t.Select(t.cellAt(x, y)) - } - consumed = true - setFocus(t) - case MouseScrollUp: - t.trackEnd = false - t.rowOffset-- - consumed = true - case MouseScrollDown: - t.rowOffset++ - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/textview.go b/vendor/github.com/rivo/tview/textview.go deleted file mode 100644 index ee823416..00000000 --- a/vendor/github.com/rivo/tview/textview.go +++ /dev/null @@ -1,1189 +0,0 @@ -package tview - -import ( - "bytes" - "fmt" - "regexp" - "strings" - "sync" - "unicode/utf8" - - "github.com/gdamore/tcell" - colorful "github.com/lucasb-eyer/go-colorful" - runewidth "github.com/mattn/go-runewidth" - "github.com/rivo/uniseg" -) - -var ( - openColorRegex = regexp.MustCompile(`\[([a-zA-Z]*|#[0-9a-zA-Z]*)$`) - openRegionRegex = regexp.MustCompile(`\["[a-zA-Z0-9_,;: \-\.]*"?$`) - newLineRegex = regexp.MustCompile(`\r?\n`) - - // TabSize is the number of spaces with which a tab character will be replaced. - TabSize = 4 -) - -// textViewIndex contains information about each line displayed in the text -// view. -type textViewIndex struct { - Line int // The index into the "buffer" variable. - Pos int // The index into the "buffer" string (byte position). - NextPos int // The (byte) index of the next character in this buffer line. - Width int // The screen width of this line. - ForegroundColor string // The starting foreground color ("" = don't change, "-" = reset). - BackgroundColor string // The starting background color ("" = don't change, "-" = reset). - Attributes string // The starting attributes ("" = don't change, "-" = reset). - Region string // The starting region ID. -} - -// textViewRegion contains information about a region. -type textViewRegion struct { - // The region ID. - ID string - - // The starting and end screen position of the region as determined the last - // time Draw() was called. A negative value indicates out-of-rect positions. - FromX, FromY, ToX, ToY int -} - -// TextView is a box which displays text. It implements the io.Writer interface -// so you can stream text to it. This does not trigger a redraw automatically -// but if a handler is installed via SetChangedFunc(), you can cause it to be -// redrawn. (See SetChangedFunc() for more details.) -// -// Navigation -// -// If the text view is scrollable (the default), text is kept in a buffer which -// may be larger than the screen and can be navigated similarly to Vim: -// -// - h, left arrow: Move left. -// - l, right arrow: Move right. -// - j, down arrow: Move down. -// - k, up arrow: Move up. -// - g, home: Move to the top. -// - G, end: Move to the bottom. -// - Ctrl-F, page down: Move down by one page. -// - Ctrl-B, page up: Move up by one page. -// -// If the text is not scrollable, any text above the top visible line is -// discarded. -// -// Use SetInputCapture() to override or modify keyboard input. -// -// Colors -// -// If dynamic colors are enabled via SetDynamicColors(), text color can be -// changed dynamically by embedding color strings in square brackets. This works -// the same way as anywhere else. Please see the package documentation for more -// information. -// -// Regions and Highlights -// -// If regions are enabled via SetRegions(), you can define text regions within -// the text and assign region IDs to them. Text regions start with region tags. -// Region tags are square brackets that contain a region ID in double quotes, -// for example: -// -// We define a ["rg"]region[""] here. -// -// A text region ends with the next region tag. Tags with no region ID ([""]) -// don't start new regions. They can therefore be used to mark the end of a -// region. Region IDs must satisfy the following regular expression: -// -// [a-zA-Z0-9_,;: \-\.]+ -// -// Regions can be highlighted by calling the Highlight() function with one or -// more region IDs. This can be used to display search results, for example. -// -// The ScrollToHighlight() function can be used to jump to the currently -// highlighted region once when the text view is drawn the next time. -// -// See https://github.com/rivo/tview/wiki/TextView for an example. -type TextView struct { - sync.Mutex - *Box - - // The text buffer. - buffer []string - - // The last bytes that have been received but are not part of the buffer yet. - recentBytes []byte - - // The processed line index. This is nil if the buffer has changed and needs - // to be re-indexed. - index []*textViewIndex - - // The text alignment, one of AlignLeft, AlignCenter, or AlignRight. - align int - - // Information about visible regions as of the last call to Draw(). - regionInfos []*textViewRegion - - // Indices into the "index" slice which correspond to the first line of the - // first highlight and the last line of the last highlight. This is calculated - // during re-indexing. Set to -1 if there is no current highlight. - fromHighlight, toHighlight int - - // The screen space column of the highlight in its first line. Set to -1 if - // there is no current highlight. - posHighlight int - - // A set of region IDs that are currently highlighted. - highlights map[string]struct{} - - // The last width for which the current table is drawn. - lastWidth int - - // The screen width of the longest line in the index (not the buffer). - longestLine int - - // The index of the first line shown in the text view. - lineOffset int - - // If set to true, the text view will always remain at the end of the content. - trackEnd bool - - // The number of characters to be skipped on each line (not in wrap mode). - columnOffset int - - // The height of the content the last time the text view was drawn. - pageSize int - - // If set to true, the text view will keep a buffer of text which can be - // navigated when the text is longer than what fits into the box. - scrollable bool - - // If set to true, lines that are longer than the available width are wrapped - // onto the next line. If set to false, any characters beyond the available - // width are discarded. - wrap bool - - // If set to true and if wrap is also true, lines are split at spaces or - // after punctuation characters. - wordWrap bool - - // The (starting) color of the text. - textColor tcell.Color - - // If set to true, the text color can be changed dynamically by piping color - // strings in square brackets to the text view. - dynamicColors bool - - // If set to true, region tags can be used to define regions. - regions bool - - // A temporary flag which, when true, will automatically bring the current - // highlight(s) into the visible screen. - scrollToHighlights bool - - // If true, setting new highlights will be a XOR instead of an overwrite - // operation. - toggleHighlights bool - - // An optional function which is called when the content of the text view has - // changed. - changed func() - - // An optional function which is called when the user presses one of the - // following keys: Escape, Enter, Tab, Backtab. - done func(tcell.Key) - - // An optional function which is called when one or more regions were - // highlighted. - highlighted func(added, removed, remaining []string) -} - -// NewTextView returns a new text view. -func NewTextView() *TextView { - return &TextView{ - Box: NewBox(), - highlights: make(map[string]struct{}), - lineOffset: -1, - scrollable: true, - align: AlignLeft, - wrap: true, - textColor: Styles.PrimaryTextColor, - regions: false, - dynamicColors: false, - } -} - -// SetScrollable sets the flag that decides whether or not the text view is -// scrollable. If true, text is kept in a buffer and can be navigated. If false, -// the last line will always be visible. -func (t *TextView) SetScrollable(scrollable bool) *TextView { - t.scrollable = scrollable - if !scrollable { - t.trackEnd = true - } - return t -} - -// SetWrap sets the flag that, if true, leads to lines that are longer than the -// available width being wrapped onto the next line. If false, any characters -// beyond the available width are not displayed. -func (t *TextView) SetWrap(wrap bool) *TextView { - if t.wrap != wrap { - t.index = nil - } - t.wrap = wrap - return t -} - -// SetWordWrap sets the flag that, if true and if the "wrap" flag is also true -// (see SetWrap()), wraps the line at spaces or after punctuation marks. Note -// that trailing spaces will not be printed. -// -// This flag is ignored if the "wrap" flag is false. -func (t *TextView) SetWordWrap(wrapOnWords bool) *TextView { - if t.wordWrap != wrapOnWords { - t.index = nil - } - t.wordWrap = wrapOnWords - return t -} - -// SetTextAlign sets the text alignment within the text view. This must be -// either AlignLeft, AlignCenter, or AlignRight. -func (t *TextView) SetTextAlign(align int) *TextView { - if t.align != align { - t.index = nil - } - t.align = align - return t -} - -// SetTextColor sets the initial color of the text (which can be changed -// dynamically by sending color strings in square brackets to the text view if -// dynamic colors are enabled). -func (t *TextView) SetTextColor(color tcell.Color) *TextView { - t.textColor = color - return t -} - -// SetText sets the text of this text view to the provided string. Previously -// contained text will be removed. -func (t *TextView) SetText(text string) *TextView { - t.Clear() - fmt.Fprint(t, text) - return t -} - -// GetText returns the current text of this text view. If "stripTags" is set -// to true, any region/color tags are stripped from the text. -func (t *TextView) GetText(stripTags bool) string { - // Get the buffer. - buffer := t.buffer - if !stripTags { - buffer = append(buffer, string(t.recentBytes)) - } - - // Add newlines again. - text := strings.Join(buffer, "\n") - - // Strip from tags if required. - if stripTags { - if t.regions { - text = regionPattern.ReplaceAllString(text, "") - } - if t.dynamicColors { - text = colorPattern.ReplaceAllStringFunc(text, func(match string) string { - if len(match) > 2 { - return "" - } - return match - }) - } - if t.regions || t.dynamicColors { - text = escapePattern.ReplaceAllString(text, `[$1$2]`) - } - } - - return text -} - -// SetDynamicColors sets the flag that allows the text color to be changed -// dynamically. See class description for details. -func (t *TextView) SetDynamicColors(dynamic bool) *TextView { - if t.dynamicColors != dynamic { - t.index = nil - } - t.dynamicColors = dynamic - return t -} - -// SetRegions sets the flag that allows to define regions in the text. See class -// description for details. -func (t *TextView) SetRegions(regions bool) *TextView { - if t.regions != regions { - t.index = nil - } - t.regions = regions - return t -} - -// SetChangedFunc sets a handler function which is called when the text of the -// text view has changed. This is useful when text is written to this io.Writer -// in a separate goroutine. Doing so does not automatically cause the screen to -// be refreshed so you may want to use the "changed" handler to redraw the -// screen. -// -// Note that to avoid race conditions or deadlocks, there are a few rules you -// should follow: -// -// - You can call Application.Draw() from this handler. -// - You can call TextView.HasFocus() from this handler. -// - During the execution of this handler, access to any other variables from -// this primitive or any other primitive must be queued using -// Application.QueueUpdate(). -// -// See package description for details on dealing with concurrency. -func (t *TextView) SetChangedFunc(handler func()) *TextView { - t.changed = handler - return t -} - -// SetDoneFunc sets a handler which is called when the user presses on the -// following keys: Escape, Enter, Tab, Backtab. The key is passed to the -// handler. -func (t *TextView) SetDoneFunc(handler func(key tcell.Key)) *TextView { - t.done = handler - return t -} - -// SetHighlightedFunc sets a handler which is called when the list of currently -// highlighted regions change. It receives a list of region IDs which were newly -// highlighted, those that are not highlighted anymore, and those that remain -// highlighted. -// -// Note that because regions are only determined during drawing, this function -// can only fire for regions that have existed during the last call to Draw(). -func (t *TextView) SetHighlightedFunc(handler func(added, removed, remaining []string)) *TextView { - t.highlighted = handler - return t -} - -// ScrollTo scrolls to the specified row and column (both starting with 0). -func (t *TextView) ScrollTo(row, column int) *TextView { - if !t.scrollable { - return t - } - t.lineOffset = row - t.columnOffset = column - t.trackEnd = false - return t -} - -// ScrollToBeginning scrolls to the top left corner of the text if the text view -// is scrollable. -func (t *TextView) ScrollToBeginning() *TextView { - if !t.scrollable { - return t - } - t.trackEnd = false - t.lineOffset = 0 - t.columnOffset = 0 - return t -} - -// ScrollToEnd scrolls to the bottom left corner of the text if the text view -// is scrollable. Adding new rows to the end of the text view will cause it to -// scroll with the new data. -func (t *TextView) ScrollToEnd() *TextView { - if !t.scrollable { - return t - } - t.trackEnd = true - t.columnOffset = 0 - return t -} - -// GetScrollOffset returns the number of rows and columns that are skipped at -// the top left corner when the text view has been scrolled. -func (t *TextView) GetScrollOffset() (row, column int) { - return t.lineOffset, t.columnOffset -} - -// Clear removes all text from the buffer. -func (t *TextView) Clear() *TextView { - t.buffer = nil - t.recentBytes = nil - t.index = nil - return t -} - -// Highlight specifies which regions should be highlighted. If highlight -// toggling is set to true (see SetToggleHighlights()), the highlight of the -// provided regions is toggled (highlighted regions are un-highlighted and vice -// versa). If toggling is set to false, the provided regions are highlighted and -// all other regions will not be highlighted (you may also provide nil to turn -// off all highlights). -// -// For more information on regions, see class description. Empty region strings -// are ignored. -// -// Text in highlighted regions will be drawn inverted, i.e. with their -// background and foreground colors swapped. -func (t *TextView) Highlight(regionIDs ...string) *TextView { - // Toggle highlights. - if t.toggleHighlights { - var newIDs []string - HighlightLoop: - for regionID := range t.highlights { - for _, id := range regionIDs { - if regionID == id { - continue HighlightLoop - } - } - newIDs = append(newIDs, regionID) - } - for _, regionID := range regionIDs { - if _, ok := t.highlights[regionID]; !ok { - newIDs = append(newIDs, regionID) - } - } - regionIDs = newIDs - } // Now we have a list of region IDs that end up being highlighted. - - // Determine added and removed regions. - var added, removed, remaining []string - if t.highlighted != nil { - for _, regionID := range regionIDs { - if _, ok := t.highlights[regionID]; ok { - remaining = append(remaining, regionID) - delete(t.highlights, regionID) - } else { - added = append(added, regionID) - } - } - for regionID := range t.highlights { - removed = append(removed, regionID) - } - } - - // Make new selection. - t.highlights = make(map[string]struct{}) - for _, id := range regionIDs { - if id == "" { - continue - } - t.highlights[id] = struct{}{} - } - t.index = nil - - // Notify. - if t.highlighted != nil && len(added) > 0 || len(removed) > 0 { - t.highlighted(added, removed, remaining) - } - - return t -} - -// GetHighlights returns the IDs of all currently highlighted regions. -func (t *TextView) GetHighlights() (regionIDs []string) { - for id := range t.highlights { - regionIDs = append(regionIDs, id) - } - return -} - -// SetToggleHighlights sets a flag to determine how regions are highlighted. -// When set to true, the Highlight() function (or a mouse click) will toggle the -// provided/selected regions. When set to false, Highlight() (or a mouse click) -// will simply highlight the provided regions. -func (t *TextView) SetToggleHighlights(toggle bool) *TextView { - t.toggleHighlights = toggle - return t -} - -// ScrollToHighlight will cause the visible area to be scrolled so that the -// highlighted regions appear in the visible area of the text view. This -// repositioning happens the next time the text view is drawn. It happens only -// once so you will need to call this function repeatedly to always keep -// highlighted regions in view. -// -// Nothing happens if there are no highlighted regions or if the text view is -// not scrollable. -func (t *TextView) ScrollToHighlight() *TextView { - if len(t.highlights) == 0 || !t.scrollable || !t.regions { - return t - } - t.index = nil - t.scrollToHighlights = true - t.trackEnd = false - return t -} - -// GetRegionText returns the text of the region with the given ID. If dynamic -// colors are enabled, color tags are stripped from the text. Newlines are -// always returned as '\n' runes. -// -// If the region does not exist or if regions are turned off, an empty string -// is returned. -func (t *TextView) GetRegionText(regionID string) string { - if !t.regions || regionID == "" { - return "" - } - - var ( - buffer bytes.Buffer - currentRegionID string - ) - - for _, str := range t.buffer { - // Find all color tags in this line. - var colorTagIndices [][]int - if t.dynamicColors { - colorTagIndices = colorPattern.FindAllStringIndex(str, -1) - } - - // Find all regions in this line. - var ( - regionIndices [][]int - regions [][]string - ) - if t.regions { - regionIndices = regionPattern.FindAllStringIndex(str, -1) - regions = regionPattern.FindAllStringSubmatch(str, -1) - } - - // Analyze this line. - var currentTag, currentRegion int - for pos, ch := range str { - // Skip any color tags. - if currentTag < len(colorTagIndices) && pos >= colorTagIndices[currentTag][0] && pos < colorTagIndices[currentTag][1] { - if pos == colorTagIndices[currentTag][1]-1 { - currentTag++ - } - if colorTagIndices[currentTag][1]-colorTagIndices[currentTag][0] > 2 { - continue - } - } - - // Skip any regions. - if currentRegion < len(regionIndices) && pos >= regionIndices[currentRegion][0] && pos < regionIndices[currentRegion][1] { - if pos == regionIndices[currentRegion][1]-1 { - if currentRegionID == regionID { - // This is the end of the requested region. We're done. - return buffer.String() - } - currentRegionID = regions[currentRegion][1] - currentRegion++ - } - continue - } - - // Add this rune. - if currentRegionID == regionID { - buffer.WriteRune(ch) - } - } - - // Add newline. - if currentRegionID == regionID { - buffer.WriteRune('\n') - } - } - - return escapePattern.ReplaceAllString(buffer.String(), `[$1$2]`) -} - -// Focus is called when this primitive receives focus. -func (t *TextView) Focus(delegate func(p Primitive)) { - // Implemented here with locking because this is used by layout primitives. - t.Lock() - defer t.Unlock() - t.hasFocus = true -} - -// HasFocus returns whether or not this primitive has focus. -func (t *TextView) HasFocus() bool { - // Implemented here with locking because this may be used in the "changed" - // callback. - t.Lock() - defer t.Unlock() - return t.hasFocus -} - -// Write lets us implement the io.Writer interface. Tab characters will be -// replaced with TabSize space characters. A "\n" or "\r\n" will be interpreted -// as a new line. -func (t *TextView) Write(p []byte) (n int, err error) { - // Notify at the end. - t.Lock() - changed := t.changed - t.Unlock() - if changed != nil { - defer func() { - // We always call the "changed" function in a separate goroutine to avoid - // deadlocks. - go changed() - }() - } - - t.Lock() - defer t.Unlock() - - // Copy data over. - newBytes := append(t.recentBytes, p...) - t.recentBytes = nil - - // If we have a trailing invalid UTF-8 byte, we'll wait. - if r, _ := utf8.DecodeLastRune(p); r == utf8.RuneError { - t.recentBytes = newBytes - return len(p), nil - } - - // If we have a trailing open dynamic color, exclude it. - if t.dynamicColors { - location := openColorRegex.FindIndex(newBytes) - if location != nil { - t.recentBytes = newBytes[location[0]:] - newBytes = newBytes[:location[0]] - } - } - - // If we have a trailing open region, exclude it. - if t.regions { - location := openRegionRegex.FindIndex(newBytes) - if location != nil { - t.recentBytes = newBytes[location[0]:] - newBytes = newBytes[:location[0]] - } - } - - // Transform the new bytes into strings. - newBytes = bytes.Replace(newBytes, []byte{'\t'}, bytes.Repeat([]byte{' '}, TabSize), -1) - for index, line := range newLineRegex.Split(string(newBytes), -1) { - if index == 0 { - if len(t.buffer) == 0 { - t.buffer = []string{line} - } else { - t.buffer[len(t.buffer)-1] += line - } - } else { - t.buffer = append(t.buffer, line) - } - } - - // Reset the index. - t.index = nil - - return len(p), nil -} - -// reindexBuffer re-indexes the buffer such that we can use it to easily draw -// the buffer onto the screen. Each line in the index will contain a pointer -// into the buffer from which on we will print text. It will also contain the -// color with which the line starts. -func (t *TextView) reindexBuffer(width int) { - if t.index != nil { - return // Nothing has changed. We can still use the current index. - } - t.index = nil - t.fromHighlight, t.toHighlight, t.posHighlight = -1, -1, -1 - - // If there's no space, there's no index. - if width < 1 { - return - } - - // Initial states. - regionID := "" - var ( - highlighted bool - foregroundColor, backgroundColor, attributes string - ) - - // Go through each line in the buffer. - for bufferIndex, str := range t.buffer { - colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedStr, _ := decomposeString(str, t.dynamicColors, t.regions) - - // Split the line if required. - var splitLines []string - str = strippedStr - if t.wrap && len(str) > 0 { - for len(str) > 0 { - extract := runewidth.Truncate(str, width, "") - if len(extract) == 0 { - // We'll extract at least one grapheme cluster. - gr := uniseg.NewGraphemes(str) - gr.Next() - _, to := gr.Positions() - extract = str[:to] - } - if t.wordWrap && len(extract) < len(str) { - // Add any spaces from the next line. - if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 { - extract = str[:len(extract)+spaces[1]] - } - - // Can we split before the mandatory end? - matches := boundaryPattern.FindAllStringIndex(extract, -1) - if len(matches) > 0 { - // Yes. Let's split there. - extract = extract[:matches[len(matches)-1][1]] - } - } - splitLines = append(splitLines, extract) - str = str[len(extract):] - } - } else { - // No need to split the line. - splitLines = []string{str} - } - - // Create index from split lines. - var originalPos, colorPos, regionPos, escapePos int - for _, splitLine := range splitLines { - line := &textViewIndex{ - Line: bufferIndex, - Pos: originalPos, - ForegroundColor: foregroundColor, - BackgroundColor: backgroundColor, - Attributes: attributes, - Region: regionID, - } - - // Shift original position with tags. - lineLength := len(splitLine) - remainingLength := lineLength - tagEnd := originalPos - totalTagLength := 0 - for { - // Which tag comes next? - nextTag := make([][3]int, 0, 3) - if colorPos < len(colorTagIndices) { - nextTag = append(nextTag, [3]int{colorTagIndices[colorPos][0], colorTagIndices[colorPos][1], 0}) // 0 = color tag. - } - if regionPos < len(regionIndices) { - nextTag = append(nextTag, [3]int{regionIndices[regionPos][0], regionIndices[regionPos][1], 1}) // 1 = region tag. - } - if escapePos < len(escapeIndices) { - nextTag = append(nextTag, [3]int{escapeIndices[escapePos][0], escapeIndices[escapePos][1], 2}) // 2 = escape tag. - } - minPos := -1 - tagIndex := -1 - for index, pair := range nextTag { - if minPos < 0 || pair[0] < minPos { - minPos = pair[0] - tagIndex = index - } - } - - // Is the next tag in range? - if tagIndex < 0 || minPos > tagEnd+remainingLength { - break // No. We're done with this line. - } - - // Advance. - strippedTagStart := nextTag[tagIndex][0] - originalPos - totalTagLength - tagEnd = nextTag[tagIndex][1] - tagLength := tagEnd - nextTag[tagIndex][0] - if nextTag[tagIndex][2] == 2 { - tagLength = 1 - } - totalTagLength += tagLength - remainingLength = lineLength - (tagEnd - originalPos - totalTagLength) - - // Process the tag. - switch nextTag[tagIndex][2] { - case 0: - // Process color tags. - foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos]) - colorPos++ - case 1: - // Process region tags. - regionID = regions[regionPos][1] - _, highlighted = t.highlights[regionID] - - // Update highlight range. - if highlighted { - line := len(t.index) - if t.fromHighlight < 0 { - t.fromHighlight, t.toHighlight = line, line - t.posHighlight = stringWidth(splitLine[:strippedTagStart]) - } else if line > t.toHighlight { - t.toHighlight = line - } - } - - regionPos++ - case 2: - // Process escape tags. - escapePos++ - } - } - - // Advance to next line. - originalPos += lineLength + totalTagLength - - // Append this line. - line.NextPos = originalPos - line.Width = stringWidth(splitLine) - t.index = append(t.index, line) - } - - // Word-wrapped lines may have trailing whitespace. Remove it. - if t.wrap && t.wordWrap { - for _, line := range t.index { - str := t.buffer[line.Line][line.Pos:line.NextPos] - spaces := spacePattern.FindAllStringIndex(str, -1) - if spaces != nil && spaces[len(spaces)-1][1] == len(str) { - oldNextPos := line.NextPos - line.NextPos -= spaces[len(spaces)-1][1] - spaces[len(spaces)-1][0] - line.Width -= stringWidth(t.buffer[line.Line][line.NextPos:oldNextPos]) - } - } - } - } - - // Calculate longest line. - t.longestLine = 0 - for _, line := range t.index { - if line.Width > t.longestLine { - t.longestLine = line.Width - } - } -} - -// Draw draws this primitive onto the screen. -func (t *TextView) Draw(screen tcell.Screen) { - t.Lock() - defer t.Unlock() - t.Box.Draw(screen) - totalWidth, totalHeight := screen.Size() - - // Get the available size. - x, y, width, height := t.GetInnerRect() - t.pageSize = height - - // If the width has changed, we need to reindex. - if width != t.lastWidth && t.wrap { - t.index = nil - } - t.lastWidth = width - - // Re-index. - t.reindexBuffer(width) - if t.regions { - t.regionInfos = nil - } - - // If we don't have an index, there's nothing to draw. - if t.index == nil { - return - } - - // Move to highlighted regions. - if t.regions && t.scrollToHighlights && t.fromHighlight >= 0 { - // Do we fit the entire height? - if t.toHighlight-t.fromHighlight+1 < height { - // Yes, let's center the highlights. - t.lineOffset = (t.fromHighlight + t.toHighlight - height) / 2 - } else { - // No, let's move to the start of the highlights. - t.lineOffset = t.fromHighlight - } - - // If the highlight is too far to the right, move it to the middle. - if t.posHighlight-t.columnOffset > 3*width/4 { - t.columnOffset = t.posHighlight - width/2 - } - - // If the highlight is off-screen on the left, move it on-screen. - if t.posHighlight-t.columnOffset < 0 { - t.columnOffset = t.posHighlight - width/4 - } - } - t.scrollToHighlights = false - - // Adjust line offset. - if t.lineOffset+height > len(t.index) { - t.trackEnd = true - } - if t.trackEnd { - t.lineOffset = len(t.index) - height - } - if t.lineOffset < 0 { - t.lineOffset = 0 - } - - // Adjust column offset. - if t.align == AlignLeft { - if t.columnOffset+width > t.longestLine { - t.columnOffset = t.longestLine - width - } - if t.columnOffset < 0 { - t.columnOffset = 0 - } - } else if t.align == AlignRight { - if t.columnOffset-width < -t.longestLine { - t.columnOffset = width - t.longestLine - } - if t.columnOffset > 0 { - t.columnOffset = 0 - } - } else { // AlignCenter. - half := (t.longestLine - width) / 2 - if half > 0 { - if t.columnOffset > half { - t.columnOffset = half - } - if t.columnOffset < -half { - t.columnOffset = -half - } - } else { - t.columnOffset = 0 - } - } - - // Draw the buffer. - defaultStyle := tcell.StyleDefault.Foreground(t.textColor) - for line := t.lineOffset; line < len(t.index); line++ { - // Are we done? - if line-t.lineOffset >= height || y+line-t.lineOffset >= totalHeight { - break - } - - // Get the text for this line. - index := t.index[line] - text := t.buffer[index.Line][index.Pos:index.NextPos] - foregroundColor := index.ForegroundColor - backgroundColor := index.BackgroundColor - attributes := index.Attributes - regionID := index.Region - if t.regions && regionID != "" && (len(t.regionInfos) == 0 || t.regionInfos[len(t.regionInfos)-1].ID != regionID) { - t.regionInfos = append(t.regionInfos, &textViewRegion{ - ID: regionID, - FromX: x, - FromY: y + line - t.lineOffset, - ToX: -1, - ToY: -1, - }) - } - - // Process tags. - colorTagIndices, colorTags, regionIndices, regions, escapeIndices, strippedText, _ := decomposeString(text, t.dynamicColors, t.regions) - - // Calculate the position of the line. - var skip, posX int - if t.align == AlignLeft { - posX = -t.columnOffset - } else if t.align == AlignRight { - posX = width - index.Width - t.columnOffset - } else { // AlignCenter. - posX = (width-index.Width)/2 - t.columnOffset - } - if posX < 0 { - skip = -posX - posX = 0 - } - - // Print the line. - if y+line-t.lineOffset >= 0 { - var colorPos, regionPos, escapePos, tagOffset, skipped int - iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - // Process tags. - for { - if colorPos < len(colorTags) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { - // Get the color. - foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colorTags[colorPos]) - tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0] - colorPos++ - } else if regionPos < len(regionIndices) && textPos+tagOffset >= regionIndices[regionPos][0] && textPos+tagOffset < regionIndices[regionPos][1] { - // Get the region. - if regionID != "" && len(t.regionInfos) > 0 && t.regionInfos[len(t.regionInfos)-1].ID == regionID { - // End last region. - t.regionInfos[len(t.regionInfos)-1].ToX = x + posX - t.regionInfos[len(t.regionInfos)-1].ToY = y + line - t.lineOffset - } - regionID = regions[regionPos][1] - if regionID != "" { - // Start new region. - t.regionInfos = append(t.regionInfos, &textViewRegion{ - ID: regionID, - FromX: x + posX, - FromY: y + line - t.lineOffset, - ToX: -1, - ToY: -1, - }) - } - tagOffset += regionIndices[regionPos][1] - regionIndices[regionPos][0] - regionPos++ - } else { - break - } - } - - // Skip the second-to-last character of an escape tag. - if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 { - tagOffset++ - escapePos++ - } - - // Mix the existing style with the new style. - _, _, existingStyle, _ := screen.GetContent(x+posX, y+line-t.lineOffset) - _, background, _ := existingStyle.Decompose() - style := overlayStyle(background, defaultStyle, foregroundColor, backgroundColor, attributes) - - // Do we highlight this character? - var highlighted bool - if regionID != "" { - if _, ok := t.highlights[regionID]; ok { - highlighted = true - } - } - if highlighted { - fg, bg, _ := style.Decompose() - if bg == tcell.ColorDefault { - r, g, b := fg.RGB() - c := colorful.Color{R: float64(r) / 255, G: float64(g) / 255, B: float64(b) / 255} - _, _, li := c.Hcl() - if li < .5 { - bg = tcell.ColorWhite - } else { - bg = tcell.ColorBlack - } - } - style = style.Background(fg).Foreground(bg) - } - - // Skip to the right. - if !t.wrap && skipped < skip { - skipped += screenWidth - return false - } - - // Stop at the right border. - if posX+screenWidth > width || x+posX >= totalWidth { - return true - } - - // Draw the character. - for offset := screenWidth - 1; offset >= 0; offset-- { - if offset == 0 { - screen.SetContent(x+posX+offset, y+line-t.lineOffset, main, comb, style) - } else { - screen.SetContent(x+posX+offset, y+line-t.lineOffset, ' ', nil, style) - } - } - - // Advance. - posX += screenWidth - return false - }) - } - } - - // If this view is not scrollable, we'll purge the buffer of lines that have - // scrolled out of view. - if !t.scrollable && t.lineOffset > 0 { - if t.lineOffset >= len(t.index) { - t.buffer = nil - } else { - t.buffer = t.buffer[t.index[t.lineOffset].Line:] - } - t.index = nil - t.lineOffset = 0 - } -} - -// InputHandler returns the handler for this primitive. -func (t *TextView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - key := event.Key() - - if key == tcell.KeyEscape || key == tcell.KeyEnter || key == tcell.KeyTab || key == tcell.KeyBacktab { - if t.done != nil { - t.done(key) - } - return - } - - if !t.scrollable { - return - } - - switch key { - case tcell.KeyRune: - switch event.Rune() { - case 'g': // Home. - t.trackEnd = false - t.lineOffset = 0 - t.columnOffset = 0 - case 'G': // End. - t.trackEnd = true - t.columnOffset = 0 - case 'j': // Down. - t.lineOffset++ - case 'k': // Up. - t.trackEnd = false - t.lineOffset-- - case 'h': // Left. - t.columnOffset-- - case 'l': // Right. - t.columnOffset++ - } - case tcell.KeyHome: - t.trackEnd = false - t.lineOffset = 0 - t.columnOffset = 0 - case tcell.KeyEnd: - t.trackEnd = true - t.columnOffset = 0 - case tcell.KeyUp: - t.trackEnd = false - t.lineOffset-- - case tcell.KeyDown: - t.lineOffset++ - case tcell.KeyLeft: - t.columnOffset-- - case tcell.KeyRight: - t.columnOffset++ - case tcell.KeyPgDn, tcell.KeyCtrlF: - t.lineOffset += t.pageSize - case tcell.KeyPgUp, tcell.KeyCtrlB: - t.trackEnd = false - t.lineOffset -= t.pageSize - } - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (t *TextView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - x, y := event.Position() - if !t.InRect(x, y) { - return false, nil - } - - switch action { - case MouseLeftClick: - if t.regions { - // Find a region to highlight. - for _, region := range t.regionInfos { - if y == region.FromY && x < region.FromX || - y == region.ToY && x >= region.ToX || - region.FromY >= 0 && y < region.FromY || - region.ToY >= 0 && y > region.ToY { - continue - } - t.Highlight(region.ID) - break - } - } - consumed = true - setFocus(t) - case MouseScrollUp: - t.trackEnd = false - t.lineOffset-- - consumed = true - case MouseScrollDown: - t.lineOffset++ - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/treeview.go b/vendor/github.com/rivo/tview/treeview.go deleted file mode 100644 index ea4585ae..00000000 --- a/vendor/github.com/rivo/tview/treeview.go +++ /dev/null @@ -1,775 +0,0 @@ -package tview - -import ( - "github.com/gdamore/tcell" -) - -// Tree navigation events. -const ( - treeNone int = iota - treeHome - treeEnd - treeUp - treeDown - treePageUp - treePageDown -) - -// TreeNode represents one node in a tree view. -type TreeNode struct { - // The reference object. - reference interface{} - - // This node's child nodes. - children []*TreeNode - - // The item's text. - text string - - // The text color. - color tcell.Color - - // Whether or not this node can be selected. - selectable bool - - // Whether or not this node's children should be displayed. - expanded bool - - // The additional horizontal indent of this node's text. - indent int - - // An optional function which is called when the user selects this node. - selected func() - - // Temporary member variables. - parent *TreeNode // The parent node (nil for the root). - level int // The hierarchy level (0 for the root, 1 for its children, and so on). - graphicsX int // The x-coordinate of the left-most graphics rune. - textX int // The x-coordinate of the first rune of the text. -} - -// NewTreeNode returns a new tree node. -func NewTreeNode(text string) *TreeNode { - return &TreeNode{ - text: text, - color: Styles.PrimaryTextColor, - indent: 2, - expanded: true, - selectable: true, - } -} - -// Walk traverses this node's subtree in depth-first, pre-order (NLR) order and -// calls the provided callback function on each traversed node (which includes -// this node) with the traversed node and its parent node (nil for this node). -// The callback returns whether traversal should continue with the traversed -// node's child nodes (true) or not recurse any deeper (false). -func (n *TreeNode) Walk(callback func(node, parent *TreeNode) bool) *TreeNode { - n.parent = nil - nodes := []*TreeNode{n} - for len(nodes) > 0 { - // Pop the top node and process it. - node := nodes[len(nodes)-1] - nodes = nodes[:len(nodes)-1] - if !callback(node, node.parent) { - // Don't add any children. - continue - } - - // Add children in reverse order. - for index := len(node.children) - 1; index >= 0; index-- { - node.children[index].parent = node - nodes = append(nodes, node.children[index]) - } - } - - return n -} - -// SetReference allows you to store a reference of any type in this node. This -// will allow you to establish a mapping between the TreeView hierarchy and your -// internal tree structure. -func (n *TreeNode) SetReference(reference interface{}) *TreeNode { - n.reference = reference - return n -} - -// GetReference returns this node's reference object. -func (n *TreeNode) GetReference() interface{} { - return n.reference -} - -// SetChildren sets this node's child nodes. -func (n *TreeNode) SetChildren(childNodes []*TreeNode) *TreeNode { - n.children = childNodes - return n -} - -// GetText returns this node's text. -func (n *TreeNode) GetText() string { - return n.text -} - -// GetChildren returns this node's children. -func (n *TreeNode) GetChildren() []*TreeNode { - return n.children -} - -// ClearChildren removes all child nodes from this node. -func (n *TreeNode) ClearChildren() *TreeNode { - n.children = nil - return n -} - -// AddChild adds a new child node to this node. -func (n *TreeNode) AddChild(node *TreeNode) *TreeNode { - n.children = append(n.children, node) - return n -} - -// SetSelectable sets a flag indicating whether this node can be selected by -// the user. -func (n *TreeNode) SetSelectable(selectable bool) *TreeNode { - n.selectable = selectable - return n -} - -// SetSelectedFunc sets a function which is called when the user selects this -// node by hitting Enter when it is selected. -func (n *TreeNode) SetSelectedFunc(handler func()) *TreeNode { - n.selected = handler - return n -} - -// SetExpanded sets whether or not this node's child nodes should be displayed. -func (n *TreeNode) SetExpanded(expanded bool) *TreeNode { - n.expanded = expanded - return n -} - -// Expand makes the child nodes of this node appear. -func (n *TreeNode) Expand() *TreeNode { - n.expanded = true - return n -} - -// Collapse makes the child nodes of this node disappear. -func (n *TreeNode) Collapse() *TreeNode { - n.expanded = false - return n -} - -// ExpandAll expands this node and all descendent nodes. -func (n *TreeNode) ExpandAll() *TreeNode { - n.Walk(func(node, parent *TreeNode) bool { - node.expanded = true - return true - }) - return n -} - -// CollapseAll collapses this node and all descendent nodes. -func (n *TreeNode) CollapseAll() *TreeNode { - n.Walk(func(node, parent *TreeNode) bool { - n.expanded = false - return true - }) - return n -} - -// IsExpanded returns whether the child nodes of this node are visible. -func (n *TreeNode) IsExpanded() bool { - return n.expanded -} - -// SetText sets the node's text which is displayed. -func (n *TreeNode) SetText(text string) *TreeNode { - n.text = text - return n -} - -// GetColor returns the node's color. -func (n *TreeNode) GetColor() tcell.Color { - return n.color -} - -// SetColor sets the node's text color. -func (n *TreeNode) SetColor(color tcell.Color) *TreeNode { - n.color = color - return n -} - -// SetIndent sets an additional indentation for this node's text. A value of 0 -// keeps the text as far left as possible with a minimum of line graphics. Any -// value greater than that moves the text to the right. -func (n *TreeNode) SetIndent(indent int) *TreeNode { - n.indent = indent - return n -} - -// TreeView displays tree structures. A tree consists of nodes (TreeNode -// objects) where each node has zero or more child nodes and exactly one parent -// node (except for the root node which has no parent node). -// -// The SetRoot() function is used to specify the root of the tree. Other nodes -// are added locally to the root node or any of its descendents. See the -// TreeNode documentation for details on node attributes. (You can use -// SetReference() to store a reference to nodes of your own tree structure.) -// -// Nodes can be selected by calling SetCurrentNode(). The user can navigate the -// selection or the tree by using the following keys: -// -// - j, down arrow, right arrow: Move (the selection) down by one node. -// - k, up arrow, left arrow: Move (the selection) up by one node. -// - g, home: Move (the selection) to the top. -// - G, end: Move (the selection) to the bottom. -// - Ctrl-F, page down: Move (the selection) down by one page. -// - Ctrl-B, page up: Move (the selection) up by one page. -// -// Selected nodes can trigger the "selected" callback when the user hits Enter. -// -// The root node corresponds to level 0, its children correspond to level 1, -// their children to level 2, and so on. Per default, the first level that is -// displayed is 0, i.e. the root node. You can call SetTopLevel() to hide -// levels. -// -// If graphics are turned on (see SetGraphics()), lines indicate the tree's -// hierarchy. Alternative (or additionally), you can set different prefixes -// using SetPrefixes() for different levels, for example to display hierarchical -// bullet point lists. -// -// See https://github.com/rivo/tview/wiki/TreeView for an example. -type TreeView struct { - *Box - - // The root node. - root *TreeNode - - // The currently selected node or nil if no node is selected. - currentNode *TreeNode - - // The movement to be performed during the call to Draw(), one of the - // constants defined above. - movement int - - // The top hierarchical level shown. (0 corresponds to the root level.) - topLevel int - - // Strings drawn before the nodes, based on their level. - prefixes []string - - // Vertical scroll offset. - offsetY int - - // If set to true, all node texts will be aligned horizontally. - align bool - - // If set to true, the tree structure is drawn using lines. - graphics bool - - // The color of the lines. - graphicsColor tcell.Color - - // An optional function which is called when the user has navigated to a new - // tree node. - changed func(node *TreeNode) - - // An optional function which is called when a tree item was selected. - selected func(node *TreeNode) - - // An optional function which is called when the user moves away from this - // primitive. - done func(key tcell.Key) - - // The visible nodes, top-down, as set by process(). - nodes []*TreeNode -} - -// NewTreeView returns a new tree view. -func NewTreeView() *TreeView { - return &TreeView{ - Box: NewBox(), - graphics: true, - graphicsColor: Styles.GraphicsColor, - } -} - -// SetRoot sets the root node of the tree. -func (t *TreeView) SetRoot(root *TreeNode) *TreeView { - t.root = root - return t -} - -// GetRoot returns the root node of the tree. If no such node was previously -// set, nil is returned. -func (t *TreeView) GetRoot() *TreeNode { - return t.root -} - -// SetCurrentNode sets the currently selected node. Provide nil to clear all -// selections. Selected nodes must be visible and selectable, or else the -// selection will be changed to the top-most selectable and visible node. -// -// This function does NOT trigger the "changed" callback. -func (t *TreeView) SetCurrentNode(node *TreeNode) *TreeView { - t.currentNode = node - return t -} - -// GetCurrentNode returns the currently selected node or nil of no node is -// currently selected. -func (t *TreeView) GetCurrentNode() *TreeNode { - return t.currentNode -} - -// SetTopLevel sets the first tree level that is visible with 0 referring to the -// root, 1 to the root's child nodes, and so on. Nodes above the top level are -// not displayed. -func (t *TreeView) SetTopLevel(topLevel int) *TreeView { - t.topLevel = topLevel - return t -} - -// SetPrefixes defines the strings drawn before the nodes' texts. This is a -// slice of strings where each element corresponds to a node's hierarchy level, -// i.e. 0 for the root, 1 for the root's children, and so on (levels will -// cycle). -// -// For example, to display a hierarchical list with bullet points: -// -// treeView.SetGraphics(false). -// SetPrefixes([]string{"* ", "- ", "x "}) -func (t *TreeView) SetPrefixes(prefixes []string) *TreeView { - t.prefixes = prefixes - return t -} - -// SetAlign controls the horizontal alignment of the node texts. If set to true, -// all texts except that of top-level nodes will be placed in the same column. -// If set to false, they will indent with the hierarchy. -func (t *TreeView) SetAlign(align bool) *TreeView { - t.align = align - return t -} - -// SetGraphics sets a flag which determines whether or not line graphics are -// drawn to illustrate the tree's hierarchy. -func (t *TreeView) SetGraphics(showGraphics bool) *TreeView { - t.graphics = showGraphics - return t -} - -// SetGraphicsColor sets the colors of the lines used to draw the tree structure. -func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView { - t.graphicsColor = color - return t -} - -// SetChangedFunc sets the function which is called when the user navigates to -// a new tree node. -func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView { - t.changed = handler - return t -} - -// SetSelectedFunc sets the function which is called when the user selects a -// node by pressing Enter on the current selection. -func (t *TreeView) SetSelectedFunc(handler func(node *TreeNode)) *TreeView { - t.selected = handler - return t -} - -// SetDoneFunc sets a handler which is called whenever the user presses the -// Escape, Tab, or Backtab key. -func (t *TreeView) SetDoneFunc(handler func(key tcell.Key)) *TreeView { - t.done = handler - return t -} - -// GetScrollOffset returns the number of node rows that were skipped at the top -// of the tree view. Note that when the user navigates the tree view, this value -// is only updated after the tree view has been redrawn. -func (t *TreeView) GetScrollOffset() int { - return t.offsetY -} - -// GetRowCount returns the number of "visible" nodes. This includes nodes which -// fall outside the tree view's box but notably does not include the children -// of collapsed nodes. Note that this value is only up to date after the tree -// view has been drawn. -func (t *TreeView) GetRowCount() int { - return len(t.nodes) -} - -// process builds the visible tree, populates the "nodes" slice, and processes -// pending selection actions. -func (t *TreeView) process() { - _, _, _, height := t.GetInnerRect() - - // Determine visible nodes and their placement. - var graphicsOffset, maxTextX int - t.nodes = nil - if t.root == nil { - return - } - selectedIndex := -1 - topLevelGraphicsX := -1 - if t.graphics { - graphicsOffset = 1 - } - t.root.Walk(func(node, parent *TreeNode) bool { - // Set node attributes. - node.parent = parent - if parent == nil { - node.level = 0 - node.graphicsX = 0 - node.textX = 0 - } else { - node.level = parent.level + 1 - node.graphicsX = parent.textX - node.textX = node.graphicsX + graphicsOffset + node.indent - } - if !t.graphics && t.align { - // Without graphics, we align nodes on the first column. - node.textX = 0 - } - if node.level == t.topLevel { - // No graphics for top level nodes. - node.graphicsX = 0 - node.textX = 0 - } - - // Add the node to the list. - if node.level >= t.topLevel { - // This node will be visible. - if node.textX > maxTextX { - maxTextX = node.textX - } - if node == t.currentNode && node.selectable { - selectedIndex = len(t.nodes) - } - - // Maybe we want to skip this level. - if t.topLevel == node.level && (topLevelGraphicsX < 0 || node.graphicsX < topLevelGraphicsX) { - topLevelGraphicsX = node.graphicsX - } - - t.nodes = append(t.nodes, node) - } - - // Recurse if desired. - return node.expanded - }) - - // Post-process positions. - for _, node := range t.nodes { - // If text must align, we correct the positions. - if t.align && node.level > t.topLevel { - node.textX = maxTextX - } - - // If we skipped levels, shift to the left. - if topLevelGraphicsX > 0 { - node.graphicsX -= topLevelGraphicsX - node.textX -= topLevelGraphicsX - } - } - - // Process selection. (Also trigger events if necessary.) - if selectedIndex >= 0 { - // Move the selection. - newSelectedIndex := selectedIndex - MovementSwitch: - switch t.movement { - case treeUp: - for newSelectedIndex > 0 { - newSelectedIndex-- - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - case treeDown: - for newSelectedIndex < len(t.nodes)-1 { - newSelectedIndex++ - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - case treeHome: - for newSelectedIndex = 0; newSelectedIndex < len(t.nodes); newSelectedIndex++ { - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - case treeEnd: - for newSelectedIndex = len(t.nodes) - 1; newSelectedIndex >= 0; newSelectedIndex-- { - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - case treePageDown: - if newSelectedIndex+height < len(t.nodes) { - newSelectedIndex += height - } else { - newSelectedIndex = len(t.nodes) - 1 - } - for ; newSelectedIndex < len(t.nodes); newSelectedIndex++ { - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - case treePageUp: - if newSelectedIndex >= height { - newSelectedIndex -= height - } else { - newSelectedIndex = 0 - } - for ; newSelectedIndex >= 0; newSelectedIndex-- { - if t.nodes[newSelectedIndex].selectable { - break MovementSwitch - } - } - newSelectedIndex = selectedIndex - } - t.currentNode = t.nodes[newSelectedIndex] - if newSelectedIndex != selectedIndex { - t.movement = treeNone - if t.changed != nil { - t.changed(t.currentNode) - } - } - selectedIndex = newSelectedIndex - - // Move selection into viewport. - if selectedIndex-t.offsetY >= height { - t.offsetY = selectedIndex - height + 1 - } - if selectedIndex < t.offsetY { - t.offsetY = selectedIndex - } - } else { - // If selection is not visible or selectable, select the first candidate. - if t.currentNode != nil { - for index, node := range t.nodes { - if node.selectable { - selectedIndex = index - t.currentNode = node - break - } - } - } - if selectedIndex < 0 { - t.currentNode = nil - } - } -} - -// Draw draws this primitive onto the screen. -func (t *TreeView) Draw(screen tcell.Screen) { - t.Box.Draw(screen) - if t.root == nil { - return - } - _, totalHeight := screen.Size() - - t.process() - - // Scroll the tree. - x, y, width, height := t.GetInnerRect() - switch t.movement { - case treeUp: - t.offsetY-- - case treeDown: - t.offsetY++ - case treeHome: - t.offsetY = 0 - case treeEnd: - t.offsetY = len(t.nodes) - case treePageUp: - t.offsetY -= height - case treePageDown: - t.offsetY += height - } - t.movement = treeNone - - // Fix invalid offsets. - if t.offsetY >= len(t.nodes)-height { - t.offsetY = len(t.nodes) - height - } - if t.offsetY < 0 { - t.offsetY = 0 - } - - // Draw the tree. - posY := y - lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor) - for index, node := range t.nodes { - // Skip invisible parts. - if posY >= y+height+1 || posY >= totalHeight { - break - } - if index < t.offsetY { - continue - } - - // Draw the graphics. - if t.graphics { - // Draw ancestor branches. - ancestor := node.parent - for ancestor != nil && ancestor.parent != nil && ancestor.parent.level >= t.topLevel { - if ancestor.graphicsX >= width { - continue - } - - // Draw a branch if this ancestor is not a last child. - if ancestor.parent.children[len(ancestor.parent.children)-1] != ancestor { - if posY-1 >= y && ancestor.textX > ancestor.graphicsX { - PrintJoinedSemigraphics(screen, x+ancestor.graphicsX, posY-1, Borders.Vertical, t.graphicsColor) - } - if posY < y+height { - screen.SetContent(x+ancestor.graphicsX, posY, Borders.Vertical, nil, lineStyle) - } - } - ancestor = ancestor.parent - } - - if node.textX > node.graphicsX && node.graphicsX < width { - // Connect to the node above. - if posY-1 >= y && t.nodes[index-1].graphicsX <= node.graphicsX && t.nodes[index-1].textX > node.graphicsX { - PrintJoinedSemigraphics(screen, x+node.graphicsX, posY-1, Borders.TopLeft, t.graphicsColor) - } - - // Join this node. - if posY < y+height { - screen.SetContent(x+node.graphicsX, posY, Borders.BottomLeft, nil, lineStyle) - for pos := node.graphicsX + 1; pos < node.textX && pos < width; pos++ { - screen.SetContent(x+pos, posY, Borders.Horizontal, nil, lineStyle) - } - } - } - } - - // Draw the prefix and the text. - if node.textX < width && posY < y+height { - // Prefix. - var prefixWidth int - if len(t.prefixes) > 0 { - _, prefixWidth = Print(screen, t.prefixes[(node.level-t.topLevel)%len(t.prefixes)], x+node.textX, posY, width-node.textX, AlignLeft, node.color) - } - - // Text. - if node.textX+prefixWidth < width { - style := tcell.StyleDefault.Foreground(node.color) - if node == t.currentNode { - style = tcell.StyleDefault.Background(node.color).Foreground(t.backgroundColor) - } - printWithStyle(screen, node.text, x+node.textX+prefixWidth, posY, width-node.textX-prefixWidth, AlignLeft, style) - } - } - - // Advance. - posY++ - } -} - -// InputHandler returns the handler for this primitive. -func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { - return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { - selectNode := func() { - node := t.currentNode - if node != nil { - if t.selected != nil { - t.selected(node) - } - if node.selected != nil { - node.selected() - } - } - } - - // Because the tree is flattened into a list only at drawing time, we also - // postpone the (selection) movement to drawing time. - switch key := event.Key(); key { - case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: - if t.done != nil { - t.done(key) - } - case tcell.KeyDown, tcell.KeyRight: - t.movement = treeDown - case tcell.KeyUp, tcell.KeyLeft: - t.movement = treeUp - case tcell.KeyHome: - t.movement = treeHome - case tcell.KeyEnd: - t.movement = treeEnd - case tcell.KeyPgDn, tcell.KeyCtrlF: - t.movement = treePageDown - case tcell.KeyPgUp, tcell.KeyCtrlB: - t.movement = treePageUp - case tcell.KeyRune: - switch event.Rune() { - case 'g': - t.movement = treeHome - case 'G': - t.movement = treeEnd - case 'j': - t.movement = treeDown - case 'k': - t.movement = treeUp - case ' ': - selectNode() - } - case tcell.KeyEnter: - selectNode() - } - - t.process() - }) -} - -// MouseHandler returns the mouse handler for this primitive. -func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { - x, y := event.Position() - if !t.InRect(x, y) { - return false, nil - } - - switch action { - case MouseLeftClick: - setFocus(t) - _, rectY, _, _ := t.GetInnerRect() - y -= rectY - if y >= 0 && y < len(t.nodes) { - node := t.nodes[y] - if node.selectable { - previousNode := t.currentNode - t.currentNode = node - if previousNode != node && t.changed != nil { - t.changed(node) - } - if t.selected != nil { - t.selected(node) - } - if node.selected != nil { - node.selected() - } - } - } - consumed = true - case MouseScrollUp: - t.movement = treeUp - consumed = true - case MouseScrollDown: - t.movement = treeDown - consumed = true - } - - return - }) -} diff --git a/vendor/github.com/rivo/tview/tview.gif b/vendor/github.com/rivo/tview/tview.gif deleted file mode 100644 index 0583d7b95ca4da412e5d7491221366a1a3f7d64b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2226085 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ĵxT}f<`tmCb#T5`LAX_DjhAko@3Z$&t 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

    tAsEhi!lt_gag)FL@0a8Bj=rtq775 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≧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

    V(ua#LDomiop1( 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�MKf`bq0E6~ZR9(wn>{>KXXSN8O~*k%dS&Bb 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

      |B~br0D=$sT~e^cWw?|EAKtf&5W;6= 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ɕ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 z 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%0D)nKxI<67@|(3soCNOo=E8 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%0D5fhmeFPMp6dXB4`zX8@&>)qMGR&ZhW~lXiw^i4 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

      $J7G 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

      _4g&@Vt1e8u9L5wxqz zdS7Y1yex`iOl?p2m9osbAl5)>CBb{Pb;Zv9Q)PFi-g-y;;!c{^nX;^dyg0+EeftBi zWI{!Gsejd7r@eI(q!Z(&w|(qZ)3vD1*n8=;?U(Lt&9;tB2XH?9 zj%OZ^4IV?SqX&P!ze$^Mq;>uMoQUDORl8ADf8Wf^ZW=!IS&9QIe#)N!i8euTeP1K{ z2&pK)Y7CTs5VY_zCz#kOdO#vUrfQpXHM$ysNL*YJ=|w$qI(F!6`3A#Q04p%m%LzRl z=eiy>u*yd^d*OgUFn!49#hQ%`?(3W>0JUqLqWiYDIjuFsf^(<10b^WN1!!(^>Ozhm zytRE7P}P>0Jn%hMf1=B`4H&PE`D3Sgm2^w z1xctc*1*)Z1Ut2*NA|*p^gUT4 z)4chuT_ioaBd!l*zil}f`iQyQP6gvIC`vV4>F1FMh?g!~SVibXv#o`GW2aWzu-ys` zk;RE|v&J=@FWk^suA=r>E@pQ?SgF0>^PC7!3q>M4-9&J@Luel zx54jct)W19lC!$dYMvsg&QNbR6VG}^8021PM=(AjoXU>QyFpAxEbv2vb!r<1jEQ_7 zm7xCWU;~u8lSe}>1%@$*7?&-Y&qw(wBsf-A2p8iU_{s`6>c@;IU$XhtXN+#9o|nG) z^$6OYN~AVPf~h4Qn9iUOd5NIn>w9$2sKoBMDOCEjWhEJ()A?cTwECZ801>Y@(g805 z0elZa=4^m{v#_ui##H4C!eCA!Mu8B!$;$YMw5e_uQ%yGO-oG;0DjRfGgG zF|(Id+&QdY^g4_oDF!F+_%H#@m5`uJbDekfovJW$T5LO~Gc;x&^V-AXhpRrE2PD`K z3#be4ga8y0Nr%v1snL1o2`reVx}QW{ZTCvQxAeXzGh}T)*OY+-Fhw2{XlK%FshH3RNO$dOOo@65x(^%B$v zQ|Qm5uDWG2O0E8B?av#C*pcH2`vaIfAb|^5Yk@x;fC>Xd%v}F9FpdfauIrqi@gZu! z3;x2Z^dGgaL(L}N#Ydv-nKQnpvdkC-67~|Cybw#)nP+m%cIk3TVB~i{uka~v2g6d# zfe7vIYSU{EMqrjLET*fF^BnN_=cp-Ps)bqMAn?$)Mc#K$f%$Y{P)Q#0x%lwBamB$! zrg8liE%cK=t9DyGUgS9)_qjEi6b@J ztY4M7JuKv7vL>PiPfqBzWzLATtfW+bJM~xBhDv(8^0OlCS+rBC$(HhONj_uiHhcR` z_dKq8yx#nsgIm=whqq$J`Nin>grWCKrEiH)%>C8}v6JtGOBGqu^nV*E4@S1hyuN?Q ztni3?IGC_KTf;9o{*3@SKe0Dk<4w-?H|}R{T_x05|L|qTEnSAb8LGUTN^{Ge*k;d- z@B0+D@J76vx-D9LVqCHLvFBpbBFbI&xm%ybzes)YnUujD-~N50x6QqorKXQf8C9#2 zdfLuh9E>c=uYoj^rRel&AR*^|w=Z=@qT&aG==J7ZsNDQM_1^va@f{5E&C>W(5`IJ( z^)1gmE^?9Qj~nlM`yJf|SB>@<4J+(}PF6-6W#8GmXN%x?bL^@J*(6Z07&No+%nVDN zF)FoNd@>`v=0%hUfplt7_9(hXZOBEy=UmovF37{WFX(Cf_ur^6+4l5BXJJf{!tJ^K zK3q_i-9{^wQy>@)tXUxKJc&8e9{FdIw#VdbUa^veN3I>|ac~tLr zt2;QWTw1!nz>Hb4d@Jzat7P6F+vDUqWx#R!f@q$`221Mt=L!tzjPb2rw+@TQJ^LX3 zKj?`IyzDGqJWwQSze^~=A+7jS+(GWQ%Ss8r+(qh^Z@y~2HpJENxqtrDqyB90+82%f zc<6cOX%!NF7z~_KL(W5pIe;+(SroFcAVO+1(Kn#LI1&`egE5xML9~3L?+l2G`O9_N zeaqx;h)3YRrI|Yna;Cu$bd0FSBIH0~8d_^OzucV<bJZ65Ce&;6U%wTd>P34JqvPOx%#Nd`SPl^Fj;DHKCttD zkv$1X*H&(Y6)hlaE?W9tJ+whBuKUfaQSXMO#7J1;%H|3GEm@MVChzX8m`=1QN~Y`f z0=UaPJKfSz_@n}G3it!WcTC=}GA7{%(7{bcJd;iq-03yiO>6BsR1Ly#L+Asw)fQY` z&s8XFd7!hUuBAH!3x}a`Rz9kn@sR-ZbP9L2=ADCYIZkgmN2qPMSv)KCrgTTu+It59 zlEGjjI;=87UH|$ea?_3IeZG~KHnP<4zFiy0)c4;P?hW3;1$?xOiLdS+M`9k$!dCBk&kDV^^GeF!eu_2BpuAtYwh;@dw@E&3mo;Oe ze5qu~xEkLU5qosOvAI8XTyXyU@!q3<<}HbEhXH(UA7fTl7uRGy+N$-@-I~g)kLK=m z`|<3bx$lx8+-qy?jo}0H(GwRXQKwr;nl6O(a9N3=#$0XWfriXMH*$SN+4FTm3m_Z9yn~`Y7T(D)3iFJmvtyhc|4) zjM(PxQ#Ky)&oSWA#u#B2z-i))V5lRhZ@gb4k8nr>wYv$lTl6r3!WPZZ%1gqp%2^XN zw~Hbkc@gJa+sj62!QAYA!^H~*5XG(u^VX1_zEdt7GV z@e~*B7IuK|!2ECB*dwoX{TX#RA-_D19-Nx?Tf7)OBElSrj2-#%$p2m7Cw_U4KO)JO z`U#}F5{DpdQRlMgxZNK#2-={L$T$+{eocp;7#lye^c?5+?CuixSBSBPE21tB4%cX1 z(5vz3i7T&tM#YskM7qa$+>DbQ+Mbe%V&=X4QCRJMw*B2m8a}z6x>8(o^>FUSdVK%?*jL*BK|751 zB3@TG1iEo~)tuG-F86gv&XG!~1lJ7T0=5Po`VqUCl8MMSH*&P14Bg2SdiF_VszGw` z(d$|_&RqAk>xUl z$4!BxQZ%5g3%~!Uz38{=wxeHcA&geikoUn*E@0d1(@+1J_&Q?Cm&L1Rf2KdyO1F+b zBIskgs;zGKEoSN~$>TlUe^Oz9=d^kq2?mKw0KI$YWy}(tpLyY_<3~`((ukrrW;#_Amt<9CnLseQ#)q+O?hYOCpo>9#V+Va~Kr*zl85Y!I57~=&` zx@Yc7Y6MNGB}+Jiuy-e|JbK-~akCEukMceDvlgcrSA32bt5f+~uKRa~kMkAmA97No z_v=Hp=Ens$Rk0jLTCV%WODi7pJdU6adl#%V8Iy0~?oM`Ief}sH|5-0xw}IqV(sVee z{C!?m^IL4xQEvZ=Ds1%g$eOjQ_bj=zMf~h;)Tk7HzDx+Cc|E;5;KA@!#Jfdi$^7`$ zE(^}J#^lJGi;lPMpyAnXX6beuerHf=L$Fo~g*G`q&M{H_A4lgI*F@UB?V0q1kPHxd zNTG%zhNh8b5;`J;Dj>R{h}eUmA_7iA3y2V~fubgK5ET>^%etY6fGr}rtn0b~EXXPk zD7wg^y!pT1`NVHBbKlo>p2s0@@TzIB0%jr~CAO_}BD!+5hi@QiMk4?&%%?0%WF`zq z17?+eP;9@c8ypi=s%P1VCNG2~y|(peE~xeY8u(1u^sbc(URZBesD+gI5UAe)gyf!> z=nH^(1-hZT-@>@#0M_D>5=#W~QQe;{lk10agVlC7irivXtqC@I97zw>cmZ6#RGeH4 z7=G%-)!UQ?P(s5o+GQZU4SH#IQ>sH`bxQtTzuhMv|O}fha{uva)c+Ft_fae}_2&hqSECrqF8|^{-D! zFne!3JtCag>o(3c$n1c-I5>osndyD?oPO@Xz4EyHE^>}~ zl74nJlDHnH5gvTY%^W3I{Y&#yafRdk~A_gf+qDuk)u&;HWy(6qo{+!Kxt!1o1Oe@ zKy@>CR?2{gY~ns6x!#RK0iJ=0K-@iKl55$BcfwqGuICUzEe%j~3(K9U0PbaMAMTtE zdGB#%qm8PVp4SU*ICA}_&h-*D$rW>Y*)!S5rsOY_87$3H^N_M?3BZ~wKX6=v$k#BG zr^mJ13%-Igvjc>^=@kZWJ`Iv1N}UGPwF^(Kk=CRJV-*zy2#hRj34JhT|Mr-48h{xt z5~8}$Lpq#7Qc29*WfJZy5P~;>#Q;ytpjiEC6Alu%*4@Pt2Q8cMA-~5@gHjiF(W4*t z1z&+lc^!42fi)J3(Zr8E>*+G50Ei~C)va%X7P=*>>?Qr*bH?qj(e$vjjO5LnYtWCZ zqBRRVtmnKvZB?83pzGUm5f#>5G(vhTRC@5!)(s_iY3_B}GbcD0-9=$9a#mBp{w3u> zAYOt5(u+tAua*J#w7yfMydn)T1R(-^b!GsO_KRU_JI2F)6r1Fg1S19VlDJoY1vc(cnQnc^GZ)v5c}hct8O?qq{G#^|A6)|-!NJ|F%=fqh?l(B&;(EluS2RcEeJ zfD|!pN$R!gn^-Gxsm~O{T0CfF;vW2!Iru|`r44pg2-K6wL(R#-;*N?K4F)zf?fk1- z&(d?}Cnu<2*m^swvY@eoyW2VsU`-CuYE2kucTsYJJtVzFJLzh&{MQBwE@d9roVELC zHG;eN#^Kfb4MZ5f*h87<2?|L?%`*@+}4&Wp{uc=Wqsv-ZS{iTZ8UQ#BLPT{Kv?3pATjXuSpvO<-t& z*VP{OJtA0tX?*npNKfMmUrwOzf}$?p;570@&V(rkfg+TlBc^}U^#cUPus8QNcLvg# zt0e?b!Q5qC^>T_8e`lRi=KpG^xegJ2zl5||2mz**o*W^8A}b>)K_-(Z-Gb&S@v}TV z|3YNy1zL=d@QtU>QsbtT`$-_(f!WFhbY{gQgU2ttgCiHjT9!d;^CsHfVNA<4e=}}& zZk(2;2By?F6JT(POMW5mY0EFHHrINg1XT(eOG%vvahW{&vaCY?nNPJ8)+LN%;>H1r zg^q9kLQ*-MAx08&K)R-XLx1Z$Q30gpCOKQD1Z4*HC?%=h?D?dEJiCWGr}h=B-pk)Qj+t&_@gf`nHAFA7ZBW^w0}!TV9Dw0x|_@R zKa*5_6y1XZA1anZI}VQ)gWCwi=M@h&2iebK^QW;j1*uU+v#X5jeVy^|C;F}X?5fW> zpKp<#8L;)cH*t`i7Ls6-XFZcC+$OUo+5PDv&5FA7vEST!uuT14&f!O9w>GKpr4gg# z^{&VFm!57)`@|k{ZyJON^Tna~gA~`Wj}3hKyuIs|z~6dN2e*`rws>g~j?KT94sI95 zf5^1d>P@w+l-2Q9^qT%IMY7d>{p*iLwO4-5tV=2hZN?|TkZnYZl?t(U_S0Mn{_sP| zOioKicgDE&y++j+u0t0b-&*QyyW#NciEjK!T5+8(Hf&_HHY?CR;X~+I3q>s^>-E-+ z*R6VWt=yK~8^1M;l9AJ;@@HZBf>UW9e(y!?-Q60>KVk8ZN2tTyxQ_RwsV~e11l|AI z&^FW_)$&gYB^=hm{B>=E7LIU^XE?7;%^J<#Ecv|n9`sfa-QQY8wNczbd0Ws>=oBk7 zvn;gLcP3yr2GrHX;Y8!cgyPRBnUVRkIon~3OcHj{V)I+ylLNO31U<3?lMqmgQvNZcpv^O`L*&(YeD+F93xd?%az!7NtmUtK&%>6Cf-N1(?Z9w z<~z=2mRJO81O(&e+Y50-IktNcTX4f0P-0KYF%B&levkfX`;|S3CQC1@fK#at>+D5f z`g+hXKJlWrmAl8bkYimh&ppI1yDSkIaSl8TQEu#Lo^dBBW7*9$AeJE@Oqwq|jEn`4 zAC+i#MxX}D62e7S|6@MGKz>^--9GWa(Of0(*JuOJsU84(d75`VvJNH@{+|SyO>l& zUsCMa%92w@9sHfi6K^otM=b{A^rR*$5zqR4Az`M69)&0vG&FH1h8_DV=t7{BR-Eo6}|Algn~FMx1Sm}oh?MRJ=Tp14sQ7tJU#wB7E3(IvNp&+ z^ZT%cNUXsO1`y>kZ5U$j5{MGDA2N+w{H9yhA=w8LxG{S_NS)XR9WH})d$)Sn9~_EF zlW0QzMB(VF>WvG1bZ0PATTaO?4dYpsP<+^b?cSLHqkXLq7!m^W@4fBj7)J!UT!NR? zNt*tRza;e#CDQK<(3N5hEeyv2a_QD$xth+oteK(cpRS}QfWHomwR*niZv}MbL3*r$ z-tb1Ze+ya)Q|$8*%PJ;~#Xb9{)6T%53{=7nP;wXgs2%gK9z%v;v#53^pb-#P7{8pe?staEO>!GXR!5EM%x7?5U;fS zAcm$GT3n^>>^LqSptBLsS7GK|C7q#gV#9AC1@w(+xq(3o<=u`!0&+P9CB)BzCRdea zZH`n4K=3Cb0V(ZuqQgQvdX(@0ai+qy$kx-@R;rG${AAwr^lCdn&+Uwdvd3uu@v7yu zx4^bXrYs@x+c*^SrmQ%q0+-zwwkEQXsIvr8@~nK?l|bmYeSZfSxW&@9K|2#(S&kWq zSAz&~wV9kee?eOfAfnK=q;_kT8a40?&|F+I8~rB>+n7w+#JIAZM}9R9F-%Z%id`%D zCSxFFSWUM~w5_hBRRg9!6Aex(4SxPd>EP#2C}>k?dUp@KS82viq?c&`D~a@8v6dae zY(CLY1zwR@NzmC9eO)W|j_5K7nL`RAi*X406PW2S_^hN)EA*xl4dY?mz0K6lW3-7L z{d0Obx>-(g-}nSEZK?-LQm*oE{7PvHh`B}g|Bpqv45q5-%TwceRiN(Fzd8mN-DB@Z z`kPZsX3T}$ZSD*Uj@)@suKg})U$m0+N{yV6|As&z?Eorc#&SuT{gCDz?)Gv@%)KoD@u`y1bbH3$&J3!(A` zn)k(<&_ctrdS8?f_3=9mCq}?ZYDkad=NF$XEs`k)tq$1xcpS6;31G1GQHHzMNJLU& zSp=QXaR(brRTn-q_hU38$C^4*H=f(Pg# zSIrb5O`S=htnj~=w48RZ|Gp2?tugoLx*hWypy}%AeMf~l;ypLu-k^}!4_!aV4?Cpt z)n1~%SCyvX_R`AE&EHR9Ra?hmea&?1C_eGetnKsGmz)eN{XBI%q0Ba>CBy&zji#yd zOC>PCDj6)W!fA!co_ZvfI9Z1P<)CdFCvC7nu)ZAV0)ltX|@SlR5{tx2FLq{ilue>T^V`=L($d&aQ&__~CgLmM_wiW3gh;*febw$) z)$u!SnO(?ieJ!((sg1Us-4%2kSrgN6t7E4BEH%{HKQ61#zZ1)L?asRLZoH9o)g%r7 z;>z}-mNo8q8#~`}3$2{m_WgNdXRLH}{@sMLr&nwP4A)$FbURzrclDr$OSI!&n=eRt z#l{m}G4Iv3hz)66Q$5@D7f&#=13XNV{(6R`&ac0-d2-wFGp4`qJNN~LYoUKnMVr0| zjQa8Jc$EK|`OysWwSIB>Z`%x)!+qvIME$wm>f)B@tlzF>CTt$$-FSBRGw+6~ct4)~ zS}ds?6XdV5WpHHXa`pVZZ$GDtYaAZGmnn{^j{J{a@P=bxSwCHq}_b!5=h)+D7jW@$1QV&4le&7+ROm>G? z#b*LsBkL4FuNg})%QbNC7Q*`0Q38n>u0@R@tQ1@TacEA8@)iCs4PPv9&;vQ<0tqEx zK&X2qaPgH7Qa`Fgr17NG)QN#&&t&c(UQvG}X2Xd%-4LOh8o>RV_6*5f>Od?@z%g88 zF{ZeyuGIA^bq==seaieE*i=vz;#_^buEJZR0@<#>rpa_n7om~-)ijs6venHFH7Y`K zO*ziV4^zElU_6gJkZY>2XpVQ2{r+fmCj42;H z@r{^J(Tb-TIO8g6933aC`AE&Ujys| zH;r>Le#XMG5kmgj7l1pbZqkGY45yV^161FQP83=umOkbEPUL%>++!OdlOH;iLKj+Y@LLOZ`R-Al{_i4}lbs$CWOTELaq#n24e9BY!Cpi`R`|+Sl?uZ}C8CVN zfSl4=L3h9%tcEw@=U~F?QcY>JW1YP#Kn(e^qG;iN8pQ#sl3-Dpb6rBqs2d_qDJrr) zBB}PAdtTp|E=Vf>*iS`swB5+f(*-lMkMh2gBzbABDxbF0lE30xq8EX~qtCYlc&aq* z{0<57q6UA=l9PAAZ&+5ruf0QgSj6=t`b8UikBTd+qHp1Y4>@yu3yYG>3 zh@dU#rH3zfKk@%MavqtIsF}oG_FfcM$aMKGmkYE~&+i+4={jgK2h}7PL=X$J%2E&( zZKyawsM#Gk5^AXg6WF#`2smvvm56Z`!Z#w+frz*;2Es-cVWh+Zniu2@l)naCWq(nL z)yh`qW7J7>-&kADu!w*aB+HE? zI|@^$=gPN^_GNEM;~}r##yC*NZ>zny`QHLi0KlYlkDVXIeQH~y#UTEt>AwMTRjEM$ z%W)r@ds02}9Os>!kc&gz83>0dM#=(blrO^)z;QWZZH&^ClvW}$O4Vno*-G4ClMMOM zB-6xyw&5<){pC+2-8KVFU7FKwJUdBQmfz=u-hh&+dw4ew;`I(c49b<&vo)#Yb=T%9 z4v#X8e@^w({`IBeKYX`^40;+_*MGcP- zC+S=M?o;OvO4f#E2WzJ=6Mu;O&^uDou&?TA0*4`F2dhT#>w(EkmC~p;h1{isXTGFpy+Q46yP&~Bc;v!H;w>9V=@ zFqi45QxXYHfKsCXd)>|=1L}m)i;jZCSIKawuL8zNg7j9J?)A(}WI(QfV;5NBYb4a} z|MLmR)v%J(p~!+XBTDQ$z`85Rz`^3P0P$`cI}l~ziz{5M8C27a7&xDm$V0SVF_t3P z8#kdej2}Pxb+`i>f>HWYzU@sf--21{-ekwm0H4Zyn4kVIjw?KLn-O91OQKXqF<|EN zz|ap;L<$y@Ocla6ZC$~`n?6>B>5uoX`u@jgA+AYp=}l^6m94ITmU2h9(VzD-JvMKe zuP=W!l=!8D@Tlmd&ee42v6z;YXn5ae`GBJp@V5d?a5mNNIxK54Jn_}++j0Gi6Pr7A zOtE6mE1;bv-vwb|cBaa>(8(z?94fCe{>@kC#WN#q9f_xtVOkH%Au&wL+=j0J&+*Lu z9uNNVS&w2-@YUDEJCU-%X)7lyEHK?#tiN-sc&FdUW{2=FBZ1g4`T+k6*Oj?-T~G2= zX7EX0s*Tt)W8kpeDO+dpPLWQLqSx$6&&uEH>6FiA!gQ$6M-N$PFnx+TaKHG#6va`j z7poK7Ip@19mWDiMjrK+Dx7l_-(v()I?~$cbRAum~(e$jIiCZ}Qp;@{*-ON#;`;S<> z_N~E=^k~b^$*0ok{zf~mf_b`}Wg}!K!s$(dz_Qeu6?{#q(;+nwTI>GPdBkC1C9ac3 zOElCM?>y62vZ1mN{9G_msiO@z1yxe+g9UaVr74k>X~E8G*6_d}yt!`gzO9GJ0u(8d``=SY^OE=y!BWMRSta?L zY~C=h7H;aE=wv`&6OvL^`5mDobTH_`YgPjvc^aNtW=@)^P_-{7-KbHHH%nT0Qv!$ zuxbTA(UFjj8E;;OB3C2z_PY7)^~B9-V}ztw_DNGGo%>G_m|65IRD~Hn%r;$&odfdy z$dCNI7h$cg&n`=HEM|`QFCPo*$6hir#Lkg0v!oa7Z)vBDT{|+ zo+UnEWN0Ldy^LcF5n@5TAE7>XI?Bj#Zu22fC$5uxo8i`94ZSj^zp|}SDaru#GBBei zw+jLF@xcE+(}w42(iFxdIeA71{1oD;@JvYjL5dUVv375__G$Av4 zcH*A8h9?7&Hc()=v5k`4B=Y>{EoXksy0|TPN~Ob6q><~~=2BdKI*1uEj=+rvesGhw zjg_zcvXQDL&9l+r+#pW_JNXc3Jm+g zHXJ+2+t;%UzF|iVjbSiwLTIUGv=DLIz_%%# zP;(V3PzL!kohm0$OY8s*n9e~4W_8t^?m;a8T50#d3(?gtlJ$jP#pfH4RDzKt!15hC1Y`3|c8qubVWD|0>Ui>;lt-05PyF$yI4Iik2u04mEMNa=P z>)x9f?wd=+Xjs2$Z2?UB%3l6ILTxa#)xCxRKSj)|NiRFd#@TdSXTq|Iyd)&<8MyvK zu*ZQPh5N^WIvaB?v16BQ8$(t3$r-S9Ez+*6$5)p6aUHoEEU9uL20{lu#qu%a{NXJG_PEf%asZt-0^3iVjqAHi^ z2L>O37UT)Ir{{6JHG`TKxiUUg1G&@w5s&>(O|W1m-&CO|?m0c^ z2#QlWU4*KWd3sat^_wb#h#+aB8*WjKC3N1T``kICpwC{wiW1%LjEm7cPq&U0n|$fgR(+rKCK!4@QX-^a6#c%%6$+Dw~GrJ!8oyc zfGv{kp3USbQ}J{4gmfG29tWnN0y))|9*ipJh{nh^Mb37v{I0X^nUiv)p}3a9?bsuV z!nhQX7dy1D+%>_;q$s6du*+_BVqn)lkOOFj9Vcj~lRT&uEgQ%(u7-ccCCg51TJkeL zr)RmP6++&jEKfQ$b8XkV2e|32K|9%yHLs<3F;#Ap5ut0$1}p@62g;zK@0o!gAuBP! zVK8TOR_R9@uMl9T`>Yo7p>=+%Lj!TQx9C4QP^#UFZm}%Oc)`%MgP^Fm9dUW<{pil= z@Ne@h`dIi#`|w;AT-~U@PjslUy5DyYT-SjyIicZn!jls0jDHk^i}hmE^mZ~BDiOx}1WAr5dYd zr(Tvc#976h+vAdM$c}rE*F)o_C$mKOK>mF`+j+Tg;5BHYpun8Stwrsge%3$bS_x^m==(_Ff9@ zr+CKfJ#4=1MXc;oq4gtaT+Z0DQw<~M|MAU#G#XM^ovi^z{;GFkJ>!fyJ2hQg7e(QW zkLCPY-q5N09Cyt^3J<31=~wF?nTs^v)nJ_l6%BxfbCAU-ZlLz&DHe0S(=yuYSeN$jpC;Q7$7DsnZu_)C zH=@JT!sQ{v{W}Z6!>O=qJ_DiO^H1p9Ar|(j#mXU?HUbsNPRktExnJb@ zC0vgaMAc3!q=c{d&_@2MexMhjYa~E!@;hpBZD6qn^#;HfP6C~-j<4lf9)Yh3ohW0i z1FK=^n6BUGl*@0F$$xwx5*tydL}SL0TmTgyK(^u}8c=$G&<3I5q;OH;Dn#uHfXkj7 zv;f>sHcn3&hCPL;FtV$4-TvN|%_6KOCafz2wo{ecFF>9Qz)yLG@bM&$w*yxh^5>Mx zl;2fbM4taA+S!vwbth12n0yaVeYXV6%Ao7dUuejv)ZI4P5aHb{mmwfW$4|}VGHNK1 zTH*hPm%TLnGG{AV_Lh7}xeUImXH)dY1^Geq&-RkIC6l=qTvF0 zTBQuv*0C9Ca+hyFN&$Qmm#{?*B(tEdwd=UJaW1w*NH_)K8&${< zE`D5q@!;S(KN8{q#GG79vn^T+O%eXkBXM_dp=b?BJ+K3kbeUtgH`{+yX<+egf_?`G z6C*?K{*PsM064IQw4}G1{uS6FQ~lDTJgy)I~v*e#Jdt8lr zZr~XOxo>DP-dgn~cemD|8<>Ab$UoHN^D^#|8hZmpT52F)?TEn4hPz#_I-OBqEDkb{ z_QI&h{{t}irwtfkHvKL6ckZ;q`8+BVe-$RX`9o4A**WfR5|liZb7?;1&|_hsFsgzq%_FvUWlxrXeUVAeQ~N%i9g# zL1YsMWds1ZDsq#`{%@iCaR9X-^NxWcrq=!2t3+&4BQ@sfUjRrkzuY+inA0xnV@T9A z6<4!nR4;w?Xl$nI9_XGfoRp!@H|iUqmkzC|@P?ohcDv-_{1ds$OJ_%5alZF{6BYn;9B=g@#4TQQ0{HY>>B|Y* z*vc1TapL@)4@b7Djl1_Oth(~3Wd4j8lYq|HH3p@MxtrZNkeVsLA}Ox5xKWG}Ny5ws z*$mxQysoo7)lU)y-#laz1E}B~e|zODp3~_)@MgtI?W`06JVCAsv7VXzYw~?5vN+$~ z#9^nAUVgrG@#H71Ns`W8k-ao0p-0hkJBvaXuozozvqD$9Z?pcCUlFV&!hBY?Z&k1G zM%E+`T-YXj#lDQ7u&uRSvw+&HayIrni-hx`Zve7dE`Z5)4;9G%_(=eyWpRN$6L?AQ zWP*!^H2J!x(8hX@dEY*xLA|}HCpP&3V76HZ=)4S9REKSiRC0LM7D$53vUpW{IxYw zdB7P$J{S z=)pAAR)fnw31tI4gg0_BG73OJ)DRp>j4(qmWdfFl1=X;*;{i-TI5A|_tOOh9#=Y+4 z=kglHZO%D-dwRb*kH9QA?-;MbuC7_AX)HS&S_$Rv`L@mP{NYrl<+;3hW-)<~>D)r! zInV4m&z`{-o)?i?@1M_RBz`rm{@RM9Cv%i&`l<3;QOz>9XhMO{Lgmm&=OrhNSU#!k zzDeHH0{=PT)FE}v;kzGfXa+Ize_#)^YTpLp)rDeBI$DDP$^4nPrO+g+^n-Y8%IT%U z*f)h@Wn%*y?p-#C^H7795p158A*Q4LC}|iiFI-j40YGH%jw`T^+D$I~4Gk;NOja4s zrf6{j#vxxhSxd{?4{kkaa7)x!_;9|vv&Au6)kwf=52|aj_J+Sb;$D!x#c;#%mU$~c zZuHJ<&%B(k53wr6r$T1guZ=vZi%He#tXAGJH%`msah>nyTGuwVJa5&nNhtZC>*ef& zRSsgFZo{`l>I;kFx-E>isJPl8;l+sYg6)P$8c6C>WQ;!F^c@>88oAm!Q_D5|Tji5i zJ9~7`ai(>W>7Nm(WCc>h1y+Lq)>-qk?DN~Xa$JdLiTY|i8*E7j@l^y%W2LOE;8DrL zWu)?*8IaY)T|FJ93w~LB$-&Z^G!>ROe4_7h$x-yK4@b6_A3OGL)~|HexFRAiwbl3_ z+9r3rY<Ix6~n{&!GFv5-ztV@^AyoxR%p!H0(HJ19HWlcKz%l!woSU zIcV-5j!eMEeITg;Vw9Do^LR)jr@pVDHk@m|jdV^}qqliDygXtj$?VT7>$XX%n6M7n zh&<#S@xt=tuNHL{J5a+-4I<`0WfR0Re4_?9lKIclpSWi(MrMvqfgg^W7w%m?$oFpw zw=t}GZ;@eD{dPX1f>&j=UR_N&h-tC@T{5v|WsCQ=;LJLAhY;$87nB5DSL+8J!9I%- z_ja7f;A-Rf5G$yD?LZ2e2W&i?hX^?QI?zogny@uwo{(!ggU{s}(v0cQ-LbPjYW}~{xoN~1!g5cBuGEG?#zX!J!?(~ax z%AG9h{tOd3{Md*4lcKjS_^%#D)lYxlCUm!pz3X@)b6;b)^R?;cKR-r@@eM(VfQ!-2 zXiAm%UYdAu@3Z!wJKbz@+GCG750o`^*8a28+~33Y(OvkWkJT)Uhh$O907XqQR&r6k zFcQlF5Z1GjU2kh{QnG}$jvcDDSTx|No-lgY?QhHa@MvHyAy1IpS>P z0}S`Z+;SG3xoA^(MQFIQrEUcnv3~m1*eTXTYu8?CS)#N<%dsQ%H}5YtmNz5h|GSlR zM7X|m%`o@bF2bO#c1JHF3c~4ka6N0nLF~&87_C$wsY0LfO!=$&ES?QQeNvd-1-M&U zrTpcjbXZCxWyow#27?H}*crRplwq@tS5d9mV=qc44CL^=X+CA}qTvUW4n~0Rt3;48 z$_BgIrY}cYj+9&?#vDaIegTH6I_>tHKtf~p!>66)p9stQncNm7QiH&yv}obQQUKU4 z6MJ+E`*USnOqAO3EY()?V;=%iH0y4*_@dQ%1sI=RvC(lDe4;A$e)u#WImWlCJ$44Z zd-{1HwHUw9@(X4n83@9uji*}3wtyNnKAIepFFNMk!tec=bW(;7@C3sz)N@!E;XV$)TALBBb1wPukN!NX`^ z^BXSlqY4Cwc*p_A&jWfmBq9$hR*AJ(vwAgYe*jK_%41RLBqT@Su!7cdX|bm~>&_$w zEBPfPM+om>NIacy`e&>+3h3<`!7ne$efSB{6}c(ne!ZG$c^#uRU^E9kId-2pIDVAD zc~hHjJH2BI0=pWCHtc4@%}I4fs%2KzD_>5Y#16NyRmi-PV^P}`Y1J_){a%&pBSJFk zD^jTF@t{8(nHjydxNauo*n&^e<$^D+DS`D$IfP;hL*-B|4FZN;6 zr8Yv1iky(}Q1?W_^4o~Axm^u{jsveX%S7rIu`Hd7cF(fM9xuoo_Vq`JapI8O%{4?e zjF94~Tu-EtHWh-&-2bz5c*~17_BQgyOg5^D(^Y``7&@oA^e3eF=v=R=Qe5JIxh&)n zPQhIv>d;_~K#C2NX>U}a*H3{Apzy#fR(|Zl&$brhA^h&Rqt%1?4Ovvdi(vsc({RVt5vK7)-_JU?o zkP-;s7%-P1-Y@|hWWgS3NJ0zD~R72C$@_|6u6low>F z^3$H>^8v(88H$mG&1EBF#(Bt%&Wbgt8V-nTA)g&2ZV;jK*aNHb>10=cwSe6p+4|v3 zGZvTcU{vt&f;+>9#YbrB@sb1;_R=xDiSS5_(a}{$N{n%Z5s`$i$MA2nU~fh@S5>5I zbk-DiWwKkd5d^RrZWw1!7w#(fLNJB!!!5{7PsKKPWAmzj+?L)Ht|V7kyebkC$Z}h| zuyzB^Smf(ibX1zrvSG+#a0>X9bQ&e@W0n9w5aoAcqIGu zCHoWZv4m|4;FgcYV=lmcpNl)+^gI~D@>zx=9~QDtPoOqNauBO0ig$aIhH?5_yGk_q zPa=SzOuI*jG&naIU% z^(fYqiAM+u@qujLjfi|;r`1|AIfw|Nnz|1XPqkSXq$zz`hL?nhpeaN!fm+OGv9|w4 zla@SnIHH4-&>(;$7uzO@rP)U!rxYea`PO+34$lc?RC6yoWz&|f`_&w9 zzn09%R#E3{?QPO}A$9+%$=wdy|;(%K-` zyxePse_{_zgmLrWg z-MrV1{Y2L$y~^cb8>{tA!}u$0VZN%;uU(hsQuPTRCU zw0w}n41d|&7;9r2-=?K#XFNJ#!>`}VYqj`v>FvkMCA!acBt0H!)fw2nlB&;wOD(<{ z*Q4H^fpSJJ2VQhbV%qAC?wmJu9lm^hSuIGII5J$-fZV0^eW&jsS06!@gBkxC=Fuf) zuH^~ytCf-x4&s^B1HLe zb*8oAhaVS=tRo7+$3iU<3$;=Ds8@9565JRgLch&ws_K2VqR&R?3_tCd8tdFvnh;QeE91d0utnAJwNWt73jtdbQ@*sF{!Lu53hqBOfsz8IM zaprP9QpVnywpjBm=FKZr%eS$$mRJ?i5%hh063d(MQ^zpTF2XjVvyAT5Si1x2=iK5xTv~DaxJ2p4-=A{mB0k+b$*@L0$m# z1>$@pNDmUfb{2#4u*}$J&F76g&p#S_d~>={xm>g4u2Cza0=~>WT3|xC^~cT=&ANYJ z-8qE^Souhy#7CY_1Aw13CP*nlLtVk7flrR&lR2gJT@_A&s4KXJViB;a!#HT+m^2@` zNmOEjLq6SdHltV{knjRgueX7JuXtpkv^xBp-`;+gr#avVYs%tDq^zQB;ms?+ z+Td!!bx~~F@{l0bDl+X+38DJW_37}@M?la7u%--{z5taV2{gF7@sGmX*`jRjK()G9 zbC|B{EAa(-m0$l*>i~4AkLRWaL7> zS1eeFDm|QaKQFnVdaT$TM&+nBh!(IJgGh@bB^Jsc&A)F42aydK<<5%Trm$K+u@gU3 zOznUdIZ~cHILJMsq<8&mdAUZ0*|!VGm@PVGcRNeZK0WD)@mq?K9BT%NO(%R-DLvS# z8zi|CvZE{tk8Biak>LD1Rb0ik!aOD7g~I4o`*`KjB*I-iu{mIq+{wBX-C#{D7&pz6-1? zdt4rCx_1Hl_E|}KEVyf`c>G6vjeK3Ps5$ZBi{Np{D!sVyL$MD8(BYF24Cs=eFi$PX zRij@e++2dYIv*D95xVk7E8(S8q*|001QR&>U%S zC^7`hr4h|q#A^2{{dj_o+V0+wlH5z0u7Bq{J853^QE3l4J63Yp475`r&5baJ*+u$n ze|G?Q^M-{%Ok$shAeBhglwQRLHL}G&n1M!xnoH%_W8b}?tjpDa_z>VCi!99Lu8tIb zcuz2K>_z7dB8h2*2ZUxnO7PjDLQ!`y1_rVhFrUy!tL{7O*`qcg5$(I8+B0D!C*XP1 zvzEa6)M}A>~W1+l^ z$M6mtF3VLGzGk3o-~-$SazueyYBcg4PjKScSimlyN8R_(6o*OvJ7+b?+(ODqoeEVRBZqss{I< zEv}wc)`k=zRtmt?n&xl^a+O9blJ?*>5|p~8h9qU-WsIyQMqZpDD+-Mj z3%DBD324qJzPGA6dvvKN2;a4y_D3-j9_U1{G+E-f6<_b%PTC~ZsJ-3I{U zk_{{no?%CaMpPBV5O52Rw`fS!&`3ap0wdauG!pOo-|3$Wudv02?0c6jF%|?l7}`nz zL<=D%s@P2tGV=)K$a=<#yTQ#C*GB5`={SqUGcx|v!|hXVF@l}nRVHuiNB(%CAJM$; z)p6{y)xl{!cr0W^N_Jxb`>%-4B>Sqr)|xCfwbf-0*T=_~7SFcg6U(!MEB-5JT*owj zG9136t!-7jec^0tpnHTNjgd{Gq33gqD6W zQby=`NgwA*0S;9u5?KyOGzcRGOPF}?I41P2!@*h2 z8&ccg+(Xu^WniK9CPE-fphP;ln$Y5y$CK_!S81)Hei*Uk6`{hCylfd4{AxCPWEXtj zyZzZ^=Uq({)AYtiEvW&gkJWwElt#$j-3zLoEx{fbDupFR^#)-Xftr-d+=@xDg=4ET zgaR-m5@~GI_VdNiRzUS4Ji0B)m_RMMtV`LKf^U#rZJJts? z+vsstR(<+Zlh`;$4=gPsIldm}nqmSqHu{TV(Eyj%|2~@EY&=^IzM;4#n3!;RWQtrGAlY*yH;M|9VO_f2BFOyx}T1i!$(6|nhzN&T-}dl7|Hoq?cwR=?i!-E!18%6;ZeeVB1!zfUNQQZ`F; zoa@L)GD1}9-OE_JJ)p0}lQ}}BX;1MAkjK0AD(o>tN!Z%2Mz*#4!@MR*`VQ@aKCi!x zz zF5Z2g5%-DZzrNec8btT8;XVvcYN>agr=MOc%8%N);qQ>j$z$5-N-^#L^lgY`aU&7$?Tzbtv*9cB55sukaco^m_ttr&+0;~kS(cL(2>^6{ zPW39j&7pw;9{rFY&TD7BRwiS)F7S<9m2&Cd6Cp-QvkM!Ce1Ta2)D$B>s){r?MA8l} zU-*-Q@MYK2)aeYC4O{6jJx*Sq$@_&-`S+!hXgFrxB54Uez0TEDc6(bfpCTUOw+j$# z^aQwjQVd!Mg?YydIepk)FT;Oqzab@Zj!slqhrWR8ggQS?`Kar-rMW_>CVNHsd1_^+t&`DmOlV zQIq#rj*&C%0a;~0zi${KE(Qrbbwkc4GFJb8p*!v}ns18i&>S=b1*Ecu9Zv?8*#5Yx zRKgoAOD6@-(G4Pb!nE)PtQJSzk+sxEPgE7I&F*jjaYOz~Qxb4tydpPHg8Yx=htxE% ziCM{k%j|&@O?79(y;zjMKa1ZsF`%5;dLm+wPdTCK59awX*M5FISVr)aC`vZ3#+71RNnM&8J_V-*@t~i?{zhJn1C~#Iq5PP1ecXS){ zBZSM`M590yt{H%WOCkgcCYkGNFBL|bWGdY3)0K=Ur+mi_0GBwm+-s5(hc=+9F#!OK zL=o_N;xa6c9Bg|a=`|yn-Y(Gh((O*4IAH6Y$n@o3*jVJyZ=D;tvdpCS z>Y0V#?byRB$FidnR*C z{Z#fBFFb6pa+>)w$96(EzggNkKkgJ|wzKzSRQfNWl9Kf0Sjn*!N4NARFw@ifwyCZ6 zKUf`B_aIiFx0aCbWW}nX@>t44)2l#@Eq>tP5!9*r;WFQQk9^YoukcU3IvKm;tA2AP zdaA1F&W$|6|0C<}-Zg>J(t1H$tfJY|{#Z zd2ho0XCWN{H&=e!aMcKZ;X9}clqESLazEw^Y{*r+)2=C~FOxM6d`G`9=`@x9QdnSn zXS(Zv)}ZGl+1n}uC^)~!z@~l!=x77TeJU29`%{djLyzW1^#hK4C1N(qU(v0eq8-g4 zoF$@nJwfDGlK>ozu5_~B)O&9^VZ7F-=uqy!ias&2^C|FT+_pn@ zK~zw+>CUke(zy{d6macIY*K*}69L#u0F*%it;B;$`w(FN=(-Aek@YSF7CcL+$@Q`rD3;#uc^%fNhx8G^n8q-l6`A+Mxy>qsC+7hXfYgII;7;^{ zvqc^XUUm{#SHg9+V!JGeG;t$VVz&A06s;6=9rUJ2U<0?HR2kLO-fv#dsjoi~L*#(H zkp6;TZ*l=a21jJdO-0iWGO9_c`daP_+num@S>9#QH##?{T` z6l94k^iQuF<64rrj*w8XxfGZn9?0c8PE1PMuGX(wrd!qwyViIU1!G}4!o!oBG&1tM zK1{Ed<6T39Uo`0GNw!i1AV)UDE97n?(w7GdQcn(fWH8_{0K3rtp^C8rLH=qptwG^!X9b5+`sw;PM0p24Orh+MBz809EkVy@)`=Z@%lCD;lB62v;~hPP?>6 z35Rf<0AMX?z?J2#_hrleU?W{gQ^&3m8%;W;7x;3i1YFuganlLKc%2wTLhd40STCx{ zLU{SK$(Xkrg#)r+seZ5bitJ;uHa(AMZQUiV6Pa#S)qkY7R3`4a)B2s4MOH+%jfCq` zKd^!1<6Hp&{%p^?cp8A1%zBgQDrO(Jp6v{ePp@QdtcL{FiRRj+BVLL9o;6(ES#LXS z$YW5n<4pcK9}eB@p8Eu(wjgrndAWb^BD6y8W3W*>Kq z)C!=>zCd2wZ`j9m@d1qyQ5K!3JIKwc7yu;$&OHd2EZcE%bB094{?FFUM4Bc8CgdPD z;sA*++Bx)&BKB?{gmuO?a_cp43S5ujDIXM1cT)0iHVSC(cl7~-i1=$D+qEikbNbN; ziZVm;L{>r+!2Hj53+T}wTz+iO$1Ts7j#4%AC6=4+q!p+I57yLMrC-rR2=6PR&>Z_qP(NKgs zceOMl7nMf#%R^!YF3Zam`}A!RX5|j9X-8oT)dN|=WBB_*bz)2ebSM=I#2FC zn~QbS#3cK7Ssn3gMAv+|dE{n6@Z+#ks)L()612LMzGb(j-Fy6lcYBWn728`?c_f@2 z5Ig?W_gwAM*7?<(*E&@$=%5cN*K91tQTNa88f>{q7_^DBF(@EB{aBws9tgbiSUFc! z63y9PUx|$GYp1WK6CdGY{l$a3u9o&)j=j`r`Ol8$ugBMJPex{5c8oW-Cl#VEUyc25 z(~;=7*t{O}nb#kWT`qK1o3KhLIC&(mhe{}&stpi4esk$*hmuxovxd~QU+co7>N9&| zmRpb4KTi8-klN`n(jd@yeA#lhRYxFAxObBO?m|JV895~A>5qXEJ(H;~@tQ9VbsPwN z>YV%H^{N6{fa|z$a9fZbT7iy|T`bDB$VSL>?bF{DDjV8mh5^Gs+snZ0$XrBGEcwZE zR1GI* zv(&eh+zRQ`Z-A{BmKmJZdd*2?uqA3!fJeUhf^Z`lFuNYysyH|~n_8AFk5yoURl>_k zgRnwBG>fq50A*IV;ym5{VV$RYoaM6U$GbannZDn9x3+XlN-Iv*Zwf-8K8QZ-IJu)& z;MpxoAbH&VW?Ko?&5O;Yy^6(>Mz(bU8p)M@>qslQpuD%g8?i*ua8{i zXi~NtJv?lbK6_^|_eHu@-BGkqb@B(ZLT~S+UOm>sYJnT*N88aY#pZblT0l#!d~dHbe?G2XBXDA;ZlN4hVa*eY7SRvi4>RtuDV~ z9LruW`7H{7TA=&_*VT>dSu>j}p!bEWv?K6j!8gnp?AkkEBa>;f8a=Ob?H0V%vy$8e z+dNsYr+|wog@4}U>ez^#mtYT8|J62o21-ny^bU(x-HLpc@7Bci!VXoV9)hv^VFS5@R-lr2ZLO_M#D{g>RRhKiH z&Kn=ubU4~I!6pR3GX70*pI&^9l}CiMhGvE>M15$COsq`{>Nvs~%&+W)9ve5B6Xmm&6?h zw&cpQkQHQr7$JwdUWqE@>d^XO)PQ`?>Jjiab#hq0jxRye;XKDplDl$*a{8zvM zsr~f34P&VNr}qG;dGF>u>>&BQ?hCLxYs}N1eO89Fh~ZT}swAGs!wn_Ppk=%BHtH30 zyn`Zjvb#XR*m=<}-g66IOkeoEZu*ipttzzLP^I=pu8i0Z{*45!WWYWD-F+P>8zQ;h z@$uQYp~?loP(c(O(VBI}1fi6&WxFOJ5ce41({Cs}&;5A?xGSXico`*gQdI|>Yasph ze#+E<4RPQd3Dx5tJlulLAbNdn2lT~AT&#D2=5u{*K&J}C(%5GepfBy94$>}5mRU#! z^u_6mZ-yBQ_Z%|@IL$iy;?&zu#ZLQT6xW|oF%ud6QfXsrkcb0W5fiW~$F4@VlESf* zLe} z?70{j!fC%0y_RGCZ+MW^KK9O_9&|ACY?FR83La!*06?1x2!#wlk!<3|5;U?4aVk+g zv(!d}OEiu(S|ZKdDlrT=7Bqo)-OR%{a2$SC7%_@4XtJ9*{S{bGvwoKQ^ox-Q1L1YW zf~ILUN3$ly{{Zjp+BP+<1%+70ciyLrH0%<|Va;Co1V6jfrxN`Tr4W%xMv-F&(CF2Y z^0>HC5_d(l|8_mz{&UndEcFtFag zlP@YJ+owzpcb>j=6!lYM9^2JCQvPT8`&<&D=UHL8`F4Fk_g0@nR@h>znmyAYCvb-$ zoxq;f(EJM3Pxh@BTOQOKj3}x-UE2z-J7e?C?e?Yf>v-o1`yA+R&x4vpS1T*0Ot;V{JrzD90u!jwz z*c=uGmIqSJWlC!Jsfexjg`EdCDwya*zj;114!y?9UVF8{ramT$&2u8e!{%J2okAst}+DUaL zfN=u8>cYcdlBr3)j*nWw6WIo;wWpFRumD0P=Jw;!34*tDRUZo?rc#so4m=L+HODIX z_r2rmhNiT&9K7&8MW(|_EiBQ9Q_6WiEI(!J1jg;$`JkhRdS*KYqpIdQG0V@A1vj0} zk9Q8Qauq8-ih3T|GrrYz>~utx#m7#_?EJ@G6Ex^`>8g^Ekbby)&c^Sc-A|#;v$L+e z_`soA>tOrz){loiH~sV%`Z&_fD?j6@10DQo-}!(`8wXOq-J`utZ2)3sKXi9w3Ru7Zq(A@dp`8bn zh)Jb}JEnCW zr!t%U#g@xo@p+eI1@H+Ow@xdW{TUcZQiwqIXuP_a9rUMa7svAvZ*-H_&u1hjXg6W3 z07WAE=81FT{dqPuhmVG`7D74Y51|0ISKYi_>a45)>r1|x4YypKk_)pb{v-+ug11OA zwGzUjLqhRks%Q0r;uY)NOqlDg>NW>XbIqE8J z-NKAw)(1!dAt?L*VJAj#0%S7#C*`)R2q`Z?@^>r2xK}>1yUFZqf7NN*Tx_nZJcBsI znYd?Uhb1RuMWL!#AGiDNI9+9VefTEVZCZyhyfI)g7losjl|8@x>~`roDn~mZ9#IA; zb2Q$tm9i1UI(A``f2B(TQehmbV>t54H|WvKjsx18=AC}3-O|WStXmEg+>H+C?Nrt; zFi5vce3 zR_T*sN9(mfuJ^>5x?byiqfZVC{llz4<)dF@b-!0+_X?}HAq9WabFCNfHl*oLn0d}1 zWD0lg_NLPNKVfBeW4v-V1u)Vy=AYZuKzedkj$J>elA2Hp%f@z>fIWL5@b&1+F(6jg z=$y_~^18|5x;l4@h=!`f7=5qP8N2u(#kiOvRcVK39=F(PrtBBoyxBGGAAWaL`@O~D z=%T{=fK`bV!|V=m#7X{qwN9Z+^q1D|oaBuM3gytc#DUa2i?+tdLgSj|=6ossz7axi zy#~F?EgEvtw_=91(eR*2XOkTa-i)z*)J>liqU)>6PZ@L{P5kSmzrFA^IAFrxR?4&b z%^KQm+XTZt)IPGt%24{j7Z{eX3SxJFS+mh`nk=^m0=NzX7^h7Oai4;$}oP zJaNazD-z32PjfKbusnaz{leCr9u3y8Q>1+rwALqH9&4injqmnNU6;t636$Qt1OZ1TvMo1)#`m`C zf7!jFzTKl=97_q`IZV4^>V7`P@eDCz_w9Q9pUQK%*i9AKxp|UXrZG-c(BX4-k6ehh z<~r+S2H6|K;>Pu^dTC4FRs}b&R>R{z9(#tP2$}8Hflt&o{wtI zoR>{5@wPeq5DMqTlKhp+*?T(;cYEaJ+UY(v!R`GPPA=Du)HeVtu%oMXlqsA(g^uZm zmy-aS)V+qY!ScFDKyLg((ZiIeaD&H2SIjor$W$Z5y5mR)+w|?abKY-20}U&W;|KMP z%1Rqc9w+X0U7pGbt2nd0V5j3|p!U&F>Tvho%O#uZZ{Lx!Kab?R?<>2gouqR3>fNLZ z-8)B@-_WjT*kJ6o?|!?Cixd*R9j`oeUl#x6mL$Yj> zE)6+{e7|ro$1HGQ@JD^=LbB7$b~Nz z0J@%XD5c5-wL9AS=WW@q2LljNJURx&_uF*eKOAbYo!Ec!-TS~d^Ftlo`$JbRN_5{- zh293EaL;iYVq?qo(ZHdjj+1>6ax4+(^hIe!U7cj4!}8^S_EyJ5AC@1z zE)M(W@UvR2i%Tg1ZqbK3Jn#OxApQGWDwP|OABjloD2Pr)A=FB=MC||i4YXj8vuU%| z{)O~#F0@d9Tz+)ObMqcD&YnOb*zQdx`g>towwj)0>zI+kln>jnIp$z5pZQ78R@G?k>Qy_PufV3-Iyr<%&8%gs=)3kx%`miq1-%tKS}PYFni4k^oc_wGoK&nTu=(w1lM<=it8a^)2SO-B5)U}tQivgP9f&j`mMGFn=>@P>J%^<(zjn3U>iX*D-b37kF2m6-uG`S zaZ{VVi_0EkZqkmCjY->%?-(cT35ZspRcn+VKR6D|g~2Cw@pe8ve7Hz+^=h{kA)8ZV zye>x9XA~5(fU`?`izjMY6ZTl9N33wHjfy#b#G81_zfNVU_Rbf74@%{^0Ag;f_9Y09 zq2rJ)3?f;1Q2n@ae_d|Bm6f6T8!G1OP0*<*Bb}zwU8~uuT#8{;KnV60KO6^B6DhTR z>oTE6ydS_5x*Qvka?+2D9=YLD+Ge&l=SkMRHqH5)R+^OHe_W0_kQ(-nC+?0i-RE>_ z&k*-xk>(EL{AZVs>pkVULFs|&-4KsR)`(YT4GqEl>lAR^4};X2F| zAwvLZVeFO9**b2^nR6tLvw%PJjkG<@=HD7;y@^<={A!I8v3+OaQgqjt6J;gY5p-Ca zk{VxZwBuW5%GSE1+oyM&V;Ofs2~J1w`e)9IvP>(Fa>8RdzBX2);Mr-CAp(>qwrnSv z|EM~gcJ`33bld8KUrlyKsh%ARcdHw_{y;#k;~7h;)!0lg(ad z_nRK7_fRFw0OIHQ>ShS60=i3A3Fzy zTKEPMBkTt41*2IK%5`)o+K{ z(9meCd_qC%E}gB#9x2Le;qq8iLh<27!ZYLyM!-ZpgcBL%-h(_n2KM&uW_gvkc{_V} zDtJRpLzxqCAoJX|1bM{H7p-5^Yfp?rv|oX%y4BIsw#0>b#Rq1@^H-n|)sCV~+w-Nd zD!CE%+ZF*^K1Ax&z=*f!v(eUpq{F`Ibx*PGWA<{5S`l=#rybFXf7XQL>bQE%BkcUXtO~xura(&7*y$_G-|COZm0Cfhg$rlD~(_eqGg7(VMbLlP|P$&@50|u zE!SgHONY#2B!mDnnLnv|y=}$pEh~3Z(sxu3MjVl1P?RNZ!z5G`sG)a1i>h-WV#bo7 z&dYE)PLa6c6<>=p432eS)W01of5<$%es6xbVZ6Y`z_RaCbD6^{bA^ZD##uzH5NscW z{&O;|xwFnDl2|4((Kbwn>a6w@7ZrO)Ik3{XUYB?deA=R|;<~cm(#{$U#=f~t`{p$w z^)mw`7R1J{(RJpg$L(B>kFGf~b7ae<^v>d{I=Cv-70?H z)+l+q>$4|z3|lp5ZT(qs%D=(};}?u)H@%)JtKk&AEZpiamT$7h;;-w#DWg8xzE%0( zGo9B9<>!RYrlTC5Z_uynm+fdk?)pJEx7O)t5oJw09eC~PpoGX#pgA0^W3h6R82JM$l z_#pPrWonzS<2p|Iq;W&62Fb-w|&6IBfXr zSF^0C+l_EyV~^;SfsNO7x1M4pFp^|L5_Jf*9TQ&y33jeG)ek&V$uCgKv}t%;bO?j(ERX!?=+=!X|HLvczxTA19^I1tK{D7=!lOE!A8 zF7eE&j%TKgExg9e82J8$d!$tS<4ZcEHNiv>{J#m0;{PT*08kCog699f3D2;gQB?5% zO?aAami*s@XC48{DBi`|0kR2CrCInnZ8QGsTh-M6oA8{ycLEoG*$wG#d2ou(#YdV8 zU)`bBqw{8r=UDCJY`)jjg6Hk0(v>}zb(Q_au`Wl;}810lF@-SrJN7QRo_=A1wa+>*pGpv~l_Omv3ud zY~09SUc$_8W*++c*!eO24Di5x${zGj&Ls#57!uOQs7H#tU9q{hIYPAc)#&vR$0mSMxcF_s-+cX7hcdq}W zLgB`oefri{5x+q2Jb>?ca4M8jxRt-IF2=ML%CF@&l$}jZEKz;h`m`iDGCh9<3E+cR zZB^DS1)@-bQNTGM4WKbu%45?F6lSVB2=Q$XteUGhBd)t`-kq&OCBEZ#@(#EpL-dM+#?A6imGNd=4GF>JjG3nKCF%TE#oEUy@6zhF+T1?VS@Os= z^0eCf&4FeM+F8ZDVSNFgYw@*Dk9zcFaFpr`k9igMLerHr-N)hS^-qnL@SsW)q%;dF6H{!9>W)=tcYsi?1P0P&}C@fQ9Y zm{V8qC~q`;ua8;%h4`rX3Ff-D--a#Y-ovEch000wBoo)9Q;TU@l7F_3>g#!m$w(o6 zg0%;yv&0*b$5qYl>9F)8c0Ip*U9YDZEWl;>TS~LNajRlNj+f9sSM3Tkku0eYbmB_) zP#vB~Y**j7A^)89@SksG7r|!U1CM0}_Ga9(^lh~F z3~g_ROB|U?DbJ?TDRq*Xa3CUd|~d{})R2Vv4h^iRrMnaBVtzYBLeCOaNNHtYSC zpjSyhF;JNwPAiOv{iaZ;qG`o@8<;aH-F-8c?Pg!;os)1X=?TD|vhFa*0*;9&h%ZmH{Nko3YTu3#7uLuB&V@&C|LP_Lyy`Fp4PP&Dy~RF zl;V0;Zn*a9lu_GR(wZP@#HjK#L#3(EV2gNCUy`)DmkZ)`~=5aj%Fi764mrFsp z{BqRuzdLti@%E{D`iwEA11wBf8luad>si`O0X(XoQ6UT264$@t3r)4F*H?N!P-jY` zu@$8K`a;+}Q!Ya+2k4Xf9)0B^ZFj+ty)IoTCxfB2;o3NQ5B7GG^_*TyG}j|F6eA-% zbK({V&>pqhcw<9C;xK2y!pGeF( zlqh{N_s2wUMj+K(ImPmm;6R38mGB5=gsf_QTY}owX&CsYq)DsR@yt?vxL!g!mSHAq zozB`LbFT;Wd-{*g|D&XS?L-}PZ3`mvLIZ9mi#+~L9r2$f>1}U4>MEGJcQ_i-dP9MX zyIA;~=1)86Zj%Bsr}bvTw3f8hPh-&*gOzy4ZZ2*%J(5TSrBfno$xYbiS9kNz8m=w$ zFb_Nn#QJ=9I9B|Pze0hIsG80=<&V&tSrn@3@OhLMhdXv|>*i#CxAFO-ZAZNE z!A-EkEcox?yAlnkZGD=+biK#^qRUE~uxVc@|E$6p>4wTS&JAA(dop$^j^k83mK%#- z8j0ir2Xvd}_pXF}#`TW#QH;)AMT{3*CAg@-^#IG&yGEpKimUV>78e|fTrwE816KC= zDdp*|rTqOq8u+nWFIx=gNn1r}iipyl_9_m%A6s$c;H2WQBuk0R8t_SeLBgS+3i*>@ z7y!DG`EaPCKWev`@&EeJeE_!8LMO&X%$7pio#^U)RNOncR(G2+gMXU$IqZ68K=4WS z{a}`bVk>Zj7@Jh}LLoX~Nu;_a+FN~Sijeawq#EsFj~hro+9o>(tD>C&XIWczK4QSn zX8p_&V!ro>S2;KToNRY}cH`VTE-`}Q^|Z%>Jwi~IET$F-)OqX%ZfPj`g3H7v3TUQ@4hz5@oHGKw?g^GJNyIaS8Y zj<+xT&oj#oT#8lto|4%<0W3*@>12Da)~wdVTltS@K9_%_9Fk8xs`vI*2I^&Cs-BPg zr1{!Xx81;3NwO(cOou(}jBb-9HmWrG*#6zkVxfYOQJp_3GK2S+Z636K<8c0)%I~FB z@pDAZ(bWSRh?nJWr+YXT9(fe5^%Rh}>L{$u>^0miGf+KXl{ztjxY_Jo#XL^Wod}sEX8wp$X)@}Zlm$dBd?5Q-c?(g@i zE7=T(wLFzmx?doTg4dgOX1$nu30&B?{O0k`r59SK7KdA|f6bI0xU=%_*BdJfSGjKd zc{g18Yy82#Z&v*u$iS*BLU3{mx?KF9hX$I&|%=XiG%ZNlz8GggE7o822hLH$bg|NeJ z)g-$L_NO0dIap2z25>xWe)3`F+bcxbaAsaz!+k(S5{kUvl^N>D{Y>KeRdBO^WEjk* zVG!_BKcvmHU%g~Y7VEl&*?Sf0hN|bT@dv#MLC5fnH>9m(N^XE(t|eX5(;ycWn!9Eg z46oq=xV#u#J_P2+(1>3Cn%m4YBcknH$hq5QGn81FngVDrm$W7sm<83z^xX8`{O!Yr z5xBhKn!?iA{Cue__PZ1qQK;)RT%ak*v-BrcQoS#SssaGmJIIEn)F27K*WYVxd)Qk7 ztR<7E-?mgf{I6wveYkz zm0TFmQ59;qT~e_#$e1DRJ@lkl0v@)W0`lm1OA6pa+0P@QF4vGQF*L#j(3&3^T?vS0 zr`l~Zz}{c$Y8GtOsNqNFSk@f!=+yuuaTp0YCmB$qfEX6mhz@!az1R{vYyeT^9WlTZ zxW&~vCJ0*FqU+YFzENE8%a0gCCoD4+0^b_SK&oGWoTeDUNbs-9b=x1ZF}9W8rB_Nd zYuK4#&QTNWkSB&T*~uA^5Xc7)jS{8=c7MsFA6W$4Ypf=K+O0;|FEn)0AdyD_?Rkao zbXcpGX!;~G=IIt|npS8ON}dOynU+AhWe^%Gm_~0{0V~ggXrkoe+5e)&A?PF-?ujtJ}bX5TATsoeCfX&jc3gbvwupB8AqqHQJTVfulf|S44 z_vWXQdJBQm=>8-$UGg3<^eiFhyg$w z@}Y<;@n|-*PJL_6RgK%cAk}o32|yAG@Km&Pu3BIUS@%#HG=}93oEk0z;Um^iqgaDp7}7{_IS6yz2in ztFFd`*Ifh=g8eI#$N7ZoKng}Iw}t}9Os-dAG>?v@AUNi{+%F+sdmeCmU)DWUw**u- z>{a}PD1}jM_5vQsD7hkpkWNScW+`a0&|Cp#hUvdRCoow`pXq3go@xvczl`jxlarGb zljZUW6HM$fPXX$5{=>)nAc~g-kPo1=#zj{NP!c0$djeY&nS!}(J{J%|3qn{i!K3Kcln&*i}BnUhZZ|0J$qS!7u6fJA>6s3E}0S4jqwSNGC9ifS_x2vr`$^ z`MPEzp&i(Tp_jVP6AJnG-$GqQD)eYT{x=hE$;ADaPPodzey7XTeNnRfhP>(5e95B% z{L4#XHwh1iNw5CG$BPg`s2H%~U(%jeT=R}Zpa?#e*ROa8s0u-vfTi*S_gzf*BL1K4 zsLj8sTyCq)#QjB-gm#LbmJEx2cB=I20Kb47K18Ip$$CmMiLiYwn8}B9(g6YwBvOFK zj({132b18@C2++Tl3xbkD2Af$pySga3QKNDnvSwY2L?Kv1 zW6j`er<$6;3qPG$bo6&b=@-*x!AI@_pAi2GfW#iZ1PYc!%x_+dh)>A6CRf+5#1I;` zlQi5w>~B8S{3$;D5-J{m>=H5Bbiz4de5fY}TI4l=yD+k;L2Hha8Z##aOB^P=O{byc@MHr`A$nJ`T?D=#8QZ$g9Uwz(FuIXb3!iwl$j96#kzH=q!~7d z6|=4`Z*-RR2qzL91O;m68A$=1vY$JGz)#VIEppfm!of5is3!Yk9u8^{aXVNfUm&e; z|KSj1_h%kfiI2DWp+sk5UP@q9mcrl!Bw|MA644jF8LBc-3w*){mQpTJ?mJ!SGjsFe z!8{+jlIkEN;^Q}e;Pdp_QP?~?Rltcp5EjkVW@PV)hpA2xN)WA0n zm5)pOzEIGys%UMhLIoepW9_~$3eTo10>BHSPT>!qCs_?*ukaVYF~YTE;QW! zLUyBl@4LC~dIo67W7O*dYxv0Fq#G!3FE|0XBNs3=B^RD!3-}6uWGNmx{QbvMy&4sF z1LS!^-Ql<7u5#T_Ud4CUhqaw-q=l5smR*toA0H|^3QAbkq=sm%Y?<#X)b!$apWOB2 zZRRn*ff9Qc4Ww1We0hbmvmN28%8+I*$hL(8L?Qkuv(*$b09^0LP~IGIpUJ z8;n06KJzDITTS1sEa0}n;nMS?=VQG`%JE z=>biFdq#6$rP05{k*v|xyVa)~4_Rd0)$DzK>ZI-keMuM2(B>iJ&JXG>F+>WE;QBMQ z50>bC+oI6QvAi$x*nnvFYec_46zZX@eIb7F;NH86Gr`AH-8t;jeTQ5BDM>EUJ>Ar( z7t2LjImmDw7PN2kfBDh+^)K~|#wiWQmzwBLhh8SU-#~s$L#zqA|}i6Z_8liuzCk$55Oa^J_|<^t|TK zoU-j~Y)EEfRx`D!{I&^2{5N2xj?4IZL7U|e*|v9C9Vj0&nqb}TmPG-lKUjd9$&~-UP>oOh4_QLi^Gl;#6g1TdY)8tHVkCY1N)xQh zw2$G#r!+BWAkr)XM*_U@wOkS~LqLL@tBCH&mnvR8MRm%5>@1o^E`G5hRzKnTmT@p3 zv_u?p;1iDVYoSnd2D2evDu6nexVU*mE&~hDshoznXNAaTeov$oXan*mL}M|6GbBFq^IC|lcX8mS0` zF!@l|sJ=fD-X~$QY{P`|Y78?XYRjcoBp)zyqRsOYE^N81Fzo9`3?m$Rh3m4T@8mnf znhySPwH5rp6N=h&D)GTF#~fblpWj0lUkVOwzk9OjNnkZQ{@1i4`N!H*S95sN9}kWb zPSp+&r37tO08h_pAF5!wBndDb4B3l2_xlO25nQ!VVd>lY-kC>5K$-~c_u1cVt772I zdKdR?(^b1%#820q1p{C#os9U18Sb24Nczpk5pH|`{`n)_S=_3oO=(4+_n`9S;L9>^ zQoRQcK`SBiDFDP%SqNGYxr?BbYg#Qh99Cmvw}s{fk8I5*kt!#9wnd+McGMfdYcfmS zzOI0UCWW7D>3SO$8+{PRU*KEaga!S#4SqC)Q?YX~W?JJ=Yjh*qXQ`IMOn8t-Oru}ZKGj1R=Q~o>4w}K)4l#|` z?6dFQ5<&VrmX{@~6w~`(le*4Am@ zr`=`@l4ChdC8gT%09obH-@x4noaG53u+lHUv>{8#ms~zG+}#$v<_>GyD#ftuA!I!c zD&U86+%F}a`YBITDJjvskB0B3uwhqO=!>flks7%QFOAqzK6XM3<`-?Cv_hQ4JIDAZ zaDAr5W_^Vz`GDNHe&7(rVi2f(HX3DHbs@eqfjIBt3sdH&(5H6NdSxzW>d#whho+}| zPesvc@-}RbihEFe$P1j($*zh{7+X}D@6>P5Ofl}wzby>7cd~2VVJh->`QX~Rpm;Xd zX>sICo_W+PM^0&?2)l8ybyz~^P=(6BI^_b*0ugBVre_(e&TZ0Ls`qKh&$CddsW_PnOPEzDx9Oy7jx%~E zv;0eEE766s(>T5XZY^C6Q_A*Q)d7tqghcD*wD)VBn<@wjoH#X@90=sj7aULkP}+gH zau}c@((8)5&AxK;5L>xH+C0%2hxOBL)ls3OuQ2tIc*ArAB#PO}9O3qeB7cmT#8aVa z2DM@R;{*pZSo)An`P?~VJo5?#-yPr)`q>t^Q0D;lN&_WX_H^rN>8E<8=j0yk}|KSls~}>bH2K=JRx!tc--&fdpcI zedAaomfzicSTYTmjDdt)Dc@TjAr{AE=&w0%@G>ya0MSkVst*;O$X4K$gtX>4X!R-4 z{-HexM;|fhZKMZlcefWbZ*ezolneNE)w;mp4b`cWdH%kOz`FQjsFQ7wT#jmA0olZ& zml5P(Z_zN+e$J@K+$`Koe41ymb>GHoZ(l5HsCh;<8~k?J7Gd@N^om8R_38a2hT9c8 zbkm^M)vA^}%}0+KQ|PA`d@T4M0#vK>7Sa@j0Mij+7Y2^$Z_>QY)TFA4>U+$NY$L3v zt5<80_C}_euZ6Z3TkASjTRXYj2_&CmKgTWXvAA|@FFi@Q`s6pKzt`_&Ob0keANbyO zYQ1G!UF@UNx;N7-bNr>@>oiGqAJjSnZl=2T`&W(Fl*H^kl9-n79IAVyrNz6tWy2L* z)Q(G$n5%ecDeYp=Vqu)NtF`vY~k8oLAqc>QSU$X{67aIkvs!Jb>5 z=5H>x9od>192S0O6aVO5-}4(@{aiO4A{#cH{N$q~@Kflqkgcy)V26zwdRCw5obm}+ zuY`avY!aJn-OW~i-uK^yt~2efkJE}PA63P%1{yateM)~}mrFOE2gkR~`pn3Md~f*P z|2UzD`@L?8zIVcjov2o>BR%ls#*j+0!#f!qRZx@o@s_OBf@2g-AFIam`>SenlZ_)S zkgg*Xxu~wNRLG}7ITtO|NlrZaCIavCSpQ<=t_IVAP3o==7KCJxP6P`fX9$lgj>uA_ zKq1HkK-t7zoe3a~ZW09)SWeX%=uu_(K=HfS>!ZHo3WE&S4CIeGYDaMY z?mBt^uvzoGN#H#&&a5^cP$iVN1a|wLI#|QStIjKXu-;s??h|QWaZ39lzI#e{7b0sV z2+mI7m1GH-{Qkf1gKJo2nZq(DW1g~-1DIobypp=@gvFC`Y3@&cmsscHyng)TsQS~< z!TW^=pTStEPP0&HC7FTGU_v@sl5A)#7-GbN&ZH|wDQsS&7V73(=(AGQA$jlc4a{Wi zNr~=~;HK+hf}B#R5bI0}3Rf88hxy(sfM>QX|f z8p0>`@dx-u(*T7A=**Dih=>?EHin=b=U4snd$jnF4@AQV^E`e{70amLFv-L%k6AvO z^a0lCoyXq!Y*PQw)`);vy4A4Mm^)DT;^qD~X$;GQHaNPqv3V0+k)E#asz68Y01)|4 z)Ud)t`pFs-f0V-x#5HVxWX!S~qbVKO6V$x-!M$S%GFG_CS&03cev=Y0dUOjQWhMNU z6nM_ky|3hP;7f;2)U7~guM;fZm7T9^E2N{$#=O-n=?Sz}q3kZ-7H<`O$n$zXXV>6A zy4p(|kLgcG)2+rmje>uxz5G(<>~;fl8J{2_oKpiTW?{F_)>maASRU$R zg)?kcUP`?#@^)*X7JN2ut%_z-$Qtl4qTo}~-Fi|1hjK;$*H$;*Yn-SAt}ORiNIOH=WrkLS!ch666eRO~ex@G9$!Z9-$}HjAfskOQoW?UY1n5IJ=ro7Jo~?4(qOHixt1NQf;jeLoWC2rnIbsx3D3d0I9xU%^`ON z3Uwng0$_ou`LOd;FYo_RbT4i#{{J7q-}mmFT5DUiYMs|QA1y-XwzZU0)&WUaN#(o} zLgn5%tpgQF2oyYAih{W?6JEI*`re*ycB z3ZB=b9>Eoq@9#L|5R?0wac_|m>W0VOd%NBz9r>)mc^w&#-x!V%*VX=@! zg9yBmm(o)-mS_nOnvAC_9WSZ`EYelaL69c8Sc92Eb*@=8ts|C;GJ8cU2LzW$2`kN58Dfl)3P!YnqmIs0Hkrq--RsuX7-||4#c9Mfc80=qB@QnJqW%Jwm7fBpOfe z(xNQ(vKu|fC)d1BShL9Wa!8{JZWg88vnJku(Q+y{ zszoBmMfmm5k>v~Zq$jeHSq8fOJ38y8%BzO_mFZ6l(VJ8eB{r%@%jt2^ZnM52%2T^k zx8(G-qT3~@RPT>lt@!B|f6||WCux^~T`+Feo)r3_B!74bIBHYlp>F4xi)ibO!o}d0LonmTebb%H~$x7OCDo^{SHMCg3#MYuLPh zZ1v2pVO3MZc;9I?xQ)~4@YJ>DTm11CKKcKv(0}u^i@1W$f1uVS*hpTd)g1pydlli9 zhIe^L&drMvt(g17-JWBCjo1Ift{TvWpICoN1UTv8httQz(XTb~szc2PZ?~3@eOqlb zavl9`!*b!m293sddYTz}^3{&SqlAT{i&gqPn%Xj>jweSw(z7l~K(xnS;0L{g|D94j z{RrH#Y&|7LPHog$&DXkSiHp_3^&loS2##ibss^x>1xEyNxmu_$Hbu)!SA6UE*c3ha z_rkmq?sN@@U3mq&pGK5c^=8Ydx1;ihFIM$lr7)yjc#onxm#=wVCC#6DQ9M*><95|_ zr+CTKd93TTTzrhg!?-aSL^fv~PD1_Iok^FzoDW}Aay~s~D_C3)FYOAf?78G0v4}25 z1nmbFbnb|DU_@YTOwck&1)o71sw@i?x*-6i?4`^SaPo(&aV>(P zBgBOm*OH$q!=>S~Lu1#kh7er;X@)KH8}CX=L}^g%>cKwyC{1d^XjiMx+u%>xX?~jZ z%*dgI)`9zFH8ASZ3^vGOw5rE))htd4XNJZjZV9K8oo~?l->JZ^%>P!+BYISq93r!3 zFpZ8YhDO&vJlk>q0X9AzQv=BO3=da-LncJ%L21f_7UPKRDj|UdQIlB%+sISy%0EH^ zDL(U{0>K1T5}wIo8` zY*r|JYY~Lo6n-;wZGoL1+L(C~JBv+92IZc;$CAeb)Pefuc%ez5>rghk2bES@l_r7x zm|vy`HW&QVEj()vNg=~z$c-MmrAwKyaunN~K7R!~iE@Y03YLi3^yWBTizVCwKgyyo zmOVv>%%XA0$n#1lwI+Kke&z0=o|+P>Bt-H*S17gg&V32)Pz{d3%0ASLJH*VUBRV_e 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 E0A$d1(EtDd diff --git a/vendor/github.com/rivo/tview/util.go b/vendor/github.com/rivo/tview/util.go deleted file mode 100644 index 3bd0d3d9..00000000 --- a/vendor/github.com/rivo/tview/util.go +++ /dev/null @@ -1,630 +0,0 @@ -package tview - -import ( - "math" - "regexp" - "sort" - "strconv" - - "github.com/gdamore/tcell" - runewidth "github.com/mattn/go-runewidth" - "github.com/rivo/uniseg" -) - -// Text alignment within a box. -const ( - AlignLeft = iota - AlignCenter - AlignRight -) - -// Common regular expressions. -var ( - colorPattern = regexp.MustCompile(`\[([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([a-zA-Z]+|#[0-9a-zA-Z]{6}|\-)?(:([lbdru]+|\-)?)?)?\]`) - regionPattern = regexp.MustCompile(`\["([a-zA-Z0-9_,;: \-\.]*)"\]`) - escapePattern = regexp.MustCompile(`\[([a-zA-Z0-9_,;: \-\."#]+)\[(\[*)\]`) - nonEscapePattern = regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\]`) - boundaryPattern = regexp.MustCompile(`(([,\.\-:;!\?&#+]|\n)[ \t\f\r]*|([ \t\f\r]+))`) - spacePattern = regexp.MustCompile(`\s+`) -) - -// Positions of substrings in regular expressions. -const ( - colorForegroundPos = 1 - colorBackgroundPos = 3 - colorFlagPos = 5 -) - -// Predefined InputField acceptance functions. -var ( - // InputFieldInteger accepts integers. - InputFieldInteger func(text string, ch rune) bool - - // InputFieldFloat accepts floating-point numbers. - InputFieldFloat func(text string, ch rune) bool - - // InputFieldMaxLength returns an input field accept handler which accepts - // input strings up to a given length. Use it like this: - // - // inputField.SetAcceptanceFunc(InputFieldMaxLength(10)) // Accept up to 10 characters. - InputFieldMaxLength func(maxLength int) func(text string, ch rune) bool -) - -// Package initialization. -func init() { - // We'll use zero width joiners. - runewidth.ZeroWidthJoiner = true - - // Initialize the predefined input field handlers. - InputFieldInteger = func(text string, ch rune) bool { - if text == "-" { - return true - } - _, err := strconv.Atoi(text) - return err == nil - } - InputFieldFloat = func(text string, ch rune) bool { - if text == "-" || text == "." || text == "-." { - return true - } - _, err := strconv.ParseFloat(text, 64) - return err == nil - } - InputFieldMaxLength = func(maxLength int) func(text string, ch rune) bool { - return func(text string, ch rune) bool { - return len([]rune(text)) <= maxLength - } - } -} - -// styleFromTag takes the given style, defined by a foreground color (fgColor), -// a background color (bgColor), and style attributes, and modifies it based on -// the substrings (tagSubstrings) extracted by the regular expression for color -// tags. The new colors and attributes are returned where empty strings mean -// "don't modify" and a dash ("-") means "reset to default". -func styleFromTag(fgColor, bgColor, attributes string, tagSubstrings []string) (newFgColor, newBgColor, newAttributes string) { - if tagSubstrings[colorForegroundPos] != "" { - color := tagSubstrings[colorForegroundPos] - if color == "-" { - fgColor = "-" - } else if color != "" { - fgColor = color - } - } - - if tagSubstrings[colorBackgroundPos-1] != "" { - color := tagSubstrings[colorBackgroundPos] - if color == "-" { - bgColor = "-" - } else if color != "" { - bgColor = color - } - } - - if tagSubstrings[colorFlagPos-1] != "" { - flags := tagSubstrings[colorFlagPos] - if flags == "-" { - attributes = "-" - } else if flags != "" { - attributes = flags - } - } - - return fgColor, bgColor, attributes -} - -// overlayStyle mixes a background color with a foreground color (fgColor), -// a (possibly new) background color (bgColor), and style attributes, and -// returns the resulting style. For a definition of the colors and attributes, -// see styleFromTag(). Reset instructions cause the corresponding part of the -// default style to be used. -func overlayStyle(background tcell.Color, defaultStyle tcell.Style, fgColor, bgColor, attributes string) tcell.Style { - defFg, defBg, defAttr := defaultStyle.Decompose() - style := defaultStyle.Background(background) - - style = style.Foreground(defFg) - if fgColor != "" { - if fgColor == "-" { - style = style.Foreground(defFg) - } else { - style = style.Foreground(tcell.GetColor(fgColor)) - } - } - - if bgColor == "-" || bgColor == "" && defBg != tcell.ColorDefault { - style = style.Background(defBg) - } else if bgColor != "" { - style = style.Background(tcell.GetColor(bgColor)) - } - - if attributes == "-" { - style = style.Bold(defAttr&tcell.AttrBold > 0) - style = style.Blink(defAttr&tcell.AttrBlink > 0) - style = style.Reverse(defAttr&tcell.AttrReverse > 0) - style = style.Underline(defAttr&tcell.AttrUnderline > 0) - style = style.Dim(defAttr&tcell.AttrDim > 0) - } else if attributes != "" { - style = style.Normal() - for _, flag := range attributes { - switch flag { - case 'l': - style = style.Blink(true) - case 'b': - style = style.Bold(true) - case 'd': - style = style.Dim(true) - case 'r': - style = style.Reverse(true) - case 'u': - style = style.Underline(true) - } - } - } - - return style -} - -// decomposeString returns information about a string which may contain color -// tags or region tags, depending on which ones are requested to be found. It -// returns the indices of the color tags (as returned by -// re.FindAllStringIndex()), the color tags themselves (as returned by -// re.FindAllStringSubmatch()), the indices of region tags and the region tags -// themselves, the indices of an escaped tags (only if at least color tags or -// region tags are requested), the string stripped by any tags and escaped, and -// the screen width of the stripped string. -func decomposeString(text string, findColors, findRegions bool) (colorIndices [][]int, colors [][]string, regionIndices [][]int, regions [][]string, escapeIndices [][]int, stripped string, width int) { - // Shortcut for the trivial case. - if !findColors && !findRegions { - return nil, nil, nil, nil, nil, text, stringWidth(text) - } - - // Get positions of any tags. - if findColors { - colorIndices = colorPattern.FindAllStringIndex(text, -1) - colors = colorPattern.FindAllStringSubmatch(text, -1) - } - if findRegions { - regionIndices = regionPattern.FindAllStringIndex(text, -1) - regions = regionPattern.FindAllStringSubmatch(text, -1) - } - escapeIndices = escapePattern.FindAllStringIndex(text, -1) - - // Because the color pattern detects empty tags, we need to filter them out. - for i := len(colorIndices) - 1; i >= 0; i-- { - if colorIndices[i][1]-colorIndices[i][0] == 2 { - colorIndices = append(colorIndices[:i], colorIndices[i+1:]...) - colors = append(colors[:i], colors[i+1:]...) - } - } - - // Make a (sorted) list of all tags. - allIndices := make([][3]int, 0, len(colorIndices)+len(regionIndices)+len(escapeIndices)) - for indexType, index := range [][][]int{colorIndices, regionIndices, escapeIndices} { - for _, tag := range index { - allIndices = append(allIndices, [3]int{tag[0], tag[1], indexType}) - } - } - sort.Slice(allIndices, func(i int, j int) bool { - return allIndices[i][0] < allIndices[j][0] - }) - - // Remove the tags from the original string. - var from int - buf := make([]byte, 0, len(text)) - for _, indices := range allIndices { - if indices[2] == 2 { // Escape sequences are not simply removed. - buf = append(buf, []byte(text[from:indices[1]-2])...) - buf = append(buf, ']') - from = indices[1] - } else { - buf = append(buf, []byte(text[from:indices[0]])...) - from = indices[1] - } - } - buf = append(buf, text[from:]...) - stripped = string(buf) - - // Get the width of the stripped string. - width = stringWidth(stripped) - - return -} - -// Print prints text onto the screen into the given box at (x,y,maxWidth,1), -// not exceeding that box. "align" is one of AlignLeft, AlignCenter, or -// AlignRight. The screen's background color will not be changed. -// -// You can change the colors and text styles mid-text by inserting a color tag. -// See the package description for details. -// -// Returns the number of actual bytes of the text printed (including color tags) -// and the actual width used for the printed runes. -func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) (int, int) { - return printWithStyle(screen, text, x, y, maxWidth, align, tcell.StyleDefault.Foreground(color)) -} - -// printWithStyle works like Print() but it takes a style instead of just a -// foreground color. -func printWithStyle(screen tcell.Screen, text string, x, y, maxWidth, align int, style tcell.Style) (int, int) { - totalWidth, totalHeight := screen.Size() - if maxWidth <= 0 || len(text) == 0 || y < 0 || y >= totalHeight { - return 0, 0 - } - - // Decompose the text. - colorIndices, colors, _, _, escapeIndices, strippedText, strippedWidth := decomposeString(text, true, false) - - // We want to reduce all alignments to AlignLeft. - if align == AlignRight { - if strippedWidth <= maxWidth { - // There's enough space for the entire text. - return printWithStyle(screen, text, x+maxWidth-strippedWidth, y, maxWidth, AlignLeft, style) - } - // Trim characters off the beginning. - var ( - bytes, width, colorPos, escapePos, tagOffset int - foregroundColor, backgroundColor, attributes string - ) - _, originalBackground, _ := style.Decompose() - iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - // Update color/escape tag offset and style. - if colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] { - foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) - style = overlayStyle(originalBackground, style, foregroundColor, backgroundColor, attributes) - tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] - colorPos++ - } - if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] { - tagOffset++ - escapePos++ - } - if strippedWidth-screenPos < maxWidth { - // We chopped off enough. - if escapePos > 0 && textPos+tagOffset-1 >= escapeIndices[escapePos-1][0] && textPos+tagOffset-1 < escapeIndices[escapePos-1][1] { - // Unescape open escape sequences. - escapeCharPos := escapeIndices[escapePos-1][1] - 2 - text = text[:escapeCharPos] + text[escapeCharPos+1:] - } - // Print and return. - bytes, width = printWithStyle(screen, text[textPos+tagOffset:], x, y, maxWidth, AlignLeft, style) - return true - } - return false - }) - return bytes, width - } else if align == AlignCenter { - if strippedWidth == maxWidth { - // Use the exact space. - return printWithStyle(screen, text, x, y, maxWidth, AlignLeft, style) - } else if strippedWidth < maxWidth { - // We have more space than we need. - half := (maxWidth - strippedWidth) / 2 - return printWithStyle(screen, text, x+half, y, maxWidth-half, AlignLeft, style) - } else { - // Chop off runes until we have a perfect fit. - var choppedLeft, choppedRight, leftIndex, rightIndex int - rightIndex = len(strippedText) - for rightIndex-1 > leftIndex && strippedWidth-choppedLeft-choppedRight > maxWidth { - if choppedLeft < choppedRight { - // Iterate on the left by one character. - iterateString(strippedText[leftIndex:], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - choppedLeft += screenWidth - leftIndex += textWidth - return true - }) - } else { - // Iterate on the right by one character. - iterateStringReverse(strippedText[leftIndex:rightIndex], func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - choppedRight += screenWidth - rightIndex -= textWidth - return true - }) - } - } - - // Add tag offsets and determine start style. - var ( - colorPos, escapePos, tagOffset int - foregroundColor, backgroundColor, attributes string - ) - _, originalBackground, _ := style.Decompose() - for index := range strippedText { - // We only need the offset of the left index. - if index > leftIndex { - // We're done. - if escapePos > 0 && leftIndex+tagOffset-1 >= escapeIndices[escapePos-1][0] && leftIndex+tagOffset-1 < escapeIndices[escapePos-1][1] { - // Unescape open escape sequences. - escapeCharPos := escapeIndices[escapePos-1][1] - 2 - text = text[:escapeCharPos] + text[escapeCharPos+1:] - } - break - } - - // Update color/escape tag offset. - if colorPos < len(colorIndices) && index+tagOffset >= colorIndices[colorPos][0] && index+tagOffset < colorIndices[colorPos][1] { - if index <= leftIndex { - foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) - style = overlayStyle(originalBackground, style, foregroundColor, backgroundColor, attributes) - } - tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] - colorPos++ - } - if escapePos < len(escapeIndices) && index+tagOffset >= escapeIndices[escapePos][0] && index+tagOffset < escapeIndices[escapePos][1] { - tagOffset++ - escapePos++ - } - } - return printWithStyle(screen, text[leftIndex+tagOffset:], x, y, maxWidth, AlignLeft, style) - } - } - - // Draw text. - var ( - drawn, drawnWidth, colorPos, escapePos, tagOffset int - foregroundColor, backgroundColor, attributes string - ) - iterateString(strippedText, func(main rune, comb []rune, textPos, length, screenPos, screenWidth int) bool { - // Only continue if there is still space. - if drawnWidth+screenWidth > maxWidth || x+drawnWidth >= totalWidth { - return true - } - - // Handle color tags. - for colorPos < len(colorIndices) && textPos+tagOffset >= colorIndices[colorPos][0] && textPos+tagOffset < colorIndices[colorPos][1] { - foregroundColor, backgroundColor, attributes = styleFromTag(foregroundColor, backgroundColor, attributes, colors[colorPos]) - tagOffset += colorIndices[colorPos][1] - colorIndices[colorPos][0] - colorPos++ - } - - // Handle scape tags. - if escapePos < len(escapeIndices) && textPos+tagOffset >= escapeIndices[escapePos][0] && textPos+tagOffset < escapeIndices[escapePos][1] { - if textPos+tagOffset == escapeIndices[escapePos][1]-2 { - tagOffset++ - escapePos++ - } - } - - // Print the rune sequence. - finalX := x + drawnWidth - _, _, finalStyle, _ := screen.GetContent(finalX, y) - _, background, _ := finalStyle.Decompose() - finalStyle = overlayStyle(background, style, foregroundColor, backgroundColor, attributes) - for offset := screenWidth - 1; offset >= 0; offset-- { - // To avoid undesired effects, we populate all cells. - if offset == 0 { - screen.SetContent(finalX+offset, y, main, comb, finalStyle) - } else { - screen.SetContent(finalX+offset, y, ' ', nil, finalStyle) - } - } - - // Advance. - drawn += length - drawnWidth += screenWidth - - return false - }) - - return drawn + tagOffset + len(escapeIndices), drawnWidth -} - -// PrintSimple prints white text to the screen at the given position. -func PrintSimple(screen tcell.Screen, text string, x, y int) { - Print(screen, text, x, y, math.MaxInt32, AlignLeft, Styles.PrimaryTextColor) -} - -// TaggedStringWidth returns the width of the given string needed to print it on -// screen. The text may contain color tags which are not counted. -func TaggedStringWidth(text string) int { - _, _, _, _, _, _, width := decomposeString(text, true, false) - return width -} - -// stringWidth returns the number of horizontal cells needed to print the given -// text. It splits the text into its grapheme clusters, calculates each -// cluster's width, and adds them up to a total. -func stringWidth(text string) (width int) { - g := uniseg.NewGraphemes(text) - for g.Next() { - var chWidth int - for _, r := range g.Runes() { - chWidth = runewidth.RuneWidth(r) - if chWidth > 0 { - break // Our best guess at this point is to use the width of the first non-zero-width rune. - } - } - width += chWidth - } - return -} - -// WordWrap splits a text such that each resulting line does not exceed the -// given screen width. Possible split points are after any punctuation or -// whitespace. Whitespace after split points will be dropped. -// -// This function considers color tags to have no width. -// -// Text is always split at newline characters ('\n'). -func WordWrap(text string, width int) (lines []string) { - colorTagIndices, _, _, _, escapeIndices, strippedText, _ := decomposeString(text, true, false) - - // Find candidate breakpoints. - breakpoints := boundaryPattern.FindAllStringSubmatchIndex(strippedText, -1) - // Results in one entry for each candidate. Each entry is an array a of - // indices into strippedText where a[6] < 0 for newline/punctuation matches - // and a[4] < 0 for whitespace matches. - - // Process stripped text one character at a time. - var ( - colorPos, escapePos, breakpointPos, tagOffset int - lastBreakpoint, lastContinuation, currentLineStart int - lineWidth, overflow int - forceBreak bool - ) - unescape := func(substr string, startIndex int) string { - // A helper function to unescape escaped tags. - for index := escapePos; index >= 0; index-- { - if index < len(escapeIndices) && startIndex > escapeIndices[index][0] && startIndex < escapeIndices[index][1]-1 { - pos := escapeIndices[index][1] - 2 - startIndex - return substr[:pos] + substr[pos+1:] - } - } - return substr - } - iterateString(strippedText, func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool { - // Handle tags. - for { - if colorPos < len(colorTagIndices) && textPos+tagOffset >= colorTagIndices[colorPos][0] && textPos+tagOffset < colorTagIndices[colorPos][1] { - // Colour tags. - tagOffset += colorTagIndices[colorPos][1] - colorTagIndices[colorPos][0] - colorPos++ - } else if escapePos < len(escapeIndices) && textPos+tagOffset == escapeIndices[escapePos][1]-2 { - // Escape tags. - tagOffset++ - escapePos++ - } else { - break - } - } - - // Is this a breakpoint? - if breakpointPos < len(breakpoints) && textPos+tagOffset == breakpoints[breakpointPos][0] { - // Yes, it is. Set up breakpoint infos depending on its type. - lastBreakpoint = breakpoints[breakpointPos][0] + tagOffset - lastContinuation = breakpoints[breakpointPos][1] + tagOffset - overflow = 0 - forceBreak = main == '\n' - if breakpoints[breakpointPos][6] < 0 && !forceBreak { - lastBreakpoint++ // Don't skip punctuation. - } - breakpointPos++ - } - - // Check if a break is warranted. - if forceBreak || lineWidth > 0 && lineWidth+screenWidth > width { - breakpoint := lastBreakpoint - continuation := lastContinuation - if forceBreak { - breakpoint = textPos + tagOffset - continuation = textPos + tagOffset + 1 - lastBreakpoint = 0 - overflow = 0 - } else if lastBreakpoint <= currentLineStart { - breakpoint = textPos + tagOffset - continuation = textPos + tagOffset - overflow = 0 - } - lines = append(lines, unescape(text[currentLineStart:breakpoint], currentLineStart)) - currentLineStart, lineWidth, forceBreak = continuation, overflow, false - } - - // Remember the characters since the last breakpoint. - if lastBreakpoint > 0 && lastContinuation <= textPos+tagOffset { - overflow += screenWidth - } - - // Advance. - lineWidth += screenWidth - - // But if we're still inside a breakpoint, skip next character (whitespace). - if textPos+tagOffset < currentLineStart { - lineWidth -= screenWidth - } - - return false - }) - - // Flush the rest. - if currentLineStart < len(text) { - lines = append(lines, unescape(text[currentLineStart:], currentLineStart)) - } - - return -} - -// Escape escapes the given text such that color and/or region tags are not -// recognized and substituted by the print functions of this package. For -// example, to include a tag-like string in a box title or in a TextView: -// -// box.SetTitle(tview.Escape("[squarebrackets]")) -// fmt.Fprint(textView, tview.Escape(`["quoted"]`)) -func Escape(text string) string { - return nonEscapePattern.ReplaceAllString(text, "$1[]") -} - -// iterateString iterates through the given string one printed character at a -// time. For each such character, the callback function is called with the -// Unicode code points of the character (the first rune and any combining runes -// which may be nil if there aren't any), the starting position (in bytes) -// within the original string, its length in bytes, the screen position of the -// character, and the screen width of it. The iteration stops if the callback -// returns true. This function returns true if the iteration was stopped before -// the last character. -func iterateString(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool { - var screenPos int - - gr := uniseg.NewGraphemes(text) - for gr.Next() { - r := gr.Runes() - from, to := gr.Positions() - width := stringWidth(gr.Str()) - var comb []rune - if len(r) > 1 { - comb = r[1:] - } - - if callback(r[0], comb, from, to-from, screenPos, width) { - return true - } - - screenPos += width - } - - return false -} - -// iterateStringReverse iterates through the given string in reverse, starting -// from the end of the string, one printed character at a time. For each such -// character, the callback function is called with the Unicode code points of -// the character (the first rune and any combining runes which may be nil if -// there aren't any), the starting position (in bytes) within the original -// string, its length in bytes, the screen position of the character, and the -// screen width of it. The iteration stops if the callback returns true. This -// function returns true if the iteration was stopped before the last character. -func iterateStringReverse(text string, callback func(main rune, comb []rune, textPos, textWidth, screenPos, screenWidth int) bool) bool { - type cluster struct { - main rune - comb []rune - textPos, textWidth, screenPos, screenWidth int - } - - // Create the grapheme clusters. - var clusters []cluster - iterateString(text, func(main rune, comb []rune, textPos int, textWidth int, screenPos int, screenWidth int) bool { - clusters = append(clusters, cluster{ - main: main, - comb: comb, - textPos: textPos, - textWidth: textWidth, - screenPos: screenPos, - screenWidth: screenWidth, - }) - return false - }) - - // Iterate in reverse. - for index := len(clusters) - 1; index >= 0; index-- { - if callback( - clusters[index].main, - clusters[index].comb, - clusters[index].textPos, - clusters[index].textWidth, - clusters[index].screenPos, - clusters[index].screenWidth, - ) { - return true - } - } - - return false -} diff --git a/vendor/github.com/rivo/uniseg/LICENSE.txt b/vendor/github.com/rivo/uniseg/LICENSE.txt deleted file mode 100644 index 5040f1ef..00000000 --- a/vendor/github.com/rivo/uniseg/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Oliver Kuederle - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/rivo/uniseg/README.md b/vendor/github.com/rivo/uniseg/README.md deleted file mode 100644 index f8da293e..00000000 --- a/vendor/github.com/rivo/uniseg/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Unicode Text Segmentation for Go - -[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/uniseg) -[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/uniseg) - -This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](http://unicode.org/reports/tr29/) (Unicode version 12.0.0). - -At this point, only the determination of grapheme cluster boundaries is implemented. - -## Background - -In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples: - -|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters| -|-|-|-|-| -|Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`| -|🏳️‍🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`| -|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`| - -This package provides a tool to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit. - -## Installation - -```bash -go get github.com/rivo/uniseg -``` - -## Basic Example - -```go -package uniseg - -import ( - "fmt" - - "github.com/rivo/uniseg" -) - -func main() { - gr := uniseg.NewGraphemes("👍🏼!") - for gr.Next() { - fmt.Printf("%x ", gr.Runes()) - } - // Output: [1f44d 1f3fc] [21] -} -``` - -## Documentation - -Refer to https://godoc.org/github.com/rivo/uniseg for the package's documentation. - -## Dependencies - -This package does not depend on any packages outside the standard library. - -## Your Feedback - -Add your issue here on GitHub. Feel free to get in touch if you have any questions. - -## Version - -Version tags will be introduced once Golang modules are official. Consider this version 0.1. diff --git a/vendor/github.com/rivo/uniseg/doc.go b/vendor/github.com/rivo/uniseg/doc.go deleted file mode 100644 index 60c737d7..00000000 --- a/vendor/github.com/rivo/uniseg/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package uniseg implements Unicode Text Segmentation according to Unicode -Standard Annex #29 (http://unicode.org/reports/tr29/). - -At this point, only the determination of grapheme cluster boundaries is -implemented. -*/ -package uniseg diff --git a/vendor/github.com/rivo/uniseg/grapheme.go b/vendor/github.com/rivo/uniseg/grapheme.go deleted file mode 100644 index fbb846b4..00000000 --- a/vendor/github.com/rivo/uniseg/grapheme.go +++ /dev/null @@ -1,258 +0,0 @@ -package uniseg - -// The states of the grapheme cluster parser. -const ( - grAny = iota - grCR - grControlLF - grL - grLVV - grLVTT - grPrepend - grExtendedPictographic - grExtendedPictographicZWJ - grRIOdd - grRIEven -) - -// The grapheme cluster parser's breaking instructions. -const ( - grNoBoundary = iota - grBoundary -) - -// The grapheme cluster parser's state transitions. Maps (state, property) to -// (new state, breaking instruction, rule number). The breaking instruction -// always refers to the boundary between the last and next code point. -// -// This map is queried as follows: -// -// 1. Find specific state + specific property. Stop if found. -// 2. Find specific state + any property. -// 3. Find any state + specific property. -// 4. If only (2) or (3) (but not both) was found, stop. -// 5. If both (2) and (3) were found, use state and breaking instruction from -// the transition with the lower rule number, prefer (3) if rule numbers -// are equal. Stop. -// 6. Assume grAny and grBoundary. -var grTransitions = map[[2]int][3]int{ - // GB5 - {grAny, prCR}: {grCR, grBoundary, 50}, - {grAny, prLF}: {grControlLF, grBoundary, 50}, - {grAny, prControl}: {grControlLF, grBoundary, 50}, - - // GB4 - {grCR, prAny}: {grAny, grBoundary, 40}, - {grControlLF, prAny}: {grAny, grBoundary, 40}, - - // GB3. - {grCR, prLF}: {grAny, grNoBoundary, 30}, - - // GB6. - {grAny, prL}: {grL, grBoundary, 9990}, - {grL, prL}: {grL, grNoBoundary, 60}, - {grL, prV}: {grLVV, grNoBoundary, 60}, - {grL, prLV}: {grLVV, grNoBoundary, 60}, - {grL, prLVT}: {grLVTT, grNoBoundary, 60}, - - // GB7. - {grAny, prLV}: {grLVV, grBoundary, 9990}, - {grAny, prV}: {grLVV, grBoundary, 9990}, - {grLVV, prV}: {grLVV, grNoBoundary, 70}, - {grLVV, prT}: {grLVTT, grNoBoundary, 70}, - - // GB8. - {grAny, prLVT}: {grLVTT, grBoundary, 9990}, - {grAny, prT}: {grLVTT, grBoundary, 9990}, - {grLVTT, prT}: {grLVTT, grNoBoundary, 80}, - - // GB9. - {grAny, prExtend}: {grAny, grNoBoundary, 90}, - {grAny, prZWJ}: {grAny, grNoBoundary, 90}, - - // GB9a. - {grAny, prSpacingMark}: {grAny, grNoBoundary, 91}, - - // GB9b. - {grAny, prPreprend}: {grPrepend, grBoundary, 9990}, - {grPrepend, prAny}: {grAny, grNoBoundary, 92}, - - // GB11. - {grAny, prExtendedPictographic}: {grExtendedPictographic, grBoundary, 9990}, - {grExtendedPictographic, prExtend}: {grExtendedPictographic, grNoBoundary, 110}, - {grExtendedPictographic, prZWJ}: {grExtendedPictographicZWJ, grNoBoundary, 110}, - {grExtendedPictographicZWJ, prExtendedPictographic}: {grExtendedPictographic, grNoBoundary, 110}, - - // GB12 / GB13. - {grAny, prRegionalIndicator}: {grRIOdd, grBoundary, 9990}, - {grRIOdd, prRegionalIndicator}: {grRIEven, grNoBoundary, 120}, - {grRIEven, prRegionalIndicator}: {grRIOdd, grBoundary, 120}, -} - -// Graphemes implements an iterator over Unicode extended grapheme clusters, -// specified in the Unicode Standard Annex #29. Grapheme clusters correspond to -// "user-perceived characters". These characters often consist of multiple -// code points (e.g. the "woman kissing woman" emoji consists of 8 code points: -// woman + ZWJ + heavy black heart (2 code points) + ZWJ + kiss mark + ZWJ + -// woman) and the rules described in Annex #29 must be applied to group those -// code points into clusters perceived by the user as one character. -type Graphemes struct { - // The code points over which this class iterates. - codePoints []rune - - // The (byte-based) indices of the code points into the original string plus - // len(original string). Thus, len(indices) = len(codePoints) + 1. - indices []int - - // The current grapheme cluster to be returned. These are indices into - // codePoints/indices. If start == end, we either haven't started iterating - // yet (0) or the iteration has already completed (1). - start, end int - - // The index of the next code point to be parsed. - pos int - - // The current state of the code point parser. - state int -} - -// NewGraphemes returns a new grapheme cluster iterator. -func NewGraphemes(s string) *Graphemes { - g := &Graphemes{} - for index, codePoint := range s { - g.codePoints = append(g.codePoints, codePoint) - g.indices = append(g.indices, index) - } - g.indices = append(g.indices, len(s)) - g.Next() // Parse ahead. - return g -} - -// Next advances the iterator by one grapheme cluster and returns false if no -// clusters are left. This function must be called before the first cluster is -// accessed. -func (g *Graphemes) Next() bool { - g.start = g.end - - // The state transition gives us a boundary instruction BEFORE the next code - // point so we always need to stay ahead by one code point. - - // Parse the next code point. - for g.pos <= len(g.codePoints) { - // GB2. - if g.pos == len(g.codePoints) { - g.end = g.pos - g.pos++ - break - } - - // Determine the property of the next character. - nextProperty := property(g.codePoints[g.pos]) - g.pos++ - - // Find the applicable transition. - var boundary bool - transition, ok := grTransitions[[2]int{g.state, nextProperty}] - if ok { - // We have a specific transition. We'll use it. - g.state = transition[0] - boundary = transition[1] == grBoundary - } else { - // No specific transition found. Try the less specific ones. - transAnyProp, okAnyProp := grTransitions[[2]int{g.state, prAny}] - transAnyState, okAnyState := grTransitions[[2]int{grAny, nextProperty}] - if okAnyProp && okAnyState { - // Both apply. We'll use a mix (see comments for grTransitions). - g.state = transAnyState[0] - boundary = transAnyState[1] == grBoundary - if transAnyProp[2] < transAnyState[2] { - g.state = transAnyProp[0] - boundary = transAnyProp[1] == grBoundary - } - } else if okAnyProp { - // We only have a specific state. - g.state = transAnyProp[0] - boundary = transAnyProp[1] == grBoundary - // This branch will probably never be reached because okAnyState will - // always be true given the current transition map. But we keep it here - // for future modifications to the transition map where this may not be - // true anymore. - } else if okAnyState { - // We only have a specific property. - g.state = transAnyState[0] - boundary = transAnyState[1] == grBoundary - } else { - // No known transition. GB999: Any x Any. - g.state = grAny - boundary = true - } - } - - // If we found a cluster boundary, let's stop here. The current cluster will - // be the one that just ended. - if g.pos-1 == 0 /* GB1 */ || boundary { - g.end = g.pos - 1 - break - } - } - - return g.start != g.end -} - -// Runes returns a slice of runes (code points) which corresponds to the current -// grapheme cluster. If the iterator is already past the end or Next() has not -// yet been called, nil is returned. -func (g *Graphemes) Runes() []rune { - if g.start == g.end { - return nil - } - return g.codePoints[g.start:g.end] -} - -// Str returns a substring of the original string which corresponds to the -// current grapheme cluster. If the iterator is already past the end or Next() -// has not yet been called, an empty string is returned. -func (g *Graphemes) Str() string { - if g.start == g.end { - return "" - } - return string(g.codePoints[g.start:g.end]) -} - -// Bytes returns a byte slice which corresponds to the current grapheme cluster. -// If the iterator is already past the end or Next() has not yet been called, -// nil is returned. -func (g *Graphemes) Bytes() []byte { - if g.start == g.end { - return nil - } - return []byte(string(g.codePoints[g.start:g.end])) -} - -// Positions returns the interval of the current grapheme cluster as byte -// positions into the original string. The first returned value "from" indexes -// the first byte and the second returned value "to" indexes the first byte that -// is not included anymore, i.e. str[from:to] is the current grapheme cluster of -// the original string "str". If Next() has not yet been called, both values are -// 0. If the iterator is already past the end, both values are 1. -func (g *Graphemes) Positions() (int, int) { - return g.indices[g.start], g.indices[g.end] -} - -// Reset puts the iterator into its initial state such that the next call to -// Next() sets it to the first grapheme cluster again. -func (g *Graphemes) Reset() { - g.start, g.end, g.pos, g.state = 0, 0, 0, grAny - g.Next() // Parse ahead again. -} - -// GraphemeClusterCount returns the number of user-perceived characters -// (grapheme clusters) for the given string. To calculate this number, it -// iterates through the string using the Graphemes iterator. -func GraphemeClusterCount(s string) (n int) { - g := NewGraphemes(s) - for g.Next() { - n++ - } - return -} diff --git a/vendor/github.com/rivo/uniseg/properties.go b/vendor/github.com/rivo/uniseg/properties.go deleted file mode 100644 index a75ab588..00000000 --- a/vendor/github.com/rivo/uniseg/properties.go +++ /dev/null @@ -1,1658 +0,0 @@ -package uniseg - -// The unicode properties. Only the ones needed in the context of this package -// are included. -const ( - prAny = iota - prPreprend - prCR - prLF - prControl - prExtend - prRegionalIndicator - prSpacingMark - prL - prV - prT - prLV - prLVT - prZWJ - prExtendedPictographic -) - -// Maps code point ranges to their properties. In the context of this package, -// any code point that is not contained may map to "prAny". The code point -// ranges in this slice are numerically sorted. -// -// These ranges were taken from -// http://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt -// as well as -// https://unicode.org/Public/emoji/latest/emoji-data.txt -// ("Extended_Pictographic" only) on March 11, 2019. See -// https://www.unicode.org/license.html for the Unicode license agreement. -var codePoints = [][3]int{ - {0x0000, 0x0009, prControl}, // Cc [10] .. - {0x000A, 0x000A, prLF}, // Cc - {0x000B, 0x000C, prControl}, // Cc [2] .. - {0x000D, 0x000D, prCR}, // Cc - {0x000E, 0x001F, prControl}, // Cc [18] .. - {0x007F, 0x009F, prControl}, // Cc [33] .. - {0x00A9, 0x00A9, prExtendedPictographic}, // 1.1 [1] (©️) copyright - {0x00AD, 0x00AD, prControl}, // Cf SOFT HYPHEN - {0x00AE, 0x00AE, prExtendedPictographic}, // 1.1 [1] (®️) registered - {0x0300, 0x036F, prExtend}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X - {0x0483, 0x0487, prExtend}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE - {0x0488, 0x0489, prExtend}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN - {0x0591, 0x05BD, prExtend}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG - {0x05BF, 0x05BF, prExtend}, // Mn HEBREW POINT RAFE - {0x05C1, 0x05C2, prExtend}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT - {0x05C4, 0x05C5, prExtend}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT - {0x05C7, 0x05C7, prExtend}, // Mn HEBREW POINT QAMATS QATAN - {0x0600, 0x0605, prPreprend}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE - {0x0610, 0x061A, prExtend}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA - {0x061C, 0x061C, prControl}, // Cf ARABIC LETTER MARK - {0x064B, 0x065F, prExtend}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW - {0x0670, 0x0670, prExtend}, // Mn ARABIC LETTER SUPERSCRIPT ALEF - {0x06D6, 0x06DC, prExtend}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN - {0x06DD, 0x06DD, prPreprend}, // Cf ARABIC END OF AYAH - {0x06DF, 0x06E4, prExtend}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA - {0x06E7, 0x06E8, prExtend}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON - {0x06EA, 0x06ED, prExtend}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM - {0x070F, 0x070F, prPreprend}, // Cf SYRIAC ABBREVIATION MARK - {0x0711, 0x0711, prExtend}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH - {0x0730, 0x074A, prExtend}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH - {0x07A6, 0x07B0, prExtend}, // Mn [11] THAANA ABAFILI..THAANA SUKUN - {0x07EB, 0x07F3, prExtend}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE - {0x07FD, 0x07FD, prExtend}, // Mn NKO DANTAYALAN - {0x0816, 0x0819, prExtend}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH - {0x081B, 0x0823, prExtend}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A - {0x0825, 0x0827, prExtend}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U - {0x0829, 0x082D, prExtend}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA - {0x0859, 0x085B, prExtend}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK - {0x08D3, 0x08E1, prExtend}, // Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA - {0x08E2, 0x08E2, prPreprend}, // Cf ARABIC DISPUTED END OF AYAH - {0x08E3, 0x0902, prExtend}, // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA - {0x0903, 0x0903, prSpacingMark}, // Mc DEVANAGARI SIGN VISARGA - {0x093A, 0x093A, prExtend}, // Mn DEVANAGARI VOWEL SIGN OE - {0x093B, 0x093B, prSpacingMark}, // Mc DEVANAGARI VOWEL SIGN OOE - {0x093C, 0x093C, prExtend}, // Mn DEVANAGARI SIGN NUKTA - {0x093E, 0x0940, prSpacingMark}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II - {0x0941, 0x0948, prExtend}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI - {0x0949, 0x094C, prSpacingMark}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU - {0x094D, 0x094D, prExtend}, // Mn DEVANAGARI SIGN VIRAMA - {0x094E, 0x094F, prSpacingMark}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW - {0x0951, 0x0957, prExtend}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE - {0x0962, 0x0963, prExtend}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL - {0x0981, 0x0981, prExtend}, // Mn BENGALI SIGN CANDRABINDU - {0x0982, 0x0983, prSpacingMark}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA - {0x09BC, 0x09BC, prExtend}, // Mn BENGALI SIGN NUKTA - {0x09BE, 0x09BE, prExtend}, // Mc BENGALI VOWEL SIGN AA - {0x09BF, 0x09C0, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II - {0x09C1, 0x09C4, prExtend}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR - {0x09C7, 0x09C8, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI - {0x09CB, 0x09CC, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU - {0x09CD, 0x09CD, prExtend}, // Mn BENGALI SIGN VIRAMA - {0x09D7, 0x09D7, prExtend}, // Mc BENGALI AU LENGTH MARK - {0x09E2, 0x09E3, prExtend}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL - {0x09FE, 0x09FE, prExtend}, // Mn BENGALI SANDHI MARK - {0x0A01, 0x0A02, prExtend}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI - {0x0A03, 0x0A03, prSpacingMark}, // Mc GURMUKHI SIGN VISARGA - {0x0A3C, 0x0A3C, prExtend}, // Mn GURMUKHI SIGN NUKTA - {0x0A3E, 0x0A40, prSpacingMark}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II - {0x0A41, 0x0A42, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU - {0x0A47, 0x0A48, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI - {0x0A4B, 0x0A4D, prExtend}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA - {0x0A51, 0x0A51, prExtend}, // Mn GURMUKHI SIGN UDAAT - {0x0A70, 0x0A71, prExtend}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK - {0x0A75, 0x0A75, prExtend}, // Mn GURMUKHI SIGN YAKASH - {0x0A81, 0x0A82, prExtend}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA - {0x0A83, 0x0A83, prSpacingMark}, // Mc GUJARATI SIGN VISARGA - {0x0ABC, 0x0ABC, prExtend}, // Mn GUJARATI SIGN NUKTA - {0x0ABE, 0x0AC0, prSpacingMark}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II - {0x0AC1, 0x0AC5, prExtend}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E - {0x0AC7, 0x0AC8, prExtend}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI - {0x0AC9, 0x0AC9, prSpacingMark}, // Mc GUJARATI VOWEL SIGN CANDRA O - {0x0ACB, 0x0ACC, prSpacingMark}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU - {0x0ACD, 0x0ACD, prExtend}, // Mn GUJARATI SIGN VIRAMA - {0x0AE2, 0x0AE3, prExtend}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL - {0x0AFA, 0x0AFF, prExtend}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE - {0x0B01, 0x0B01, prExtend}, // Mn ORIYA SIGN CANDRABINDU - {0x0B02, 0x0B03, prSpacingMark}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA - {0x0B3C, 0x0B3C, prExtend}, // Mn ORIYA SIGN NUKTA - {0x0B3E, 0x0B3E, prExtend}, // Mc ORIYA VOWEL SIGN AA - {0x0B3F, 0x0B3F, prExtend}, // Mn ORIYA VOWEL SIGN I - {0x0B40, 0x0B40, prSpacingMark}, // Mc ORIYA VOWEL SIGN II - {0x0B41, 0x0B44, prExtend}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR - {0x0B47, 0x0B48, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI - {0x0B4B, 0x0B4C, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU - {0x0B4D, 0x0B4D, prExtend}, // Mn ORIYA SIGN VIRAMA - {0x0B56, 0x0B56, prExtend}, // Mn ORIYA AI LENGTH MARK - {0x0B57, 0x0B57, prExtend}, // Mc ORIYA AU LENGTH MARK - {0x0B62, 0x0B63, prExtend}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL - {0x0B82, 0x0B82, prExtend}, // Mn TAMIL SIGN ANUSVARA - {0x0BBE, 0x0BBE, prExtend}, // Mc TAMIL VOWEL SIGN AA - {0x0BBF, 0x0BBF, prSpacingMark}, // Mc TAMIL VOWEL SIGN I - {0x0BC0, 0x0BC0, prExtend}, // Mn TAMIL VOWEL SIGN II - {0x0BC1, 0x0BC2, prSpacingMark}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU - {0x0BC6, 0x0BC8, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI - {0x0BCA, 0x0BCC, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU - {0x0BCD, 0x0BCD, prExtend}, // Mn TAMIL SIGN VIRAMA - {0x0BD7, 0x0BD7, prExtend}, // Mc TAMIL AU LENGTH MARK - {0x0C00, 0x0C00, prExtend}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE - {0x0C01, 0x0C03, prSpacingMark}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA - {0x0C04, 0x0C04, prExtend}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE - {0x0C3E, 0x0C40, prExtend}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II - {0x0C41, 0x0C44, prSpacingMark}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR - {0x0C46, 0x0C48, prExtend}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI - {0x0C4A, 0x0C4D, prExtend}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA - {0x0C55, 0x0C56, prExtend}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK - {0x0C62, 0x0C63, prExtend}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL - {0x0C81, 0x0C81, prExtend}, // Mn KANNADA SIGN CANDRABINDU - {0x0C82, 0x0C83, prSpacingMark}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA - {0x0CBC, 0x0CBC, prExtend}, // Mn KANNADA SIGN NUKTA - {0x0CBE, 0x0CBE, prSpacingMark}, // Mc KANNADA VOWEL SIGN AA - {0x0CBF, 0x0CBF, prExtend}, // Mn KANNADA VOWEL SIGN I - {0x0CC0, 0x0CC1, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U - {0x0CC2, 0x0CC2, prExtend}, // Mc KANNADA VOWEL SIGN UU - {0x0CC3, 0x0CC4, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR - {0x0CC6, 0x0CC6, prExtend}, // Mn KANNADA VOWEL SIGN E - {0x0CC7, 0x0CC8, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI - {0x0CCA, 0x0CCB, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO - {0x0CCC, 0x0CCD, prExtend}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA - {0x0CD5, 0x0CD6, prExtend}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK - {0x0CE2, 0x0CE3, prExtend}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL - {0x0D00, 0x0D01, prExtend}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU - {0x0D02, 0x0D03, prSpacingMark}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA - {0x0D3B, 0x0D3C, prExtend}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA - {0x0D3E, 0x0D3E, prExtend}, // Mc MALAYALAM VOWEL SIGN AA - {0x0D3F, 0x0D40, prSpacingMark}, // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II - {0x0D41, 0x0D44, prExtend}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR - {0x0D46, 0x0D48, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI - {0x0D4A, 0x0D4C, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU - {0x0D4D, 0x0D4D, prExtend}, // Mn MALAYALAM SIGN VIRAMA - {0x0D4E, 0x0D4E, prPreprend}, // Lo MALAYALAM LETTER DOT REPH - {0x0D57, 0x0D57, prExtend}, // Mc MALAYALAM AU LENGTH MARK - {0x0D62, 0x0D63, prExtend}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL - {0x0D82, 0x0D83, prSpacingMark}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA - {0x0DCA, 0x0DCA, prExtend}, // Mn SINHALA SIGN AL-LAKUNA - {0x0DCF, 0x0DCF, prExtend}, // Mc SINHALA VOWEL SIGN AELA-PILLA - {0x0DD0, 0x0DD1, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA - {0x0DD2, 0x0DD4, prExtend}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA - {0x0DD6, 0x0DD6, prExtend}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA - {0x0DD8, 0x0DDE, prSpacingMark}, // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA - {0x0DDF, 0x0DDF, prExtend}, // Mc SINHALA VOWEL SIGN GAYANUKITTA - {0x0DF2, 0x0DF3, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA - {0x0E31, 0x0E31, prExtend}, // Mn THAI CHARACTER MAI HAN-AKAT - {0x0E33, 0x0E33, prSpacingMark}, // Lo THAI CHARACTER SARA AM - {0x0E34, 0x0E3A, prExtend}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU - {0x0E47, 0x0E4E, prExtend}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN - {0x0EB1, 0x0EB1, prExtend}, // Mn LAO VOWEL SIGN MAI KAN - {0x0EB3, 0x0EB3, prSpacingMark}, // Lo LAO VOWEL SIGN AM - {0x0EB4, 0x0EBC, prExtend}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO - {0x0EC8, 0x0ECD, prExtend}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA - {0x0F18, 0x0F19, prExtend}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS - {0x0F35, 0x0F35, prExtend}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA - {0x0F37, 0x0F37, prExtend}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS - {0x0F39, 0x0F39, prExtend}, // Mn TIBETAN MARK TSA -PHRU - {0x0F3E, 0x0F3F, prSpacingMark}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES - {0x0F71, 0x0F7E, prExtend}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO - {0x0F7F, 0x0F7F, prSpacingMark}, // Mc TIBETAN SIGN RNAM BCAD - {0x0F80, 0x0F84, prExtend}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA - {0x0F86, 0x0F87, prExtend}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS - {0x0F8D, 0x0F97, prExtend}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA - {0x0F99, 0x0FBC, prExtend}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA - {0x0FC6, 0x0FC6, prExtend}, // Mn TIBETAN SYMBOL PADMA GDAN - {0x102D, 0x1030, prExtend}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU - {0x1031, 0x1031, prSpacingMark}, // Mc MYANMAR VOWEL SIGN E - {0x1032, 0x1037, prExtend}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW - {0x1039, 0x103A, prExtend}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT - {0x103B, 0x103C, prSpacingMark}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA - {0x103D, 0x103E, prExtend}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA - {0x1056, 0x1057, prSpacingMark}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR - {0x1058, 0x1059, prExtend}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL - {0x105E, 0x1060, prExtend}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA - {0x1071, 0x1074, prExtend}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE - {0x1082, 0x1082, prExtend}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA - {0x1084, 0x1084, prSpacingMark}, // Mc MYANMAR VOWEL SIGN SHAN E - {0x1085, 0x1086, prExtend}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y - {0x108D, 0x108D, prExtend}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE - {0x109D, 0x109D, prExtend}, // Mn MYANMAR VOWEL SIGN AITON AI - {0x1100, 0x115F, prL}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER - {0x1160, 0x11A7, prV}, // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE - {0x11A8, 0x11FF, prT}, // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN - {0x135D, 0x135F, prExtend}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK - {0x1712, 0x1714, prExtend}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA - {0x1732, 0x1734, prExtend}, // Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD - {0x1752, 0x1753, prExtend}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U - {0x1772, 0x1773, prExtend}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U - {0x17B4, 0x17B5, prExtend}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA - {0x17B6, 0x17B6, prSpacingMark}, // Mc KHMER VOWEL SIGN AA - {0x17B7, 0x17BD, prExtend}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA - {0x17BE, 0x17C5, prSpacingMark}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU - {0x17C6, 0x17C6, prExtend}, // Mn KHMER SIGN NIKAHIT - {0x17C7, 0x17C8, prSpacingMark}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU - {0x17C9, 0x17D3, prExtend}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT - {0x17DD, 0x17DD, prExtend}, // Mn KHMER SIGN ATTHACAN - {0x180B, 0x180D, prExtend}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE - {0x180E, 0x180E, prControl}, // Cf MONGOLIAN VOWEL SEPARATOR - {0x1885, 0x1886, prExtend}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA - {0x18A9, 0x18A9, prExtend}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA - {0x1920, 0x1922, prExtend}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U - {0x1923, 0x1926, prSpacingMark}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU - {0x1927, 0x1928, prExtend}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O - {0x1929, 0x192B, prSpacingMark}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA - {0x1930, 0x1931, prSpacingMark}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA - {0x1932, 0x1932, prExtend}, // Mn LIMBU SMALL LETTER ANUSVARA - {0x1933, 0x1938, prSpacingMark}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA - {0x1939, 0x193B, prExtend}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I - {0x1A17, 0x1A18, prExtend}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U - {0x1A19, 0x1A1A, prSpacingMark}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O - {0x1A1B, 0x1A1B, prExtend}, // Mn BUGINESE VOWEL SIGN AE - {0x1A55, 0x1A55, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA - {0x1A56, 0x1A56, prExtend}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA - {0x1A57, 0x1A57, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI - {0x1A58, 0x1A5E, prExtend}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA - {0x1A60, 0x1A60, prExtend}, // Mn TAI THAM SIGN SAKOT - {0x1A62, 0x1A62, prExtend}, // Mn TAI THAM VOWEL SIGN MAI SAT - {0x1A65, 0x1A6C, prExtend}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW - {0x1A6D, 0x1A72, prSpacingMark}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI - {0x1A73, 0x1A7C, prExtend}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN - {0x1A7F, 0x1A7F, prExtend}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT - {0x1AB0, 0x1ABD, prExtend}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW - {0x1ABE, 0x1ABE, prExtend}, // Me COMBINING PARENTHESES OVERLAY - {0x1B00, 0x1B03, prExtend}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG - {0x1B04, 0x1B04, prSpacingMark}, // Mc BALINESE SIGN BISAH - {0x1B34, 0x1B34, prExtend}, // Mn BALINESE SIGN REREKAN - {0x1B35, 0x1B35, prExtend}, // Mc BALINESE VOWEL SIGN TEDUNG - {0x1B36, 0x1B3A, prExtend}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA - {0x1B3B, 0x1B3B, prSpacingMark}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG - {0x1B3C, 0x1B3C, prExtend}, // Mn BALINESE VOWEL SIGN LA LENGA - {0x1B3D, 0x1B41, prSpacingMark}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG - {0x1B42, 0x1B42, prExtend}, // Mn BALINESE VOWEL SIGN PEPET - {0x1B43, 0x1B44, prSpacingMark}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG - {0x1B6B, 0x1B73, prExtend}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG - {0x1B80, 0x1B81, prExtend}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR - {0x1B82, 0x1B82, prSpacingMark}, // Mc SUNDANESE SIGN PANGWISAD - {0x1BA1, 0x1BA1, prSpacingMark}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL - {0x1BA2, 0x1BA5, prExtend}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU - {0x1BA6, 0x1BA7, prSpacingMark}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG - {0x1BA8, 0x1BA9, prExtend}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG - {0x1BAA, 0x1BAA, prSpacingMark}, // Mc SUNDANESE SIGN PAMAAEH - {0x1BAB, 0x1BAD, prExtend}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA - {0x1BE6, 0x1BE6, prExtend}, // Mn BATAK SIGN TOMPI - {0x1BE7, 0x1BE7, prSpacingMark}, // Mc BATAK VOWEL SIGN E - {0x1BE8, 0x1BE9, prExtend}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE - {0x1BEA, 0x1BEC, prSpacingMark}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O - {0x1BED, 0x1BED, prExtend}, // Mn BATAK VOWEL SIGN KARO O - {0x1BEE, 0x1BEE, prSpacingMark}, // Mc BATAK VOWEL SIGN U - {0x1BEF, 0x1BF1, prExtend}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H - {0x1BF2, 0x1BF3, prSpacingMark}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN - {0x1C24, 0x1C2B, prSpacingMark}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU - {0x1C2C, 0x1C33, prExtend}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T - {0x1C34, 0x1C35, prSpacingMark}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG - {0x1C36, 0x1C37, prExtend}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA - {0x1CD0, 0x1CD2, prExtend}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA - {0x1CD4, 0x1CE0, prExtend}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA - {0x1CE1, 0x1CE1, prSpacingMark}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA - {0x1CE2, 0x1CE8, prExtend}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL - {0x1CED, 0x1CED, prExtend}, // Mn VEDIC SIGN TIRYAK - {0x1CF4, 0x1CF4, prExtend}, // Mn VEDIC TONE CANDRA ABOVE - {0x1CF7, 0x1CF7, prSpacingMark}, // Mc VEDIC SIGN ATIKRAMA - {0x1CF8, 0x1CF9, prExtend}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE - {0x1DC0, 0x1DF9, prExtend}, // Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW - {0x1DFB, 0x1DFF, prExtend}, // Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW - {0x200B, 0x200B, prControl}, // Cf ZERO WIDTH SPACE - {0x200C, 0x200C, prExtend}, // Cf ZERO WIDTH NON-JOINER - {0x200D, 0x200D, prZWJ}, // Cf ZERO WIDTH JOINER - {0x200E, 0x200F, prControl}, // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK - {0x2028, 0x2028, prControl}, // Zl LINE SEPARATOR - {0x2029, 0x2029, prControl}, // Zp PARAGRAPH SEPARATOR - {0x202A, 0x202E, prControl}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE - {0x203C, 0x203C, prExtendedPictographic}, // 1.1 [1] (‼️) double exclamation mark - {0x2049, 0x2049, prExtendedPictographic}, // 3.0 [1] (⁉️) exclamation question mark - {0x2060, 0x2064, prControl}, // Cf [5] WORD JOINER..INVISIBLE PLUS - {0x2065, 0x2065, prControl}, // Cn - {0x2066, 0x206F, prControl}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES - {0x20D0, 0x20DC, prExtend}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE - {0x20DD, 0x20E0, prExtend}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH - {0x20E1, 0x20E1, prExtend}, // Mn COMBINING LEFT RIGHT ARROW ABOVE - {0x20E2, 0x20E4, prExtend}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE - {0x20E5, 0x20F0, prExtend}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE - {0x2122, 0x2122, prExtendedPictographic}, // 1.1 [1] (™️) trade mark - {0x2139, 0x2139, prExtendedPictographic}, // 3.0 [1] (ℹ️) information - {0x2194, 0x2199, prExtendedPictographic}, // 1.1 [6] (↔️..↙️) left-right arrow..down-left arrow - {0x21A9, 0x21AA, prExtendedPictographic}, // 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right - {0x231A, 0x231B, prExtendedPictographic}, // 1.1 [2] (⌚..⌛) watch..hourglass done - {0x2328, 0x2328, prExtendedPictographic}, // 1.1 [1] (⌨️) keyboard - {0x2388, 0x2388, prExtendedPictographic}, // 3.0 [1] (⎈) HELM SYMBOL - {0x23CF, 0x23CF, prExtendedPictographic}, // 4.0 [1] (⏏️) eject button - {0x23E9, 0x23F3, prExtendedPictographic}, // 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done - {0x23F8, 0x23FA, prExtendedPictographic}, // 7.0 [3] (⏸️..⏺️) pause button..record button - {0x24C2, 0x24C2, prExtendedPictographic}, // 1.1 [1] (Ⓜ️) circled M - {0x25AA, 0x25AB, prExtendedPictographic}, // 1.1 [2] (▪️..▫️) black small square..white small square - {0x25B6, 0x25B6, prExtendedPictographic}, // 1.1 [1] (▶️) play button - {0x25C0, 0x25C0, prExtendedPictographic}, // 1.1 [1] (◀️) reverse button - {0x25FB, 0x25FE, prExtendedPictographic}, // 3.2 [4] (◻️..◾) white medium square..black medium-small square - {0x2600, 0x2605, prExtendedPictographic}, // 1.1 [6] (☀️..★) sun..BLACK STAR - {0x2607, 0x2612, prExtendedPictographic}, // 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X - {0x2614, 0x2615, prExtendedPictographic}, // 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage - {0x2616, 0x2617, prExtendedPictographic}, // 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE - {0x2618, 0x2618, prExtendedPictographic}, // 4.1 [1] (☘️) shamrock - {0x2619, 0x2619, prExtendedPictographic}, // 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET - {0x261A, 0x266F, prExtendedPictographic}, // 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN - {0x2670, 0x2671, prExtendedPictographic}, // 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS - {0x2672, 0x267D, prExtendedPictographic}, // 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL - {0x267E, 0x267F, prExtendedPictographic}, // 4.1 [2] (♾️..♿) infinity..wheelchair symbol - {0x2680, 0x2685, prExtendedPictographic}, // 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6 - {0x2690, 0x2691, prExtendedPictographic}, // 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG - {0x2692, 0x269C, prExtendedPictographic}, // 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis - {0x269D, 0x269D, prExtendedPictographic}, // 5.1 [1] (⚝) OUTLINED WHITE STAR - {0x269E, 0x269F, prExtendedPictographic}, // 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT - {0x26A0, 0x26A1, prExtendedPictographic}, // 4.0 [2] (⚠️..⚡) warning..high voltage - {0x26A2, 0x26B1, prExtendedPictographic}, // 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn - {0x26B2, 0x26B2, prExtendedPictographic}, // 5.0 [1] (⚲) NEUTER - {0x26B3, 0x26BC, prExtendedPictographic}, // 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE - {0x26BD, 0x26BF, prExtendedPictographic}, // 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY - {0x26C0, 0x26C3, prExtendedPictographic}, // 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING - {0x26C4, 0x26CD, prExtendedPictographic}, // 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR - {0x26CE, 0x26CE, prExtendedPictographic}, // 6.0 [1] (⛎) Ophiuchus - {0x26CF, 0x26E1, prExtendedPictographic}, // 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2 - {0x26E2, 0x26E2, prExtendedPictographic}, // 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS - {0x26E3, 0x26E3, prExtendedPictographic}, // 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE - {0x26E4, 0x26E7, prExtendedPictographic}, // 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM - {0x26E8, 0x26FF, prExtendedPictographic}, // 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE - {0x2700, 0x2700, prExtendedPictographic}, // 7.0 [1] (✀) BLACK SAFETY SCISSORS - {0x2701, 0x2704, prExtendedPictographic}, // 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS - {0x2705, 0x2705, prExtendedPictographic}, // 6.0 [1] (✅) check mark button - {0x2708, 0x2709, prExtendedPictographic}, // 1.1 [2] (✈️..✉️) airplane..envelope - {0x270A, 0x270B, prExtendedPictographic}, // 6.0 [2] (✊..✋) raised fist..raised hand - {0x270C, 0x2712, prExtendedPictographic}, // 1.1 [7] (✌️..✒️) victory hand..black nib - {0x2714, 0x2714, prExtendedPictographic}, // 1.1 [1] (✔️) check mark - {0x2716, 0x2716, prExtendedPictographic}, // 1.1 [1] (✖️) multiplication sign - {0x271D, 0x271D, prExtendedPictographic}, // 1.1 [1] (✝️) latin cross - {0x2721, 0x2721, prExtendedPictographic}, // 1.1 [1] (✡️) star of David - {0x2728, 0x2728, prExtendedPictographic}, // 6.0 [1] (✨) sparkles - {0x2733, 0x2734, prExtendedPictographic}, // 1.1 [2] (✳️..✴️) eight-spoked asterisk..eight-pointed star - {0x2744, 0x2744, prExtendedPictographic}, // 1.1 [1] (❄️) snowflake - {0x2747, 0x2747, prExtendedPictographic}, // 1.1 [1] (❇️) sparkle - {0x274C, 0x274C, prExtendedPictographic}, // 6.0 [1] (❌) cross mark - {0x274E, 0x274E, prExtendedPictographic}, // 6.0 [1] (❎) cross mark button - {0x2753, 0x2755, prExtendedPictographic}, // 6.0 [3] (❓..❕) question mark..white exclamation mark - {0x2757, 0x2757, prExtendedPictographic}, // 5.2 [1] (❗) exclamation mark - {0x2763, 0x2767, prExtendedPictographic}, // 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET - {0x2795, 0x2797, prExtendedPictographic}, // 6.0 [3] (➕..➗) plus sign..division sign - {0x27A1, 0x27A1, prExtendedPictographic}, // 1.1 [1] (➡️) right arrow - {0x27B0, 0x27B0, prExtendedPictographic}, // 6.0 [1] (➰) curly loop - {0x27BF, 0x27BF, prExtendedPictographic}, // 6.0 [1] (➿) double curly loop - {0x2934, 0x2935, prExtendedPictographic}, // 3.2 [2] (⤴️..⤵️) right arrow curving up..right arrow curving down - {0x2B05, 0x2B07, prExtendedPictographic}, // 4.0 [3] (⬅️..⬇️) left arrow..down arrow - {0x2B1B, 0x2B1C, prExtendedPictographic}, // 5.1 [2] (⬛..⬜) black large square..white large square - {0x2B50, 0x2B50, prExtendedPictographic}, // 5.1 [1] (⭐) star - {0x2B55, 0x2B55, prExtendedPictographic}, // 5.2 [1] (⭕) hollow red circle - {0x2CEF, 0x2CF1, prExtend}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS - {0x2D7F, 0x2D7F, prExtend}, // Mn TIFINAGH CONSONANT JOINER - {0x2DE0, 0x2DFF, prExtend}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS - {0x302A, 0x302D, prExtend}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK - {0x302E, 0x302F, prExtend}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK - {0x3030, 0x3030, prExtendedPictographic}, // 1.1 [1] (〰️) wavy dash - {0x303D, 0x303D, prExtendedPictographic}, // 3.2 [1] (〽️) part alternation mark - {0x3099, 0x309A, prExtend}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK - {0x3297, 0x3297, prExtendedPictographic}, // 1.1 [1] (㊗️) Japanese “congratulations” button - {0x3299, 0x3299, prExtendedPictographic}, // 1.1 [1] (㊙️) Japanese “secret” button - {0xA66F, 0xA66F, prExtend}, // Mn COMBINING CYRILLIC VZMET - {0xA670, 0xA672, prExtend}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN - {0xA674, 0xA67D, prExtend}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK - {0xA69E, 0xA69F, prExtend}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E - {0xA6F0, 0xA6F1, prExtend}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS - {0xA802, 0xA802, prExtend}, // Mn SYLOTI NAGRI SIGN DVISVARA - {0xA806, 0xA806, prExtend}, // Mn SYLOTI NAGRI SIGN HASANTA - {0xA80B, 0xA80B, prExtend}, // Mn SYLOTI NAGRI SIGN ANUSVARA - {0xA823, 0xA824, prSpacingMark}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I - {0xA825, 0xA826, prExtend}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E - {0xA827, 0xA827, prSpacingMark}, // Mc SYLOTI NAGRI VOWEL SIGN OO - {0xA880, 0xA881, prSpacingMark}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA - {0xA8B4, 0xA8C3, prSpacingMark}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU - {0xA8C4, 0xA8C5, prExtend}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU - {0xA8E0, 0xA8F1, prExtend}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA - {0xA8FF, 0xA8FF, prExtend}, // Mn DEVANAGARI VOWEL SIGN AY - {0xA926, 0xA92D, prExtend}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU - {0xA947, 0xA951, prExtend}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R - {0xA952, 0xA953, prSpacingMark}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA - {0xA960, 0xA97C, prL}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH - {0xA980, 0xA982, prExtend}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR - {0xA983, 0xA983, prSpacingMark}, // Mc JAVANESE SIGN WIGNYAN - {0xA9B3, 0xA9B3, prExtend}, // Mn JAVANESE SIGN CECAK TELU - {0xA9B4, 0xA9B5, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG - {0xA9B6, 0xA9B9, prExtend}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT - {0xA9BA, 0xA9BB, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE - {0xA9BC, 0xA9BD, prExtend}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET - {0xA9BE, 0xA9C0, prSpacingMark}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON - {0xA9E5, 0xA9E5, prExtend}, // Mn MYANMAR SIGN SHAN SAW - {0xAA29, 0xAA2E, prExtend}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE - {0xAA2F, 0xAA30, prSpacingMark}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI - {0xAA31, 0xAA32, prExtend}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE - {0xAA33, 0xAA34, prSpacingMark}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA - {0xAA35, 0xAA36, prExtend}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA - {0xAA43, 0xAA43, prExtend}, // Mn CHAM CONSONANT SIGN FINAL NG - {0xAA4C, 0xAA4C, prExtend}, // Mn CHAM CONSONANT SIGN FINAL M - {0xAA4D, 0xAA4D, prSpacingMark}, // Mc CHAM CONSONANT SIGN FINAL H - {0xAA7C, 0xAA7C, prExtend}, // Mn MYANMAR SIGN TAI LAING TONE-2 - {0xAAB0, 0xAAB0, prExtend}, // Mn TAI VIET MAI KANG - {0xAAB2, 0xAAB4, prExtend}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U - {0xAAB7, 0xAAB8, prExtend}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA - {0xAABE, 0xAABF, prExtend}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK - {0xAAC1, 0xAAC1, prExtend}, // Mn TAI VIET TONE MAI THO - {0xAAEB, 0xAAEB, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN II - {0xAAEC, 0xAAED, prExtend}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI - {0xAAEE, 0xAAEF, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU - {0xAAF5, 0xAAF5, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA - {0xAAF6, 0xAAF6, prExtend}, // Mn MEETEI MAYEK VIRAMA - {0xABE3, 0xABE4, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP - {0xABE5, 0xABE5, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN ANAP - {0xABE6, 0xABE7, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP - {0xABE8, 0xABE8, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN UNAP - {0xABE9, 0xABEA, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG - {0xABEC, 0xABEC, prSpacingMark}, // Mc MEETEI MAYEK LUM IYEK - {0xABED, 0xABED, prExtend}, // Mn MEETEI MAYEK APUN IYEK - {0xAC00, 0xAC00, prLV}, // Lo HANGUL SYLLABLE GA - {0xAC01, 0xAC1B, prLVT}, // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH - {0xAC1C, 0xAC1C, prLV}, // Lo HANGUL SYLLABLE GAE - {0xAC1D, 0xAC37, prLVT}, // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH - {0xAC38, 0xAC38, prLV}, // Lo HANGUL SYLLABLE GYA - {0xAC39, 0xAC53, prLVT}, // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH - {0xAC54, 0xAC54, prLV}, // Lo HANGUL SYLLABLE GYAE - {0xAC55, 0xAC6F, prLVT}, // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH - {0xAC70, 0xAC70, prLV}, // Lo HANGUL SYLLABLE GEO - {0xAC71, 0xAC8B, prLVT}, // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH - {0xAC8C, 0xAC8C, prLV}, // Lo HANGUL SYLLABLE GE - {0xAC8D, 0xACA7, prLVT}, // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH - {0xACA8, 0xACA8, prLV}, // Lo HANGUL SYLLABLE GYEO - {0xACA9, 0xACC3, prLVT}, // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH - {0xACC4, 0xACC4, prLV}, // Lo HANGUL SYLLABLE GYE - {0xACC5, 0xACDF, prLVT}, // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH - {0xACE0, 0xACE0, prLV}, // Lo HANGUL SYLLABLE GO - {0xACE1, 0xACFB, prLVT}, // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH - {0xACFC, 0xACFC, prLV}, // Lo HANGUL SYLLABLE GWA - {0xACFD, 0xAD17, prLVT}, // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH - {0xAD18, 0xAD18, prLV}, // Lo HANGUL SYLLABLE GWAE - {0xAD19, 0xAD33, prLVT}, // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH - {0xAD34, 0xAD34, prLV}, // Lo HANGUL SYLLABLE GOE - {0xAD35, 0xAD4F, prLVT}, // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH - {0xAD50, 0xAD50, prLV}, // Lo HANGUL SYLLABLE GYO - {0xAD51, 0xAD6B, prLVT}, // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH - {0xAD6C, 0xAD6C, prLV}, // Lo HANGUL SYLLABLE GU - {0xAD6D, 0xAD87, prLVT}, // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH - {0xAD88, 0xAD88, prLV}, // Lo HANGUL SYLLABLE GWEO - {0xAD89, 0xADA3, prLVT}, // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH - {0xADA4, 0xADA4, prLV}, // Lo HANGUL SYLLABLE GWE - {0xADA5, 0xADBF, prLVT}, // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH - {0xADC0, 0xADC0, prLV}, // Lo HANGUL SYLLABLE GWI - {0xADC1, 0xADDB, prLVT}, // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH - {0xADDC, 0xADDC, prLV}, // Lo HANGUL SYLLABLE GYU - {0xADDD, 0xADF7, prLVT}, // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH - {0xADF8, 0xADF8, prLV}, // Lo HANGUL SYLLABLE GEU - {0xADF9, 0xAE13, prLVT}, // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH - {0xAE14, 0xAE14, prLV}, // Lo HANGUL SYLLABLE GYI - {0xAE15, 0xAE2F, prLVT}, // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH - {0xAE30, 0xAE30, prLV}, // Lo HANGUL SYLLABLE GI - {0xAE31, 0xAE4B, prLVT}, // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH - {0xAE4C, 0xAE4C, prLV}, // Lo HANGUL SYLLABLE GGA - {0xAE4D, 0xAE67, prLVT}, // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH - {0xAE68, 0xAE68, prLV}, // Lo HANGUL SYLLABLE GGAE - {0xAE69, 0xAE83, prLVT}, // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH - {0xAE84, 0xAE84, prLV}, // Lo HANGUL SYLLABLE GGYA - {0xAE85, 0xAE9F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH - {0xAEA0, 0xAEA0, prLV}, // Lo HANGUL SYLLABLE GGYAE - {0xAEA1, 0xAEBB, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH - {0xAEBC, 0xAEBC, prLV}, // Lo HANGUL SYLLABLE GGEO - {0xAEBD, 0xAED7, prLVT}, // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH - {0xAED8, 0xAED8, prLV}, // Lo HANGUL SYLLABLE GGE - {0xAED9, 0xAEF3, prLVT}, // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH - {0xAEF4, 0xAEF4, prLV}, // Lo HANGUL SYLLABLE GGYEO - {0xAEF5, 0xAF0F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH - {0xAF10, 0xAF10, prLV}, // Lo HANGUL SYLLABLE GGYE - {0xAF11, 0xAF2B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH - {0xAF2C, 0xAF2C, prLV}, // Lo HANGUL SYLLABLE GGO - {0xAF2D, 0xAF47, prLVT}, // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH - {0xAF48, 0xAF48, prLV}, // Lo HANGUL SYLLABLE GGWA - {0xAF49, 0xAF63, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH - {0xAF64, 0xAF64, prLV}, // Lo HANGUL SYLLABLE GGWAE - {0xAF65, 0xAF7F, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH - {0xAF80, 0xAF80, prLV}, // Lo HANGUL SYLLABLE GGOE - {0xAF81, 0xAF9B, prLVT}, // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH - {0xAF9C, 0xAF9C, prLV}, // Lo HANGUL SYLLABLE GGYO - {0xAF9D, 0xAFB7, prLVT}, // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH - {0xAFB8, 0xAFB8, prLV}, // Lo HANGUL SYLLABLE GGU - {0xAFB9, 0xAFD3, prLVT}, // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH - {0xAFD4, 0xAFD4, prLV}, // Lo HANGUL SYLLABLE GGWEO - {0xAFD5, 0xAFEF, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH - {0xAFF0, 0xAFF0, prLV}, // Lo HANGUL SYLLABLE GGWE - {0xAFF1, 0xB00B, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH - {0xB00C, 0xB00C, prLV}, // Lo HANGUL SYLLABLE GGWI - {0xB00D, 0xB027, prLVT}, // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH - {0xB028, 0xB028, prLV}, // Lo HANGUL SYLLABLE GGYU - {0xB029, 0xB043, prLVT}, // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH - {0xB044, 0xB044, prLV}, // Lo HANGUL SYLLABLE GGEU - {0xB045, 0xB05F, prLVT}, // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH - {0xB060, 0xB060, prLV}, // Lo HANGUL SYLLABLE GGYI - {0xB061, 0xB07B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH - {0xB07C, 0xB07C, prLV}, // Lo HANGUL SYLLABLE GGI - {0xB07D, 0xB097, prLVT}, // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH - {0xB098, 0xB098, prLV}, // Lo HANGUL SYLLABLE NA - {0xB099, 0xB0B3, prLVT}, // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH - {0xB0B4, 0xB0B4, prLV}, // Lo HANGUL SYLLABLE NAE - {0xB0B5, 0xB0CF, prLVT}, // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH - {0xB0D0, 0xB0D0, prLV}, // Lo HANGUL SYLLABLE NYA - {0xB0D1, 0xB0EB, prLVT}, // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH - {0xB0EC, 0xB0EC, prLV}, // Lo HANGUL SYLLABLE NYAE - {0xB0ED, 0xB107, prLVT}, // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH - {0xB108, 0xB108, prLV}, // Lo HANGUL SYLLABLE NEO - {0xB109, 0xB123, prLVT}, // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH - {0xB124, 0xB124, prLV}, // Lo HANGUL SYLLABLE NE - {0xB125, 0xB13F, prLVT}, // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH - {0xB140, 0xB140, prLV}, // Lo HANGUL SYLLABLE NYEO - {0xB141, 0xB15B, prLVT}, // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH - {0xB15C, 0xB15C, prLV}, // Lo HANGUL SYLLABLE NYE - {0xB15D, 0xB177, prLVT}, // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH - {0xB178, 0xB178, prLV}, // Lo HANGUL SYLLABLE NO - {0xB179, 0xB193, prLVT}, // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH - {0xB194, 0xB194, prLV}, // Lo HANGUL SYLLABLE NWA - {0xB195, 0xB1AF, prLVT}, // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH - {0xB1B0, 0xB1B0, prLV}, // Lo HANGUL SYLLABLE NWAE - {0xB1B1, 0xB1CB, prLVT}, // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH - {0xB1CC, 0xB1CC, prLV}, // Lo HANGUL SYLLABLE NOE - {0xB1CD, 0xB1E7, prLVT}, // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH - {0xB1E8, 0xB1E8, prLV}, // Lo HANGUL SYLLABLE NYO - {0xB1E9, 0xB203, prLVT}, // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH - {0xB204, 0xB204, prLV}, // Lo HANGUL SYLLABLE NU - {0xB205, 0xB21F, prLVT}, // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH - {0xB220, 0xB220, prLV}, // Lo HANGUL SYLLABLE NWEO - {0xB221, 0xB23B, prLVT}, // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH - {0xB23C, 0xB23C, prLV}, // Lo HANGUL SYLLABLE NWE - {0xB23D, 0xB257, prLVT}, // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH - {0xB258, 0xB258, prLV}, // Lo HANGUL SYLLABLE NWI - {0xB259, 0xB273, prLVT}, // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH - {0xB274, 0xB274, prLV}, // Lo HANGUL SYLLABLE NYU - {0xB275, 0xB28F, prLVT}, // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH - {0xB290, 0xB290, prLV}, // Lo HANGUL SYLLABLE NEU - {0xB291, 0xB2AB, prLVT}, // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH - {0xB2AC, 0xB2AC, prLV}, // Lo HANGUL SYLLABLE NYI - {0xB2AD, 0xB2C7, prLVT}, // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH - {0xB2C8, 0xB2C8, prLV}, // Lo HANGUL SYLLABLE NI - {0xB2C9, 0xB2E3, prLVT}, // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH - {0xB2E4, 0xB2E4, prLV}, // Lo HANGUL SYLLABLE DA - {0xB2E5, 0xB2FF, prLVT}, // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH - {0xB300, 0xB300, prLV}, // Lo HANGUL SYLLABLE DAE - {0xB301, 0xB31B, prLVT}, // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH - {0xB31C, 0xB31C, prLV}, // Lo HANGUL SYLLABLE DYA - {0xB31D, 0xB337, prLVT}, // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH - {0xB338, 0xB338, prLV}, // Lo HANGUL SYLLABLE DYAE - {0xB339, 0xB353, prLVT}, // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH - {0xB354, 0xB354, prLV}, // Lo HANGUL SYLLABLE DEO - {0xB355, 0xB36F, prLVT}, // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH - {0xB370, 0xB370, prLV}, // Lo HANGUL SYLLABLE DE - {0xB371, 0xB38B, prLVT}, // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH - {0xB38C, 0xB38C, prLV}, // Lo HANGUL SYLLABLE DYEO - {0xB38D, 0xB3A7, prLVT}, // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH - {0xB3A8, 0xB3A8, prLV}, // Lo HANGUL SYLLABLE DYE - {0xB3A9, 0xB3C3, prLVT}, // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH - {0xB3C4, 0xB3C4, prLV}, // Lo HANGUL SYLLABLE DO - {0xB3C5, 0xB3DF, prLVT}, // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH - {0xB3E0, 0xB3E0, prLV}, // Lo HANGUL SYLLABLE DWA - {0xB3E1, 0xB3FB, prLVT}, // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH - {0xB3FC, 0xB3FC, prLV}, // Lo HANGUL SYLLABLE DWAE - {0xB3FD, 0xB417, prLVT}, // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH - {0xB418, 0xB418, prLV}, // Lo HANGUL SYLLABLE DOE - {0xB419, 0xB433, prLVT}, // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH - {0xB434, 0xB434, prLV}, // Lo HANGUL SYLLABLE DYO - {0xB435, 0xB44F, prLVT}, // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH - {0xB450, 0xB450, prLV}, // Lo HANGUL SYLLABLE DU - {0xB451, 0xB46B, prLVT}, // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH - {0xB46C, 0xB46C, prLV}, // Lo HANGUL SYLLABLE DWEO - {0xB46D, 0xB487, prLVT}, // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH - {0xB488, 0xB488, prLV}, // Lo HANGUL SYLLABLE DWE - {0xB489, 0xB4A3, prLVT}, // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH - {0xB4A4, 0xB4A4, prLV}, // Lo HANGUL SYLLABLE DWI - {0xB4A5, 0xB4BF, prLVT}, // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH - {0xB4C0, 0xB4C0, prLV}, // Lo HANGUL SYLLABLE DYU - {0xB4C1, 0xB4DB, prLVT}, // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH - {0xB4DC, 0xB4DC, prLV}, // Lo HANGUL SYLLABLE DEU - {0xB4DD, 0xB4F7, prLVT}, // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH - {0xB4F8, 0xB4F8, prLV}, // Lo HANGUL SYLLABLE DYI - {0xB4F9, 0xB513, prLVT}, // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH - {0xB514, 0xB514, prLV}, // Lo HANGUL SYLLABLE DI - {0xB515, 0xB52F, prLVT}, // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH - {0xB530, 0xB530, prLV}, // Lo HANGUL SYLLABLE DDA - {0xB531, 0xB54B, prLVT}, // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH - {0xB54C, 0xB54C, prLV}, // Lo HANGUL SYLLABLE DDAE - {0xB54D, 0xB567, prLVT}, // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH - {0xB568, 0xB568, prLV}, // Lo HANGUL SYLLABLE DDYA - {0xB569, 0xB583, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH - {0xB584, 0xB584, prLV}, // Lo HANGUL SYLLABLE DDYAE - {0xB585, 0xB59F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH - {0xB5A0, 0xB5A0, prLV}, // Lo HANGUL SYLLABLE DDEO - {0xB5A1, 0xB5BB, prLVT}, // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH - {0xB5BC, 0xB5BC, prLV}, // Lo HANGUL SYLLABLE DDE - {0xB5BD, 0xB5D7, prLVT}, // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH - {0xB5D8, 0xB5D8, prLV}, // Lo HANGUL SYLLABLE DDYEO - {0xB5D9, 0xB5F3, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH - {0xB5F4, 0xB5F4, prLV}, // Lo HANGUL SYLLABLE DDYE - {0xB5F5, 0xB60F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH - {0xB610, 0xB610, prLV}, // Lo HANGUL SYLLABLE DDO - {0xB611, 0xB62B, prLVT}, // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH - {0xB62C, 0xB62C, prLV}, // Lo HANGUL SYLLABLE DDWA - {0xB62D, 0xB647, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH - {0xB648, 0xB648, prLV}, // Lo HANGUL SYLLABLE DDWAE - {0xB649, 0xB663, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH - {0xB664, 0xB664, prLV}, // Lo HANGUL SYLLABLE DDOE - {0xB665, 0xB67F, prLVT}, // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH - {0xB680, 0xB680, prLV}, // Lo HANGUL SYLLABLE DDYO - {0xB681, 0xB69B, prLVT}, // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH - {0xB69C, 0xB69C, prLV}, // Lo HANGUL SYLLABLE DDU - {0xB69D, 0xB6B7, prLVT}, // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH - {0xB6B8, 0xB6B8, prLV}, // Lo HANGUL SYLLABLE DDWEO - {0xB6B9, 0xB6D3, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH - {0xB6D4, 0xB6D4, prLV}, // Lo HANGUL SYLLABLE DDWE - {0xB6D5, 0xB6EF, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH - {0xB6F0, 0xB6F0, prLV}, // Lo HANGUL SYLLABLE DDWI - {0xB6F1, 0xB70B, prLVT}, // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH - {0xB70C, 0xB70C, prLV}, // Lo HANGUL SYLLABLE DDYU - {0xB70D, 0xB727, prLVT}, // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH - {0xB728, 0xB728, prLV}, // Lo HANGUL SYLLABLE DDEU - {0xB729, 0xB743, prLVT}, // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH - {0xB744, 0xB744, prLV}, // Lo HANGUL SYLLABLE DDYI - {0xB745, 0xB75F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH - {0xB760, 0xB760, prLV}, // Lo HANGUL SYLLABLE DDI - {0xB761, 0xB77B, prLVT}, // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH - {0xB77C, 0xB77C, prLV}, // Lo HANGUL SYLLABLE RA - {0xB77D, 0xB797, prLVT}, // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH - {0xB798, 0xB798, prLV}, // Lo HANGUL SYLLABLE RAE - {0xB799, 0xB7B3, prLVT}, // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH - {0xB7B4, 0xB7B4, prLV}, // Lo HANGUL SYLLABLE RYA - {0xB7B5, 0xB7CF, prLVT}, // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH - {0xB7D0, 0xB7D0, prLV}, // Lo HANGUL SYLLABLE RYAE - {0xB7D1, 0xB7EB, prLVT}, // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH - {0xB7EC, 0xB7EC, prLV}, // Lo HANGUL SYLLABLE REO - {0xB7ED, 0xB807, prLVT}, // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH - {0xB808, 0xB808, prLV}, // Lo HANGUL SYLLABLE RE - {0xB809, 0xB823, prLVT}, // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH - {0xB824, 0xB824, prLV}, // Lo HANGUL SYLLABLE RYEO - {0xB825, 0xB83F, prLVT}, // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH - {0xB840, 0xB840, prLV}, // Lo HANGUL SYLLABLE RYE - {0xB841, 0xB85B, prLVT}, // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH - {0xB85C, 0xB85C, prLV}, // Lo HANGUL SYLLABLE RO - {0xB85D, 0xB877, prLVT}, // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH - {0xB878, 0xB878, prLV}, // Lo HANGUL SYLLABLE RWA - {0xB879, 0xB893, prLVT}, // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH - {0xB894, 0xB894, prLV}, // Lo HANGUL SYLLABLE RWAE - {0xB895, 0xB8AF, prLVT}, // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH - {0xB8B0, 0xB8B0, prLV}, // Lo HANGUL SYLLABLE ROE - {0xB8B1, 0xB8CB, prLVT}, // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH - {0xB8CC, 0xB8CC, prLV}, // Lo HANGUL SYLLABLE RYO - {0xB8CD, 0xB8E7, prLVT}, // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH - {0xB8E8, 0xB8E8, prLV}, // Lo HANGUL SYLLABLE RU - {0xB8E9, 0xB903, prLVT}, // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH - {0xB904, 0xB904, prLV}, // Lo HANGUL SYLLABLE RWEO - {0xB905, 0xB91F, prLVT}, // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH - {0xB920, 0xB920, prLV}, // Lo HANGUL SYLLABLE RWE - {0xB921, 0xB93B, prLVT}, // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH - {0xB93C, 0xB93C, prLV}, // Lo HANGUL SYLLABLE RWI - {0xB93D, 0xB957, prLVT}, // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH - {0xB958, 0xB958, prLV}, // Lo HANGUL SYLLABLE RYU - {0xB959, 0xB973, prLVT}, // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH - {0xB974, 0xB974, prLV}, // Lo HANGUL SYLLABLE REU - {0xB975, 0xB98F, prLVT}, // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH - {0xB990, 0xB990, prLV}, // Lo HANGUL SYLLABLE RYI - {0xB991, 0xB9AB, prLVT}, // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH - {0xB9AC, 0xB9AC, prLV}, // Lo HANGUL SYLLABLE RI - {0xB9AD, 0xB9C7, prLVT}, // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH - {0xB9C8, 0xB9C8, prLV}, // Lo HANGUL SYLLABLE MA - {0xB9C9, 0xB9E3, prLVT}, // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH - {0xB9E4, 0xB9E4, prLV}, // Lo HANGUL SYLLABLE MAE - {0xB9E5, 0xB9FF, prLVT}, // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH - {0xBA00, 0xBA00, prLV}, // Lo HANGUL SYLLABLE MYA - {0xBA01, 0xBA1B, prLVT}, // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH - {0xBA1C, 0xBA1C, prLV}, // Lo HANGUL SYLLABLE MYAE - {0xBA1D, 0xBA37, prLVT}, // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH - {0xBA38, 0xBA38, prLV}, // Lo HANGUL SYLLABLE MEO - {0xBA39, 0xBA53, prLVT}, // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH - {0xBA54, 0xBA54, prLV}, // Lo HANGUL SYLLABLE ME - {0xBA55, 0xBA6F, prLVT}, // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH - {0xBA70, 0xBA70, prLV}, // Lo HANGUL SYLLABLE MYEO - {0xBA71, 0xBA8B, prLVT}, // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH - {0xBA8C, 0xBA8C, prLV}, // Lo HANGUL SYLLABLE MYE - {0xBA8D, 0xBAA7, prLVT}, // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH - {0xBAA8, 0xBAA8, prLV}, // Lo HANGUL SYLLABLE MO - {0xBAA9, 0xBAC3, prLVT}, // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH - {0xBAC4, 0xBAC4, prLV}, // Lo HANGUL SYLLABLE MWA - {0xBAC5, 0xBADF, prLVT}, // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH - {0xBAE0, 0xBAE0, prLV}, // Lo HANGUL SYLLABLE MWAE - {0xBAE1, 0xBAFB, prLVT}, // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH - {0xBAFC, 0xBAFC, prLV}, // Lo HANGUL SYLLABLE MOE - {0xBAFD, 0xBB17, prLVT}, // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH - {0xBB18, 0xBB18, prLV}, // Lo HANGUL SYLLABLE MYO - {0xBB19, 0xBB33, prLVT}, // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH - {0xBB34, 0xBB34, prLV}, // Lo HANGUL SYLLABLE MU - {0xBB35, 0xBB4F, prLVT}, // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH - {0xBB50, 0xBB50, prLV}, // Lo HANGUL SYLLABLE MWEO - {0xBB51, 0xBB6B, prLVT}, // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH - {0xBB6C, 0xBB6C, prLV}, // Lo HANGUL SYLLABLE MWE - {0xBB6D, 0xBB87, prLVT}, // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH - {0xBB88, 0xBB88, prLV}, // Lo HANGUL SYLLABLE MWI - {0xBB89, 0xBBA3, prLVT}, // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH - {0xBBA4, 0xBBA4, prLV}, // Lo HANGUL SYLLABLE MYU - {0xBBA5, 0xBBBF, prLVT}, // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH - {0xBBC0, 0xBBC0, prLV}, // Lo HANGUL SYLLABLE MEU - {0xBBC1, 0xBBDB, prLVT}, // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH - {0xBBDC, 0xBBDC, prLV}, // Lo HANGUL SYLLABLE MYI - {0xBBDD, 0xBBF7, prLVT}, // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH - {0xBBF8, 0xBBF8, prLV}, // Lo HANGUL SYLLABLE MI - {0xBBF9, 0xBC13, prLVT}, // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH - {0xBC14, 0xBC14, prLV}, // Lo HANGUL SYLLABLE BA - {0xBC15, 0xBC2F, prLVT}, // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH - {0xBC30, 0xBC30, prLV}, // Lo HANGUL SYLLABLE BAE - {0xBC31, 0xBC4B, prLVT}, // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH - {0xBC4C, 0xBC4C, prLV}, // Lo HANGUL SYLLABLE BYA - {0xBC4D, 0xBC67, prLVT}, // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH - {0xBC68, 0xBC68, prLV}, // Lo HANGUL SYLLABLE BYAE - {0xBC69, 0xBC83, prLVT}, // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH - {0xBC84, 0xBC84, prLV}, // Lo HANGUL SYLLABLE BEO - {0xBC85, 0xBC9F, prLVT}, // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH - {0xBCA0, 0xBCA0, prLV}, // Lo HANGUL SYLLABLE BE - {0xBCA1, 0xBCBB, prLVT}, // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH - {0xBCBC, 0xBCBC, prLV}, // Lo HANGUL SYLLABLE BYEO - {0xBCBD, 0xBCD7, prLVT}, // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH - {0xBCD8, 0xBCD8, prLV}, // Lo HANGUL SYLLABLE BYE - {0xBCD9, 0xBCF3, prLVT}, // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH - {0xBCF4, 0xBCF4, prLV}, // Lo HANGUL SYLLABLE BO - {0xBCF5, 0xBD0F, prLVT}, // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH - {0xBD10, 0xBD10, prLV}, // Lo HANGUL SYLLABLE BWA - {0xBD11, 0xBD2B, prLVT}, // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH - {0xBD2C, 0xBD2C, prLV}, // Lo HANGUL SYLLABLE BWAE - {0xBD2D, 0xBD47, prLVT}, // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH - {0xBD48, 0xBD48, prLV}, // Lo HANGUL SYLLABLE BOE - {0xBD49, 0xBD63, prLVT}, // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH - {0xBD64, 0xBD64, prLV}, // Lo HANGUL SYLLABLE BYO - {0xBD65, 0xBD7F, prLVT}, // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH - {0xBD80, 0xBD80, prLV}, // Lo HANGUL SYLLABLE BU - {0xBD81, 0xBD9B, prLVT}, // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH - {0xBD9C, 0xBD9C, prLV}, // Lo HANGUL SYLLABLE BWEO - {0xBD9D, 0xBDB7, prLVT}, // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH - {0xBDB8, 0xBDB8, prLV}, // Lo HANGUL SYLLABLE BWE - {0xBDB9, 0xBDD3, prLVT}, // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH - {0xBDD4, 0xBDD4, prLV}, // Lo HANGUL SYLLABLE BWI - {0xBDD5, 0xBDEF, prLVT}, // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH - {0xBDF0, 0xBDF0, prLV}, // Lo HANGUL SYLLABLE BYU - {0xBDF1, 0xBE0B, prLVT}, // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH - {0xBE0C, 0xBE0C, prLV}, // Lo HANGUL SYLLABLE BEU - {0xBE0D, 0xBE27, prLVT}, // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH - {0xBE28, 0xBE28, prLV}, // Lo HANGUL SYLLABLE BYI - {0xBE29, 0xBE43, prLVT}, // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH - {0xBE44, 0xBE44, prLV}, // Lo HANGUL SYLLABLE BI - {0xBE45, 0xBE5F, prLVT}, // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH - {0xBE60, 0xBE60, prLV}, // Lo HANGUL SYLLABLE BBA - {0xBE61, 0xBE7B, prLVT}, // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH - {0xBE7C, 0xBE7C, prLV}, // Lo HANGUL SYLLABLE BBAE - {0xBE7D, 0xBE97, prLVT}, // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH - {0xBE98, 0xBE98, prLV}, // Lo HANGUL SYLLABLE BBYA - {0xBE99, 0xBEB3, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH - {0xBEB4, 0xBEB4, prLV}, // Lo HANGUL SYLLABLE BBYAE - {0xBEB5, 0xBECF, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH - {0xBED0, 0xBED0, prLV}, // Lo HANGUL SYLLABLE BBEO - {0xBED1, 0xBEEB, prLVT}, // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH - {0xBEEC, 0xBEEC, prLV}, // Lo HANGUL SYLLABLE BBE - {0xBEED, 0xBF07, prLVT}, // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH - {0xBF08, 0xBF08, prLV}, // Lo HANGUL SYLLABLE BBYEO - {0xBF09, 0xBF23, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH - {0xBF24, 0xBF24, prLV}, // Lo HANGUL SYLLABLE BBYE - {0xBF25, 0xBF3F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH - {0xBF40, 0xBF40, prLV}, // Lo HANGUL SYLLABLE BBO - {0xBF41, 0xBF5B, prLVT}, // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH - {0xBF5C, 0xBF5C, prLV}, // Lo HANGUL SYLLABLE BBWA - {0xBF5D, 0xBF77, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH - {0xBF78, 0xBF78, prLV}, // Lo HANGUL SYLLABLE BBWAE - {0xBF79, 0xBF93, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH - {0xBF94, 0xBF94, prLV}, // Lo HANGUL SYLLABLE BBOE - {0xBF95, 0xBFAF, prLVT}, // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH - {0xBFB0, 0xBFB0, prLV}, // Lo HANGUL SYLLABLE BBYO - {0xBFB1, 0xBFCB, prLVT}, // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH - {0xBFCC, 0xBFCC, prLV}, // Lo HANGUL SYLLABLE BBU - {0xBFCD, 0xBFE7, prLVT}, // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH - {0xBFE8, 0xBFE8, prLV}, // Lo HANGUL SYLLABLE BBWEO - {0xBFE9, 0xC003, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH - {0xC004, 0xC004, prLV}, // Lo HANGUL SYLLABLE BBWE - {0xC005, 0xC01F, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH - {0xC020, 0xC020, prLV}, // Lo HANGUL SYLLABLE BBWI - {0xC021, 0xC03B, prLVT}, // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH - {0xC03C, 0xC03C, prLV}, // Lo HANGUL SYLLABLE BBYU - {0xC03D, 0xC057, prLVT}, // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH - {0xC058, 0xC058, prLV}, // Lo HANGUL SYLLABLE BBEU - {0xC059, 0xC073, prLVT}, // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH - {0xC074, 0xC074, prLV}, // Lo HANGUL SYLLABLE BBYI - {0xC075, 0xC08F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH - {0xC090, 0xC090, prLV}, // Lo HANGUL SYLLABLE BBI - {0xC091, 0xC0AB, prLVT}, // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH - {0xC0AC, 0xC0AC, prLV}, // Lo HANGUL SYLLABLE SA - {0xC0AD, 0xC0C7, prLVT}, // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH - {0xC0C8, 0xC0C8, prLV}, // Lo HANGUL SYLLABLE SAE - {0xC0C9, 0xC0E3, prLVT}, // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH - {0xC0E4, 0xC0E4, prLV}, // Lo HANGUL SYLLABLE SYA - {0xC0E5, 0xC0FF, prLVT}, // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH - {0xC100, 0xC100, prLV}, // Lo HANGUL SYLLABLE SYAE - {0xC101, 0xC11B, prLVT}, // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH - {0xC11C, 0xC11C, prLV}, // Lo HANGUL SYLLABLE SEO - {0xC11D, 0xC137, prLVT}, // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH - {0xC138, 0xC138, prLV}, // Lo HANGUL SYLLABLE SE - {0xC139, 0xC153, prLVT}, // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH - {0xC154, 0xC154, prLV}, // Lo HANGUL SYLLABLE SYEO - {0xC155, 0xC16F, prLVT}, // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH - {0xC170, 0xC170, prLV}, // Lo HANGUL SYLLABLE SYE - {0xC171, 0xC18B, prLVT}, // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH - {0xC18C, 0xC18C, prLV}, // Lo HANGUL SYLLABLE SO - {0xC18D, 0xC1A7, prLVT}, // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH - {0xC1A8, 0xC1A8, prLV}, // Lo HANGUL SYLLABLE SWA - {0xC1A9, 0xC1C3, prLVT}, // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH - {0xC1C4, 0xC1C4, prLV}, // Lo HANGUL SYLLABLE SWAE - {0xC1C5, 0xC1DF, prLVT}, // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH - {0xC1E0, 0xC1E0, prLV}, // Lo HANGUL SYLLABLE SOE - {0xC1E1, 0xC1FB, prLVT}, // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH - {0xC1FC, 0xC1FC, prLV}, // Lo HANGUL SYLLABLE SYO - {0xC1FD, 0xC217, prLVT}, // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH - {0xC218, 0xC218, prLV}, // Lo HANGUL SYLLABLE SU - {0xC219, 0xC233, prLVT}, // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH - {0xC234, 0xC234, prLV}, // Lo HANGUL SYLLABLE SWEO - {0xC235, 0xC24F, prLVT}, // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH - {0xC250, 0xC250, prLV}, // Lo HANGUL SYLLABLE SWE - {0xC251, 0xC26B, prLVT}, // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH - {0xC26C, 0xC26C, prLV}, // Lo HANGUL SYLLABLE SWI - {0xC26D, 0xC287, prLVT}, // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH - {0xC288, 0xC288, prLV}, // Lo HANGUL SYLLABLE SYU - {0xC289, 0xC2A3, prLVT}, // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH - {0xC2A4, 0xC2A4, prLV}, // Lo HANGUL SYLLABLE SEU - {0xC2A5, 0xC2BF, prLVT}, // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH - {0xC2C0, 0xC2C0, prLV}, // Lo HANGUL SYLLABLE SYI - {0xC2C1, 0xC2DB, prLVT}, // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH - {0xC2DC, 0xC2DC, prLV}, // Lo HANGUL SYLLABLE SI - {0xC2DD, 0xC2F7, prLVT}, // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH - {0xC2F8, 0xC2F8, prLV}, // Lo HANGUL SYLLABLE SSA - {0xC2F9, 0xC313, prLVT}, // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH - {0xC314, 0xC314, prLV}, // Lo HANGUL SYLLABLE SSAE - {0xC315, 0xC32F, prLVT}, // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH - {0xC330, 0xC330, prLV}, // Lo HANGUL SYLLABLE SSYA - {0xC331, 0xC34B, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH - {0xC34C, 0xC34C, prLV}, // Lo HANGUL SYLLABLE SSYAE - {0xC34D, 0xC367, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH - {0xC368, 0xC368, prLV}, // Lo HANGUL SYLLABLE SSEO - {0xC369, 0xC383, prLVT}, // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH - {0xC384, 0xC384, prLV}, // Lo HANGUL SYLLABLE SSE - {0xC385, 0xC39F, prLVT}, // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH - {0xC3A0, 0xC3A0, prLV}, // Lo HANGUL SYLLABLE SSYEO - {0xC3A1, 0xC3BB, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH - {0xC3BC, 0xC3BC, prLV}, // Lo HANGUL SYLLABLE SSYE - {0xC3BD, 0xC3D7, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH - {0xC3D8, 0xC3D8, prLV}, // Lo HANGUL SYLLABLE SSO - {0xC3D9, 0xC3F3, prLVT}, // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH - {0xC3F4, 0xC3F4, prLV}, // Lo HANGUL SYLLABLE SSWA - {0xC3F5, 0xC40F, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH - {0xC410, 0xC410, prLV}, // Lo HANGUL SYLLABLE SSWAE - {0xC411, 0xC42B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH - {0xC42C, 0xC42C, prLV}, // Lo HANGUL SYLLABLE SSOE - {0xC42D, 0xC447, prLVT}, // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH - {0xC448, 0xC448, prLV}, // Lo HANGUL SYLLABLE SSYO - {0xC449, 0xC463, prLVT}, // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH - {0xC464, 0xC464, prLV}, // Lo HANGUL SYLLABLE SSU - {0xC465, 0xC47F, prLVT}, // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH - {0xC480, 0xC480, prLV}, // Lo HANGUL SYLLABLE SSWEO - {0xC481, 0xC49B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH - {0xC49C, 0xC49C, prLV}, // Lo HANGUL SYLLABLE SSWE - {0xC49D, 0xC4B7, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH - {0xC4B8, 0xC4B8, prLV}, // Lo HANGUL SYLLABLE SSWI - {0xC4B9, 0xC4D3, prLVT}, // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH - {0xC4D4, 0xC4D4, prLV}, // Lo HANGUL SYLLABLE SSYU - {0xC4D5, 0xC4EF, prLVT}, // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH - {0xC4F0, 0xC4F0, prLV}, // Lo HANGUL SYLLABLE SSEU - {0xC4F1, 0xC50B, prLVT}, // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH - {0xC50C, 0xC50C, prLV}, // Lo HANGUL SYLLABLE SSYI - {0xC50D, 0xC527, prLVT}, // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH - {0xC528, 0xC528, prLV}, // Lo HANGUL SYLLABLE SSI - {0xC529, 0xC543, prLVT}, // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH - {0xC544, 0xC544, prLV}, // Lo HANGUL SYLLABLE A - {0xC545, 0xC55F, prLVT}, // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH - {0xC560, 0xC560, prLV}, // Lo HANGUL SYLLABLE AE - {0xC561, 0xC57B, prLVT}, // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH - {0xC57C, 0xC57C, prLV}, // Lo HANGUL SYLLABLE YA - {0xC57D, 0xC597, prLVT}, // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH - {0xC598, 0xC598, prLV}, // Lo HANGUL SYLLABLE YAE - {0xC599, 0xC5B3, prLVT}, // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH - {0xC5B4, 0xC5B4, prLV}, // Lo HANGUL SYLLABLE EO - {0xC5B5, 0xC5CF, prLVT}, // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH - {0xC5D0, 0xC5D0, prLV}, // Lo HANGUL SYLLABLE E - {0xC5D1, 0xC5EB, prLVT}, // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH - {0xC5EC, 0xC5EC, prLV}, // Lo HANGUL SYLLABLE YEO - {0xC5ED, 0xC607, prLVT}, // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH - {0xC608, 0xC608, prLV}, // Lo HANGUL SYLLABLE YE - {0xC609, 0xC623, prLVT}, // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH - {0xC624, 0xC624, prLV}, // Lo HANGUL SYLLABLE O - {0xC625, 0xC63F, prLVT}, // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH - {0xC640, 0xC640, prLV}, // Lo HANGUL SYLLABLE WA - {0xC641, 0xC65B, prLVT}, // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH - {0xC65C, 0xC65C, prLV}, // Lo HANGUL SYLLABLE WAE - {0xC65D, 0xC677, prLVT}, // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH - {0xC678, 0xC678, prLV}, // Lo HANGUL SYLLABLE OE - {0xC679, 0xC693, prLVT}, // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH - {0xC694, 0xC694, prLV}, // Lo HANGUL SYLLABLE YO - {0xC695, 0xC6AF, prLVT}, // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH - {0xC6B0, 0xC6B0, prLV}, // Lo HANGUL SYLLABLE U - {0xC6B1, 0xC6CB, prLVT}, // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH - {0xC6CC, 0xC6CC, prLV}, // Lo HANGUL SYLLABLE WEO - {0xC6CD, 0xC6E7, prLVT}, // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH - {0xC6E8, 0xC6E8, prLV}, // Lo HANGUL SYLLABLE WE - {0xC6E9, 0xC703, prLVT}, // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH - {0xC704, 0xC704, prLV}, // Lo HANGUL SYLLABLE WI - {0xC705, 0xC71F, prLVT}, // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH - {0xC720, 0xC720, prLV}, // Lo HANGUL SYLLABLE YU - {0xC721, 0xC73B, prLVT}, // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH - {0xC73C, 0xC73C, prLV}, // Lo HANGUL SYLLABLE EU - {0xC73D, 0xC757, prLVT}, // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH - {0xC758, 0xC758, prLV}, // Lo HANGUL SYLLABLE YI - {0xC759, 0xC773, prLVT}, // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH - {0xC774, 0xC774, prLV}, // Lo HANGUL SYLLABLE I - {0xC775, 0xC78F, prLVT}, // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH - {0xC790, 0xC790, prLV}, // Lo HANGUL SYLLABLE JA - {0xC791, 0xC7AB, prLVT}, // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH - {0xC7AC, 0xC7AC, prLV}, // Lo HANGUL SYLLABLE JAE - {0xC7AD, 0xC7C7, prLVT}, // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH - {0xC7C8, 0xC7C8, prLV}, // Lo HANGUL SYLLABLE JYA - {0xC7C9, 0xC7E3, prLVT}, // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH - {0xC7E4, 0xC7E4, prLV}, // Lo HANGUL SYLLABLE JYAE - {0xC7E5, 0xC7FF, prLVT}, // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH - {0xC800, 0xC800, prLV}, // Lo HANGUL SYLLABLE JEO - {0xC801, 0xC81B, prLVT}, // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH - {0xC81C, 0xC81C, prLV}, // Lo HANGUL SYLLABLE JE - {0xC81D, 0xC837, prLVT}, // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH - {0xC838, 0xC838, prLV}, // Lo HANGUL SYLLABLE JYEO - {0xC839, 0xC853, prLVT}, // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH - {0xC854, 0xC854, prLV}, // Lo HANGUL SYLLABLE JYE - {0xC855, 0xC86F, prLVT}, // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH - {0xC870, 0xC870, prLV}, // Lo HANGUL SYLLABLE JO - {0xC871, 0xC88B, prLVT}, // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH - {0xC88C, 0xC88C, prLV}, // Lo HANGUL SYLLABLE JWA - {0xC88D, 0xC8A7, prLVT}, // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH - {0xC8A8, 0xC8A8, prLV}, // Lo HANGUL SYLLABLE JWAE - {0xC8A9, 0xC8C3, prLVT}, // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH - {0xC8C4, 0xC8C4, prLV}, // Lo HANGUL SYLLABLE JOE - {0xC8C5, 0xC8DF, prLVT}, // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH - {0xC8E0, 0xC8E0, prLV}, // Lo HANGUL SYLLABLE JYO - {0xC8E1, 0xC8FB, prLVT}, // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH - {0xC8FC, 0xC8FC, prLV}, // Lo HANGUL SYLLABLE JU - {0xC8FD, 0xC917, prLVT}, // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH - {0xC918, 0xC918, prLV}, // Lo HANGUL SYLLABLE JWEO - {0xC919, 0xC933, prLVT}, // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH - {0xC934, 0xC934, prLV}, // Lo HANGUL SYLLABLE JWE - {0xC935, 0xC94F, prLVT}, // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH - {0xC950, 0xC950, prLV}, // Lo HANGUL SYLLABLE JWI - {0xC951, 0xC96B, prLVT}, // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH - {0xC96C, 0xC96C, prLV}, // Lo HANGUL SYLLABLE JYU - {0xC96D, 0xC987, prLVT}, // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH - {0xC988, 0xC988, prLV}, // Lo HANGUL SYLLABLE JEU - {0xC989, 0xC9A3, prLVT}, // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH - {0xC9A4, 0xC9A4, prLV}, // Lo HANGUL SYLLABLE JYI - {0xC9A5, 0xC9BF, prLVT}, // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH - {0xC9C0, 0xC9C0, prLV}, // Lo HANGUL SYLLABLE JI - {0xC9C1, 0xC9DB, prLVT}, // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH - {0xC9DC, 0xC9DC, prLV}, // Lo HANGUL SYLLABLE JJA - {0xC9DD, 0xC9F7, prLVT}, // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH - {0xC9F8, 0xC9F8, prLV}, // Lo HANGUL SYLLABLE JJAE - {0xC9F9, 0xCA13, prLVT}, // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH - {0xCA14, 0xCA14, prLV}, // Lo HANGUL SYLLABLE JJYA - {0xCA15, 0xCA2F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH - {0xCA30, 0xCA30, prLV}, // Lo HANGUL SYLLABLE JJYAE - {0xCA31, 0xCA4B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH - {0xCA4C, 0xCA4C, prLV}, // Lo HANGUL SYLLABLE JJEO - {0xCA4D, 0xCA67, prLVT}, // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH - {0xCA68, 0xCA68, prLV}, // Lo HANGUL SYLLABLE JJE - {0xCA69, 0xCA83, prLVT}, // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH - {0xCA84, 0xCA84, prLV}, // Lo HANGUL SYLLABLE JJYEO - {0xCA85, 0xCA9F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH - {0xCAA0, 0xCAA0, prLV}, // Lo HANGUL SYLLABLE JJYE - {0xCAA1, 0xCABB, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH - {0xCABC, 0xCABC, prLV}, // Lo HANGUL SYLLABLE JJO - {0xCABD, 0xCAD7, prLVT}, // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH - {0xCAD8, 0xCAD8, prLV}, // Lo HANGUL SYLLABLE JJWA - {0xCAD9, 0xCAF3, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH - {0xCAF4, 0xCAF4, prLV}, // Lo HANGUL SYLLABLE JJWAE - {0xCAF5, 0xCB0F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH - {0xCB10, 0xCB10, prLV}, // Lo HANGUL SYLLABLE JJOE - {0xCB11, 0xCB2B, prLVT}, // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH - {0xCB2C, 0xCB2C, prLV}, // Lo HANGUL SYLLABLE JJYO - {0xCB2D, 0xCB47, prLVT}, // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH - {0xCB48, 0xCB48, prLV}, // Lo HANGUL SYLLABLE JJU - {0xCB49, 0xCB63, prLVT}, // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH - {0xCB64, 0xCB64, prLV}, // Lo HANGUL SYLLABLE JJWEO - {0xCB65, 0xCB7F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH - {0xCB80, 0xCB80, prLV}, // Lo HANGUL SYLLABLE JJWE - {0xCB81, 0xCB9B, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH - {0xCB9C, 0xCB9C, prLV}, // Lo HANGUL SYLLABLE JJWI - {0xCB9D, 0xCBB7, prLVT}, // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH - {0xCBB8, 0xCBB8, prLV}, // Lo HANGUL SYLLABLE JJYU - {0xCBB9, 0xCBD3, prLVT}, // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH - {0xCBD4, 0xCBD4, prLV}, // Lo HANGUL SYLLABLE JJEU - {0xCBD5, 0xCBEF, prLVT}, // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH - {0xCBF0, 0xCBF0, prLV}, // Lo HANGUL SYLLABLE JJYI - {0xCBF1, 0xCC0B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH - {0xCC0C, 0xCC0C, prLV}, // Lo HANGUL SYLLABLE JJI - {0xCC0D, 0xCC27, prLVT}, // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH - {0xCC28, 0xCC28, prLV}, // Lo HANGUL SYLLABLE CA - {0xCC29, 0xCC43, prLVT}, // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH - {0xCC44, 0xCC44, prLV}, // Lo HANGUL SYLLABLE CAE - {0xCC45, 0xCC5F, prLVT}, // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH - {0xCC60, 0xCC60, prLV}, // Lo HANGUL SYLLABLE CYA - {0xCC61, 0xCC7B, prLVT}, // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH - {0xCC7C, 0xCC7C, prLV}, // Lo HANGUL SYLLABLE CYAE - {0xCC7D, 0xCC97, prLVT}, // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH - {0xCC98, 0xCC98, prLV}, // Lo HANGUL SYLLABLE CEO - {0xCC99, 0xCCB3, prLVT}, // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH - {0xCCB4, 0xCCB4, prLV}, // Lo HANGUL SYLLABLE CE - {0xCCB5, 0xCCCF, prLVT}, // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH - {0xCCD0, 0xCCD0, prLV}, // Lo HANGUL SYLLABLE CYEO - {0xCCD1, 0xCCEB, prLVT}, // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH - {0xCCEC, 0xCCEC, prLV}, // Lo HANGUL SYLLABLE CYE - {0xCCED, 0xCD07, prLVT}, // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH - {0xCD08, 0xCD08, prLV}, // Lo HANGUL SYLLABLE CO - {0xCD09, 0xCD23, prLVT}, // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH - {0xCD24, 0xCD24, prLV}, // Lo HANGUL SYLLABLE CWA - {0xCD25, 0xCD3F, prLVT}, // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH - {0xCD40, 0xCD40, prLV}, // Lo HANGUL SYLLABLE CWAE - {0xCD41, 0xCD5B, prLVT}, // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH - {0xCD5C, 0xCD5C, prLV}, // Lo HANGUL SYLLABLE COE - {0xCD5D, 0xCD77, prLVT}, // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH - {0xCD78, 0xCD78, prLV}, // Lo HANGUL SYLLABLE CYO - {0xCD79, 0xCD93, prLVT}, // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH - {0xCD94, 0xCD94, prLV}, // Lo HANGUL SYLLABLE CU - {0xCD95, 0xCDAF, prLVT}, // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH - {0xCDB0, 0xCDB0, prLV}, // Lo HANGUL SYLLABLE CWEO - {0xCDB1, 0xCDCB, prLVT}, // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH - {0xCDCC, 0xCDCC, prLV}, // Lo HANGUL SYLLABLE CWE - {0xCDCD, 0xCDE7, prLVT}, // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH - {0xCDE8, 0xCDE8, prLV}, // Lo HANGUL SYLLABLE CWI - {0xCDE9, 0xCE03, prLVT}, // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH - {0xCE04, 0xCE04, prLV}, // Lo HANGUL SYLLABLE CYU - {0xCE05, 0xCE1F, prLVT}, // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH - {0xCE20, 0xCE20, prLV}, // Lo HANGUL SYLLABLE CEU - {0xCE21, 0xCE3B, prLVT}, // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH - {0xCE3C, 0xCE3C, prLV}, // Lo HANGUL SYLLABLE CYI - {0xCE3D, 0xCE57, prLVT}, // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH - {0xCE58, 0xCE58, prLV}, // Lo HANGUL SYLLABLE CI - {0xCE59, 0xCE73, prLVT}, // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH - {0xCE74, 0xCE74, prLV}, // Lo HANGUL SYLLABLE KA - {0xCE75, 0xCE8F, prLVT}, // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH - {0xCE90, 0xCE90, prLV}, // Lo HANGUL SYLLABLE KAE - {0xCE91, 0xCEAB, prLVT}, // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH - {0xCEAC, 0xCEAC, prLV}, // Lo HANGUL SYLLABLE KYA - {0xCEAD, 0xCEC7, prLVT}, // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH - {0xCEC8, 0xCEC8, prLV}, // Lo HANGUL SYLLABLE KYAE - {0xCEC9, 0xCEE3, prLVT}, // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH - {0xCEE4, 0xCEE4, prLV}, // Lo HANGUL SYLLABLE KEO - {0xCEE5, 0xCEFF, prLVT}, // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH - {0xCF00, 0xCF00, prLV}, // Lo HANGUL SYLLABLE KE - {0xCF01, 0xCF1B, prLVT}, // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH - {0xCF1C, 0xCF1C, prLV}, // Lo HANGUL SYLLABLE KYEO - {0xCF1D, 0xCF37, prLVT}, // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH - {0xCF38, 0xCF38, prLV}, // Lo HANGUL SYLLABLE KYE - {0xCF39, 0xCF53, prLVT}, // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH - {0xCF54, 0xCF54, prLV}, // Lo HANGUL SYLLABLE KO - {0xCF55, 0xCF6F, prLVT}, // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH - {0xCF70, 0xCF70, prLV}, // Lo HANGUL SYLLABLE KWA - {0xCF71, 0xCF8B, prLVT}, // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH - {0xCF8C, 0xCF8C, prLV}, // Lo HANGUL SYLLABLE KWAE - {0xCF8D, 0xCFA7, prLVT}, // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH - {0xCFA8, 0xCFA8, prLV}, // Lo HANGUL SYLLABLE KOE - {0xCFA9, 0xCFC3, prLVT}, // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH - {0xCFC4, 0xCFC4, prLV}, // Lo HANGUL SYLLABLE KYO - {0xCFC5, 0xCFDF, prLVT}, // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH - {0xCFE0, 0xCFE0, prLV}, // Lo HANGUL SYLLABLE KU - {0xCFE1, 0xCFFB, prLVT}, // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH - {0xCFFC, 0xCFFC, prLV}, // Lo HANGUL SYLLABLE KWEO - {0xCFFD, 0xD017, prLVT}, // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH - {0xD018, 0xD018, prLV}, // Lo HANGUL SYLLABLE KWE - {0xD019, 0xD033, prLVT}, // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH - {0xD034, 0xD034, prLV}, // Lo HANGUL SYLLABLE KWI - {0xD035, 0xD04F, prLVT}, // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH - {0xD050, 0xD050, prLV}, // Lo HANGUL SYLLABLE KYU - {0xD051, 0xD06B, prLVT}, // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH - {0xD06C, 0xD06C, prLV}, // Lo HANGUL SYLLABLE KEU - {0xD06D, 0xD087, prLVT}, // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH - {0xD088, 0xD088, prLV}, // Lo HANGUL SYLLABLE KYI - {0xD089, 0xD0A3, prLVT}, // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH - {0xD0A4, 0xD0A4, prLV}, // Lo HANGUL SYLLABLE KI - {0xD0A5, 0xD0BF, prLVT}, // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH - {0xD0C0, 0xD0C0, prLV}, // Lo HANGUL SYLLABLE TA - {0xD0C1, 0xD0DB, prLVT}, // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH - {0xD0DC, 0xD0DC, prLV}, // Lo HANGUL SYLLABLE TAE - {0xD0DD, 0xD0F7, prLVT}, // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH - {0xD0F8, 0xD0F8, prLV}, // Lo HANGUL SYLLABLE TYA - {0xD0F9, 0xD113, prLVT}, // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH - {0xD114, 0xD114, prLV}, // Lo HANGUL SYLLABLE TYAE - {0xD115, 0xD12F, prLVT}, // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH - {0xD130, 0xD130, prLV}, // Lo HANGUL SYLLABLE TEO - {0xD131, 0xD14B, prLVT}, // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH - {0xD14C, 0xD14C, prLV}, // Lo HANGUL SYLLABLE TE - {0xD14D, 0xD167, prLVT}, // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH - {0xD168, 0xD168, prLV}, // Lo HANGUL SYLLABLE TYEO - {0xD169, 0xD183, prLVT}, // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH - {0xD184, 0xD184, prLV}, // Lo HANGUL SYLLABLE TYE - {0xD185, 0xD19F, prLVT}, // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH - {0xD1A0, 0xD1A0, prLV}, // Lo HANGUL SYLLABLE TO - {0xD1A1, 0xD1BB, prLVT}, // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH - {0xD1BC, 0xD1BC, prLV}, // Lo HANGUL SYLLABLE TWA - {0xD1BD, 0xD1D7, prLVT}, // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH - {0xD1D8, 0xD1D8, prLV}, // Lo HANGUL SYLLABLE TWAE - {0xD1D9, 0xD1F3, prLVT}, // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH - {0xD1F4, 0xD1F4, prLV}, // Lo HANGUL SYLLABLE TOE - {0xD1F5, 0xD20F, prLVT}, // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH - {0xD210, 0xD210, prLV}, // Lo HANGUL SYLLABLE TYO - {0xD211, 0xD22B, prLVT}, // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH - {0xD22C, 0xD22C, prLV}, // Lo HANGUL SYLLABLE TU - {0xD22D, 0xD247, prLVT}, // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH - {0xD248, 0xD248, prLV}, // Lo HANGUL SYLLABLE TWEO - {0xD249, 0xD263, prLVT}, // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH - {0xD264, 0xD264, prLV}, // Lo HANGUL SYLLABLE TWE - {0xD265, 0xD27F, prLVT}, // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH - {0xD280, 0xD280, prLV}, // Lo HANGUL SYLLABLE TWI - {0xD281, 0xD29B, prLVT}, // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH - {0xD29C, 0xD29C, prLV}, // Lo HANGUL SYLLABLE TYU - {0xD29D, 0xD2B7, prLVT}, // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH - {0xD2B8, 0xD2B8, prLV}, // Lo HANGUL SYLLABLE TEU - {0xD2B9, 0xD2D3, prLVT}, // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH - {0xD2D4, 0xD2D4, prLV}, // Lo HANGUL SYLLABLE TYI - {0xD2D5, 0xD2EF, prLVT}, // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH - {0xD2F0, 0xD2F0, prLV}, // Lo HANGUL SYLLABLE TI - {0xD2F1, 0xD30B, prLVT}, // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH - {0xD30C, 0xD30C, prLV}, // Lo HANGUL SYLLABLE PA - {0xD30D, 0xD327, prLVT}, // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH - {0xD328, 0xD328, prLV}, // Lo HANGUL SYLLABLE PAE - {0xD329, 0xD343, prLVT}, // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH - {0xD344, 0xD344, prLV}, // Lo HANGUL SYLLABLE PYA - {0xD345, 0xD35F, prLVT}, // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH - {0xD360, 0xD360, prLV}, // Lo HANGUL SYLLABLE PYAE - {0xD361, 0xD37B, prLVT}, // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH - {0xD37C, 0xD37C, prLV}, // Lo HANGUL SYLLABLE PEO - {0xD37D, 0xD397, prLVT}, // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH - {0xD398, 0xD398, prLV}, // Lo HANGUL SYLLABLE PE - {0xD399, 0xD3B3, prLVT}, // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH - {0xD3B4, 0xD3B4, prLV}, // Lo HANGUL SYLLABLE PYEO - {0xD3B5, 0xD3CF, prLVT}, // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH - {0xD3D0, 0xD3D0, prLV}, // Lo HANGUL SYLLABLE PYE - {0xD3D1, 0xD3EB, prLVT}, // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH - {0xD3EC, 0xD3EC, prLV}, // Lo HANGUL SYLLABLE PO - {0xD3ED, 0xD407, prLVT}, // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH - {0xD408, 0xD408, prLV}, // Lo HANGUL SYLLABLE PWA - {0xD409, 0xD423, prLVT}, // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH - {0xD424, 0xD424, prLV}, // Lo HANGUL SYLLABLE PWAE - {0xD425, 0xD43F, prLVT}, // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH - {0xD440, 0xD440, prLV}, // Lo HANGUL SYLLABLE POE - {0xD441, 0xD45B, prLVT}, // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH - {0xD45C, 0xD45C, prLV}, // Lo HANGUL SYLLABLE PYO - {0xD45D, 0xD477, prLVT}, // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH - {0xD478, 0xD478, prLV}, // Lo HANGUL SYLLABLE PU - {0xD479, 0xD493, prLVT}, // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH - {0xD494, 0xD494, prLV}, // Lo HANGUL SYLLABLE PWEO - {0xD495, 0xD4AF, prLVT}, // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH - {0xD4B0, 0xD4B0, prLV}, // Lo HANGUL SYLLABLE PWE - {0xD4B1, 0xD4CB, prLVT}, // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH - {0xD4CC, 0xD4CC, prLV}, // Lo HANGUL SYLLABLE PWI - {0xD4CD, 0xD4E7, prLVT}, // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH - {0xD4E8, 0xD4E8, prLV}, // Lo HANGUL SYLLABLE PYU - {0xD4E9, 0xD503, prLVT}, // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH - {0xD504, 0xD504, prLV}, // Lo HANGUL SYLLABLE PEU - {0xD505, 0xD51F, prLVT}, // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH - {0xD520, 0xD520, prLV}, // Lo HANGUL SYLLABLE PYI - {0xD521, 0xD53B, prLVT}, // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH - {0xD53C, 0xD53C, prLV}, // Lo HANGUL SYLLABLE PI - {0xD53D, 0xD557, prLVT}, // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH - {0xD558, 0xD558, prLV}, // Lo HANGUL SYLLABLE HA - {0xD559, 0xD573, prLVT}, // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH - {0xD574, 0xD574, prLV}, // Lo HANGUL SYLLABLE HAE - {0xD575, 0xD58F, prLVT}, // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH - {0xD590, 0xD590, prLV}, // Lo HANGUL SYLLABLE HYA - {0xD591, 0xD5AB, prLVT}, // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH - {0xD5AC, 0xD5AC, prLV}, // Lo HANGUL SYLLABLE HYAE - {0xD5AD, 0xD5C7, prLVT}, // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH - {0xD5C8, 0xD5C8, prLV}, // Lo HANGUL SYLLABLE HEO - {0xD5C9, 0xD5E3, prLVT}, // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH - {0xD5E4, 0xD5E4, prLV}, // Lo HANGUL SYLLABLE HE - {0xD5E5, 0xD5FF, prLVT}, // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH - {0xD600, 0xD600, prLV}, // Lo HANGUL SYLLABLE HYEO - {0xD601, 0xD61B, prLVT}, // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH - {0xD61C, 0xD61C, prLV}, // Lo HANGUL SYLLABLE HYE - {0xD61D, 0xD637, prLVT}, // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH - {0xD638, 0xD638, prLV}, // Lo HANGUL SYLLABLE HO - {0xD639, 0xD653, prLVT}, // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH - {0xD654, 0xD654, prLV}, // Lo HANGUL SYLLABLE HWA - {0xD655, 0xD66F, prLVT}, // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH - {0xD670, 0xD670, prLV}, // Lo HANGUL SYLLABLE HWAE - {0xD671, 0xD68B, prLVT}, // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH - {0xD68C, 0xD68C, prLV}, // Lo HANGUL SYLLABLE HOE - {0xD68D, 0xD6A7, prLVT}, // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH - {0xD6A8, 0xD6A8, prLV}, // Lo HANGUL SYLLABLE HYO - {0xD6A9, 0xD6C3, prLVT}, // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH - {0xD6C4, 0xD6C4, prLV}, // Lo HANGUL SYLLABLE HU - {0xD6C5, 0xD6DF, prLVT}, // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH - {0xD6E0, 0xD6E0, prLV}, // Lo HANGUL SYLLABLE HWEO - {0xD6E1, 0xD6FB, prLVT}, // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH - {0xD6FC, 0xD6FC, prLV}, // Lo HANGUL SYLLABLE HWE - {0xD6FD, 0xD717, prLVT}, // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH - {0xD718, 0xD718, prLV}, // Lo HANGUL SYLLABLE HWI - {0xD719, 0xD733, prLVT}, // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH - {0xD734, 0xD734, prLV}, // Lo HANGUL SYLLABLE HYU - {0xD735, 0xD74F, prLVT}, // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH - {0xD750, 0xD750, prLV}, // Lo HANGUL SYLLABLE HEU - {0xD751, 0xD76B, prLVT}, // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH - {0xD76C, 0xD76C, prLV}, // Lo HANGUL SYLLABLE HYI - {0xD76D, 0xD787, prLVT}, // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH - {0xD788, 0xD788, prLV}, // Lo HANGUL SYLLABLE HI - {0xD789, 0xD7A3, prLVT}, // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH - {0xD7B0, 0xD7C6, prV}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E - {0xD7CB, 0xD7FB, prT}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH - {0xFB1E, 0xFB1E, prExtend}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA - {0xFE00, 0xFE0F, prExtend}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 - {0xFE20, 0xFE2F, prExtend}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF - {0xFEFF, 0xFEFF, prControl}, // Cf ZERO WIDTH NO-BREAK SPACE - {0xFF9E, 0xFF9F, prExtend}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK - {0xFFF0, 0xFFF8, prControl}, // Cn [9] .. - {0xFFF9, 0xFFFB, prControl}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR - {0x101FD, 0x101FD, prExtend}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE - {0x102E0, 0x102E0, prExtend}, // Mn COPTIC EPACT THOUSANDS MARK - {0x10376, 0x1037A, prExtend}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII - {0x10A01, 0x10A03, prExtend}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R - {0x10A05, 0x10A06, prExtend}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O - {0x10A0C, 0x10A0F, prExtend}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA - {0x10A38, 0x10A3A, prExtend}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW - {0x10A3F, 0x10A3F, prExtend}, // Mn KHAROSHTHI VIRAMA - {0x10AE5, 0x10AE6, prExtend}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW - {0x10D24, 0x10D27, prExtend}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI - {0x10F46, 0x10F50, prExtend}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW - {0x11000, 0x11000, prSpacingMark}, // Mc BRAHMI SIGN CANDRABINDU - {0x11001, 0x11001, prExtend}, // Mn BRAHMI SIGN ANUSVARA - {0x11002, 0x11002, prSpacingMark}, // Mc BRAHMI SIGN VISARGA - {0x11038, 0x11046, prExtend}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA - {0x1107F, 0x11081, prExtend}, // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA - {0x11082, 0x11082, prSpacingMark}, // Mc KAITHI SIGN VISARGA - {0x110B0, 0x110B2, prSpacingMark}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II - {0x110B3, 0x110B6, prExtend}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI - {0x110B7, 0x110B8, prSpacingMark}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU - {0x110B9, 0x110BA, prExtend}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA - {0x110BD, 0x110BD, prPreprend}, // Cf KAITHI NUMBER SIGN - {0x110CD, 0x110CD, prPreprend}, // Cf KAITHI NUMBER SIGN ABOVE - {0x11100, 0x11102, prExtend}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA - {0x11127, 0x1112B, prExtend}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU - {0x1112C, 0x1112C, prSpacingMark}, // Mc CHAKMA VOWEL SIGN E - {0x1112D, 0x11134, prExtend}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA - {0x11145, 0x11146, prSpacingMark}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI - {0x11173, 0x11173, prExtend}, // Mn MAHAJANI SIGN NUKTA - {0x11180, 0x11181, prExtend}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA - {0x11182, 0x11182, prSpacingMark}, // Mc SHARADA SIGN VISARGA - {0x111B3, 0x111B5, prSpacingMark}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II - {0x111B6, 0x111BE, prExtend}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O - {0x111BF, 0x111C0, prSpacingMark}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA - {0x111C2, 0x111C3, prPreprend}, // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA - {0x111C9, 0x111CC, prExtend}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK - {0x1122C, 0x1122E, prSpacingMark}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II - {0x1122F, 0x11231, prExtend}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI - {0x11232, 0x11233, prSpacingMark}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU - {0x11234, 0x11234, prExtend}, // Mn KHOJKI SIGN ANUSVARA - {0x11235, 0x11235, prSpacingMark}, // Mc KHOJKI SIGN VIRAMA - {0x11236, 0x11237, prExtend}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA - {0x1123E, 0x1123E, prExtend}, // Mn KHOJKI SIGN SUKUN - {0x112DF, 0x112DF, prExtend}, // Mn KHUDAWADI SIGN ANUSVARA - {0x112E0, 0x112E2, prSpacingMark}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II - {0x112E3, 0x112EA, prExtend}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA - {0x11300, 0x11301, prExtend}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU - {0x11302, 0x11303, prSpacingMark}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA - {0x1133B, 0x1133C, prExtend}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA - {0x1133E, 0x1133E, prExtend}, // Mc GRANTHA VOWEL SIGN AA - {0x1133F, 0x1133F, prSpacingMark}, // Mc GRANTHA VOWEL SIGN I - {0x11340, 0x11340, prExtend}, // Mn GRANTHA VOWEL SIGN II - {0x11341, 0x11344, prSpacingMark}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR - {0x11347, 0x11348, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI - {0x1134B, 0x1134D, prSpacingMark}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA - {0x11357, 0x11357, prExtend}, // Mc GRANTHA AU LENGTH MARK - {0x11362, 0x11363, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL - {0x11366, 0x1136C, prExtend}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX - {0x11370, 0x11374, prExtend}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA - {0x11435, 0x11437, prSpacingMark}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II - {0x11438, 0x1143F, prExtend}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI - {0x11440, 0x11441, prSpacingMark}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU - {0x11442, 0x11444, prExtend}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA - {0x11445, 0x11445, prSpacingMark}, // Mc NEWA SIGN VISARGA - {0x11446, 0x11446, prExtend}, // Mn NEWA SIGN NUKTA - {0x1145E, 0x1145E, prExtend}, // Mn NEWA SANDHI MARK - {0x114B0, 0x114B0, prExtend}, // Mc TIRHUTA VOWEL SIGN AA - {0x114B1, 0x114B2, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II - {0x114B3, 0x114B8, prExtend}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL - {0x114B9, 0x114B9, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN E - {0x114BA, 0x114BA, prExtend}, // Mn TIRHUTA VOWEL SIGN SHORT E - {0x114BB, 0x114BC, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O - {0x114BD, 0x114BD, prExtend}, // Mc TIRHUTA VOWEL SIGN SHORT O - {0x114BE, 0x114BE, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN AU - {0x114BF, 0x114C0, prExtend}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA - {0x114C1, 0x114C1, prSpacingMark}, // Mc TIRHUTA SIGN VISARGA - {0x114C2, 0x114C3, prExtend}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA - {0x115AF, 0x115AF, prExtend}, // Mc SIDDHAM VOWEL SIGN AA - {0x115B0, 0x115B1, prSpacingMark}, // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II - {0x115B2, 0x115B5, prExtend}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR - {0x115B8, 0x115BB, prSpacingMark}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU - {0x115BC, 0x115BD, prExtend}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA - {0x115BE, 0x115BE, prSpacingMark}, // Mc SIDDHAM SIGN VISARGA - {0x115BF, 0x115C0, prExtend}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA - {0x115DC, 0x115DD, prExtend}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU - {0x11630, 0x11632, prSpacingMark}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II - {0x11633, 0x1163A, prExtend}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI - {0x1163B, 0x1163C, prSpacingMark}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU - {0x1163D, 0x1163D, prExtend}, // Mn MODI SIGN ANUSVARA - {0x1163E, 0x1163E, prSpacingMark}, // Mc MODI SIGN VISARGA - {0x1163F, 0x11640, prExtend}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA - {0x116AB, 0x116AB, prExtend}, // Mn TAKRI SIGN ANUSVARA - {0x116AC, 0x116AC, prSpacingMark}, // Mc TAKRI SIGN VISARGA - {0x116AD, 0x116AD, prExtend}, // Mn TAKRI VOWEL SIGN AA - {0x116AE, 0x116AF, prSpacingMark}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II - {0x116B0, 0x116B5, prExtend}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU - {0x116B6, 0x116B6, prSpacingMark}, // Mc TAKRI SIGN VIRAMA - {0x116B7, 0x116B7, prExtend}, // Mn TAKRI SIGN NUKTA - {0x1171D, 0x1171F, prExtend}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA - {0x11720, 0x11721, prSpacingMark}, // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA - {0x11722, 0x11725, prExtend}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU - {0x11726, 0x11726, prSpacingMark}, // Mc AHOM VOWEL SIGN E - {0x11727, 0x1172B, prExtend}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER - {0x1182C, 0x1182E, prSpacingMark}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II - {0x1182F, 0x11837, prExtend}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA - {0x11838, 0x11838, prSpacingMark}, // Mc DOGRA SIGN VISARGA - {0x11839, 0x1183A, prExtend}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA - {0x119D1, 0x119D3, prSpacingMark}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II - {0x119D4, 0x119D7, prExtend}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR - {0x119DA, 0x119DB, prExtend}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI - {0x119DC, 0x119DF, prSpacingMark}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA - {0x119E0, 0x119E0, prExtend}, // Mn NANDINAGARI SIGN VIRAMA - {0x119E4, 0x119E4, prSpacingMark}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E - {0x11A01, 0x11A0A, prExtend}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK - {0x11A33, 0x11A38, prExtend}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA - {0x11A39, 0x11A39, prSpacingMark}, // Mc ZANABAZAR SQUARE SIGN VISARGA - {0x11A3A, 0x11A3A, prPreprend}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA - {0x11A3B, 0x11A3E, prExtend}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA - {0x11A47, 0x11A47, prExtend}, // Mn ZANABAZAR SQUARE SUBJOINER - {0x11A51, 0x11A56, prExtend}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE - {0x11A57, 0x11A58, prSpacingMark}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU - {0x11A59, 0x11A5B, prExtend}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK - {0x11A84, 0x11A89, prPreprend}, // Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA - {0x11A8A, 0x11A96, prExtend}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA - {0x11A97, 0x11A97, prSpacingMark}, // Mc SOYOMBO SIGN VISARGA - {0x11A98, 0x11A99, prExtend}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER - {0x11C2F, 0x11C2F, prSpacingMark}, // Mc BHAIKSUKI VOWEL SIGN AA - {0x11C30, 0x11C36, prExtend}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L - {0x11C38, 0x11C3D, prExtend}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA - {0x11C3E, 0x11C3E, prSpacingMark}, // Mc BHAIKSUKI SIGN VISARGA - {0x11C3F, 0x11C3F, prExtend}, // Mn BHAIKSUKI SIGN VIRAMA - {0x11C92, 0x11CA7, prExtend}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA - {0x11CA9, 0x11CA9, prSpacingMark}, // Mc MARCHEN SUBJOINED LETTER YA - {0x11CAA, 0x11CB0, prExtend}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA - {0x11CB1, 0x11CB1, prSpacingMark}, // Mc MARCHEN VOWEL SIGN I - {0x11CB2, 0x11CB3, prExtend}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E - {0x11CB4, 0x11CB4, prSpacingMark}, // Mc MARCHEN VOWEL SIGN O - {0x11CB5, 0x11CB6, prExtend}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU - {0x11D31, 0x11D36, prExtend}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R - {0x11D3A, 0x11D3A, prExtend}, // Mn MASARAM GONDI VOWEL SIGN E - {0x11D3C, 0x11D3D, prExtend}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O - {0x11D3F, 0x11D45, prExtend}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA - {0x11D46, 0x11D46, prPreprend}, // Lo MASARAM GONDI REPHA - {0x11D47, 0x11D47, prExtend}, // Mn MASARAM GONDI RA-KARA - {0x11D8A, 0x11D8E, prSpacingMark}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU - {0x11D90, 0x11D91, prExtend}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI - {0x11D93, 0x11D94, prSpacingMark}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU - {0x11D95, 0x11D95, prExtend}, // Mn GUNJALA GONDI SIGN ANUSVARA - {0x11D96, 0x11D96, prSpacingMark}, // Mc GUNJALA GONDI SIGN VISARGA - {0x11D97, 0x11D97, prExtend}, // Mn GUNJALA GONDI VIRAMA - {0x11EF3, 0x11EF4, prExtend}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U - {0x11EF5, 0x11EF6, prSpacingMark}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O - {0x13430, 0x13438, prControl}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT - {0x16AF0, 0x16AF4, prExtend}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE - {0x16B30, 0x16B36, prExtend}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM - {0x16F4F, 0x16F4F, prExtend}, // Mn MIAO SIGN CONSONANT MODIFIER BAR - {0x16F51, 0x16F87, prSpacingMark}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI - {0x16F8F, 0x16F92, prExtend}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW - {0x1BC9D, 0x1BC9E, prExtend}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK - {0x1BCA0, 0x1BCA3, prControl}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP - {0x1D165, 0x1D165, prExtend}, // Mc MUSICAL SYMBOL COMBINING STEM - {0x1D166, 0x1D166, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM - {0x1D167, 0x1D169, prExtend}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 - {0x1D16D, 0x1D16D, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT - {0x1D16E, 0x1D172, prExtend}, // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 - {0x1D173, 0x1D17A, prControl}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE - {0x1D17B, 0x1D182, prExtend}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE - {0x1D185, 0x1D18B, prExtend}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE - {0x1D1AA, 0x1D1AD, prExtend}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO - {0x1D242, 0x1D244, prExtend}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME - {0x1DA00, 0x1DA36, prExtend}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN - {0x1DA3B, 0x1DA6C, prExtend}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT - {0x1DA75, 0x1DA75, prExtend}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS - {0x1DA84, 0x1DA84, prExtend}, // Mn SIGNWRITING LOCATION HEAD NECK - {0x1DA9B, 0x1DA9F, prExtend}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 - {0x1DAA1, 0x1DAAF, prExtend}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 - {0x1E000, 0x1E006, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE - {0x1E008, 0x1E018, prExtend}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU - {0x1E01B, 0x1E021, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI - {0x1E023, 0x1E024, prExtend}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS - {0x1E026, 0x1E02A, prExtend}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA - {0x1E130, 0x1E136, prExtend}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D - {0x1E2EC, 0x1E2EF, prExtend}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI - {0x1E8D0, 0x1E8D6, prExtend}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS - {0x1E944, 0x1E94A, prExtend}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA - {0x1F000, 0x1F02B, prExtendedPictographic}, // 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK - {0x1F02C, 0x1F02F, prExtendedPictographic}, // NA [4] (🀬..🀯) .. - {0x1F030, 0x1F093, prExtendedPictographic}, // 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 - {0x1F094, 0x1F09F, prExtendedPictographic}, // NA [12] (🂔..🂟) .. - {0x1F0A0, 0x1F0AE, prExtendedPictographic}, // 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES - {0x1F0AF, 0x1F0B0, prExtendedPictographic}, // NA [2] (🂯..🂰) .. - {0x1F0B1, 0x1F0BE, prExtendedPictographic}, // 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS - {0x1F0BF, 0x1F0BF, prExtendedPictographic}, // 7.0 [1] (🂿) PLAYING CARD RED JOKER - {0x1F0C0, 0x1F0C0, prExtendedPictographic}, // NA [1] (🃀) - {0x1F0C1, 0x1F0CF, prExtendedPictographic}, // 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker - {0x1F0D0, 0x1F0D0, prExtendedPictographic}, // NA [1] (🃐) - {0x1F0D1, 0x1F0DF, prExtendedPictographic}, // 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER - {0x1F0E0, 0x1F0F5, prExtendedPictographic}, // 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21 - {0x1F0F6, 0x1F0FF, prExtendedPictographic}, // NA [10] (🃶..🃿) .. - {0x1F10D, 0x1F10F, prExtendedPictographic}, // NA [3] (🄍..🄏) .. - {0x1F12F, 0x1F12F, prExtendedPictographic}, // 11.0 [1] (🄯) COPYLEFT SYMBOL - {0x1F16C, 0x1F16C, prExtendedPictographic}, // 12.0 [1] (🅬) RAISED MR SIGN - {0x1F16D, 0x1F16F, prExtendedPictographic}, // NA [3] (🅭..🅯) .. - {0x1F170, 0x1F171, prExtendedPictographic}, // 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type) - {0x1F17E, 0x1F17E, prExtendedPictographic}, // 6.0 [1] (🅾️) O button (blood type) - {0x1F17F, 0x1F17F, prExtendedPictographic}, // 5.2 [1] (🅿️) P button - {0x1F18E, 0x1F18E, prExtendedPictographic}, // 6.0 [1] (🆎) AB button (blood type) - {0x1F191, 0x1F19A, prExtendedPictographic}, // 6.0 [10] (🆑..🆚) CL button..VS button - {0x1F1AD, 0x1F1E5, prExtendedPictographic}, // NA [57] (🆭..🇥) .. - {0x1F1E6, 0x1F1FF, prRegionalIndicator}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z - {0x1F201, 0x1F202, prExtendedPictographic}, // 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button - {0x1F203, 0x1F20F, prExtendedPictographic}, // NA [13] (🈃..🈏) .. - {0x1F21A, 0x1F21A, prExtendedPictographic}, // 5.2 [1] (🈚) Japanese “free of charge” button - {0x1F22F, 0x1F22F, prExtendedPictographic}, // 5.2 [1] (🈯) Japanese “reserved” button - {0x1F232, 0x1F23A, prExtendedPictographic}, // 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button - {0x1F23C, 0x1F23F, prExtendedPictographic}, // NA [4] (🈼..🈿) .. - {0x1F249, 0x1F24F, prExtendedPictographic}, // NA [7] (🉉..🉏) .. - {0x1F250, 0x1F251, prExtendedPictographic}, // 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button - {0x1F252, 0x1F25F, prExtendedPictographic}, // NA [14] (🉒..🉟) .. - {0x1F260, 0x1F265, prExtendedPictographic}, // 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI - {0x1F266, 0x1F2FF, prExtendedPictographic}, // NA[154] (🉦..🋿) .. - {0x1F300, 0x1F320, prExtendedPictographic}, // 6.0 [33] (🌀..🌠) cyclone..shooting star - {0x1F321, 0x1F32C, prExtendedPictographic}, // 7.0 [12] (🌡️..🌬️) thermometer..wind face - {0x1F32D, 0x1F32F, prExtendedPictographic}, // 8.0 [3] (🌭..🌯) hot dog..burrito - {0x1F330, 0x1F335, prExtendedPictographic}, // 6.0 [6] (🌰..🌵) chestnut..cactus - {0x1F336, 0x1F336, prExtendedPictographic}, // 7.0 [1] (🌶️) hot pepper - {0x1F337, 0x1F37C, prExtendedPictographic}, // 6.0 [70] (🌷..🍼) tulip..baby bottle - {0x1F37D, 0x1F37D, prExtendedPictographic}, // 7.0 [1] (🍽️) fork and knife with plate - {0x1F37E, 0x1F37F, prExtendedPictographic}, // 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn - {0x1F380, 0x1F393, prExtendedPictographic}, // 6.0 [20] (🎀..🎓) ribbon..graduation cap - {0x1F394, 0x1F39F, prExtendedPictographic}, // 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets - {0x1F3A0, 0x1F3C4, prExtendedPictographic}, // 6.0 [37] (🎠..🏄) carousel horse..person surfing - {0x1F3C5, 0x1F3C5, prExtendedPictographic}, // 7.0 [1] (🏅) sports medal - {0x1F3C6, 0x1F3CA, prExtendedPictographic}, // 6.0 [5] (🏆..🏊) trophy..person swimming - {0x1F3CB, 0x1F3CE, prExtendedPictographic}, // 7.0 [4] (🏋️..🏎️) person lifting weights..racing car - {0x1F3CF, 0x1F3D3, prExtendedPictographic}, // 8.0 [5] (🏏..🏓) cricket game..ping pong - {0x1F3D4, 0x1F3DF, prExtendedPictographic}, // 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium - {0x1F3E0, 0x1F3F0, prExtendedPictographic}, // 6.0 [17] (🏠..🏰) house..castle - {0x1F3F1, 0x1F3F7, prExtendedPictographic}, // 7.0 [7] (🏱..🏷️) WHITE PENNANT..label - {0x1F3F8, 0x1F3FA, prExtendedPictographic}, // 8.0 [3] (🏸..🏺) badminton..amphora - {0x1F3FB, 0x1F3FF, prExtend}, // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 - {0x1F400, 0x1F43E, prExtendedPictographic}, // 6.0 [63] (🐀..🐾) rat..paw prints - {0x1F43F, 0x1F43F, prExtendedPictographic}, // 7.0 [1] (🐿️) chipmunk - {0x1F440, 0x1F440, prExtendedPictographic}, // 6.0 [1] (👀) eyes - {0x1F441, 0x1F441, prExtendedPictographic}, // 7.0 [1] (👁️) eye - {0x1F442, 0x1F4F7, prExtendedPictographic}, // 6.0[182] (👂..📷) ear..camera - {0x1F4F8, 0x1F4F8, prExtendedPictographic}, // 7.0 [1] (📸) camera with flash - {0x1F4F9, 0x1F4FC, prExtendedPictographic}, // 6.0 [4] (📹..📼) video camera..videocassette - {0x1F4FD, 0x1F4FE, prExtendedPictographic}, // 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO - {0x1F4FF, 0x1F4FF, prExtendedPictographic}, // 8.0 [1] (📿) prayer beads - {0x1F500, 0x1F53D, prExtendedPictographic}, // 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button - {0x1F546, 0x1F54A, prExtendedPictographic}, // 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove - {0x1F54B, 0x1F54F, prExtendedPictographic}, // 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA - {0x1F550, 0x1F567, prExtendedPictographic}, // 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty - {0x1F568, 0x1F579, prExtendedPictographic}, // 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick - {0x1F57A, 0x1F57A, prExtendedPictographic}, // 9.0 [1] (🕺) man dancing - {0x1F57B, 0x1F5A3, prExtendedPictographic}, // 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX - {0x1F5A4, 0x1F5A4, prExtendedPictographic}, // 9.0 [1] (🖤) black heart - {0x1F5A5, 0x1F5FA, prExtendedPictographic}, // 7.0 [86] (🖥️..🗺️) desktop computer..world map - {0x1F5FB, 0x1F5FF, prExtendedPictographic}, // 6.0 [5] (🗻..🗿) mount fuji..moai - {0x1F600, 0x1F600, prExtendedPictographic}, // 6.1 [1] (😀) grinning face - {0x1F601, 0x1F610, prExtendedPictographic}, // 6.0 [16] (😁..😐) beaming face with smiling eyes..neutral face - {0x1F611, 0x1F611, prExtendedPictographic}, // 6.1 [1] (😑) expressionless face - {0x1F612, 0x1F614, prExtendedPictographic}, // 6.0 [3] (😒..😔) unamused face..pensive face - {0x1F615, 0x1F615, prExtendedPictographic}, // 6.1 [1] (😕) confused face - {0x1F616, 0x1F616, prExtendedPictographic}, // 6.0 [1] (😖) confounded face - {0x1F617, 0x1F617, prExtendedPictographic}, // 6.1 [1] (😗) kissing face - {0x1F618, 0x1F618, prExtendedPictographic}, // 6.0 [1] (😘) face blowing a kiss - {0x1F619, 0x1F619, prExtendedPictographic}, // 6.1 [1] (😙) kissing face with smiling eyes - {0x1F61A, 0x1F61A, prExtendedPictographic}, // 6.0 [1] (😚) kissing face with closed eyes - {0x1F61B, 0x1F61B, prExtendedPictographic}, // 6.1 [1] (😛) face with tongue - {0x1F61C, 0x1F61E, prExtendedPictographic}, // 6.0 [3] (😜..😞) winking face with tongue..disappointed face - {0x1F61F, 0x1F61F, prExtendedPictographic}, // 6.1 [1] (😟) worried face - {0x1F620, 0x1F625, prExtendedPictographic}, // 6.0 [6] (😠..😥) angry face..sad but relieved face - {0x1F626, 0x1F627, prExtendedPictographic}, // 6.1 [2] (😦..😧) frowning face with open mouth..anguished face - {0x1F628, 0x1F62B, prExtendedPictographic}, // 6.0 [4] (😨..😫) fearful face..tired face - {0x1F62C, 0x1F62C, prExtendedPictographic}, // 6.1 [1] (😬) grimacing face - {0x1F62D, 0x1F62D, prExtendedPictographic}, // 6.0 [1] (😭) loudly crying face - {0x1F62E, 0x1F62F, prExtendedPictographic}, // 6.1 [2] (😮..😯) face with open mouth..hushed face - {0x1F630, 0x1F633, prExtendedPictographic}, // 6.0 [4] (😰..😳) anxious face with sweat..flushed face - {0x1F634, 0x1F634, prExtendedPictographic}, // 6.1 [1] (😴) sleeping face - {0x1F635, 0x1F640, prExtendedPictographic}, // 6.0 [12] (😵..🙀) dizzy face..weary cat - {0x1F641, 0x1F642, prExtendedPictographic}, // 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face - {0x1F643, 0x1F644, prExtendedPictographic}, // 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes - {0x1F645, 0x1F64F, prExtendedPictographic}, // 6.0 [11] (🙅..🙏) person gesturing NO..folded hands - {0x1F680, 0x1F6C5, prExtendedPictographic}, // 6.0 [70] (🚀..🛅) rocket..left luggage - {0x1F6C6, 0x1F6CF, prExtendedPictographic}, // 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed - {0x1F6D0, 0x1F6D0, prExtendedPictographic}, // 8.0 [1] (🛐) place of worship - {0x1F6D1, 0x1F6D2, prExtendedPictographic}, // 9.0 [2] (🛑..🛒) stop sign..shopping cart - {0x1F6D3, 0x1F6D4, prExtendedPictographic}, // 10.0 [2] (🛓..🛔) STUPA..PAGODA - {0x1F6D5, 0x1F6D5, prExtendedPictographic}, // 12.0 [1] (🛕) hindu temple - {0x1F6D6, 0x1F6DF, prExtendedPictographic}, // NA [10] (🛖..🛟) .. - {0x1F6E0, 0x1F6EC, prExtendedPictographic}, // 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival - {0x1F6ED, 0x1F6EF, prExtendedPictographic}, // NA [3] (🛭..🛯) .. - {0x1F6F0, 0x1F6F3, prExtendedPictographic}, // 7.0 [4] (🛰️..🛳️) satellite..passenger ship - {0x1F6F4, 0x1F6F6, prExtendedPictographic}, // 9.0 [3] (🛴..🛶) kick scooter..canoe - {0x1F6F7, 0x1F6F8, prExtendedPictographic}, // 10.0 [2] (🛷..🛸) sled..flying saucer - {0x1F6F9, 0x1F6F9, prExtendedPictographic}, // 11.0 [1] (🛹) skateboard - {0x1F6FA, 0x1F6FA, prExtendedPictographic}, // 12.0 [1] (🛺) auto rickshaw - {0x1F6FB, 0x1F6FF, prExtendedPictographic}, // NA [5] (🛻..🛿) .. - {0x1F774, 0x1F77F, prExtendedPictographic}, // NA [12] (🝴..🝿) .. - {0x1F7D5, 0x1F7D8, prExtendedPictographic}, // 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE - {0x1F7D9, 0x1F7DF, prExtendedPictographic}, // NA [7] (🟙..🟟) .. - {0x1F7E0, 0x1F7EB, prExtendedPictographic}, // 12.0 [12] (🟠..🟫) orange circle..brown square - {0x1F7EC, 0x1F7FF, prExtendedPictographic}, // NA [20] (🟬..🟿) .. - {0x1F80C, 0x1F80F, prExtendedPictographic}, // NA [4] (🠌..🠏) .. - {0x1F848, 0x1F84F, prExtendedPictographic}, // NA [8] (🡈..🡏) .. - {0x1F85A, 0x1F85F, prExtendedPictographic}, // NA [6] (🡚..🡟) .. - {0x1F888, 0x1F88F, prExtendedPictographic}, // NA [8] (🢈..🢏) .. - {0x1F8AE, 0x1F8FF, prExtendedPictographic}, // NA [82] (🢮..🣿) .. - {0x1F90C, 0x1F90C, prExtendedPictographic}, // NA [1] (🤌) - {0x1F90D, 0x1F90F, prExtendedPictographic}, // 12.0 [3] (🤍..🤏) white heart..pinching hand - {0x1F910, 0x1F918, prExtendedPictographic}, // 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns - {0x1F919, 0x1F91E, prExtendedPictographic}, // 9.0 [6] (🤙..🤞) call me hand..crossed fingers - {0x1F91F, 0x1F91F, prExtendedPictographic}, // 10.0 [1] (🤟) love-you gesture - {0x1F920, 0x1F927, prExtendedPictographic}, // 9.0 [8] (🤠..🤧) cowboy hat face..sneezing face - {0x1F928, 0x1F92F, prExtendedPictographic}, // 10.0 [8] (🤨..🤯) face with raised eyebrow..exploding head - {0x1F930, 0x1F930, prExtendedPictographic}, // 9.0 [1] (🤰) pregnant woman - {0x1F931, 0x1F932, prExtendedPictographic}, // 10.0 [2] (🤱..🤲) breast-feeding..palms up together - {0x1F933, 0x1F93A, prExtendedPictographic}, // 9.0 [8] (🤳..🤺) selfie..person fencing - {0x1F93C, 0x1F93E, prExtendedPictographic}, // 9.0 [3] (🤼..🤾) people wrestling..person playing handball - {0x1F93F, 0x1F93F, prExtendedPictographic}, // 12.0 [1] (🤿) diving mask - {0x1F940, 0x1F945, prExtendedPictographic}, // 9.0 [6] (🥀..🥅) wilted flower..goal net - {0x1F947, 0x1F94B, prExtendedPictographic}, // 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform - {0x1F94C, 0x1F94C, prExtendedPictographic}, // 10.0 [1] (🥌) curling stone - {0x1F94D, 0x1F94F, prExtendedPictographic}, // 11.0 [3] (🥍..🥏) lacrosse..flying disc - {0x1F950, 0x1F95E, prExtendedPictographic}, // 9.0 [15] (🥐..🥞) croissant..pancakes - {0x1F95F, 0x1F96B, prExtendedPictographic}, // 10.0 [13] (🥟..🥫) dumpling..canned food - {0x1F96C, 0x1F970, prExtendedPictographic}, // 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts - {0x1F971, 0x1F971, prExtendedPictographic}, // 12.0 [1] (🥱) yawning face - {0x1F972, 0x1F972, prExtendedPictographic}, // NA [1] (🥲) - {0x1F973, 0x1F976, prExtendedPictographic}, // 11.0 [4] (🥳..🥶) partying face..cold face - {0x1F977, 0x1F979, prExtendedPictographic}, // NA [3] (🥷..🥹) .. - {0x1F97A, 0x1F97A, prExtendedPictographic}, // 11.0 [1] (🥺) pleading face - {0x1F97B, 0x1F97B, prExtendedPictographic}, // 12.0 [1] (🥻) sari - {0x1F97C, 0x1F97F, prExtendedPictographic}, // 11.0 [4] (🥼..🥿) lab coat..flat shoe - {0x1F980, 0x1F984, prExtendedPictographic}, // 8.0 [5] (🦀..🦄) crab..unicorn - {0x1F985, 0x1F991, prExtendedPictographic}, // 9.0 [13] (🦅..🦑) eagle..squid - {0x1F992, 0x1F997, prExtendedPictographic}, // 10.0 [6] (🦒..🦗) giraffe..cricket - {0x1F998, 0x1F9A2, prExtendedPictographic}, // 11.0 [11] (🦘..🦢) kangaroo..swan - {0x1F9A3, 0x1F9A4, prExtendedPictographic}, // NA [2] (🦣..🦤) .. - {0x1F9A5, 0x1F9AA, prExtendedPictographic}, // 12.0 [6] (🦥..🦪) sloth..oyster - {0x1F9AB, 0x1F9AD, prExtendedPictographic}, // NA [3] (🦫..🦭) .. - {0x1F9AE, 0x1F9AF, prExtendedPictographic}, // 12.0 [2] (🦮..🦯) guide dog..probing cane - {0x1F9B0, 0x1F9B9, prExtendedPictographic}, // 11.0 [10] (🦰..🦹) red hair..supervillain - {0x1F9BA, 0x1F9BF, prExtendedPictographic}, // 12.0 [6] (🦺..🦿) safety vest..mechanical leg - {0x1F9C0, 0x1F9C0, prExtendedPictographic}, // 8.0 [1] (🧀) cheese wedge - {0x1F9C1, 0x1F9C2, prExtendedPictographic}, // 11.0 [2] (🧁..🧂) cupcake..salt - {0x1F9C3, 0x1F9CA, prExtendedPictographic}, // 12.0 [8] (🧃..🧊) beverage box..ice cube - {0x1F9CB, 0x1F9CC, prExtendedPictographic}, // NA [2] (🧋..🧌) .. - {0x1F9CD, 0x1F9CF, prExtendedPictographic}, // 12.0 [3] (🧍..🧏) person standing..deaf person - {0x1F9D0, 0x1F9E6, prExtendedPictographic}, // 10.0 [23] (🧐..🧦) face with monocle..socks - {0x1F9E7, 0x1F9FF, prExtendedPictographic}, // 11.0 [25] (🧧..🧿) red envelope..nazar amulet - {0x1FA00, 0x1FA53, prExtendedPictographic}, // 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP - {0x1FA54, 0x1FA5F, prExtendedPictographic}, // NA [12] (🩔..🩟) .. - {0x1FA60, 0x1FA6D, prExtendedPictographic}, // 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER - {0x1FA6E, 0x1FA6F, prExtendedPictographic}, // NA [2] (🩮..🩯) .. - {0x1FA70, 0x1FA73, prExtendedPictographic}, // 12.0 [4] (🩰..🩳) ballet shoes..shorts - {0x1FA74, 0x1FA77, prExtendedPictographic}, // NA [4] (🩴..🩷) .. - {0x1FA78, 0x1FA7A, prExtendedPictographic}, // 12.0 [3] (🩸..🩺) drop of blood..stethoscope - {0x1FA7B, 0x1FA7F, prExtendedPictographic}, // NA [5] (🩻..🩿) .. - {0x1FA80, 0x1FA82, prExtendedPictographic}, // 12.0 [3] (🪀..🪂) yo-yo..parachute - {0x1FA83, 0x1FA8F, prExtendedPictographic}, // NA [13] (🪃..🪏) .. - {0x1FA90, 0x1FA95, prExtendedPictographic}, // 12.0 [6] (🪐..🪕) ringed planet..banjo - {0x1FA96, 0x1FFFD, prExtendedPictographic}, // NA[1384] (🪖..🿽) .. - {0xE0000, 0xE0000, prControl}, // Cn - {0xE0001, 0xE0001, prControl}, // Cf LANGUAGE TAG - {0xE0002, 0xE001F, prControl}, // Cn [30] .. - {0xE0020, 0xE007F, prExtend}, // Cf [96] TAG SPACE..CANCEL TAG - {0xE0080, 0xE00FF, prControl}, // Cn [128] .. - {0xE0100, 0xE01EF, prExtend}, // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 - {0xE01F0, 0xE0FFF, prControl}, // Cn [3600] .. -} - -// property returns the Unicode property value (see constants above) of the -// given code point. -func property(r rune) int { - // Run a binary search. - from := 0 - to := len(codePoints) - for to > from { - middle := (from + to) / 2 - cpRange := codePoints[middle] - if int(r) < cpRange[0] { - to = middle - continue - } - if int(r) > cpRange[1] { - from = middle + 1 - continue - } - return cpRange[2] - } - return prAny -} diff --git a/vendor/golang.org/x/text/encoding/encoding.go b/vendor/golang.org/x/text/encoding/encoding.go deleted file mode 100644 index a0bd7cd4..00000000 --- a/vendor/golang.org/x/text/encoding/encoding.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package encoding defines an interface for character encodings, such as Shift -// JIS and Windows 1252, that can convert to and from UTF-8. -// -// Encoding implementations are provided in other packages, such as -// golang.org/x/text/encoding/charmap and -// golang.org/x/text/encoding/japanese. -package encoding // import "golang.org/x/text/encoding" - -import ( - "errors" - "io" - "strconv" - "unicode/utf8" - - "golang.org/x/text/encoding/internal/identifier" - "golang.org/x/text/transform" -) - -// TODO: -// - There seems to be some inconsistency in when decoders return errors -// and when not. Also documentation seems to suggest they shouldn't return -// errors at all (except for UTF-16). -// - Encoders seem to rely on or at least benefit from the input being in NFC -// normal form. Perhaps add an example how users could prepare their output. - -// Encoding is a character set encoding that can be transformed to and from -// UTF-8. -type Encoding interface { - // NewDecoder returns a Decoder. - NewDecoder() *Decoder - - // NewEncoder returns an Encoder. - NewEncoder() *Encoder -} - -// A Decoder converts bytes to UTF-8. It implements transform.Transformer. -// -// Transforming source bytes that are not of that encoding will not result in an -// error per se. Each byte that cannot be transcoded will be represented in the -// output by the UTF-8 encoding of '\uFFFD', the replacement rune. -type Decoder struct { - transform.Transformer - - // This forces external creators of Decoders to use names in struct - // initializers, allowing for future extendibility without having to break - // code. - _ struct{} -} - -// Bytes converts the given encoded bytes to UTF-8. It returns the converted -// bytes or nil, err if any error occurred. -func (d *Decoder) Bytes(b []byte) ([]byte, error) { - b, _, err := transform.Bytes(d, b) - if err != nil { - return nil, err - } - return b, nil -} - -// String converts the given encoded string to UTF-8. It returns the converted -// string or "", err if any error occurred. -func (d *Decoder) String(s string) (string, error) { - s, _, err := transform.String(d, s) - if err != nil { - return "", err - } - return s, nil -} - -// Reader wraps another Reader to decode its bytes. -// -// The Decoder may not be used for any other operation as long as the returned -// Reader is in use. -func (d *Decoder) Reader(r io.Reader) io.Reader { - return transform.NewReader(r, d) -} - -// An Encoder converts bytes from UTF-8. It implements transform.Transformer. -// -// Each rune that cannot be transcoded will result in an error. In this case, -// the transform will consume all source byte up to, not including the offending -// rune. Transforming source bytes that are not valid UTF-8 will be replaced by -// `\uFFFD`. To return early with an error instead, use transform.Chain to -// preprocess the data with a UTF8Validator. -type Encoder struct { - transform.Transformer - - // This forces external creators of Encoders to use names in struct - // initializers, allowing for future extendibility without having to break - // code. - _ struct{} -} - -// Bytes converts bytes from UTF-8. It returns the converted bytes or nil, err if -// any error occurred. -func (e *Encoder) Bytes(b []byte) ([]byte, error) { - b, _, err := transform.Bytes(e, b) - if err != nil { - return nil, err - } - return b, nil -} - -// String converts a string from UTF-8. It returns the converted string or -// "", err if any error occurred. -func (e *Encoder) String(s string) (string, error) { - s, _, err := transform.String(e, s) - if err != nil { - return "", err - } - return s, nil -} - -// Writer wraps another Writer to encode its UTF-8 output. -// -// The Encoder may not be used for any other operation as long as the returned -// Writer is in use. -func (e *Encoder) Writer(w io.Writer) io.Writer { - return transform.NewWriter(w, e) -} - -// ASCIISub is the ASCII substitute character, as recommended by -// https://unicode.org/reports/tr36/#Text_Comparison -const ASCIISub = '\x1a' - -// Nop is the nop encoding. Its transformed bytes are the same as the source -// bytes; it does not replace invalid UTF-8 sequences. -var Nop Encoding = nop{} - -type nop struct{} - -func (nop) NewDecoder() *Decoder { - return &Decoder{Transformer: transform.Nop} -} -func (nop) NewEncoder() *Encoder { - return &Encoder{Transformer: transform.Nop} -} - -// Replacement is the replacement encoding. Decoding from the replacement -// encoding yields a single '\uFFFD' replacement rune. Encoding from UTF-8 to -// the replacement encoding yields the same as the source bytes except that -// invalid UTF-8 is converted to '\uFFFD'. -// -// It is defined at http://encoding.spec.whatwg.org/#replacement -var Replacement Encoding = replacement{} - -type replacement struct{} - -func (replacement) NewDecoder() *Decoder { - return &Decoder{Transformer: replacementDecoder{}} -} - -func (replacement) NewEncoder() *Encoder { - return &Encoder{Transformer: replacementEncoder{}} -} - -func (replacement) ID() (mib identifier.MIB, other string) { - return identifier.Replacement, "" -} - -type replacementDecoder struct{ transform.NopResetter } - -func (replacementDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { - if len(dst) < 3 { - return 0, 0, transform.ErrShortDst - } - if atEOF { - const fffd = "\ufffd" - dst[0] = fffd[0] - dst[1] = fffd[1] - dst[2] = fffd[2] - nDst = 3 - } - return nDst, len(src), nil -} - -type replacementEncoder struct{ transform.NopResetter } - -func (replacementEncoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { - r, size := rune(0), 0 - - for ; nSrc < len(src); nSrc += size { - r = rune(src[nSrc]) - - // Decode a 1-byte rune. - if r < utf8.RuneSelf { - size = 1 - - } else { - // Decode a multi-byte rune. - r, size = utf8.DecodeRune(src[nSrc:]) - if size == 1 { - // All valid runes of size 1 (those below utf8.RuneSelf) were - // handled above. We have invalid UTF-8 or we haven't seen the - // full character yet. - if !atEOF && !utf8.FullRune(src[nSrc:]) { - err = transform.ErrShortSrc - break - } - r = '\ufffd' - } - } - - if nDst+utf8.RuneLen(r) > len(dst) { - err = transform.ErrShortDst - break - } - nDst += utf8.EncodeRune(dst[nDst:], r) - } - return nDst, nSrc, err -} - -// HTMLEscapeUnsupported wraps encoders to replace source runes outside the -// repertoire of the destination encoding with HTML escape sequences. -// -// This wrapper exists to comply to URL and HTML forms requiring a -// non-terminating legacy encoder. The produced sequences may lead to data -// loss as they are indistinguishable from legitimate input. To avoid this -// issue, use UTF-8 encodings whenever possible. -func HTMLEscapeUnsupported(e *Encoder) *Encoder { - return &Encoder{Transformer: &errorHandler{e, errorToHTML}} -} - -// ReplaceUnsupported wraps encoders to replace source runes outside the -// repertoire of the destination encoding with an encoding-specific -// replacement. -// -// This wrapper is only provided for backwards compatibility and legacy -// handling. Its use is strongly discouraged. Use UTF-8 whenever possible. -func ReplaceUnsupported(e *Encoder) *Encoder { - return &Encoder{Transformer: &errorHandler{e, errorToReplacement}} -} - -type errorHandler struct { - *Encoder - handler func(dst []byte, r rune, err repertoireError) (n int, ok bool) -} - -// TODO: consider making this error public in some form. -type repertoireError interface { - Replacement() byte -} - -func (h errorHandler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { - nDst, nSrc, err = h.Transformer.Transform(dst, src, atEOF) - for err != nil { - rerr, ok := err.(repertoireError) - if !ok { - return nDst, nSrc, err - } - r, sz := utf8.DecodeRune(src[nSrc:]) - n, ok := h.handler(dst[nDst:], r, rerr) - if !ok { - return nDst, nSrc, transform.ErrShortDst - } - err = nil - nDst += n - if nSrc += sz; nSrc < len(src) { - var dn, sn int - dn, sn, err = h.Transformer.Transform(dst[nDst:], src[nSrc:], atEOF) - nDst += dn - nSrc += sn - } - } - return nDst, nSrc, err -} - -func errorToHTML(dst []byte, r rune, err repertoireError) (n int, ok bool) { - buf := [8]byte{} - b := strconv.AppendUint(buf[:0], uint64(r), 10) - if n = len(b) + len("&#;"); n >= len(dst) { - return 0, false - } - dst[0] = '&' - dst[1] = '#' - dst[copy(dst[2:], b)+2] = ';' - return n, true -} - -func errorToReplacement(dst []byte, r rune, err repertoireError) (n int, ok bool) { - if len(dst) == 0 { - return 0, false - } - dst[0] = err.Replacement() - return 1, true -} - -// ErrInvalidUTF8 means that a transformer encountered invalid UTF-8. -var ErrInvalidUTF8 = errors.New("encoding: invalid UTF-8") - -// UTF8Validator is a transformer that returns ErrInvalidUTF8 on the first -// input byte that is not valid UTF-8. -var UTF8Validator transform.Transformer = utf8Validator{} - -type utf8Validator struct{ transform.NopResetter } - -func (utf8Validator) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { - n := len(src) - if n > len(dst) { - n = len(dst) - } - for i := 0; i < n; { - if c := src[i]; c < utf8.RuneSelf { - dst[i] = c - i++ - continue - } - _, size := utf8.DecodeRune(src[i:]) - if size == 1 { - // All valid runes of size 1 (those below utf8.RuneSelf) were - // handled above. We have invalid UTF-8 or we haven't seen the - // full character yet. - err = ErrInvalidUTF8 - if !atEOF && !utf8.FullRune(src[i:]) { - err = transform.ErrShortSrc - } - return i, i, err - } - if i+size > len(dst) { - return i, i, transform.ErrShortDst - } - for ; size > 0; size-- { - dst[i] = src[i] - i++ - } - } - if len(src) > len(dst) { - err = transform.ErrShortDst - } - return n, n, err -} diff --git a/vendor/golang.org/x/text/encoding/internal/identifier/identifier.go b/vendor/golang.org/x/text/encoding/internal/identifier/identifier.go deleted file mode 100644 index 5c9b85c2..00000000 --- a/vendor/golang.org/x/text/encoding/internal/identifier/identifier.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate go run gen.go - -// Package identifier defines the contract between implementations of Encoding -// and Index by defining identifiers that uniquely identify standardized coded -// character sets (CCS) and character encoding schemes (CES), which we will -// together refer to as encodings, for which Encoding implementations provide -// converters to and from UTF-8. This package is typically only of concern to -// implementers of Indexes and Encodings. -// -// One part of the identifier is the MIB code, which is defined by IANA and -// uniquely identifies a CCS or CES. Each code is associated with data that -// references authorities, official documentation as well as aliases and MIME -// names. -// -// Not all CESs are covered by the IANA registry. The "other" string that is -// returned by ID can be used to identify other character sets or versions of -// existing ones. -// -// It is recommended that each package that provides a set of Encodings provide -// the All and Common variables to reference all supported encodings and -// commonly used subset. This allows Index implementations to include all -// available encodings without explicitly referencing or knowing about them. -package identifier - -// Note: this package is internal, but could be made public if there is a need -// for writing third-party Indexes and Encodings. - -// References: -// - http://source.icu-project.org/repos/icu/icu/trunk/source/data/mappings/convrtrs.txt -// - http://www.iana.org/assignments/character-sets/character-sets.xhtml -// - http://www.iana.org/assignments/ianacharset-mib/ianacharset-mib -// - http://www.ietf.org/rfc/rfc2978.txt -// - https://www.unicode.org/reports/tr22/ -// - http://www.w3.org/TR/encoding/ -// - https://encoding.spec.whatwg.org/ -// - https://encoding.spec.whatwg.org/encodings.json -// - https://tools.ietf.org/html/rfc6657#section-5 - -// Interface can be implemented by Encodings to define the CCS or CES for which -// it implements conversions. -type Interface interface { - // ID returns an encoding identifier. Exactly one of the mib and other - // values should be non-zero. - // - // In the usual case it is only necessary to indicate the MIB code. The - // other string can be used to specify encodings for which there is no MIB, - // such as "x-mac-dingbat". - // - // The other string may only contain the characters a-z, A-Z, 0-9, - and _. - ID() (mib MIB, other string) - - // NOTE: the restrictions on the encoding are to allow extending the syntax - // with additional information such as versions, vendors and other variants. -} - -// A MIB identifies an encoding. It is derived from the IANA MIB codes and adds -// some identifiers for some encodings that are not covered by the IANA -// standard. -// -// See http://www.iana.org/assignments/ianacharset-mib. -type MIB uint16 - -// These additional MIB types are not defined in IANA. They are added because -// they are common and defined within the text repo. -const ( - // Unofficial marks the start of encodings not registered by IANA. - Unofficial MIB = 10000 + iota - - // Replacement is the WhatWG replacement encoding. - Replacement - - // XUserDefined is the code for x-user-defined. - XUserDefined - - // MacintoshCyrillic is the code for x-mac-cyrillic. - MacintoshCyrillic -) diff --git a/vendor/golang.org/x/text/encoding/internal/identifier/mib.go b/vendor/golang.org/x/text/encoding/internal/identifier/mib.go deleted file mode 100644 index fc7df1bc..00000000 --- a/vendor/golang.org/x/text/encoding/internal/identifier/mib.go +++ /dev/null @@ -1,1619 +0,0 @@ -// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. - -package identifier - -const ( - // ASCII is the MIB identifier with IANA name US-ASCII (MIME: US-ASCII). - // - // ANSI X3.4-1986 - // Reference: RFC2046 - ASCII MIB = 3 - - // ISOLatin1 is the MIB identifier with IANA name ISO_8859-1:1987 (MIME: ISO-8859-1). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin1 MIB = 4 - - // ISOLatin2 is the MIB identifier with IANA name ISO_8859-2:1987 (MIME: ISO-8859-2). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin2 MIB = 5 - - // ISOLatin3 is the MIB identifier with IANA name ISO_8859-3:1988 (MIME: ISO-8859-3). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin3 MIB = 6 - - // ISOLatin4 is the MIB identifier with IANA name ISO_8859-4:1988 (MIME: ISO-8859-4). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin4 MIB = 7 - - // ISOLatinCyrillic is the MIB identifier with IANA name ISO_8859-5:1988 (MIME: ISO-8859-5). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatinCyrillic MIB = 8 - - // ISOLatinArabic is the MIB identifier with IANA name ISO_8859-6:1987 (MIME: ISO-8859-6). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatinArabic MIB = 9 - - // ISOLatinGreek is the MIB identifier with IANA name ISO_8859-7:1987 (MIME: ISO-8859-7). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1947 - // Reference: RFC1345 - ISOLatinGreek MIB = 10 - - // ISOLatinHebrew is the MIB identifier with IANA name ISO_8859-8:1988 (MIME: ISO-8859-8). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatinHebrew MIB = 11 - - // ISOLatin5 is the MIB identifier with IANA name ISO_8859-9:1989 (MIME: ISO-8859-9). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin5 MIB = 12 - - // ISOLatin6 is the MIB identifier with IANA name ISO-8859-10 (MIME: ISO-8859-10). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOLatin6 MIB = 13 - - // ISOTextComm is the MIB identifier with IANA name ISO_6937-2-add. - // - // ISO-IR: International Register of Escape Sequences and ISO 6937-2:1983 - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISOTextComm MIB = 14 - - // HalfWidthKatakana is the MIB identifier with IANA name JIS_X0201. - // - // JIS X 0201-1976. One byte only, this is equivalent to - // JIS/Roman (similar to ASCII) plus eight-bit half-width - // Katakana - // Reference: RFC1345 - HalfWidthKatakana MIB = 15 - - // JISEncoding is the MIB identifier with IANA name JIS_Encoding. - // - // JIS X 0202-1991. Uses ISO 2022 escape sequences to - // shift code sets as documented in JIS X 0202-1991. - JISEncoding MIB = 16 - - // ShiftJIS is the MIB identifier with IANA name Shift_JIS (MIME: Shift_JIS). - // - // This charset is an extension of csHalfWidthKatakana by - // adding graphic characters in JIS X 0208. The CCS's are - // JIS X0201:1997 and JIS X0208:1997. The - // complete definition is shown in Appendix 1 of JIS - // X0208:1997. - // This charset can be used for the top-level media type "text". - ShiftJIS MIB = 17 - - // EUCPkdFmtJapanese is the MIB identifier with IANA name Extended_UNIX_Code_Packed_Format_for_Japanese (MIME: EUC-JP). - // - // Standardized by OSF, UNIX International, and UNIX Systems - // Laboratories Pacific. Uses ISO 2022 rules to select - // code set 0: US-ASCII (a single 7-bit byte set) - // code set 1: JIS X0208-1990 (a double 8-bit byte set) - // restricted to A0-FF in both bytes - // code set 2: Half Width Katakana (a single 7-bit byte set) - // requiring SS2 as the character prefix - // code set 3: JIS X0212-1990 (a double 7-bit byte set) - // restricted to A0-FF in both bytes - // requiring SS3 as the character prefix - EUCPkdFmtJapanese MIB = 18 - - // EUCFixWidJapanese is the MIB identifier with IANA name Extended_UNIX_Code_Fixed_Width_for_Japanese. - // - // Used in Japan. Each character is 2 octets. - // code set 0: US-ASCII (a single 7-bit byte set) - // 1st byte = 00 - // 2nd byte = 20-7E - // code set 1: JIS X0208-1990 (a double 7-bit byte set) - // restricted to A0-FF in both bytes - // code set 2: Half Width Katakana (a single 7-bit byte set) - // 1st byte = 00 - // 2nd byte = A0-FF - // code set 3: JIS X0212-1990 (a double 7-bit byte set) - // restricted to A0-FF in - // the first byte - // and 21-7E in the second byte - EUCFixWidJapanese MIB = 19 - - // ISO4UnitedKingdom is the MIB identifier with IANA name BS_4730. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO4UnitedKingdom MIB = 20 - - // ISO11SwedishForNames is the MIB identifier with IANA name SEN_850200_C. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO11SwedishForNames MIB = 21 - - // ISO15Italian is the MIB identifier with IANA name IT. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO15Italian MIB = 22 - - // ISO17Spanish is the MIB identifier with IANA name ES. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO17Spanish MIB = 23 - - // ISO21German is the MIB identifier with IANA name DIN_66003. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO21German MIB = 24 - - // ISO60Norwegian1 is the MIB identifier with IANA name NS_4551-1. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO60Norwegian1 MIB = 25 - - // ISO69French is the MIB identifier with IANA name NF_Z_62-010. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO69French MIB = 26 - - // ISO10646UTF1 is the MIB identifier with IANA name ISO-10646-UTF-1. - // - // Universal Transfer Format (1), this is the multibyte - // encoding, that subsets ASCII-7. It does not have byte - // ordering issues. - ISO10646UTF1 MIB = 27 - - // ISO646basic1983 is the MIB identifier with IANA name ISO_646.basic:1983. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO646basic1983 MIB = 28 - - // INVARIANT is the MIB identifier with IANA name INVARIANT. - // - // Reference: RFC1345 - INVARIANT MIB = 29 - - // ISO2IntlRefVersion is the MIB identifier with IANA name ISO_646.irv:1983. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO2IntlRefVersion MIB = 30 - - // NATSSEFI is the MIB identifier with IANA name NATS-SEFI. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - NATSSEFI MIB = 31 - - // NATSSEFIADD is the MIB identifier with IANA name NATS-SEFI-ADD. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - NATSSEFIADD MIB = 32 - - // NATSDANO is the MIB identifier with IANA name NATS-DANO. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - NATSDANO MIB = 33 - - // NATSDANOADD is the MIB identifier with IANA name NATS-DANO-ADD. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - NATSDANOADD MIB = 34 - - // ISO10Swedish is the MIB identifier with IANA name SEN_850200_B. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO10Swedish MIB = 35 - - // KSC56011987 is the MIB identifier with IANA name KS_C_5601-1987. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - KSC56011987 MIB = 36 - - // ISO2022KR is the MIB identifier with IANA name ISO-2022-KR (MIME: ISO-2022-KR). - // - // rfc1557 (see also KS_C_5601-1987) - // Reference: RFC1557 - ISO2022KR MIB = 37 - - // EUCKR is the MIB identifier with IANA name EUC-KR (MIME: EUC-KR). - // - // rfc1557 (see also KS_C_5861-1992) - // Reference: RFC1557 - EUCKR MIB = 38 - - // ISO2022JP is the MIB identifier with IANA name ISO-2022-JP (MIME: ISO-2022-JP). - // - // rfc1468 (see also rfc2237 ) - // Reference: RFC1468 - ISO2022JP MIB = 39 - - // ISO2022JP2 is the MIB identifier with IANA name ISO-2022-JP-2 (MIME: ISO-2022-JP-2). - // - // rfc1554 - // Reference: RFC1554 - ISO2022JP2 MIB = 40 - - // ISO13JISC6220jp is the MIB identifier with IANA name JIS_C6220-1969-jp. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO13JISC6220jp MIB = 41 - - // ISO14JISC6220ro is the MIB identifier with IANA name JIS_C6220-1969-ro. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO14JISC6220ro MIB = 42 - - // ISO16Portuguese is the MIB identifier with IANA name PT. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO16Portuguese MIB = 43 - - // ISO18Greek7Old is the MIB identifier with IANA name greek7-old. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO18Greek7Old MIB = 44 - - // ISO19LatinGreek is the MIB identifier with IANA name latin-greek. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO19LatinGreek MIB = 45 - - // ISO25French is the MIB identifier with IANA name NF_Z_62-010_(1973). - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO25French MIB = 46 - - // ISO27LatinGreek1 is the MIB identifier with IANA name Latin-greek-1. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO27LatinGreek1 MIB = 47 - - // ISO5427Cyrillic is the MIB identifier with IANA name ISO_5427. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO5427Cyrillic MIB = 48 - - // ISO42JISC62261978 is the MIB identifier with IANA name JIS_C6226-1978. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO42JISC62261978 MIB = 49 - - // ISO47BSViewdata is the MIB identifier with IANA name BS_viewdata. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO47BSViewdata MIB = 50 - - // ISO49INIS is the MIB identifier with IANA name INIS. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO49INIS MIB = 51 - - // ISO50INIS8 is the MIB identifier with IANA name INIS-8. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO50INIS8 MIB = 52 - - // ISO51INISCyrillic is the MIB identifier with IANA name INIS-cyrillic. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO51INISCyrillic MIB = 53 - - // ISO54271981 is the MIB identifier with IANA name ISO_5427:1981. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO54271981 MIB = 54 - - // ISO5428Greek is the MIB identifier with IANA name ISO_5428:1980. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO5428Greek MIB = 55 - - // ISO57GB1988 is the MIB identifier with IANA name GB_1988-80. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO57GB1988 MIB = 56 - - // ISO58GB231280 is the MIB identifier with IANA name GB_2312-80. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO58GB231280 MIB = 57 - - // ISO61Norwegian2 is the MIB identifier with IANA name NS_4551-2. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO61Norwegian2 MIB = 58 - - // ISO70VideotexSupp1 is the MIB identifier with IANA name videotex-suppl. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO70VideotexSupp1 MIB = 59 - - // ISO84Portuguese2 is the MIB identifier with IANA name PT2. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO84Portuguese2 MIB = 60 - - // ISO85Spanish2 is the MIB identifier with IANA name ES2. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO85Spanish2 MIB = 61 - - // ISO86Hungarian is the MIB identifier with IANA name MSZ_7795.3. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO86Hungarian MIB = 62 - - // ISO87JISX0208 is the MIB identifier with IANA name JIS_C6226-1983. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO87JISX0208 MIB = 63 - - // ISO88Greek7 is the MIB identifier with IANA name greek7. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO88Greek7 MIB = 64 - - // ISO89ASMO449 is the MIB identifier with IANA name ASMO_449. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO89ASMO449 MIB = 65 - - // ISO90 is the MIB identifier with IANA name iso-ir-90. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO90 MIB = 66 - - // ISO91JISC62291984a is the MIB identifier with IANA name JIS_C6229-1984-a. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO91JISC62291984a MIB = 67 - - // ISO92JISC62991984b is the MIB identifier with IANA name JIS_C6229-1984-b. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO92JISC62991984b MIB = 68 - - // ISO93JIS62291984badd is the MIB identifier with IANA name JIS_C6229-1984-b-add. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO93JIS62291984badd MIB = 69 - - // ISO94JIS62291984hand is the MIB identifier with IANA name JIS_C6229-1984-hand. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO94JIS62291984hand MIB = 70 - - // ISO95JIS62291984handadd is the MIB identifier with IANA name JIS_C6229-1984-hand-add. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO95JIS62291984handadd MIB = 71 - - // ISO96JISC62291984kana is the MIB identifier with IANA name JIS_C6229-1984-kana. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO96JISC62291984kana MIB = 72 - - // ISO2033 is the MIB identifier with IANA name ISO_2033-1983. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO2033 MIB = 73 - - // ISO99NAPLPS is the MIB identifier with IANA name ANSI_X3.110-1983. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO99NAPLPS MIB = 74 - - // ISO102T617bit is the MIB identifier with IANA name T.61-7bit. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO102T617bit MIB = 75 - - // ISO103T618bit is the MIB identifier with IANA name T.61-8bit. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO103T618bit MIB = 76 - - // ISO111ECMACyrillic is the MIB identifier with IANA name ECMA-cyrillic. - // - // ISO registry - ISO111ECMACyrillic MIB = 77 - - // ISO121Canadian1 is the MIB identifier with IANA name CSA_Z243.4-1985-1. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO121Canadian1 MIB = 78 - - // ISO122Canadian2 is the MIB identifier with IANA name CSA_Z243.4-1985-2. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO122Canadian2 MIB = 79 - - // ISO123CSAZ24341985gr is the MIB identifier with IANA name CSA_Z243.4-1985-gr. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO123CSAZ24341985gr MIB = 80 - - // ISO88596E is the MIB identifier with IANA name ISO_8859-6-E (MIME: ISO-8859-6-E). - // - // rfc1556 - // Reference: RFC1556 - ISO88596E MIB = 81 - - // ISO88596I is the MIB identifier with IANA name ISO_8859-6-I (MIME: ISO-8859-6-I). - // - // rfc1556 - // Reference: RFC1556 - ISO88596I MIB = 82 - - // ISO128T101G2 is the MIB identifier with IANA name T.101-G2. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO128T101G2 MIB = 83 - - // ISO88598E is the MIB identifier with IANA name ISO_8859-8-E (MIME: ISO-8859-8-E). - // - // rfc1556 - // Reference: RFC1556 - ISO88598E MIB = 84 - - // ISO88598I is the MIB identifier with IANA name ISO_8859-8-I (MIME: ISO-8859-8-I). - // - // rfc1556 - // Reference: RFC1556 - ISO88598I MIB = 85 - - // ISO139CSN369103 is the MIB identifier with IANA name CSN_369103. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO139CSN369103 MIB = 86 - - // ISO141JUSIB1002 is the MIB identifier with IANA name JUS_I.B1.002. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO141JUSIB1002 MIB = 87 - - // ISO143IECP271 is the MIB identifier with IANA name IEC_P27-1. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO143IECP271 MIB = 88 - - // ISO146Serbian is the MIB identifier with IANA name JUS_I.B1.003-serb. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO146Serbian MIB = 89 - - // ISO147Macedonian is the MIB identifier with IANA name JUS_I.B1.003-mac. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO147Macedonian MIB = 90 - - // ISO150GreekCCITT is the MIB identifier with IANA name greek-ccitt. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO150GreekCCITT MIB = 91 - - // ISO151Cuba is the MIB identifier with IANA name NC_NC00-10:81. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO151Cuba MIB = 92 - - // ISO6937Add is the MIB identifier with IANA name ISO_6937-2-25. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO6937Add MIB = 93 - - // ISO153GOST1976874 is the MIB identifier with IANA name GOST_19768-74. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO153GOST1976874 MIB = 94 - - // ISO8859Supp is the MIB identifier with IANA name ISO_8859-supp. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO8859Supp MIB = 95 - - // ISO10367Box is the MIB identifier with IANA name ISO_10367-box. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO10367Box MIB = 96 - - // ISO158Lap is the MIB identifier with IANA name latin-lap. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO158Lap MIB = 97 - - // ISO159JISX02121990 is the MIB identifier with IANA name JIS_X0212-1990. - // - // ISO-IR: International Register of Escape Sequences - // Note: The current registration authority is IPSJ/ITSCJ, Japan. - // Reference: RFC1345 - ISO159JISX02121990 MIB = 98 - - // ISO646Danish is the MIB identifier with IANA name DS_2089. - // - // Danish Standard, DS 2089, February 1974 - // Reference: RFC1345 - ISO646Danish MIB = 99 - - // USDK is the MIB identifier with IANA name us-dk. - // - // Reference: RFC1345 - USDK MIB = 100 - - // DKUS is the MIB identifier with IANA name dk-us. - // - // Reference: RFC1345 - DKUS MIB = 101 - - // KSC5636 is the MIB identifier with IANA name KSC5636. - // - // Reference: RFC1345 - KSC5636 MIB = 102 - - // Unicode11UTF7 is the MIB identifier with IANA name UNICODE-1-1-UTF-7. - // - // rfc1642 - // Reference: RFC1642 - Unicode11UTF7 MIB = 103 - - // ISO2022CN is the MIB identifier with IANA name ISO-2022-CN. - // - // rfc1922 - // Reference: RFC1922 - ISO2022CN MIB = 104 - - // ISO2022CNEXT is the MIB identifier with IANA name ISO-2022-CN-EXT. - // - // rfc1922 - // Reference: RFC1922 - ISO2022CNEXT MIB = 105 - - // UTF8 is the MIB identifier with IANA name UTF-8. - // - // rfc3629 - // Reference: RFC3629 - UTF8 MIB = 106 - - // ISO885913 is the MIB identifier with IANA name ISO-8859-13. - // - // ISO See https://www.iana.org/assignments/charset-reg/ISO-8859-13 https://www.iana.org/assignments/charset-reg/ISO-8859-13 - ISO885913 MIB = 109 - - // ISO885914 is the MIB identifier with IANA name ISO-8859-14. - // - // ISO See https://www.iana.org/assignments/charset-reg/ISO-8859-14 - ISO885914 MIB = 110 - - // ISO885915 is the MIB identifier with IANA name ISO-8859-15. - // - // ISO - // Please see: https://www.iana.org/assignments/charset-reg/ISO-8859-15 - ISO885915 MIB = 111 - - // ISO885916 is the MIB identifier with IANA name ISO-8859-16. - // - // ISO - ISO885916 MIB = 112 - - // GBK is the MIB identifier with IANA name GBK. - // - // Chinese IT Standardization Technical Committee - // Please see: https://www.iana.org/assignments/charset-reg/GBK - GBK MIB = 113 - - // GB18030 is the MIB identifier with IANA name GB18030. - // - // Chinese IT Standardization Technical Committee - // Please see: https://www.iana.org/assignments/charset-reg/GB18030 - GB18030 MIB = 114 - - // OSDEBCDICDF0415 is the MIB identifier with IANA name OSD_EBCDIC_DF04_15. - // - // Fujitsu-Siemens standard mainframe EBCDIC encoding - // Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-15 - OSDEBCDICDF0415 MIB = 115 - - // OSDEBCDICDF03IRV is the MIB identifier with IANA name OSD_EBCDIC_DF03_IRV. - // - // Fujitsu-Siemens standard mainframe EBCDIC encoding - // Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF03-IRV - OSDEBCDICDF03IRV MIB = 116 - - // OSDEBCDICDF041 is the MIB identifier with IANA name OSD_EBCDIC_DF04_1. - // - // Fujitsu-Siemens standard mainframe EBCDIC encoding - // Please see: https://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-1 - OSDEBCDICDF041 MIB = 117 - - // ISO115481 is the MIB identifier with IANA name ISO-11548-1. - // - // See https://www.iana.org/assignments/charset-reg/ISO-11548-1 - ISO115481 MIB = 118 - - // KZ1048 is the MIB identifier with IANA name KZ-1048. - // - // See https://www.iana.org/assignments/charset-reg/KZ-1048 - KZ1048 MIB = 119 - - // Unicode is the MIB identifier with IANA name ISO-10646-UCS-2. - // - // the 2-octet Basic Multilingual Plane, aka Unicode - // this needs to specify network byte order: the standard - // does not specify (it is a 16-bit integer space) - Unicode MIB = 1000 - - // UCS4 is the MIB identifier with IANA name ISO-10646-UCS-4. - // - // the full code space. (same comment about byte order, - // these are 31-bit numbers. - UCS4 MIB = 1001 - - // UnicodeASCII is the MIB identifier with IANA name ISO-10646-UCS-Basic. - // - // ASCII subset of Unicode. Basic Latin = collection 1 - // See ISO 10646, Appendix A - UnicodeASCII MIB = 1002 - - // UnicodeLatin1 is the MIB identifier with IANA name ISO-10646-Unicode-Latin1. - // - // ISO Latin-1 subset of Unicode. Basic Latin and Latin-1 - // Supplement = collections 1 and 2. See ISO 10646, - // Appendix A. See rfc1815 . - UnicodeLatin1 MIB = 1003 - - // UnicodeJapanese is the MIB identifier with IANA name ISO-10646-J-1. - // - // ISO 10646 Japanese, see rfc1815 . - UnicodeJapanese MIB = 1004 - - // UnicodeIBM1261 is the MIB identifier with IANA name ISO-Unicode-IBM-1261. - // - // IBM Latin-2, -3, -5, Extended Presentation Set, GCSGID: 1261 - UnicodeIBM1261 MIB = 1005 - - // UnicodeIBM1268 is the MIB identifier with IANA name ISO-Unicode-IBM-1268. - // - // IBM Latin-4 Extended Presentation Set, GCSGID: 1268 - UnicodeIBM1268 MIB = 1006 - - // UnicodeIBM1276 is the MIB identifier with IANA name ISO-Unicode-IBM-1276. - // - // IBM Cyrillic Greek Extended Presentation Set, GCSGID: 1276 - UnicodeIBM1276 MIB = 1007 - - // UnicodeIBM1264 is the MIB identifier with IANA name ISO-Unicode-IBM-1264. - // - // IBM Arabic Presentation Set, GCSGID: 1264 - UnicodeIBM1264 MIB = 1008 - - // UnicodeIBM1265 is the MIB identifier with IANA name ISO-Unicode-IBM-1265. - // - // IBM Hebrew Presentation Set, GCSGID: 1265 - UnicodeIBM1265 MIB = 1009 - - // Unicode11 is the MIB identifier with IANA name UNICODE-1-1. - // - // rfc1641 - // Reference: RFC1641 - Unicode11 MIB = 1010 - - // SCSU is the MIB identifier with IANA name SCSU. - // - // SCSU See https://www.iana.org/assignments/charset-reg/SCSU - SCSU MIB = 1011 - - // UTF7 is the MIB identifier with IANA name UTF-7. - // - // rfc2152 - // Reference: RFC2152 - UTF7 MIB = 1012 - - // UTF16BE is the MIB identifier with IANA name UTF-16BE. - // - // rfc2781 - // Reference: RFC2781 - UTF16BE MIB = 1013 - - // UTF16LE is the MIB identifier with IANA name UTF-16LE. - // - // rfc2781 - // Reference: RFC2781 - UTF16LE MIB = 1014 - - // UTF16 is the MIB identifier with IANA name UTF-16. - // - // rfc2781 - // Reference: RFC2781 - UTF16 MIB = 1015 - - // CESU8 is the MIB identifier with IANA name CESU-8. - // - // https://www.unicode.org/reports/tr26 - CESU8 MIB = 1016 - - // UTF32 is the MIB identifier with IANA name UTF-32. - // - // https://www.unicode.org/reports/tr19/ - UTF32 MIB = 1017 - - // UTF32BE is the MIB identifier with IANA name UTF-32BE. - // - // https://www.unicode.org/reports/tr19/ - UTF32BE MIB = 1018 - - // UTF32LE is the MIB identifier with IANA name UTF-32LE. - // - // https://www.unicode.org/reports/tr19/ - UTF32LE MIB = 1019 - - // BOCU1 is the MIB identifier with IANA name BOCU-1. - // - // https://www.unicode.org/notes/tn6/ - BOCU1 MIB = 1020 - - // Windows30Latin1 is the MIB identifier with IANA name ISO-8859-1-Windows-3.0-Latin-1. - // - // Extended ISO 8859-1 Latin-1 for Windows 3.0. - // PCL Symbol Set id: 9U - Windows30Latin1 MIB = 2000 - - // Windows31Latin1 is the MIB identifier with IANA name ISO-8859-1-Windows-3.1-Latin-1. - // - // Extended ISO 8859-1 Latin-1 for Windows 3.1. - // PCL Symbol Set id: 19U - Windows31Latin1 MIB = 2001 - - // Windows31Latin2 is the MIB identifier with IANA name ISO-8859-2-Windows-Latin-2. - // - // Extended ISO 8859-2. Latin-2 for Windows 3.1. - // PCL Symbol Set id: 9E - Windows31Latin2 MIB = 2002 - - // Windows31Latin5 is the MIB identifier with IANA name ISO-8859-9-Windows-Latin-5. - // - // Extended ISO 8859-9. Latin-5 for Windows 3.1 - // PCL Symbol Set id: 5T - Windows31Latin5 MIB = 2003 - - // HPRoman8 is the MIB identifier with IANA name hp-roman8. - // - // LaserJet IIP Printer User's Manual, - // HP part no 33471-90901, Hewlet-Packard, June 1989. - // Reference: RFC1345 - HPRoman8 MIB = 2004 - - // AdobeStandardEncoding is the MIB identifier with IANA name Adobe-Standard-Encoding. - // - // PostScript Language Reference Manual - // PCL Symbol Set id: 10J - AdobeStandardEncoding MIB = 2005 - - // VenturaUS is the MIB identifier with IANA name Ventura-US. - // - // Ventura US. ASCII plus characters typically used in - // publishing, like pilcrow, copyright, registered, trade mark, - // section, dagger, and double dagger in the range A0 (hex) - // to FF (hex). - // PCL Symbol Set id: 14J - VenturaUS MIB = 2006 - - // VenturaInternational is the MIB identifier with IANA name Ventura-International. - // - // Ventura International. ASCII plus coded characters similar - // to Roman8. - // PCL Symbol Set id: 13J - VenturaInternational MIB = 2007 - - // DECMCS is the MIB identifier with IANA name DEC-MCS. - // - // VAX/VMS User's Manual, - // Order Number: AI-Y517A-TE, April 1986. - // Reference: RFC1345 - DECMCS MIB = 2008 - - // PC850Multilingual is the MIB identifier with IANA name IBM850. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - PC850Multilingual MIB = 2009 - - // PC8DanishNorwegian is the MIB identifier with IANA name PC8-Danish-Norwegian. - // - // PC Danish Norwegian - // 8-bit PC set for Danish Norwegian - // PCL Symbol Set id: 11U - PC8DanishNorwegian MIB = 2012 - - // PC862LatinHebrew is the MIB identifier with IANA name IBM862. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - PC862LatinHebrew MIB = 2013 - - // PC8Turkish is the MIB identifier with IANA name PC8-Turkish. - // - // PC Latin Turkish. PCL Symbol Set id: 9T - PC8Turkish MIB = 2014 - - // IBMSymbols is the MIB identifier with IANA name IBM-Symbols. - // - // Presentation Set, CPGID: 259 - IBMSymbols MIB = 2015 - - // IBMThai is the MIB identifier with IANA name IBM-Thai. - // - // Presentation Set, CPGID: 838 - IBMThai MIB = 2016 - - // HPLegal is the MIB identifier with IANA name HP-Legal. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 1U - HPLegal MIB = 2017 - - // HPPiFont is the MIB identifier with IANA name HP-Pi-font. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 15U - HPPiFont MIB = 2018 - - // HPMath8 is the MIB identifier with IANA name HP-Math8. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 8M - HPMath8 MIB = 2019 - - // HPPSMath is the MIB identifier with IANA name Adobe-Symbol-Encoding. - // - // PostScript Language Reference Manual - // PCL Symbol Set id: 5M - HPPSMath MIB = 2020 - - // HPDesktop is the MIB identifier with IANA name HP-DeskTop. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 7J - HPDesktop MIB = 2021 - - // VenturaMath is the MIB identifier with IANA name Ventura-Math. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 6M - VenturaMath MIB = 2022 - - // MicrosoftPublishing is the MIB identifier with IANA name Microsoft-Publishing. - // - // PCL 5 Comparison Guide, Hewlett-Packard, - // HP part number 5961-0510, October 1992 - // PCL Symbol Set id: 6J - MicrosoftPublishing MIB = 2023 - - // Windows31J is the MIB identifier with IANA name Windows-31J. - // - // Windows Japanese. A further extension of Shift_JIS - // to include NEC special characters (Row 13), NEC - // selection of IBM extensions (Rows 89 to 92), and IBM - // extensions (Rows 115 to 119). The CCS's are - // JIS X0201:1997, JIS X0208:1997, and these extensions. - // This charset can be used for the top-level media type "text", - // but it is of limited or specialized use (see rfc2278 ). - // PCL Symbol Set id: 19K - Windows31J MIB = 2024 - - // GB2312 is the MIB identifier with IANA name GB2312 (MIME: GB2312). - // - // Chinese for People's Republic of China (PRC) mixed one byte, - // two byte set: - // 20-7E = one byte ASCII - // A1-FE = two byte PRC Kanji - // See GB 2312-80 - // PCL Symbol Set Id: 18C - GB2312 MIB = 2025 - - // Big5 is the MIB identifier with IANA name Big5 (MIME: Big5). - // - // Chinese for Taiwan Multi-byte set. - // PCL Symbol Set Id: 18T - Big5 MIB = 2026 - - // Macintosh is the MIB identifier with IANA name macintosh. - // - // The Unicode Standard ver1.0, ISBN 0-201-56788-1, Oct 1991 - // Reference: RFC1345 - Macintosh MIB = 2027 - - // IBM037 is the MIB identifier with IANA name IBM037. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM037 MIB = 2028 - - // IBM038 is the MIB identifier with IANA name IBM038. - // - // IBM 3174 Character Set Ref, GA27-3831-02, March 1990 - // Reference: RFC1345 - IBM038 MIB = 2029 - - // IBM273 is the MIB identifier with IANA name IBM273. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM273 MIB = 2030 - - // IBM274 is the MIB identifier with IANA name IBM274. - // - // IBM 3174 Character Set Ref, GA27-3831-02, March 1990 - // Reference: RFC1345 - IBM274 MIB = 2031 - - // IBM275 is the MIB identifier with IANA name IBM275. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM275 MIB = 2032 - - // IBM277 is the MIB identifier with IANA name IBM277. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM277 MIB = 2033 - - // IBM278 is the MIB identifier with IANA name IBM278. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM278 MIB = 2034 - - // IBM280 is the MIB identifier with IANA name IBM280. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM280 MIB = 2035 - - // IBM281 is the MIB identifier with IANA name IBM281. - // - // IBM 3174 Character Set Ref, GA27-3831-02, March 1990 - // Reference: RFC1345 - IBM281 MIB = 2036 - - // IBM284 is the MIB identifier with IANA name IBM284. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM284 MIB = 2037 - - // IBM285 is the MIB identifier with IANA name IBM285. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM285 MIB = 2038 - - // IBM290 is the MIB identifier with IANA name IBM290. - // - // IBM 3174 Character Set Ref, GA27-3831-02, March 1990 - // Reference: RFC1345 - IBM290 MIB = 2039 - - // IBM297 is the MIB identifier with IANA name IBM297. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM297 MIB = 2040 - - // IBM420 is the MIB identifier with IANA name IBM420. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990, - // IBM NLS RM p 11-11 - // Reference: RFC1345 - IBM420 MIB = 2041 - - // IBM423 is the MIB identifier with IANA name IBM423. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM423 MIB = 2042 - - // IBM424 is the MIB identifier with IANA name IBM424. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM424 MIB = 2043 - - // PC8CodePage437 is the MIB identifier with IANA name IBM437. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - PC8CodePage437 MIB = 2011 - - // IBM500 is the MIB identifier with IANA name IBM500. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM500 MIB = 2044 - - // IBM851 is the MIB identifier with IANA name IBM851. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM851 MIB = 2045 - - // PCp852 is the MIB identifier with IANA name IBM852. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - PCp852 MIB = 2010 - - // IBM855 is the MIB identifier with IANA name IBM855. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM855 MIB = 2046 - - // IBM857 is the MIB identifier with IANA name IBM857. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM857 MIB = 2047 - - // IBM860 is the MIB identifier with IANA name IBM860. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM860 MIB = 2048 - - // IBM861 is the MIB identifier with IANA name IBM861. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM861 MIB = 2049 - - // IBM863 is the MIB identifier with IANA name IBM863. - // - // IBM Keyboard layouts and code pages, PN 07G4586 June 1991 - // Reference: RFC1345 - IBM863 MIB = 2050 - - // IBM864 is the MIB identifier with IANA name IBM864. - // - // IBM Keyboard layouts and code pages, PN 07G4586 June 1991 - // Reference: RFC1345 - IBM864 MIB = 2051 - - // IBM865 is the MIB identifier with IANA name IBM865. - // - // IBM DOS 3.3 Ref (Abridged), 94X9575 (Feb 1987) - // Reference: RFC1345 - IBM865 MIB = 2052 - - // IBM868 is the MIB identifier with IANA name IBM868. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM868 MIB = 2053 - - // IBM869 is the MIB identifier with IANA name IBM869. - // - // IBM Keyboard layouts and code pages, PN 07G4586 June 1991 - // Reference: RFC1345 - IBM869 MIB = 2054 - - // IBM870 is the MIB identifier with IANA name IBM870. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM870 MIB = 2055 - - // IBM871 is the MIB identifier with IANA name IBM871. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM871 MIB = 2056 - - // IBM880 is the MIB identifier with IANA name IBM880. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM880 MIB = 2057 - - // IBM891 is the MIB identifier with IANA name IBM891. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM891 MIB = 2058 - - // IBM903 is the MIB identifier with IANA name IBM903. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM903 MIB = 2059 - - // IBBM904 is the MIB identifier with IANA name IBM904. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBBM904 MIB = 2060 - - // IBM905 is the MIB identifier with IANA name IBM905. - // - // IBM 3174 Character Set Ref, GA27-3831-02, March 1990 - // Reference: RFC1345 - IBM905 MIB = 2061 - - // IBM918 is the MIB identifier with IANA name IBM918. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM918 MIB = 2062 - - // IBM1026 is the MIB identifier with IANA name IBM1026. - // - // IBM NLS RM Vol2 SE09-8002-01, March 1990 - // Reference: RFC1345 - IBM1026 MIB = 2063 - - // IBMEBCDICATDE is the MIB identifier with IANA name EBCDIC-AT-DE. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - IBMEBCDICATDE MIB = 2064 - - // EBCDICATDEA is the MIB identifier with IANA name EBCDIC-AT-DE-A. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICATDEA MIB = 2065 - - // EBCDICCAFR is the MIB identifier with IANA name EBCDIC-CA-FR. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICCAFR MIB = 2066 - - // EBCDICDKNO is the MIB identifier with IANA name EBCDIC-DK-NO. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICDKNO MIB = 2067 - - // EBCDICDKNOA is the MIB identifier with IANA name EBCDIC-DK-NO-A. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICDKNOA MIB = 2068 - - // EBCDICFISE is the MIB identifier with IANA name EBCDIC-FI-SE. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICFISE MIB = 2069 - - // EBCDICFISEA is the MIB identifier with IANA name EBCDIC-FI-SE-A. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICFISEA MIB = 2070 - - // EBCDICFR is the MIB identifier with IANA name EBCDIC-FR. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICFR MIB = 2071 - - // EBCDICIT is the MIB identifier with IANA name EBCDIC-IT. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICIT MIB = 2072 - - // EBCDICPT is the MIB identifier with IANA name EBCDIC-PT. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICPT MIB = 2073 - - // EBCDICES is the MIB identifier with IANA name EBCDIC-ES. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICES MIB = 2074 - - // EBCDICESA is the MIB identifier with IANA name EBCDIC-ES-A. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICESA MIB = 2075 - - // EBCDICESS is the MIB identifier with IANA name EBCDIC-ES-S. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICESS MIB = 2076 - - // EBCDICUK is the MIB identifier with IANA name EBCDIC-UK. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICUK MIB = 2077 - - // EBCDICUS is the MIB identifier with IANA name EBCDIC-US. - // - // IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 - // Reference: RFC1345 - EBCDICUS MIB = 2078 - - // Unknown8BiT is the MIB identifier with IANA name UNKNOWN-8BIT. - // - // Reference: RFC1428 - Unknown8BiT MIB = 2079 - - // Mnemonic is the MIB identifier with IANA name MNEMONIC. - // - // rfc1345 , also known as "mnemonic+ascii+38" - // Reference: RFC1345 - Mnemonic MIB = 2080 - - // Mnem is the MIB identifier with IANA name MNEM. - // - // rfc1345 , also known as "mnemonic+ascii+8200" - // Reference: RFC1345 - Mnem MIB = 2081 - - // VISCII is the MIB identifier with IANA name VISCII. - // - // rfc1456 - // Reference: RFC1456 - VISCII MIB = 2082 - - // VIQR is the MIB identifier with IANA name VIQR. - // - // rfc1456 - // Reference: RFC1456 - VIQR MIB = 2083 - - // KOI8R is the MIB identifier with IANA name KOI8-R (MIME: KOI8-R). - // - // rfc1489 , based on GOST-19768-74, ISO-6937/8, - // INIS-Cyrillic, ISO-5427. - // Reference: RFC1489 - KOI8R MIB = 2084 - - // HZGB2312 is the MIB identifier with IANA name HZ-GB-2312. - // - // rfc1842 , rfc1843 rfc1843 rfc1842 - HZGB2312 MIB = 2085 - - // IBM866 is the MIB identifier with IANA name IBM866. - // - // IBM NLDG Volume 2 (SE09-8002-03) August 1994 - IBM866 MIB = 2086 - - // PC775Baltic is the MIB identifier with IANA name IBM775. - // - // HP PCL 5 Comparison Guide (P/N 5021-0329) pp B-13, 1996 - PC775Baltic MIB = 2087 - - // KOI8U is the MIB identifier with IANA name KOI8-U. - // - // rfc2319 - // Reference: RFC2319 - KOI8U MIB = 2088 - - // IBM00858 is the MIB identifier with IANA name IBM00858. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM00858 - IBM00858 MIB = 2089 - - // IBM00924 is the MIB identifier with IANA name IBM00924. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM00924 - IBM00924 MIB = 2090 - - // IBM01140 is the MIB identifier with IANA name IBM01140. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01140 - IBM01140 MIB = 2091 - - // IBM01141 is the MIB identifier with IANA name IBM01141. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01141 - IBM01141 MIB = 2092 - - // IBM01142 is the MIB identifier with IANA name IBM01142. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01142 - IBM01142 MIB = 2093 - - // IBM01143 is the MIB identifier with IANA name IBM01143. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01143 - IBM01143 MIB = 2094 - - // IBM01144 is the MIB identifier with IANA name IBM01144. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01144 - IBM01144 MIB = 2095 - - // IBM01145 is the MIB identifier with IANA name IBM01145. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01145 - IBM01145 MIB = 2096 - - // IBM01146 is the MIB identifier with IANA name IBM01146. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01146 - IBM01146 MIB = 2097 - - // IBM01147 is the MIB identifier with IANA name IBM01147. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01147 - IBM01147 MIB = 2098 - - // IBM01148 is the MIB identifier with IANA name IBM01148. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01148 - IBM01148 MIB = 2099 - - // IBM01149 is the MIB identifier with IANA name IBM01149. - // - // IBM See https://www.iana.org/assignments/charset-reg/IBM01149 - IBM01149 MIB = 2100 - - // Big5HKSCS is the MIB identifier with IANA name Big5-HKSCS. - // - // See https://www.iana.org/assignments/charset-reg/Big5-HKSCS - Big5HKSCS MIB = 2101 - - // IBM1047 is the MIB identifier with IANA name IBM1047. - // - // IBM1047 (EBCDIC Latin 1/Open Systems) https://www-1.ibm.com/servers/eserver/iseries/software/globalization/pdf/cp01047z.pdf - IBM1047 MIB = 2102 - - // PTCP154 is the MIB identifier with IANA name PTCP154. - // - // See https://www.iana.org/assignments/charset-reg/PTCP154 - PTCP154 MIB = 2103 - - // Amiga1251 is the MIB identifier with IANA name Amiga-1251. - // - // See https://www.amiga.ultranet.ru/Amiga-1251.html - Amiga1251 MIB = 2104 - - // KOI7switched is the MIB identifier with IANA name KOI7-switched. - // - // See https://www.iana.org/assignments/charset-reg/KOI7-switched - KOI7switched MIB = 2105 - - // BRF is the MIB identifier with IANA name BRF. - // - // See https://www.iana.org/assignments/charset-reg/BRF - BRF MIB = 2106 - - // TSCII is the MIB identifier with IANA name TSCII. - // - // See https://www.iana.org/assignments/charset-reg/TSCII - TSCII MIB = 2107 - - // CP51932 is the MIB identifier with IANA name CP51932. - // - // See https://www.iana.org/assignments/charset-reg/CP51932 - CP51932 MIB = 2108 - - // Windows874 is the MIB identifier with IANA name windows-874. - // - // See https://www.iana.org/assignments/charset-reg/windows-874 - Windows874 MIB = 2109 - - // Windows1250 is the MIB identifier with IANA name windows-1250. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1250 - Windows1250 MIB = 2250 - - // Windows1251 is the MIB identifier with IANA name windows-1251. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1251 - Windows1251 MIB = 2251 - - // Windows1252 is the MIB identifier with IANA name windows-1252. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1252 - Windows1252 MIB = 2252 - - // Windows1253 is the MIB identifier with IANA name windows-1253. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1253 - Windows1253 MIB = 2253 - - // Windows1254 is the MIB identifier with IANA name windows-1254. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1254 - Windows1254 MIB = 2254 - - // Windows1255 is the MIB identifier with IANA name windows-1255. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1255 - Windows1255 MIB = 2255 - - // Windows1256 is the MIB identifier with IANA name windows-1256. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1256 - Windows1256 MIB = 2256 - - // Windows1257 is the MIB identifier with IANA name windows-1257. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1257 - Windows1257 MIB = 2257 - - // Windows1258 is the MIB identifier with IANA name windows-1258. - // - // Microsoft https://www.iana.org/assignments/charset-reg/windows-1258 - Windows1258 MIB = 2258 - - // TIS620 is the MIB identifier with IANA name TIS-620. - // - // Thai Industrial Standards Institute (TISI) - TIS620 MIB = 2259 - - // CP50220 is the MIB identifier with IANA name CP50220. - // - // See https://www.iana.org/assignments/charset-reg/CP50220 - CP50220 MIB = 2260 -) diff --git a/vendor/modules.txt b/vendor/modules.txt index d18395cb..ef54d20e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -88,61 +88,6 @@ github.com/flynn/go-shlex # github.com/fsnotify/fsnotify v1.4.9 ## explicit; go 1.13 github.com/fsnotify/fsnotify -# github.com/gdamore/encoding v1.0.0 -## explicit; go 1.9 -github.com/gdamore/encoding -# github.com/gdamore/tcell v1.3.0 -## explicit; go 1.12 -github.com/gdamore/tcell -github.com/gdamore/tcell/terminfo -github.com/gdamore/tcell/terminfo/a/adm3a -github.com/gdamore/tcell/terminfo/a/aixterm -github.com/gdamore/tcell/terminfo/a/alacritty -github.com/gdamore/tcell/terminfo/a/ansi -github.com/gdamore/tcell/terminfo/a/aterm -github.com/gdamore/tcell/terminfo/b/beterm -github.com/gdamore/tcell/terminfo/b/bsdos_pc -github.com/gdamore/tcell/terminfo/base -github.com/gdamore/tcell/terminfo/c/cygwin -github.com/gdamore/tcell/terminfo/d/d200 -github.com/gdamore/tcell/terminfo/d/d210 -github.com/gdamore/tcell/terminfo/d/dtterm -github.com/gdamore/tcell/terminfo/dynamic -github.com/gdamore/tcell/terminfo/e/emacs -github.com/gdamore/tcell/terminfo/e/eterm -github.com/gdamore/tcell/terminfo/extended -github.com/gdamore/tcell/terminfo/g/gnome -github.com/gdamore/tcell/terminfo/h/hpterm -github.com/gdamore/tcell/terminfo/h/hz1500 -github.com/gdamore/tcell/terminfo/k/konsole -github.com/gdamore/tcell/terminfo/k/kterm -github.com/gdamore/tcell/terminfo/l/linux -github.com/gdamore/tcell/terminfo/p/pcansi -github.com/gdamore/tcell/terminfo/r/rxvt -github.com/gdamore/tcell/terminfo/s/screen -github.com/gdamore/tcell/terminfo/s/simpleterm -github.com/gdamore/tcell/terminfo/s/sun -github.com/gdamore/tcell/terminfo/t/termite -github.com/gdamore/tcell/terminfo/t/tvi910 -github.com/gdamore/tcell/terminfo/t/tvi912 -github.com/gdamore/tcell/terminfo/t/tvi921 -github.com/gdamore/tcell/terminfo/t/tvi925 -github.com/gdamore/tcell/terminfo/t/tvi950 -github.com/gdamore/tcell/terminfo/t/tvi970 -github.com/gdamore/tcell/terminfo/v/vt100 -github.com/gdamore/tcell/terminfo/v/vt102 -github.com/gdamore/tcell/terminfo/v/vt220 -github.com/gdamore/tcell/terminfo/v/vt320 -github.com/gdamore/tcell/terminfo/v/vt400 -github.com/gdamore/tcell/terminfo/v/vt420 -github.com/gdamore/tcell/terminfo/v/vt52 -github.com/gdamore/tcell/terminfo/w/wy50 -github.com/gdamore/tcell/terminfo/w/wy60 -github.com/gdamore/tcell/terminfo/w/wy99_ansi -github.com/gdamore/tcell/terminfo/x/xfce -github.com/gdamore/tcell/terminfo/x/xnuppc -github.com/gdamore/tcell/terminfo/x/xterm -github.com/gdamore/tcell/terminfo/x/xterm_kitty # github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 ## explicit github.com/getsentry/raven-go @@ -216,9 +161,6 @@ github.com/lucas-clemente/quic-go/internal/utils github.com/lucas-clemente/quic-go/internal/wire github.com/lucas-clemente/quic-go/logging github.com/lucas-clemente/quic-go/quicvarint -# github.com/lucasb-eyer/go-colorful v1.0.3 -## explicit; go 1.12 -github.com/lucasb-eyer/go-colorful # github.com/marten-seemann/qtls-go1-16 v0.1.5 ## explicit; go 1.16 github.com/marten-seemann/qtls-go1-16 @@ -234,9 +176,6 @@ github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.12 ## explicit; go 1.12 github.com/mattn/go-isatty -# github.com/mattn/go-runewidth v0.0.8 -## explicit; go 1.9 -github.com/mattn/go-runewidth # github.com/matttproud/golang_protobuf_extensions v1.0.1 ## explicit github.com/matttproud/golang_protobuf_extensions/pbutil @@ -318,12 +257,6 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92 -## explicit; go 1.12 -github.com/rivo/tview -# github.com/rivo/uniseg v0.1.0 -## explicit; go 1.12 -github.com/rivo/uniseg # github.com/rs/zerolog v1.20.0 ## explicit github.com/rs/zerolog @@ -447,8 +380,6 @@ golang.org/x/sys/windows/svc/mgr golang.org/x/term # golang.org/x/text v0.3.7 ## explicit; go 1.17 -golang.org/x/text/encoding -golang.org/x/text/encoding/internal/identifier golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi From e63ec3450381851689ae726202b6325fb2e3924d Mon Sep 17 00:00:00 2001 From: Anton Kozlov Date: Wed, 20 Jul 2022 15:46:41 +0100 Subject: [PATCH 140/238] cURL supports stdin and uses os pipes directly without copying --- cmd/cloudflared/access/cmd.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cloudflared/access/cmd.go b/cmd/cloudflared/access/cmd.go index fa06fd08..85f59d8f 100644 --- a/cmd/cloudflared/access/cmd.go +++ b/cmd/cloudflared/access/cmd.go @@ -296,6 +296,7 @@ func curl(c *cli.Context) error { // run kicks off a shell task and pipe the results to the respective std pipes func run(cmd string, args ...string) error { c := exec.Command(cmd, args...) + c.Stdin = os.Stdin stderr, err := c.StderrPipe() if err != nil { return err From 032ba7b5e49ef093993e123fafb8cb1e6a60fa90 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 25 Jul 2022 16:14:38 +0100 Subject: [PATCH 141/238] TUN-6598: Remove auto assignees on github issues This PR removes automatic assignees on github issues because it sends a slightly wrong message about triaging. We will continue to triage issues and find a more focussed method to nominate assignees. --- .github/ISSUE_TEMPLATE/---bug-report.md | 1 - .github/ISSUE_TEMPLATE/---documentation.md | 1 - .github/ISSUE_TEMPLATE/---feature-request.md | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 9aaa512c..8d26bfe7 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -3,7 +3,6 @@ name: "\U0001F41B Bug report" about: Create a report to help us improve cloudflared title: "\U0001F41B" labels: 'Priority: Normal, Type: Bug' -assignees: abelinkinbio --- diff --git a/.github/ISSUE_TEMPLATE/---documentation.md b/.github/ISSUE_TEMPLATE/---documentation.md index 4231ddf4..740125be 100644 --- a/.github/ISSUE_TEMPLATE/---documentation.md +++ b/.github/ISSUE_TEMPLATE/---documentation.md @@ -3,7 +3,6 @@ name: "\U0001F4DD Documentation" about: Request new or updated documentation for cloudflared title: "\U0001F4DD" labels: 'Priority: Normal, Type: Documentation' -assignees: abelinkinbio, ranbel --- diff --git a/.github/ISSUE_TEMPLATE/---feature-request.md b/.github/ISSUE_TEMPLATE/---feature-request.md index 2862e063..25982e77 100644 --- a/.github/ISSUE_TEMPLATE/---feature-request.md +++ b/.github/ISSUE_TEMPLATE/---feature-request.md @@ -3,7 +3,6 @@ name: "\U0001F4A1 Feature request" about: Suggest a feature or enhancement for cloudflared title: "\U0001F4A1" labels: 'Priority: Normal, Type: Feature Request' -assignees: abelinkinbio, sssilver --- From d96c39196da7abb1779124b4a7779a94b2bb2733 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 27 Jul 2022 10:05:15 +0100 Subject: [PATCH 142/238] TUN-6601: Update gopkg.in/yaml.v3 references in modules --- go.mod | 2 ++ go.sum | 3 --- vendor/modules.txt | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 1f97b27c..7c7a66ef 100644 --- a/go.mod +++ b/go.mod @@ -103,3 +103,5 @@ replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.2 // Avoid 'CVE-2022-21698' replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 + +replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index c5b3b0d6..d3aa3662 100644 --- a/go.sum +++ b/go.sum @@ -1090,9 +1090,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= diff --git a/vendor/modules.txt b/vendor/modules.txt index ef54d20e..cb1a2db6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -523,7 +523,7 @@ gopkg.in/tomb.v1 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# gopkg.in/yaml.v3 v3.0.1 +# gopkg.in/yaml.v3 v3.0.1 => gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 # zombiezen.com/go/capnproto2 v2.18.0+incompatible @@ -545,3 +545,4 @@ zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d # github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 +# gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 From f48a7cd3dd6c59d62ab0fd9c66b2b5f4e91e923b Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 26 Jul 2022 14:00:53 -0700 Subject: [PATCH 143/238] TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span --- connection/connection.go | 19 ++++-- connection/connection_test.go | 4 +- connection/h2mux.go | 4 +- connection/h2mux_test.go | 2 +- connection/http2.go | 2 +- connection/quic.go | 24 +++++-- connection/quic_test.go | 9 ++- orchestration/orchestrator_test.go | 4 +- proxy/proxy.go | 22 ++++-- proxy/proxy_test.go | 16 +++-- supervisor/tunnel.go | 1 + tracing/tracing.go | 106 +++++++++++++++++++++++------ tracing/tracing_test.go | 17 +++-- 13 files changed, 168 insertions(+), 62 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index bcc24b64..5200f4bb 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -123,23 +123,24 @@ func (t Type) String() string { // OriginProxy is how data flows from cloudflared to the origin services running behind it. type OriginProxy interface { - ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error + ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error ProxyTCP(ctx context.Context, rwa ReadWriteAcker, req *TCPRequest) error } // TCPRequest defines the input format needed to perform a TCP proxy. type TCPRequest struct { - Dest string - CFRay string - LBProbe bool - FlowID string + Dest string + CFRay string + LBProbe bool + FlowID string + CfTraceID string } // ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has // accepted the connection. type ReadWriteAcker interface { io.ReadWriter - AckConnection() error + AckConnection(tracePropagation string) error } // HTTPResponseReadWriteAcker is an HTTP implementation of ReadWriteAcker. @@ -168,7 +169,7 @@ func (h *HTTPResponseReadWriteAcker) Write(p []byte) (int, error) { // AckConnection acks an HTTP connection by sending a switch protocols status code that enables the caller to // upgrade to streams. -func (h *HTTPResponseReadWriteAcker) AckConnection() error { +func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) error { resp := &http.Response{ Status: switchingProtocolText, StatusCode: http.StatusSwitchingProtocols, @@ -179,6 +180,10 @@ func (h *HTTPResponseReadWriteAcker) AckConnection() error { resp.Header = websocket.NewResponseHeader(h.req) } + if tracePropagation != "" { + resp.Header.Add(tracing.CanonicalCloudflaredTracingHeader, tracePropagation) + } + return h.w.WriteRespHeaders(resp.StatusCode, resp.Header) } diff --git a/connection/connection_test.go b/connection/connection_test.go index b8c3b674..ae37db75 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -30,6 +30,8 @@ var ( testLargeResp = make([]byte, largeFileSize) ) +var _ ReadWriteAcker = (*HTTPResponseReadWriteAcker)(nil) + type testRequest struct { name string endpoint string @@ -60,7 +62,7 @@ type mockOriginProxy struct{} func (moc *mockOriginProxy) ProxyHTTP( w ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, isWebsocket bool, ) error { req := tr.Request diff --git a/connection/h2mux.go b/connection/h2mux.go index 0bd35cbe..b78b433f 100644 --- a/connection/h2mux.go +++ b/connection/h2mux.go @@ -69,6 +69,7 @@ func NewH2muxConnection( connIndex uint8, observer *Observer, gracefulShutdownC <-chan struct{}, + log *zerolog.Logger, ) (*h2muxConnection, error, bool) { h := &h2muxConnection{ orchestrator: orchestrator, @@ -79,6 +80,7 @@ func NewH2muxConnection( observer: observer, gracefulShutdownC: gracefulShutdownC, newRPCClientFunc: newRegistrationRPCClient, + log: log, } // Establish a muxed connection with the edge @@ -234,7 +236,7 @@ func (h *h2muxConnection) ServeStream(stream *h2mux.MuxedStream) error { return err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), sourceConnectionType == TypeWebsocket) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, h.log), sourceConnectionType == TypeWebsocket) if err != nil { respWriter.WriteErrorResponse() } diff --git a/connection/h2mux_test.go b/connection/h2mux_test.go index cf2337ec..53c28227 100644 --- a/connection/h2mux_test.go +++ b/connection/h2mux_test.go @@ -48,7 +48,7 @@ func newH2MuxConnection(t require.TestingT) (*h2muxConnection, *h2mux.Muxer) { }() var connIndex = uint8(0) testObserver := NewObserver(&log, &log) - h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil) + h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil, &log) require.NoError(t, err) return h2muxConn, <-edgeMuxChan } diff --git a/connection/http2.go b/connection/http2.go index f7ead1f0..1b78d748 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -132,7 +132,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { case TypeWebsocket, TypeHTTP: stripWebsocketUpgradeHeader(r) // Check for tracing on request - tr := tracing.NewTracedRequest(r) + tr := tracing.NewTracedHTTPRequest(r, c.log) if err := originProxy.ProxyHTTP(respWriter, tr, connType == TypeWebsocket); err != nil { err := fmt.Errorf("Failed to proxy HTTP: %w", err) c.log.Error().Err(err) diff --git a/connection/quic.go b/connection/quic.go index 7adc03c2..12e1d2b6 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -197,7 +197,7 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R switch request.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - tracedReq, err := buildHTTPRequest(ctx, request, stream) + tracedReq, err := buildHTTPRequest(ctx, request, stream, q.logger) if err != nil { return err } @@ -208,8 +208,9 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R rwa := &streamReadWriteAcker{stream} metadata := request.MetadataMap() return originProxy.ProxyTCP(ctx, rwa, &TCPRequest{ - Dest: request.Dest, - FlowID: metadata[QUICMetadataFlowID], + Dest: request.Dest, + FlowID: metadata[QUICMetadataFlowID], + CfTraceID: metadata[tracing.TracerContextName], }) } return nil @@ -296,8 +297,12 @@ type streamReadWriteAcker struct { } // AckConnection acks response back to the proxy. -func (s *streamReadWriteAcker) AckConnection() error { - return s.WriteConnectResponseData(nil) +func (s *streamReadWriteAcker) AckConnection(tracePropagation string) error { + metadata := quicpogs.Metadata{ + Key: tracing.CanonicalCloudflaredTracingHeader, + Val: tracePropagation, + } + return s.WriteConnectResponseData(nil, metadata) } // httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC. @@ -325,7 +330,12 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) { hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } -func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { +func buildHTTPRequest( + ctx context.Context, + connectRequest *quicpogs.ConnectRequest, + body io.ReadCloser, + log *zerolog.Logger, +) (*tracing.TracedHTTPRequest, error) { metadata := connectRequest.MetadataMap() dest := connectRequest.Dest method := metadata[HTTPMethodKey] @@ -367,7 +377,7 @@ func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectReque stripWebsocketUpgradeHeader(req) // Check for tracing on request - tracedReq := tracing.NewTracedRequest(req) + tracedReq := tracing.NewTracedHTTPRequest(req, log) return tracedReq, err } diff --git a/connection/quic_test.go b/connection/quic_test.go index 33da98ac..2557e67b 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -36,6 +36,8 @@ var ( } ) +var _ ReadWriteAcker = (*streamReadWriteAcker)(nil) + // TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol. // It also serves as a demonstration for communication with the QUIC connection started by a cloudflared. func TestQUICServer(t *testing.T) { @@ -220,7 +222,7 @@ func quicServer( type mockOriginProxyWithRequest struct{} -func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error { +func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error { // These are a series of crude tests to ensure the headers and http related data is transferred from // metadata. r := tr.Request @@ -475,9 +477,10 @@ func TestBuildHTTPRequest(t *testing.T) { }, } + log := zerolog.Nop() for _, test := range tests { t.Run(test.name, func(t *testing.T) { - req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body) + req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body, &log) assert.NoError(t, err) test.req = test.req.WithContext(req.Context()) assert.Equal(t, test.req, req.Request) @@ -486,7 +489,7 @@ func TestBuildHTTPRequest(t *testing.T) { } func (moc *mockOriginProxyWithRequest) ProxyTCP(ctx context.Context, rwa ReadWriteAcker, tcpRequest *TCPRequest) error { - rwa.AckConnection() + rwa.AckConnection("") io.Copy(rwa, rwa) return nil } diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index dfac28a0..dc94d29c 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -355,7 +355,7 @@ func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Respo return nil, err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), false) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), false) if err != nil { return nil, err } @@ -604,7 +604,7 @@ func TestPersistentConnection(t *testing.T) { respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket, &log) require.NoError(t, err) - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), true) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), true) require.NoError(t, err) }() diff --git a/proxy/proxy.go b/proxy/proxy.go index 19f14778..0c2c21c0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -63,7 +63,7 @@ func NewOriginProxy( // a simple roundtrip or a tcp/websocket dial depending on ingres rule setup. func (p *Proxy) ProxyHTTP( w connection.ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, isWebsocket bool, ) error { incrementRequests() @@ -108,7 +108,7 @@ func (p *Proxy) ProxyHTTP( } rws := connection.NewHTTPResponseReadWriterAcker(w, req) - if err := p.proxyStream(req.Context(), rws, dest, originProxy); err != nil { + if err := p.proxyStream(tr.ToTracedContext(), rws, dest, originProxy); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) p.logRequestError(err, cfRay, "", rule, srv) return err @@ -137,9 +137,11 @@ func (p *Proxy) ProxyTCP( serveCtx, cancel := context.WithCancel(ctx) defer cancel() + tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, p.log) + p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started") - if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { + if err := p.proxyStream(tracedCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting) return err } @@ -160,7 +162,7 @@ func ruleField(ing ingress.Ingress, ruleNum int) (ruleID string, srv string) { // ProxyHTTPRequest proxies requests of underlying type http and websocket to the origin service. func (p *Proxy) proxyHTTPRequest( w connection.ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, httpService ingress.HTTPOriginProxy, isWebsocket bool, disableChunkedEncoding bool, @@ -211,7 +213,7 @@ func (p *Proxy) proxyHTTPRequest( } // Add spans to response header (if available) - tr.AddSpans(resp.Header, p.log) + tr.AddSpans(resp.Header) err = w.WriteRespHeaders(resp.StatusCode, resp.Header) if err != nil { @@ -248,17 +250,23 @@ func (p *Proxy) proxyHTTPRequest( // proxyStream proxies type TCP and other underlying types if the connection is defined as a stream oriented // ingress rule. func (p *Proxy) proxyStream( - ctx context.Context, + tr *tracing.TracedContext, rwa connection.ReadWriteAcker, dest string, connectionProxy ingress.StreamBasedOriginProxy, ) error { + ctx := tr.Context + _, connectSpan := tr.Tracer().Start(ctx, "stream_connect") originConn, err := connectionProxy.EstablishConnection(ctx, dest) if err != nil { + tracing.EndWithErrorStatus(connectSpan, err) return err } + connectSpan.End() - if err := rwa.AckConnection(); err != nil { + encodedSpans := tr.GetSpans() + + if err := rwa.AckConnection(encodedSpans); err != nil { return err } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 0e0019f1..f85281b3 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -157,7 +157,8 @@ func testProxyHTTP(proxy connection.OriginProxy) func(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) for _, tag := range testTags { assert.Equal(t, tag.Value, req.Header.Get(TagHeaderNamePrefix+tag.Name)) @@ -184,7 +185,8 @@ func testProxyWebsocket(proxy connection.OriginProxy) func(t *testing.T) { errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), true) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), true) require.NoError(t, err) require.Equal(t, http.StatusSwitchingProtocols, responseWriter.Code) @@ -245,7 +247,8 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) require.Equal(t, http.StatusOK, responseWriter.Code) @@ -357,7 +360,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat req, err := http.NewRequest(http.MethodGet, test.url, nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) assert.Equal(t, test.expectedStatus, responseWriter.Code) @@ -404,7 +407,7 @@ func TestProxyError(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil) assert.NoError(t, err) - assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false)) + assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false)) } type replayer struct { @@ -682,7 +685,8 @@ func TestConnections(t *testing.T) { rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { - err = proxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), test.args.connectionType == connection.TypeWebsocket) + log := zerolog.Nop() + err = proxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), test.args.connectionType == connection.TypeWebsocket) } cancel() diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 4f208dd4..2abf09f5 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -557,6 +557,7 @@ func ServeH2mux( connIndex, config.Observer, gracefulShutdownC, + config.Log, ) if err != nil { if !recoverable { diff --git a/tracing/tracing.go b/tracing/tracing.go index e9172cd5..7eba4c52 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "runtime" + "strings" "github.com/rs/zerolog" otelContrib "go.opentelemetry.io/contrib/propagators/jaeger" @@ -33,6 +34,9 @@ const ( MaxErrorDescriptionLen = 100 traceHttpStatusCodeKey = "upstreamStatusCode" + + traceID128bitsWidth = 128 / 4 + separator = ":" ) var ( @@ -66,22 +70,50 @@ func Init(version string) { cloudflaredVersionAttribute = semconv.ProcessRuntimeVersionKey.String(version) } -type TracedRequest struct { +type TracedHTTPRequest struct { *http.Request - trace.TracerProvider - exporter InMemoryClient + *cfdTracer } -// NewTracedRequest creates a new tracer for the current request context. -func NewTracedRequest(req *http.Request) *TracedRequest { +// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. +func NewTracedHTTPRequest(req *http.Request, log *zerolog.Logger) *TracedHTTPRequest { ctx, exists := extractTrace(req) if !exists { - return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + return &TracedHTTPRequest{req, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} } + return &TracedHTTPRequest{req.WithContext(ctx), newCfdTracer(ctx, log)} +} + +func (tr *TracedHTTPRequest) ToTracedContext() *TracedContext { + return &TracedContext{tr.Context(), tr.cfdTracer} +} + +type TracedContext struct { + context.Context + *cfdTracer +} + +// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. +func NewTracedContext(ctx context.Context, traceContext string, log *zerolog.Logger) *TracedContext { + ctx, exists := extractTraceFromString(ctx, traceContext) + if !exists { + return &TracedContext{ctx, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} + } + return &TracedContext{ctx, newCfdTracer(ctx, log)} +} + +type cfdTracer struct { + trace.TracerProvider + exporter InMemoryClient + log *zerolog.Logger +} + +// NewCfdTracer creates a new tracer for the current request context. +func newCfdTracer(ctx context.Context, log *zerolog.Logger) *cfdTracer { mc := new(InMemoryOtlpClient) - exp, err := otlptrace.New(req.Context(), mc) + exp, err := otlptrace.New(ctx, mc) if err != nil { - return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + return &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log} } tp := tracesdk.NewTracerProvider( // We want to dump to in-memory exporter immediately @@ -98,36 +130,43 @@ func NewTracedRequest(req *http.Request) *TracedRequest { )), ) - return &TracedRequest{req.WithContext(ctx), tp, mc} + return &cfdTracer{tp, mc, log} } -func (cft *TracedRequest) Tracer() trace.Tracer { +func (cft *cfdTracer) Tracer() trace.Tracer { return cft.TracerProvider.Tracer(tracerInstrumentName) } -// Spans returns the spans as base64 encoded protobuf otlp traces. -func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { - if headers == nil { - log.Error().Msgf("provided headers map is nil") - return - } - +// GetSpans returns the spans as base64 encoded string of protobuf otlp traces. +func (cft *cfdTracer) GetSpans() (enc string) { enc, err := cft.exporter.Spans() switch err { case nil: break case errNoTraces: - log.Error().Err(err).Msgf("expected traces to be available") + cft.log.Error().Err(err).Msgf("expected traces to be available") return case errNoopTracer: return // noop tracer has no traces default: - log.Error().Err(err) + cft.log.Error().Err(err) return } + return +} + +// AddSpans assigns spans as base64 encoded protobuf otlp traces to provided +// HTTP headers. +func (cft *cfdTracer) AddSpans(headers http.Header) { + if headers == nil { + cft.log.Error().Msgf("provided headers map is nil") + return + } + + enc := cft.GetSpans() // No need to add header if no traces if enc == "" { - log.Error().Msgf("no traces provided and no error from exporter") + cft.log.Error().Msgf("no traces provided and no error from exporter") return } @@ -166,6 +205,33 @@ func endSpan(span trace.Span, upstreamStatusCode int, spanStatusCode codes.Code, span.End() } +// extractTraceFromString will extract the trace information from the provided +// propagated trace string context. +func extractTraceFromString(ctx context.Context, trace string) (context.Context, bool) { + if trace == "" { + return ctx, false + } + // Jaeger specific separator + parts := strings.Split(trace, separator) + if len(parts) != 4 { + return ctx, false + } + if parts[0] == "" { + return ctx, false + } + // Correctly left pad the trace to a length of 32 + if len(parts[0]) < traceID128bitsWidth { + left := traceID128bitsWidth - len(parts[0]) + parts[0] = strings.Repeat("0", left) + parts[0] + trace = strings.Join(parts, separator) + } + + // Override the 'cf-trace-id' as 'uber-trace-id' so the jaeger propagator can extract it. + traceHeader := map[string]string{TracerContextNameOverride: trace} + remoteCtx := otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(traceHeader)) + return remoteCtx, true +} + // extractTrace attempts to check for a cf-trace-id from a request and return the // trace context with the provided http.Request. func extractTrace(req *http.Request) (context.Context, bool) { diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go index 68a272fc..5750056e 100644 --- a/tracing/tracing_test.go +++ b/tracing/tracing_test.go @@ -14,38 +14,42 @@ import ( ) func TestNewCfTracer(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerMultiple(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerNilHeader(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header[http.CanonicalHeaderKey(TracerContextName)] = nil - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) } func TestNewCfTracerInvalidHeaders(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) for _, test := range [][]string{nil, {""}} { req.Header[http.CanonicalHeaderKey(TracerContextName)] = test - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) @@ -53,9 +57,10 @@ func TestNewCfTracerInvalidHeaders(t *testing.T) { } func TestAddingSpansWithNilMap(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) exporter := tr.exporter.(*InMemoryOtlpClient) @@ -65,5 +70,5 @@ func TestAddingSpansWithNilMap(t *testing.T) { assert.NoError(t, err) // a panic shouldn't occur - tr.AddSpans(nil, &zerolog.Logger{}) + tr.AddSpans(nil) } From 7f1c890a82a84e4eeeac3d7d687674f9c8c91121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Tue, 2 Aug 2022 11:13:24 +0100 Subject: [PATCH 144/238] Revert "TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span" This reverts commit f48a7cd3dd6c59d62ab0fd9c66b2b5f4e91e923b. --- connection/connection.go | 19 ++--- connection/connection_test.go | 4 +- connection/h2mux.go | 4 +- connection/h2mux_test.go | 2 +- connection/http2.go | 2 +- connection/quic.go | 24 ++----- connection/quic_test.go | 9 +-- orchestration/orchestrator_test.go | 4 +- proxy/proxy.go | 22 ++---- proxy/proxy_test.go | 16 ++--- supervisor/tunnel.go | 1 - tracing/tracing.go | 108 ++++++----------------------- tracing/tracing_test.go | 17 ++--- 13 files changed, 63 insertions(+), 169 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index 5200f4bb..bcc24b64 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -123,24 +123,23 @@ func (t Type) String() string { // OriginProxy is how data flows from cloudflared to the origin services running behind it. type OriginProxy interface { - ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error + ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error ProxyTCP(ctx context.Context, rwa ReadWriteAcker, req *TCPRequest) error } // TCPRequest defines the input format needed to perform a TCP proxy. type TCPRequest struct { - Dest string - CFRay string - LBProbe bool - FlowID string - CfTraceID string + Dest string + CFRay string + LBProbe bool + FlowID string } // ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has // accepted the connection. type ReadWriteAcker interface { io.ReadWriter - AckConnection(tracePropagation string) error + AckConnection() error } // HTTPResponseReadWriteAcker is an HTTP implementation of ReadWriteAcker. @@ -169,7 +168,7 @@ func (h *HTTPResponseReadWriteAcker) Write(p []byte) (int, error) { // AckConnection acks an HTTP connection by sending a switch protocols status code that enables the caller to // upgrade to streams. -func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) error { +func (h *HTTPResponseReadWriteAcker) AckConnection() error { resp := &http.Response{ Status: switchingProtocolText, StatusCode: http.StatusSwitchingProtocols, @@ -180,10 +179,6 @@ func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) erro resp.Header = websocket.NewResponseHeader(h.req) } - if tracePropagation != "" { - resp.Header.Add(tracing.CanonicalCloudflaredTracingHeader, tracePropagation) - } - return h.w.WriteRespHeaders(resp.StatusCode, resp.Header) } diff --git a/connection/connection_test.go b/connection/connection_test.go index ae37db75..b8c3b674 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -30,8 +30,6 @@ var ( testLargeResp = make([]byte, largeFileSize) ) -var _ ReadWriteAcker = (*HTTPResponseReadWriteAcker)(nil) - type testRequest struct { name string endpoint string @@ -62,7 +60,7 @@ type mockOriginProxy struct{} func (moc *mockOriginProxy) ProxyHTTP( w ResponseWriter, - tr *tracing.TracedHTTPRequest, + tr *tracing.TracedRequest, isWebsocket bool, ) error { req := tr.Request diff --git a/connection/h2mux.go b/connection/h2mux.go index b78b433f..0bd35cbe 100644 --- a/connection/h2mux.go +++ b/connection/h2mux.go @@ -69,7 +69,6 @@ func NewH2muxConnection( connIndex uint8, observer *Observer, gracefulShutdownC <-chan struct{}, - log *zerolog.Logger, ) (*h2muxConnection, error, bool) { h := &h2muxConnection{ orchestrator: orchestrator, @@ -80,7 +79,6 @@ func NewH2muxConnection( observer: observer, gracefulShutdownC: gracefulShutdownC, newRPCClientFunc: newRegistrationRPCClient, - log: log, } // Establish a muxed connection with the edge @@ -236,7 +234,7 @@ func (h *h2muxConnection) ServeStream(stream *h2mux.MuxedStream) error { return err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, h.log), sourceConnectionType == TypeWebsocket) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), sourceConnectionType == TypeWebsocket) if err != nil { respWriter.WriteErrorResponse() } diff --git a/connection/h2mux_test.go b/connection/h2mux_test.go index 53c28227..cf2337ec 100644 --- a/connection/h2mux_test.go +++ b/connection/h2mux_test.go @@ -48,7 +48,7 @@ func newH2MuxConnection(t require.TestingT) (*h2muxConnection, *h2mux.Muxer) { }() var connIndex = uint8(0) testObserver := NewObserver(&log, &log) - h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil, &log) + h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil) require.NoError(t, err) return h2muxConn, <-edgeMuxChan } diff --git a/connection/http2.go b/connection/http2.go index 1b78d748..f7ead1f0 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -132,7 +132,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { case TypeWebsocket, TypeHTTP: stripWebsocketUpgradeHeader(r) // Check for tracing on request - tr := tracing.NewTracedHTTPRequest(r, c.log) + tr := tracing.NewTracedRequest(r) if err := originProxy.ProxyHTTP(respWriter, tr, connType == TypeWebsocket); err != nil { err := fmt.Errorf("Failed to proxy HTTP: %w", err) c.log.Error().Err(err) diff --git a/connection/quic.go b/connection/quic.go index 12e1d2b6..7adc03c2 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -197,7 +197,7 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R switch request.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - tracedReq, err := buildHTTPRequest(ctx, request, stream, q.logger) + tracedReq, err := buildHTTPRequest(ctx, request, stream) if err != nil { return err } @@ -208,9 +208,8 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R rwa := &streamReadWriteAcker{stream} metadata := request.MetadataMap() return originProxy.ProxyTCP(ctx, rwa, &TCPRequest{ - Dest: request.Dest, - FlowID: metadata[QUICMetadataFlowID], - CfTraceID: metadata[tracing.TracerContextName], + Dest: request.Dest, + FlowID: metadata[QUICMetadataFlowID], }) } return nil @@ -297,12 +296,8 @@ type streamReadWriteAcker struct { } // AckConnection acks response back to the proxy. -func (s *streamReadWriteAcker) AckConnection(tracePropagation string) error { - metadata := quicpogs.Metadata{ - Key: tracing.CanonicalCloudflaredTracingHeader, - Val: tracePropagation, - } - return s.WriteConnectResponseData(nil, metadata) +func (s *streamReadWriteAcker) AckConnection() error { + return s.WriteConnectResponseData(nil) } // httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC. @@ -330,12 +325,7 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) { hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } -func buildHTTPRequest( - ctx context.Context, - connectRequest *quicpogs.ConnectRequest, - body io.ReadCloser, - log *zerolog.Logger, -) (*tracing.TracedHTTPRequest, error) { +func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { metadata := connectRequest.MetadataMap() dest := connectRequest.Dest method := metadata[HTTPMethodKey] @@ -377,7 +367,7 @@ func buildHTTPRequest( stripWebsocketUpgradeHeader(req) // Check for tracing on request - tracedReq := tracing.NewTracedHTTPRequest(req, log) + tracedReq := tracing.NewTracedRequest(req) return tracedReq, err } diff --git a/connection/quic_test.go b/connection/quic_test.go index 2557e67b..33da98ac 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -36,8 +36,6 @@ var ( } ) -var _ ReadWriteAcker = (*streamReadWriteAcker)(nil) - // TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol. // It also serves as a demonstration for communication with the QUIC connection started by a cloudflared. func TestQUICServer(t *testing.T) { @@ -222,7 +220,7 @@ func quicServer( type mockOriginProxyWithRequest struct{} -func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error { +func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error { // These are a series of crude tests to ensure the headers and http related data is transferred from // metadata. r := tr.Request @@ -477,10 +475,9 @@ func TestBuildHTTPRequest(t *testing.T) { }, } - log := zerolog.Nop() for _, test := range tests { t.Run(test.name, func(t *testing.T) { - req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body, &log) + req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body) assert.NoError(t, err) test.req = test.req.WithContext(req.Context()) assert.Equal(t, test.req, req.Request) @@ -489,7 +486,7 @@ func TestBuildHTTPRequest(t *testing.T) { } func (moc *mockOriginProxyWithRequest) ProxyTCP(ctx context.Context, rwa ReadWriteAcker, tcpRequest *TCPRequest) error { - rwa.AckConnection("") + rwa.AckConnection() io.Copy(rwa, rwa) return nil } diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index dc94d29c..dfac28a0 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -355,7 +355,7 @@ func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Respo return nil, err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), false) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), false) if err != nil { return nil, err } @@ -604,7 +604,7 @@ func TestPersistentConnection(t *testing.T) { respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket, &log) require.NoError(t, err) - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), true) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), true) require.NoError(t, err) }() diff --git a/proxy/proxy.go b/proxy/proxy.go index 0c2c21c0..19f14778 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -63,7 +63,7 @@ func NewOriginProxy( // a simple roundtrip or a tcp/websocket dial depending on ingres rule setup. func (p *Proxy) ProxyHTTP( w connection.ResponseWriter, - tr *tracing.TracedHTTPRequest, + tr *tracing.TracedRequest, isWebsocket bool, ) error { incrementRequests() @@ -108,7 +108,7 @@ func (p *Proxy) ProxyHTTP( } rws := connection.NewHTTPResponseReadWriterAcker(w, req) - if err := p.proxyStream(tr.ToTracedContext(), rws, dest, originProxy); err != nil { + if err := p.proxyStream(req.Context(), rws, dest, originProxy); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) p.logRequestError(err, cfRay, "", rule, srv) return err @@ -137,11 +137,9 @@ func (p *Proxy) ProxyTCP( serveCtx, cancel := context.WithCancel(ctx) defer cancel() - tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, p.log) - p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started") - if err := p.proxyStream(tracedCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { + if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting) return err } @@ -162,7 +160,7 @@ func ruleField(ing ingress.Ingress, ruleNum int) (ruleID string, srv string) { // ProxyHTTPRequest proxies requests of underlying type http and websocket to the origin service. func (p *Proxy) proxyHTTPRequest( w connection.ResponseWriter, - tr *tracing.TracedHTTPRequest, + tr *tracing.TracedRequest, httpService ingress.HTTPOriginProxy, isWebsocket bool, disableChunkedEncoding bool, @@ -213,7 +211,7 @@ func (p *Proxy) proxyHTTPRequest( } // Add spans to response header (if available) - tr.AddSpans(resp.Header) + tr.AddSpans(resp.Header, p.log) err = w.WriteRespHeaders(resp.StatusCode, resp.Header) if err != nil { @@ -250,23 +248,17 @@ func (p *Proxy) proxyHTTPRequest( // proxyStream proxies type TCP and other underlying types if the connection is defined as a stream oriented // ingress rule. func (p *Proxy) proxyStream( - tr *tracing.TracedContext, + ctx context.Context, rwa connection.ReadWriteAcker, dest string, connectionProxy ingress.StreamBasedOriginProxy, ) error { - ctx := tr.Context - _, connectSpan := tr.Tracer().Start(ctx, "stream_connect") originConn, err := connectionProxy.EstablishConnection(ctx, dest) if err != nil { - tracing.EndWithErrorStatus(connectSpan, err) return err } - connectSpan.End() - encodedSpans := tr.GetSpans() - - if err := rwa.AckConnection(encodedSpans); err != nil { + if err := rwa.AckConnection(); err != nil { return err } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index f85281b3..0e0019f1 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -157,8 +157,7 @@ func testProxyHTTP(proxy connection.OriginProxy) func(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil) require.NoError(t, err) - log := zerolog.Nop() - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) for _, tag := range testTags { assert.Equal(t, tag.Value, req.Header.Get(TagHeaderNamePrefix+tag.Name)) @@ -185,8 +184,7 @@ func testProxyWebsocket(proxy connection.OriginProxy) func(t *testing.T) { errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { - log := zerolog.Nop() - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), true) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), true) require.NoError(t, err) require.Equal(t, http.StatusSwitchingProtocols, responseWriter.Code) @@ -247,8 +245,7 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - log := zerolog.Nop() - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) require.Equal(t, http.StatusOK, responseWriter.Code) @@ -360,7 +357,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat req, err := http.NewRequest(http.MethodGet, test.url, nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) require.NoError(t, err) assert.Equal(t, test.expectedStatus, responseWriter.Code) @@ -407,7 +404,7 @@ func TestProxyError(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil) assert.NoError(t, err) - assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false)) + assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false)) } type replayer struct { @@ -685,8 +682,7 @@ func TestConnections(t *testing.T) { rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { - log := zerolog.Nop() - err = proxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), test.args.connectionType == connection.TypeWebsocket) + err = proxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), test.args.connectionType == connection.TypeWebsocket) } cancel() diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 2abf09f5..4f208dd4 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -557,7 +557,6 @@ func ServeH2mux( connIndex, config.Observer, gracefulShutdownC, - config.Log, ) if err != nil { if !recoverable { diff --git a/tracing/tracing.go b/tracing/tracing.go index 7eba4c52..e9172cd5 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -8,7 +8,6 @@ import ( "net/http" "os" "runtime" - "strings" "github.com/rs/zerolog" otelContrib "go.opentelemetry.io/contrib/propagators/jaeger" @@ -34,9 +33,6 @@ const ( MaxErrorDescriptionLen = 100 traceHttpStatusCodeKey = "upstreamStatusCode" - - traceID128bitsWidth = 128 / 4 - separator = ":" ) var ( @@ -70,50 +66,22 @@ func Init(version string) { cloudflaredVersionAttribute = semconv.ProcessRuntimeVersionKey.String(version) } -type TracedHTTPRequest struct { +type TracedRequest struct { *http.Request - *cfdTracer -} - -// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. -func NewTracedHTTPRequest(req *http.Request, log *zerolog.Logger) *TracedHTTPRequest { - ctx, exists := extractTrace(req) - if !exists { - return &TracedHTTPRequest{req, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} - } - return &TracedHTTPRequest{req.WithContext(ctx), newCfdTracer(ctx, log)} -} - -func (tr *TracedHTTPRequest) ToTracedContext() *TracedContext { - return &TracedContext{tr.Context(), tr.cfdTracer} -} - -type TracedContext struct { - context.Context - *cfdTracer -} - -// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. -func NewTracedContext(ctx context.Context, traceContext string, log *zerolog.Logger) *TracedContext { - ctx, exists := extractTraceFromString(ctx, traceContext) - if !exists { - return &TracedContext{ctx, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} - } - return &TracedContext{ctx, newCfdTracer(ctx, log)} -} - -type cfdTracer struct { trace.TracerProvider exporter InMemoryClient - log *zerolog.Logger } -// NewCfdTracer creates a new tracer for the current request context. -func newCfdTracer(ctx context.Context, log *zerolog.Logger) *cfdTracer { +// NewTracedRequest creates a new tracer for the current request context. +func NewTracedRequest(req *http.Request) *TracedRequest { + ctx, exists := extractTrace(req) + if !exists { + return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + } mc := new(InMemoryOtlpClient) - exp, err := otlptrace.New(ctx, mc) + exp, err := otlptrace.New(req.Context(), mc) if err != nil { - return &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log} + return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} } tp := tracesdk.NewTracerProvider( // We want to dump to in-memory exporter immediately @@ -130,43 +98,36 @@ func newCfdTracer(ctx context.Context, log *zerolog.Logger) *cfdTracer { )), ) - return &cfdTracer{tp, mc, log} + return &TracedRequest{req.WithContext(ctx), tp, mc} } -func (cft *cfdTracer) Tracer() trace.Tracer { +func (cft *TracedRequest) Tracer() trace.Tracer { return cft.TracerProvider.Tracer(tracerInstrumentName) } -// GetSpans returns the spans as base64 encoded string of protobuf otlp traces. -func (cft *cfdTracer) GetSpans() (enc string) { +// Spans returns the spans as base64 encoded protobuf otlp traces. +func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { + if headers == nil { + log.Error().Msgf("provided headers map is nil") + return + } + enc, err := cft.exporter.Spans() switch err { case nil: break case errNoTraces: - cft.log.Error().Err(err).Msgf("expected traces to be available") + log.Error().Err(err).Msgf("expected traces to be available") return case errNoopTracer: return // noop tracer has no traces default: - cft.log.Error().Err(err) + log.Error().Err(err) return } - return -} - -// AddSpans assigns spans as base64 encoded protobuf otlp traces to provided -// HTTP headers. -func (cft *cfdTracer) AddSpans(headers http.Header) { - if headers == nil { - cft.log.Error().Msgf("provided headers map is nil") - return - } - - enc := cft.GetSpans() // No need to add header if no traces if enc == "" { - cft.log.Error().Msgf("no traces provided and no error from exporter") + log.Error().Msgf("no traces provided and no error from exporter") return } @@ -205,33 +166,6 @@ func endSpan(span trace.Span, upstreamStatusCode int, spanStatusCode codes.Code, span.End() } -// extractTraceFromString will extract the trace information from the provided -// propagated trace string context. -func extractTraceFromString(ctx context.Context, trace string) (context.Context, bool) { - if trace == "" { - return ctx, false - } - // Jaeger specific separator - parts := strings.Split(trace, separator) - if len(parts) != 4 { - return ctx, false - } - if parts[0] == "" { - return ctx, false - } - // Correctly left pad the trace to a length of 32 - if len(parts[0]) < traceID128bitsWidth { - left := traceID128bitsWidth - len(parts[0]) - parts[0] = strings.Repeat("0", left) + parts[0] - trace = strings.Join(parts, separator) - } - - // Override the 'cf-trace-id' as 'uber-trace-id' so the jaeger propagator can extract it. - traceHeader := map[string]string{TracerContextNameOverride: trace} - remoteCtx := otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(traceHeader)) - return remoteCtx, true -} - // extractTrace attempts to check for a cf-trace-id from a request and return the // trace context with the provided http.Request. func extractTrace(req *http.Request) (context.Context, bool) { diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go index 5750056e..68a272fc 100644 --- a/tracing/tracing_test.go +++ b/tracing/tracing_test.go @@ -14,42 +14,38 @@ import ( ) func TestNewCfTracer(t *testing.T) { - log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedHTTPRequest(req, &log) + tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerMultiple(t *testing.T) { - log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedHTTPRequest(req, &log) + tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerNilHeader(t *testing.T) { - log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header[http.CanonicalHeaderKey(TracerContextName)] = nil - tr := NewTracedHTTPRequest(req, &log) + tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) } func TestNewCfTracerInvalidHeaders(t *testing.T) { - log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) for _, test := range [][]string{nil, {""}} { req.Header[http.CanonicalHeaderKey(TracerContextName)] = test - tr := NewTracedHTTPRequest(req, &log) + tr := NewTracedRequest(req) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) @@ -57,10 +53,9 @@ func TestNewCfTracerInvalidHeaders(t *testing.T) { } func TestAddingSpansWithNilMap(t *testing.T) { - log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedHTTPRequest(req, &log) + tr := NewTracedRequest(req) exporter := tr.exporter.(*InMemoryOtlpClient) @@ -70,5 +65,5 @@ func TestAddingSpansWithNilMap(t *testing.T) { assert.NoError(t, err) // a panic shouldn't occur - tr.AddSpans(nil) + tr.AddSpans(nil, &zerolog.Logger{}) } From b9cba7f2aed644d0ac06468d63aaa82007980411 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Tue, 26 Jul 2022 14:00:53 -0700 Subject: [PATCH 145/238] TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span (cherry picked from commit f48a7cd3dd6c59d62ab0fd9c66b2b5f4e91e923b) --- connection/connection.go | 19 ++++-- connection/connection_test.go | 4 +- connection/h2mux.go | 4 +- connection/h2mux_test.go | 2 +- connection/http2.go | 2 +- connection/quic.go | 24 +++++-- connection/quic_test.go | 9 ++- orchestration/orchestrator_test.go | 4 +- proxy/proxy.go | 22 ++++-- proxy/proxy_test.go | 16 +++-- supervisor/tunnel.go | 1 + tracing/tracing.go | 104 +++++++++++++++++++++++------ tracing/tracing_test.go | 17 +++-- 13 files changed, 166 insertions(+), 62 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index bcc24b64..5200f4bb 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -123,23 +123,24 @@ func (t Type) String() string { // OriginProxy is how data flows from cloudflared to the origin services running behind it. type OriginProxy interface { - ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error + ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error ProxyTCP(ctx context.Context, rwa ReadWriteAcker, req *TCPRequest) error } // TCPRequest defines the input format needed to perform a TCP proxy. type TCPRequest struct { - Dest string - CFRay string - LBProbe bool - FlowID string + Dest string + CFRay string + LBProbe bool + FlowID string + CfTraceID string } // ReadWriteAcker is a readwriter with the ability to Acknowledge to the downstream (edge) that the origin has // accepted the connection. type ReadWriteAcker interface { io.ReadWriter - AckConnection() error + AckConnection(tracePropagation string) error } // HTTPResponseReadWriteAcker is an HTTP implementation of ReadWriteAcker. @@ -168,7 +169,7 @@ func (h *HTTPResponseReadWriteAcker) Write(p []byte) (int, error) { // AckConnection acks an HTTP connection by sending a switch protocols status code that enables the caller to // upgrade to streams. -func (h *HTTPResponseReadWriteAcker) AckConnection() error { +func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) error { resp := &http.Response{ Status: switchingProtocolText, StatusCode: http.StatusSwitchingProtocols, @@ -179,6 +180,10 @@ func (h *HTTPResponseReadWriteAcker) AckConnection() error { resp.Header = websocket.NewResponseHeader(h.req) } + if tracePropagation != "" { + resp.Header.Add(tracing.CanonicalCloudflaredTracingHeader, tracePropagation) + } + return h.w.WriteRespHeaders(resp.StatusCode, resp.Header) } diff --git a/connection/connection_test.go b/connection/connection_test.go index b8c3b674..ae37db75 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -30,6 +30,8 @@ var ( testLargeResp = make([]byte, largeFileSize) ) +var _ ReadWriteAcker = (*HTTPResponseReadWriteAcker)(nil) + type testRequest struct { name string endpoint string @@ -60,7 +62,7 @@ type mockOriginProxy struct{} func (moc *mockOriginProxy) ProxyHTTP( w ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, isWebsocket bool, ) error { req := tr.Request diff --git a/connection/h2mux.go b/connection/h2mux.go index 0bd35cbe..b78b433f 100644 --- a/connection/h2mux.go +++ b/connection/h2mux.go @@ -69,6 +69,7 @@ func NewH2muxConnection( connIndex uint8, observer *Observer, gracefulShutdownC <-chan struct{}, + log *zerolog.Logger, ) (*h2muxConnection, error, bool) { h := &h2muxConnection{ orchestrator: orchestrator, @@ -79,6 +80,7 @@ func NewH2muxConnection( observer: observer, gracefulShutdownC: gracefulShutdownC, newRPCClientFunc: newRegistrationRPCClient, + log: log, } // Establish a muxed connection with the edge @@ -234,7 +236,7 @@ func (h *h2muxConnection) ServeStream(stream *h2mux.MuxedStream) error { return err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), sourceConnectionType == TypeWebsocket) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, h.log), sourceConnectionType == TypeWebsocket) if err != nil { respWriter.WriteErrorResponse() } diff --git a/connection/h2mux_test.go b/connection/h2mux_test.go index cf2337ec..53c28227 100644 --- a/connection/h2mux_test.go +++ b/connection/h2mux_test.go @@ -48,7 +48,7 @@ func newH2MuxConnection(t require.TestingT) (*h2muxConnection, *h2mux.Muxer) { }() var connIndex = uint8(0) testObserver := NewObserver(&log, &log) - h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil) + h2muxConn, err, _ := NewH2muxConnection(testOrchestrator, testGracePeriod, testMuxerConfig, originConn, connIndex, testObserver, nil, &log) require.NoError(t, err) return h2muxConn, <-edgeMuxChan } diff --git a/connection/http2.go b/connection/http2.go index f7ead1f0..1b78d748 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -132,7 +132,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { case TypeWebsocket, TypeHTTP: stripWebsocketUpgradeHeader(r) // Check for tracing on request - tr := tracing.NewTracedRequest(r) + tr := tracing.NewTracedHTTPRequest(r, c.log) if err := originProxy.ProxyHTTP(respWriter, tr, connType == TypeWebsocket); err != nil { err := fmt.Errorf("Failed to proxy HTTP: %w", err) c.log.Error().Err(err) diff --git a/connection/quic.go b/connection/quic.go index 7adc03c2..12e1d2b6 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -197,7 +197,7 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R switch request.Type { case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket: - tracedReq, err := buildHTTPRequest(ctx, request, stream) + tracedReq, err := buildHTTPRequest(ctx, request, stream, q.logger) if err != nil { return err } @@ -208,8 +208,9 @@ func (q *QUICConnection) dispatchRequest(ctx context.Context, stream *quicpogs.R rwa := &streamReadWriteAcker{stream} metadata := request.MetadataMap() return originProxy.ProxyTCP(ctx, rwa, &TCPRequest{ - Dest: request.Dest, - FlowID: metadata[QUICMetadataFlowID], + Dest: request.Dest, + FlowID: metadata[QUICMetadataFlowID], + CfTraceID: metadata[tracing.TracerContextName], }) } return nil @@ -296,8 +297,12 @@ type streamReadWriteAcker struct { } // AckConnection acks response back to the proxy. -func (s *streamReadWriteAcker) AckConnection() error { - return s.WriteConnectResponseData(nil) +func (s *streamReadWriteAcker) AckConnection(tracePropagation string) error { + metadata := quicpogs.Metadata{ + Key: tracing.CanonicalCloudflaredTracingHeader, + Val: tracePropagation, + } + return s.WriteConnectResponseData(nil, metadata) } // httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC. @@ -325,7 +330,12 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) { hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } -func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*tracing.TracedRequest, error) { +func buildHTTPRequest( + ctx context.Context, + connectRequest *quicpogs.ConnectRequest, + body io.ReadCloser, + log *zerolog.Logger, +) (*tracing.TracedHTTPRequest, error) { metadata := connectRequest.MetadataMap() dest := connectRequest.Dest method := metadata[HTTPMethodKey] @@ -367,7 +377,7 @@ func buildHTTPRequest(ctx context.Context, connectRequest *quicpogs.ConnectReque stripWebsocketUpgradeHeader(req) // Check for tracing on request - tracedReq := tracing.NewTracedRequest(req) + tracedReq := tracing.NewTracedHTTPRequest(req, log) return tracedReq, err } diff --git a/connection/quic_test.go b/connection/quic_test.go index 33da98ac..2557e67b 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -36,6 +36,8 @@ var ( } ) +var _ ReadWriteAcker = (*streamReadWriteAcker)(nil) + // TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol. // It also serves as a demonstration for communication with the QUIC connection started by a cloudflared. func TestQUICServer(t *testing.T) { @@ -220,7 +222,7 @@ func quicServer( type mockOriginProxyWithRequest struct{} -func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedRequest, isWebsocket bool) error { +func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, tr *tracing.TracedHTTPRequest, isWebsocket bool) error { // These are a series of crude tests to ensure the headers and http related data is transferred from // metadata. r := tr.Request @@ -475,9 +477,10 @@ func TestBuildHTTPRequest(t *testing.T) { }, } + log := zerolog.Nop() for _, test := range tests { t.Run(test.name, func(t *testing.T) { - req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body) + req, err := buildHTTPRequest(context.Background(), test.connectRequest, test.body, &log) assert.NoError(t, err) test.req = test.req.WithContext(req.Context()) assert.Equal(t, test.req, req.Request) @@ -486,7 +489,7 @@ func TestBuildHTTPRequest(t *testing.T) { } func (moc *mockOriginProxyWithRequest) ProxyTCP(ctx context.Context, rwa ReadWriteAcker, tcpRequest *TCPRequest) error { - rwa.AckConnection() + rwa.AckConnection("") io.Copy(rwa, rwa) return nil } diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index dfac28a0..dc94d29c 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -355,7 +355,7 @@ func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Respo return nil, err } - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), false) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), false) if err != nil { return nil, err } @@ -604,7 +604,7 @@ func TestPersistentConnection(t *testing.T) { respWriter, err := connection.NewHTTP2RespWriter(req, wsRespReadWriter, connection.TypeWebsocket, &log) require.NoError(t, err) - err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), true) + err = originProxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), true) require.NoError(t, err) }() diff --git a/proxy/proxy.go b/proxy/proxy.go index 19f14778..0c2c21c0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -63,7 +63,7 @@ func NewOriginProxy( // a simple roundtrip or a tcp/websocket dial depending on ingres rule setup. func (p *Proxy) ProxyHTTP( w connection.ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, isWebsocket bool, ) error { incrementRequests() @@ -108,7 +108,7 @@ func (p *Proxy) ProxyHTTP( } rws := connection.NewHTTPResponseReadWriterAcker(w, req) - if err := p.proxyStream(req.Context(), rws, dest, originProxy); err != nil { + if err := p.proxyStream(tr.ToTracedContext(), rws, dest, originProxy); err != nil { rule, srv := ruleField(p.ingressRules, ruleNum) p.logRequestError(err, cfRay, "", rule, srv) return err @@ -137,9 +137,11 @@ func (p *Proxy) ProxyTCP( serveCtx, cancel := context.WithCancel(ctx) defer cancel() + tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, p.log) + p.log.Debug().Str(LogFieldFlowID, req.FlowID).Msg("tcp proxy stream started") - if err := p.proxyStream(serveCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { + if err := p.proxyStream(tracedCtx, rwa, req.Dest, p.warpRouting.Proxy); err != nil { p.logRequestError(err, req.CFRay, req.FlowID, "", ingress.ServiceWarpRouting) return err } @@ -160,7 +162,7 @@ func ruleField(ing ingress.Ingress, ruleNum int) (ruleID string, srv string) { // ProxyHTTPRequest proxies requests of underlying type http and websocket to the origin service. func (p *Proxy) proxyHTTPRequest( w connection.ResponseWriter, - tr *tracing.TracedRequest, + tr *tracing.TracedHTTPRequest, httpService ingress.HTTPOriginProxy, isWebsocket bool, disableChunkedEncoding bool, @@ -211,7 +213,7 @@ func (p *Proxy) proxyHTTPRequest( } // Add spans to response header (if available) - tr.AddSpans(resp.Header, p.log) + tr.AddSpans(resp.Header) err = w.WriteRespHeaders(resp.StatusCode, resp.Header) if err != nil { @@ -248,17 +250,23 @@ func (p *Proxy) proxyHTTPRequest( // proxyStream proxies type TCP and other underlying types if the connection is defined as a stream oriented // ingress rule. func (p *Proxy) proxyStream( - ctx context.Context, + tr *tracing.TracedContext, rwa connection.ReadWriteAcker, dest string, connectionProxy ingress.StreamBasedOriginProxy, ) error { + ctx := tr.Context + _, connectSpan := tr.Tracer().Start(ctx, "stream_connect") originConn, err := connectionProxy.EstablishConnection(ctx, dest) if err != nil { + tracing.EndWithErrorStatus(connectSpan, err) return err } + connectSpan.End() - if err := rwa.AckConnection(); err != nil { + encodedSpans := tr.GetSpans() + + if err := rwa.AckConnection(encodedSpans); err != nil { return err } diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 0e0019f1..f85281b3 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -157,7 +157,8 @@ func testProxyHTTP(proxy connection.OriginProxy) func(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) for _, tag := range testTags { assert.Equal(t, tag.Value, req.Header.Get(TagHeaderNamePrefix+tag.Name)) @@ -184,7 +185,8 @@ func testProxyWebsocket(proxy connection.OriginProxy) func(t *testing.T) { errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), true) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), true) require.NoError(t, err) require.Equal(t, http.StatusSwitchingProtocols, responseWriter.Code) @@ -245,7 +247,8 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + log := zerolog.Nop() + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) require.Equal(t, http.StatusOK, responseWriter.Code) @@ -357,7 +360,7 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat req, err := http.NewRequest(http.MethodGet, test.url, nil) require.NoError(t, err) - err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false) + err = proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false) require.NoError(t, err) assert.Equal(t, test.expectedStatus, responseWriter.Code) @@ -404,7 +407,7 @@ func TestProxyError(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil) assert.NoError(t, err) - assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedRequest(req), false)) + assert.Error(t, proxy.ProxyHTTP(responseWriter, tracing.NewTracedHTTPRequest(req, &log), false)) } type replayer struct { @@ -682,7 +685,8 @@ func TestConnections(t *testing.T) { rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { - err = proxy.ProxyHTTP(respWriter, tracing.NewTracedRequest(req), test.args.connectionType == connection.TypeWebsocket) + log := zerolog.Nop() + err = proxy.ProxyHTTP(respWriter, tracing.NewTracedHTTPRequest(req, &log), test.args.connectionType == connection.TypeWebsocket) } cancel() diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 4f208dd4..2abf09f5 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -557,6 +557,7 @@ func ServeH2mux( connIndex, config.Observer, gracefulShutdownC, + config.Log, ) if err != nil { if !recoverable { diff --git a/tracing/tracing.go b/tracing/tracing.go index e9172cd5..1788b0ef 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "runtime" + "strings" "github.com/rs/zerolog" otelContrib "go.opentelemetry.io/contrib/propagators/jaeger" @@ -33,6 +34,9 @@ const ( MaxErrorDescriptionLen = 100 traceHttpStatusCodeKey = "upstreamStatusCode" + + traceID128bitsWidth = 128 / 4 + separator = ":" ) var ( @@ -66,22 +70,50 @@ func Init(version string) { cloudflaredVersionAttribute = semconv.ProcessRuntimeVersionKey.String(version) } -type TracedRequest struct { +type TracedHTTPRequest struct { *http.Request - trace.TracerProvider - exporter InMemoryClient + *cfdTracer } -// NewTracedRequest creates a new tracer for the current request context. -func NewTracedRequest(req *http.Request) *TracedRequest { +// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. +func NewTracedHTTPRequest(req *http.Request, log *zerolog.Logger) *TracedHTTPRequest { ctx, exists := extractTrace(req) if !exists { - return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + return &TracedHTTPRequest{req, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} } + return &TracedHTTPRequest{req.WithContext(ctx), newCfdTracer(ctx, log)} +} + +func (tr *TracedHTTPRequest) ToTracedContext() *TracedContext { + return &TracedContext{tr.Context(), tr.cfdTracer} +} + +type TracedContext struct { + context.Context + *cfdTracer +} + +// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. +func NewTracedContext(ctx context.Context, traceContext string, log *zerolog.Logger) *TracedContext { + ctx, exists := extractTraceFromString(ctx, traceContext) + if !exists { + return &TracedContext{ctx, &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log}} + } + return &TracedContext{ctx, newCfdTracer(ctx, log)} +} + +type cfdTracer struct { + trace.TracerProvider + exporter InMemoryClient + log *zerolog.Logger +} + +// NewCfdTracer creates a new tracer for the current request context. +func newCfdTracer(ctx context.Context, log *zerolog.Logger) *cfdTracer { mc := new(InMemoryOtlpClient) - exp, err := otlptrace.New(req.Context(), mc) + exp, err := otlptrace.New(ctx, mc) if err != nil { - return &TracedRequest{req, trace.NewNoopTracerProvider(), &NoopOtlpClient{}} + return &cfdTracer{trace.NewNoopTracerProvider(), &NoopOtlpClient{}, log} } tp := tracesdk.NewTracerProvider( // We want to dump to in-memory exporter immediately @@ -98,36 +130,41 @@ func NewTracedRequest(req *http.Request) *TracedRequest { )), ) - return &TracedRequest{req.WithContext(ctx), tp, mc} + return &cfdTracer{tp, mc, log} } -func (cft *TracedRequest) Tracer() trace.Tracer { +func (cft *cfdTracer) Tracer() trace.Tracer { return cft.TracerProvider.Tracer(tracerInstrumentName) } -// Spans returns the spans as base64 encoded protobuf otlp traces. -func (cft *TracedRequest) AddSpans(headers http.Header, log *zerolog.Logger) { - if headers == nil { - log.Error().Msgf("provided headers map is nil") - return - } - +// GetSpans returns the spans as base64 encoded string of protobuf otlp traces. +func (cft *cfdTracer) GetSpans() (enc string) { enc, err := cft.exporter.Spans() switch err { case nil: break case errNoTraces: - log.Error().Err(err).Msgf("expected traces to be available") + cft.log.Trace().Err(err).Msgf("expected traces to be available") return case errNoopTracer: return // noop tracer has no traces default: - log.Error().Err(err) + cft.log.Debug().Err(err) return } + return +} + +// AddSpans assigns spans as base64 encoded protobuf otlp traces to provided +// HTTP headers. +func (cft *cfdTracer) AddSpans(headers http.Header) { + if headers == nil { + return + } + + enc := cft.GetSpans() // No need to add header if no traces if enc == "" { - log.Error().Msgf("no traces provided and no error from exporter") return } @@ -166,6 +203,33 @@ func endSpan(span trace.Span, upstreamStatusCode int, spanStatusCode codes.Code, span.End() } +// extractTraceFromString will extract the trace information from the provided +// propagated trace string context. +func extractTraceFromString(ctx context.Context, trace string) (context.Context, bool) { + if trace == "" { + return ctx, false + } + // Jaeger specific separator + parts := strings.Split(trace, separator) + if len(parts) != 4 { + return ctx, false + } + if parts[0] == "" { + return ctx, false + } + // Correctly left pad the trace to a length of 32 + if len(parts[0]) < traceID128bitsWidth { + left := traceID128bitsWidth - len(parts[0]) + parts[0] = strings.Repeat("0", left) + parts[0] + trace = strings.Join(parts, separator) + } + + // Override the 'cf-trace-id' as 'uber-trace-id' so the jaeger propagator can extract it. + traceHeader := map[string]string{TracerContextNameOverride: trace} + remoteCtx := otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(traceHeader)) + return remoteCtx, true +} + // extractTrace attempts to check for a cf-trace-id from a request and return the // trace context with the provided http.Request. func extractTrace(req *http.Request) (context.Context, bool) { diff --git a/tracing/tracing_test.go b/tracing/tracing_test.go index 68a272fc..5750056e 100644 --- a/tracing/tracing_test.go +++ b/tracing/tracing_test.go @@ -14,38 +14,42 @@ import ( ) func TestNewCfTracer(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerMultiple(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1") req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, tracesdk.NewTracerProvider(), tr.TracerProvider) assert.IsType(t, &InMemoryOtlpClient{}, tr.exporter) } func TestNewCfTracerNilHeader(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header[http.CanonicalHeaderKey(TracerContextName)] = nil - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) } func TestNewCfTracerInvalidHeaders(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) for _, test := range [][]string{nil, {""}} { req.Header[http.CanonicalHeaderKey(TracerContextName)] = test - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) assert.NotNil(t, tr) assert.IsType(t, trace.NewNoopTracerProvider(), tr.TracerProvider) assert.IsType(t, &NoopOtlpClient{}, tr.exporter) @@ -53,9 +57,10 @@ func TestNewCfTracerInvalidHeaders(t *testing.T) { } func TestAddingSpansWithNilMap(t *testing.T) { + log := zerolog.Nop() req := httptest.NewRequest("GET", "http://localhost", nil) req.Header.Add(TracerContextName, "14cb070dde8e51fc5ae8514e69ba42ca:b38f1bf5eae406f3:0:1") - tr := NewTracedRequest(req) + tr := NewTracedHTTPRequest(req, &log) exporter := tr.exporter.(*InMemoryOtlpClient) @@ -65,5 +70,5 @@ func TestAddingSpansWithNilMap(t *testing.T) { assert.NoError(t, err) // a panic shouldn't occur - tr.AddSpans(nil, &zerolog.Logger{}) + tr.AddSpans(nil) } From 7a9207a6e1c08fe7689bb04734fbadbe5d535c5c Mon Sep 17 00:00:00 2001 From: Opeyemi Onikute Date: Thu, 4 Aug 2022 11:12:26 +0100 Subject: [PATCH 146/238] EDGEPLAT-3918: build cloudflared for Bookworm Adds bookworm to cfsetup.yaml --- cfsetup.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 130f6bbe..6189cb44 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -2,7 +2,7 @@ pinned_go: &pinned_go go=1.17.10-1 pinned_go_fips: &pinned_go_fips go-boring=1.17.9-1 build_dir: &build_dir /cfsetup_build -default-flavor: buster +default-flavor: bullseye stretch: &stretch build: build_dir: *build_dir @@ -246,6 +246,7 @@ stretch: &stretch buster: *stretch bullseye: *stretch +bookworm: *stretch centos-7: publish-rpm: build_dir: *build_dir From 046a30e3c7f212e36caf2e9a388989949a16f16b Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 8 Aug 2022 15:49:10 +0100 Subject: [PATCH 147/238] TUN-6637: Upgrade go version and quic-go --- Dockerfile | 2 +- cfsetup.yaml | 10 +- dev.Dockerfile | 2 +- go.mod | 17 +- go.sum | 28 +- .../lucas-clemente/quic-go/README.md | 5 +- .../lucas-clemente/quic-go/client.go | 2 + .../lucas-clemente/quic-go/connection.go | 8 + .../lucas-clemente/quic-go/interface.go | 10 +- .../quic-go/internal/ackhandler/interfaces.go | 4 + .../ackhandler/sent_packet_handler.go | 4 +- .../ackhandler/sent_packet_history.go | 99 +- .../quic-go/internal/handshake/aead.go | 12 +- .../internal/handshake/crypto_setup.go | 49 +- .../internal/handshake/header_protector.go | 23 +- .../internal/handshake/initial_aead.go | 35 +- .../internal/handshake/updatable_aead.go | 24 +- .../quic-go/internal/protocol/params.go | 3 +- .../quic-go/internal/protocol/version.go | 5 +- .../quic-go/internal/qtls/go118.go | 4 +- .../quic-go/internal/qtls/go119.go | 96 +- .../quic-go/internal/qtls/go120.go | 6 + .../quic-go/internal/wire/extended_header.go | 36 +- .../quic-go/internal/wire/header.go | 39 +- .../internal/wire/transport_parameters.go | 2 +- .../internal/wire/version_negotiation.go | 4 +- .../quic-go/packet_handler_map.go | 1 + .../lucas-clemente/quic-go/quicvarint/io.go | 5 +- .../lucas-clemente/quic-go/receive_stream.go | 10 +- .../lucas-clemente/quic-go/send_stream.go | 8 + .../lucas-clemente/quic-go/server.go | 11 +- .../lucas-clemente/quic-go/sys_conn.go | 10 +- .../quic-go/sys_conn_df_linux.go | 5 +- .../marten-seemann/qtls-go1-17/conn.go | 11 + .../qtls-go1-17/handshake_server_tls13.go | 1 + .../marten-seemann/qtls-go1-17/ticket.go | 15 + .../marten-seemann/qtls-go1-18/conn.go | 11 + .../qtls-go1-18/handshake_server_tls13.go | 1 + .../marten-seemann/qtls-go1-18/ticket.go | 15 + .../marten-seemann/qtls-go1-19/LICENSE | 27 + .../marten-seemann/qtls-go1-19/README.md | 6 + .../marten-seemann/qtls-go1-19/alert.go | 102 + .../marten-seemann/qtls-go1-19/auth.go | 293 +++ .../qtls-go1-19/cipher_suites.go | 693 +++++++ .../marten-seemann/qtls-go1-19/common.go | 1512 ++++++++++++++ .../marten-seemann/qtls-go1-19/conn.go | 1610 ++++++++++++++ .../marten-seemann/qtls-go1-19/cpu.go | 22 + .../marten-seemann/qtls-go1-19/cpu_other.go | 12 + .../qtls-go1-19/handshake_client.go | 1117 ++++++++++ .../qtls-go1-19/handshake_client_tls13.go | 736 +++++++ .../qtls-go1-19/handshake_messages.go | 1843 +++++++++++++++++ .../qtls-go1-19/handshake_server.go | 905 ++++++++ .../qtls-go1-19/handshake_server_tls13.go | 900 ++++++++ .../qtls-go1-19/key_agreement.go | 357 ++++ .../qtls-go1-19/key_schedule.go | 199 ++ .../marten-seemann/qtls-go1-19/notboring.go | 18 + .../marten-seemann/qtls-go1-19/prf.go | 283 +++ .../marten-seemann/qtls-go1-19/ticket.go | 274 +++ .../marten-seemann/qtls-go1-19/tls.go | 362 ++++ .../marten-seemann/qtls-go1-19/unsafe.go | 96 + vendor/golang.org/x/net/bpf/doc.go | 6 +- vendor/golang.org/x/net/context/context.go | 6 +- vendor/golang.org/x/net/context/go17.go | 10 +- vendor/golang.org/x/net/context/pre_go17.go | 10 +- .../golang.org/x/net/http/httpguts/httplex.go | 54 +- .../x/net/http2/client_conn_pool.go | 3 +- vendor/golang.org/x/net/http2/errors.go | 2 +- vendor/golang.org/x/net/http2/frame.go | 3 +- vendor/golang.org/x/net/http2/go118.go | 17 + .../golang.org/x/net/http2/hpack/huffman.go | 87 +- vendor/golang.org/x/net/http2/http2.go | 14 +- vendor/golang.org/x/net/http2/not_go118.go | 17 + vendor/golang.org/x/net/http2/server.go | 135 +- vendor/golang.org/x/net/http2/transport.go | 59 +- .../x/net/http2/writesched_priority.go | 9 +- vendor/golang.org/x/net/idna/trieval.go | 34 +- .../x/net/internal/socket/cmsghdr_unix.go | 4 +- .../net/internal/socket/cmsghdr_zos_s390x.go | 14 - .../x/net/internal/socket/mmsghdr_unix.go | 80 +- .../x/net/internal/socket/rawconn_mmsg.go | 37 +- .../x/net/internal/socket/zsys_linux_ppc.go | 32 +- vendor/golang.org/x/net/ipv4/doc.go | 12 +- vendor/golang.org/x/net/ipv6/doc.go | 12 +- .../golang.org/x/net/websocket/websocket.go | 5 +- vendor/golang.org/x/sys/cpu/byteorder.go | 1 + vendor/golang.org/x/sys/cpu/cpu.go | 4 +- vendor/golang.org/x/sys/cpu/cpu_loong64.go | 13 + .../golang.org/x/sys/cpu/syscall_aix_gccgo.go | 2 +- vendor/golang.org/x/sys/execabs/execabs.go | 2 +- .../golang.org/x/sys/execabs/execabs_go118.go | 12 + .../golang.org/x/sys/execabs/execabs_go119.go | 15 + vendor/golang.org/x/sys/plan9/syscall.go | 1 + .../golang.org/x/sys/plan9/syscall_plan9.go | 10 + .../golang.org/x/sys/unix/asm_linux_loong64.s | 54 + vendor/golang.org/x/sys/unix/endian_little.go | 4 +- vendor/golang.org/x/sys/unix/ifreq_linux.go | 9 +- vendor/golang.org/x/sys/unix/ioctl_linux.go | 23 + vendor/golang.org/x/sys/unix/mkerrors.sh | 7 + vendor/golang.org/x/sys/unix/syscall_aix.go | 22 +- vendor/golang.org/x/sys/unix/syscall_bsd.go | 35 +- .../golang.org/x/sys/unix/syscall_darwin.go | 47 +- .../x/sys/unix/syscall_dragonfly.go | 11 +- .../golang.org/x/sys/unix/syscall_freebsd.go | 9 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 204 +- .../x/sys/unix/syscall_linux_386.go | 12 +- .../x/sys/unix/syscall_linux_alarm.go | 14 + .../x/sys/unix/syscall_linux_amd64.go | 6 +- .../x/sys/unix/syscall_linux_arm.go | 5 +- .../x/sys/unix/syscall_linux_arm64.go | 6 +- .../x/sys/unix/syscall_linux_loong64.go | 191 ++ .../x/sys/unix/syscall_linux_mips64x.go | 5 +- .../x/sys/unix/syscall_linux_mipsx.go | 5 +- .../x/sys/unix/syscall_linux_ppc.go | 5 +- .../x/sys/unix/syscall_linux_ppc64x.go | 5 +- .../x/sys/unix/syscall_linux_riscv64.go | 5 +- .../x/sys/unix/syscall_linux_s390x.go | 13 +- .../x/sys/unix/syscall_linux_sparc64.go | 5 +- .../golang.org/x/sys/unix/syscall_netbsd.go | 9 +- .../golang.org/x/sys/unix/syscall_openbsd.go | 11 +- .../golang.org/x/sys/unix/syscall_solaris.go | 152 +- vendor/golang.org/x/sys/unix/syscall_unix.go | 51 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 53 +- .../x/sys/unix/zerrors_linux_386.go | 2 +- .../x/sys/unix/zerrors_linux_amd64.go | 2 +- .../x/sys/unix/zerrors_linux_arm.go | 2 +- .../x/sys/unix/zerrors_linux_arm64.go | 2 +- .../x/sys/unix/zerrors_linux_loong64.go | 818 ++++++++ .../x/sys/unix/zerrors_linux_mips.go | 2 +- .../x/sys/unix/zerrors_linux_mips64.go | 2 +- .../x/sys/unix/zerrors_linux_mips64le.go | 2 +- .../x/sys/unix/zerrors_linux_mipsle.go | 2 +- .../x/sys/unix/zerrors_linux_ppc.go | 2 +- .../x/sys/unix/zerrors_linux_ppc64.go | 2 +- .../x/sys/unix/zerrors_linux_ppc64le.go | 2 +- .../x/sys/unix/zerrors_linux_riscv64.go | 2 +- .../x/sys/unix/zerrors_linux_s390x.go | 2 +- .../x/sys/unix/zerrors_linux_sparc64.go | 2 +- .../golang.org/x/sys/unix/zsyscall_aix_ppc.go | 4 +- .../x/sys/unix/zsyscall_aix_ppc64.go | 4 +- .../x/sys/unix/zsyscall_darwin_amd64.go | 41 +- .../x/sys/unix/zsyscall_darwin_amd64.s | 14 +- .../x/sys/unix/zsyscall_darwin_arm64.go | 41 +- .../x/sys/unix/zsyscall_darwin_arm64.s | 14 +- .../x/sys/unix/zsyscall_freebsd_386.go | 4 +- .../x/sys/unix/zsyscall_freebsd_amd64.go | 4 +- .../x/sys/unix/zsyscall_freebsd_arm.go | 4 +- .../x/sys/unix/zsyscall_freebsd_arm64.go | 4 +- .../golang.org/x/sys/unix/zsyscall_linux.go | 119 ++ .../x/sys/unix/zsyscall_linux_386.go | 17 +- .../x/sys/unix/zsyscall_linux_amd64.go | 39 +- .../x/sys/unix/zsyscall_linux_arm.go | 15 +- .../x/sys/unix/zsyscall_linux_arm64.go | 26 +- .../x/sys/unix/zsyscall_linux_loong64.go | 552 +++++ .../x/sys/unix/zsyscall_linux_mips.go | 28 +- .../x/sys/unix/zsyscall_linux_mips64.go | 28 +- .../x/sys/unix/zsyscall_linux_mips64le.go | 15 +- .../x/sys/unix/zsyscall_linux_mipsle.go | 28 +- .../x/sys/unix/zsyscall_linux_ppc.go | 28 +- .../x/sys/unix/zsyscall_linux_ppc64.go | 28 +- .../x/sys/unix/zsyscall_linux_ppc64le.go | 28 +- .../x/sys/unix/zsyscall_linux_riscv64.go | 15 +- .../x/sys/unix/zsyscall_linux_s390x.go | 17 +- .../x/sys/unix/zsyscall_linux_sparc64.go | 28 +- .../x/sys/unix/zsyscall_netbsd_386.go | 4 +- .../x/sys/unix/zsyscall_netbsd_amd64.go | 4 +- .../x/sys/unix/zsyscall_netbsd_arm.go | 4 +- .../x/sys/unix/zsyscall_netbsd_arm64.go | 4 +- .../x/sys/unix/zsyscall_openbsd_386.go | 4 +- .../x/sys/unix/zsyscall_openbsd_amd64.go | 4 +- .../x/sys/unix/zsyscall_openbsd_arm.go | 4 +- .../x/sys/unix/zsyscall_openbsd_arm64.go | 4 +- .../x/sys/unix/zsyscall_openbsd_mips64.go | 4 +- .../x/sys/unix/zsyscall_solaris_amd64.go | 16 +- .../x/sys/unix/zsysnum_linux_386.go | 1 + .../x/sys/unix/zsysnum_linux_amd64.go | 1 + .../x/sys/unix/zsysnum_linux_arm.go | 1 + .../x/sys/unix/zsysnum_linux_arm64.go | 1 + .../x/sys/unix/zsysnum_linux_loong64.go | 313 +++ .../x/sys/unix/zsysnum_linux_mips.go | 1 + .../x/sys/unix/zsysnum_linux_mips64.go | 1 + .../x/sys/unix/zsysnum_linux_mips64le.go | 1 + .../x/sys/unix/zsysnum_linux_mipsle.go | 1 + .../x/sys/unix/zsysnum_linux_ppc.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64.go | 1 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 1 + .../x/sys/unix/zsysnum_linux_riscv64.go | 1 + .../x/sys/unix/zsysnum_linux_s390x.go | 1 + .../x/sys/unix/zsysnum_linux_sparc64.go | 1 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 1525 +++++++++++++- .../golang.org/x/sys/unix/ztypes_linux_386.go | 15 +- .../x/sys/unix/ztypes_linux_amd64.go | 16 +- .../golang.org/x/sys/unix/ztypes_linux_arm.go | 15 +- .../x/sys/unix/ztypes_linux_arm64.go | 16 +- .../x/sys/unix/ztypes_linux_loong64.go | 679 ++++++ .../x/sys/unix/ztypes_linux_mips.go | 15 +- .../x/sys/unix/ztypes_linux_mips64.go | 16 +- .../x/sys/unix/ztypes_linux_mips64le.go | 16 +- .../x/sys/unix/ztypes_linux_mipsle.go | 15 +- .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 15 +- .../x/sys/unix/ztypes_linux_ppc64.go | 16 +- .../x/sys/unix/ztypes_linux_ppc64le.go | 16 +- .../x/sys/unix/ztypes_linux_riscv64.go | 16 +- .../x/sys/unix/ztypes_linux_s390x.go | 20 +- .../x/sys/unix/ztypes_linux_sparc64.go | 16 +- .../x/sys/unix/ztypes_openbsd_386.go | 8 +- .../x/sys/unix/ztypes_openbsd_amd64.go | 8 +- .../x/sys/unix/ztypes_openbsd_arm.go | 8 +- .../x/sys/unix/ztypes_openbsd_arm64.go | 8 +- .../x/sys/unix/ztypes_openbsd_mips64.go | 8 +- .../golang.org/x/sys/windows/exec_windows.go | 10 +- .../golang.org/x/sys/windows/registry/key.go | 1 - .../x/sys/windows/svc/eventlog/log.go | 1 - .../golang.org/x/sys/windows/svc/mgr/mgr.go | 1 - .../golang.org/x/sys/windows/svc/service.go | 1 - .../x/sys/windows/syscall_windows.go | 43 +- .../x/sys/windows/zsyscall_windows.go | 4 +- vendor/golang.org/x/term/codereview.cfg | 1 + vendor/golang.org/x/term/term.go | 2 + vendor/modules.txt | 17 +- 219 files changed, 17578 insertions(+), 1040 deletions(-) create mode 100644 vendor/github.com/lucas-clemente/quic-go/internal/qtls/go120.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/LICENSE create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/README.md create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/alert.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/auth.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/cipher_suites.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/common.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/conn.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/cpu.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/cpu_other.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/key_schedule.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/notboring.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/prf.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/ticket.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/tls.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/unsafe.go create mode 100644 vendor/golang.org/x/net/http2/go118.go create mode 100644 vendor/golang.org/x/net/http2/not_go118.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_loong64.go create mode 100644 vendor/golang.org/x/sys/execabs/execabs_go118.go create mode 100644 vendor/golang.org/x/sys/execabs/execabs_go119.go create mode 100644 vendor/golang.org/x/sys/unix/asm_linux_loong64.s create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_alarm.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go create mode 100644 vendor/golang.org/x/term/codereview.cfg diff --git a/Dockerfile b/Dockerfile index 65224553..f10076d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # use a builder image for building cloudflare ARG TARGET_GOOS ARG TARGET_GOARCH -FROM golang:1.17.1 as builder +FROM golang:1.18.1 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ TARGET_GOOS=${TARGET_GOOS} \ diff --git a/cfsetup.yaml b/cfsetup.yaml index 6189cb44..96ce9c2a 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -1,5 +1,5 @@ -pinned_go: &pinned_go go=1.17.10-1 -pinned_go_fips: &pinned_go_fips go-boring=1.17.9-1 +pinned_go: &pinned_go go=1.18-1 +pinned_go_fips: &pinned_go_fips go-boring=1.18-1 build_dir: &build_dir /cfsetup_build default-flavor: bullseye @@ -152,7 +152,7 @@ stretch: &stretch - build-essential - gotest-to-teamcity pre-cache: &test_pre_cache - - go get golang.org/x/tools/cmd/goimports + - go install golang.org/x/tools/cmd/goimports@latest post-cache: - export GOOS=linux - export GOARCH=amd64 @@ -255,8 +255,8 @@ centos-7: pre-cache: - yum install -y fakeroot - yum upgrade -y binutils-2.27-44.base.el7.x86_64 - - wget https://go.dev/dl/go1.17.10.linux-amd64.tar.gz -P /tmp/ - - tar -C /usr/local -xzf /tmp/go1.17.10.linux-amd64.tar.gz + - wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz -P /tmp/ + - tar -C /usr/local -xzf /tmp/go1.18.1.linux-amd64.tar.gz post-cache: - export PATH=$PATH:/usr/local/go/bin - export GOOS=linux diff --git a/dev.Dockerfile b/dev.Dockerfile index 64fdc790..f2e34bbb 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17.10 as builder +FROM golang:1.18.1 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 WORKDIR /go/src/github.com/cloudflare/cloudflared/ diff --git a/go.mod b/go.mod index 7c7a66ef..ecda62d2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cloudflare/cloudflared -go 1.17 +go 1.18 require ( github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 @@ -16,7 +16,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/json-iterator/go v1.1.12 - github.com/lucas-clemente/quic-go v0.27.1 + github.com/lucas-clemente/quic-go v0.28.1 github.com/mattn/go-colorable v0.1.8 github.com/miekg/dns v1.1.45 github.com/mitchellh/go-homedir v1.1.0 @@ -34,10 +34,10 @@ require ( go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f - golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 - golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/protobuf v1.28.0 gopkg.in/coreos/go-oidc.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -71,8 +71,9 @@ require ( github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect + github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -99,7 +100,7 @@ require ( replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da +replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e // Avoid 'CVE-2022-21698' replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 diff --git a/go.sum b/go.sum index d3aa3662..9732bc39 100644 --- a/go.sum +++ b/go.sum @@ -103,15 +103,14 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da h1:FmuwbQ8RU/ftTKnfz5diawqvQFH1KDB9wN2Q8S2wqds= -github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= +github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e h1:HanU8Gx2eTN9X0miD0HNdl/doTs08ZLQzlQMIrGVHgk= +github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -338,7 +337,6 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= @@ -399,10 +397,12 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= -github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= -github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= +github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= +github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= +github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32BZqe/LEEnBrWcH/cOqQ= +github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -507,7 +507,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -707,11 +706,10 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs= -golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -815,11 +813,13 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/lucas-clemente/quic-go/README.md b/vendor/github.com/lucas-clemente/quic-go/README.md index b28062d5..fc25e0b4 100644 --- a/vendor/github.com/lucas-clemente/quic-go/README.md +++ b/vendor/github.com/lucas-clemente/quic-go/README.md @@ -5,8 +5,9 @@ [![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go) [![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/) -quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go. -In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem. +quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go, including the [Unreliable Datagram Extension, RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221). It has support for HTTP/3 [RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114). + +In addition the RFCs listed above, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem. ## Guides diff --git a/vendor/github.com/lucas-clemente/quic-go/client.go b/vendor/github.com/lucas-clemente/quic-go/client.go index c29a1c29..be8390e6 100644 --- a/vendor/github.com/lucas-clemente/quic-go/client.go +++ b/vendor/github.com/lucas-clemente/quic-go/client.go @@ -231,6 +231,8 @@ func newClient( ) (*client, error) { if tlsConf == nil { tlsConf = &tls.Config{} + } else { + tlsConf = tlsConf.Clone() } if tlsConf.ServerName == "" { sni := host diff --git a/vendor/github.com/lucas-clemente/quic-go/connection.go b/vendor/github.com/lucas-clemente/quic-go/connection.go index 2f17565a..6495eddf 100644 --- a/vendor/github.com/lucas-clemente/quic-go/connection.go +++ b/vendor/github.com/lucas-clemente/quic-go/connection.go @@ -190,6 +190,7 @@ type connection struct { clientHelloWritten <-chan *wire.TransportParameters earlyConnReadyChan chan struct{} handshakeCompleteChan chan struct{} // is closed when the handshake completes + sentFirstPacket bool handshakeComplete bool handshakeConfirmed bool @@ -1522,6 +1523,12 @@ func (s *connection) handleCloseError(closeErr *closeError) { s.connIDGenerator.RemoveAll() return } + // Don't send out any CONNECTION_CLOSE if this is an error that occurred + // before we even sent out the first packet. + if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket { + s.connIDGenerator.RemoveAll() + return + } connClosePacket, err := s.sendConnectionClose(e) if err != nil { s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) @@ -1763,6 +1770,7 @@ func (s *connection) sendPacket() (bool, error) { if err != nil || packet == nil { return false, err } + s.sentFirstPacket = true s.logCoalescedPacket(packet) for _, p := range packet.packets { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { diff --git a/vendor/github.com/lucas-clemente/quic-go/interface.go b/vendor/github.com/lucas-clemente/quic-go/interface.go index 73154e4a..e0c9f439 100644 --- a/vendor/github.com/lucas-clemente/quic-go/interface.go +++ b/vendor/github.com/lucas-clemente/quic-go/interface.go @@ -23,6 +23,7 @@ const ( VersionDraft29 = protocol.VersionDraft29 // Version1 is RFC 9000 Version1 = protocol.Version1 + Version2 = protocol.Version2 ) // A Token can be used to verify the ownership of the client address. @@ -127,7 +128,6 @@ type SendStream interface { // The Context is canceled as soon as the write-side of the stream is closed. // This happens when Close() or CancelWrite() is called, or when the peer // cancels the read-side of their stream. - // Warning: This API should not be considered stable and might change soon. Context() context.Context // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. @@ -184,18 +184,15 @@ type Connection interface { // The error string will be sent to the peer. CloseWithError(ApplicationErrorCode, string) error // The context is cancelled when the connection is closed. - // Warning: This API should not be considered stable and might change soon. Context() context.Context // ConnectionState returns basic details about the QUIC connection. // It blocks until the handshake completes. // Warning: This API should not be considered stable and might change soon. ConnectionState() ConnectionState - // SendMessage sends a message as a datagram. - // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. + // SendMessage sends a message as a datagram, as specified in RFC 9221. SendMessage([]byte) error - // ReceiveMessage gets a message received in a datagram. - // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. + // ReceiveMessage gets a message received in a datagram, as specified in RFC 9221. ReceiveMessage() ([]byte, error) } @@ -218,7 +215,6 @@ type EarlyConnection interface { type Config struct { // The QUIC versions that can be negotiated. // If not set, it uses all versions available. - // Warning: This API should not be considered stable and will change soon. Versions []VersionNumber // The length of the connection ID in bytes. // It can be 0, or any value between 4 and 18. diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go index 5777d97a..226bfcbb 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go @@ -23,6 +23,10 @@ type Packet struct { skippedPacket bool } +func (p *Packet) outstanding() bool { + return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket +} + // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go index 7df91f23..ff8cc8d2 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go @@ -598,7 +598,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E pnSpace.lossTime = lossTime } if packetLost { - p.declaredLost = true + p = pnSpace.history.DeclareLost(p) // the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) @@ -767,7 +767,7 @@ func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) // TODO: don't declare the packet lost here. // Keep track of acknowledged frames instead. h.removeFromBytesInFlight(p) - p.declaredLost = true + pnSpace.history.DeclareLost(p) return true } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go index 36489367..d5704dd2 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go @@ -9,18 +9,20 @@ import ( ) type sentPacketHistory struct { - rttStats *utils.RTTStats - packetList *PacketList - packetMap map[protocol.PacketNumber]*PacketElement - highestSent protocol.PacketNumber + rttStats *utils.RTTStats + outstandingPacketList *PacketList + etcPacketList *PacketList + packetMap map[protocol.PacketNumber]*PacketElement + highestSent protocol.PacketNumber } func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory { return &sentPacketHistory{ - rttStats: rttStats, - packetList: NewPacketList(), - packetMap: make(map[protocol.PacketNumber]*PacketElement), - highestSent: protocol.InvalidPacketNumber, + rttStats: rttStats, + outstandingPacketList: NewPacketList(), + etcPacketList: NewPacketList(), + packetMap: make(map[protocol.PacketNumber]*PacketElement), + highestSent: protocol.InvalidPacketNumber, } } @@ -30,7 +32,7 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) { } // Skipped packet numbers. for pn := h.highestSent + 1; pn < p.PacketNumber; pn++ { - el := h.packetList.PushBack(Packet{ + el := h.etcPacketList.PushBack(Packet{ PacketNumber: pn, EncryptionLevel: p.EncryptionLevel, SendTime: p.SendTime, @@ -41,7 +43,12 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) { h.highestSent = p.PacketNumber if isAckEliciting { - el := h.packetList.PushBack(*p) + var el *PacketElement + if p.outstanding() { + el = h.outstandingPacketList.PushBack(*p) + } else { + el = h.etcPacketList.PushBack(*p) + } h.packetMap[p.PacketNumber] = el } } @@ -49,10 +56,25 @@ func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) { // Iterate iterates through all packets. func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error { cont := true - var next *PacketElement - for el := h.packetList.Front(); cont && el != nil; el = next { + outstandingEl := h.outstandingPacketList.Front() + etcEl := h.etcPacketList.Front() + var el *PacketElement + // whichever has the next packet number is returned first + for cont { + if outstandingEl == nil || (etcEl != nil && etcEl.Value.PacketNumber < outstandingEl.Value.PacketNumber) { + el = etcEl + } else { + el = outstandingEl + } + if el == nil { + return nil + } + if el == outstandingEl { + outstandingEl = outstandingEl.Next() + } else { + etcEl = etcEl.Next() + } var err error - next = el.Next() cont, err = cb(&el.Value) if err != nil { return err @@ -61,15 +83,13 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err return nil } -// FirstOutStanding returns the first outstanding packet. +// FirstOutstanding returns the first outstanding packet. func (h *sentPacketHistory) FirstOutstanding() *Packet { - for el := h.packetList.Front(); el != nil; el = el.Next() { - p := &el.Value - if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket { - return p - } + el := h.outstandingPacketList.Front() + if el == nil { + return nil } - return nil + return &el.Value } func (h *sentPacketHistory) Len() int { @@ -81,28 +101,53 @@ func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error { if !ok { return fmt.Errorf("packet %d not found in sent packet history", p) } - h.packetList.Remove(el) + h.outstandingPacketList.Remove(el) + h.etcPacketList.Remove(el) delete(h.packetMap, p) return nil } func (h *sentPacketHistory) HasOutstandingPackets() bool { - return h.FirstOutstanding() != nil + return h.outstandingPacketList.Len() > 0 } func (h *sentPacketHistory) DeleteOldPackets(now time.Time) { maxAge := 3 * h.rttStats.PTO(false) var nextEl *PacketElement - for el := h.packetList.Front(); el != nil; el = nextEl { + // we don't iterate outstandingPacketList, as we should not delete outstanding packets. + // being outstanding for more than 3*PTO should only happen in the case of drastic RTT changes. + for el := h.etcPacketList.Front(); el != nil; el = nextEl { nextEl = el.Next() p := el.Value if p.SendTime.After(now.Add(-maxAge)) { break } - if !p.skippedPacket && !p.declaredLost { // should only happen in the case of drastic RTT changes - continue - } delete(h.packetMap, p.PacketNumber) - h.packetList.Remove(el) + h.etcPacketList.Remove(el) } } + +func (h *sentPacketHistory) DeclareLost(p *Packet) *Packet { + el, ok := h.packetMap[p.PacketNumber] + if !ok { + return nil + } + // try to remove it from both lists, as we don't know which one it currently belongs to. + // Remove is a no-op for elements that are not in the list. + h.outstandingPacketList.Remove(el) + h.etcPacketList.Remove(el) + p.declaredLost = true + // move it to the correct position in the etc list (based on the packet number) + for el = h.etcPacketList.Back(); el != nil; el = el.Prev() { + if el.Value.PacketNumber < p.PacketNumber { + break + } + } + if el == nil { + el = h.etcPacketList.PushFront(*p) + } else { + el = h.etcPacketList.InsertAfter(*p, el) + } + h.packetMap[p.PacketNumber] = el + return &el.Value +} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go index 54eda9b7..03b03928 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go @@ -9,9 +9,15 @@ import ( "github.com/lucas-clemente/quic-go/internal/utils" ) -func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) cipher.AEAD { - key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic key", suite.KeyLen) - iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic iv", suite.IVLen()) +func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, v protocol.VersionNumber) cipher.AEAD { + keyLabel := hkdfLabelKeyV1 + ivLabel := hkdfLabelIVV1 + if v == protocol.Version2 { + keyLabel = hkdfLabelKeyV2 + ivLabel = hkdfLabelIVV2 + } + key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, keyLabel, suite.KeyLen) + iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, ivLabel, suite.IVLen()) return suite.AEAD(key, iv) } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go index 7543be99..31d9bf0a 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go @@ -113,7 +113,8 @@ type cryptoSetup struct { zeroRTTParameters *wire.TransportParameters clientHelloWritten bool - clientHelloWrittenChan chan *wire.TransportParameters + clientHelloWrittenChan chan struct{} // is closed as soon as the ClientHello is written + zeroRTTParametersChan chan<- *wire.TransportParameters rttStats *utils.RTTStats @@ -238,13 +239,14 @@ func newCryptoSetup( tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version) + zeroRTTParametersChan := make(chan *wire.TransportParameters, 1) cs := &cryptoSetup{ tlsConf: tlsConf, initialStream: initialStream, initialSealer: initialSealer, initialOpener: initialOpener, handshakeStream: handshakeStream, - aead: newUpdatableAEAD(rttStats, tracer, logger), + aead: newUpdatableAEAD(rttStats, tracer, logger, version), readEncLevel: protocol.EncryptionInitial, writeEncLevel: protocol.EncryptionInitial, runner: runner, @@ -256,7 +258,8 @@ func newCryptoSetup( perspective: perspective, handshakeDone: make(chan struct{}), alertChan: make(chan uint8), - clientHelloWrittenChan: make(chan *wire.TransportParameters, 1), + clientHelloWrittenChan: make(chan struct{}), + zeroRTTParametersChan: zeroRTTParametersChan, messageChan: make(chan []byte, 100), isReadingHandshakeMessage: make(chan struct{}), closeChan: make(chan struct{}), @@ -278,7 +281,7 @@ func newCryptoSetup( GetAppDataForSessionState: cs.marshalDataForSessionState, SetAppDataFromSessionState: cs.handleDataFromSessionState, } - return cs, cs.clientHelloWrittenChan + return cs, zeroRTTParametersChan } func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { @@ -308,6 +311,15 @@ func (h *cryptoSetup) RunHandshake() { close(handshakeComplete) }() + if h.perspective == protocol.PerspectiveClient { + select { + case err := <-handshakeErrChan: + h.onError(0, err.Error()) + return + case <-h.clientHelloWrittenChan: + } + } + select { case <-handshakeComplete: // return when the handshake is done h.mutex.Lock() @@ -324,7 +336,13 @@ func (h *cryptoSetup) RunHandshake() { } func (h *cryptoSetup) onError(alert uint8, message string) { - h.runner.OnError(qerr.NewCryptoError(alert, message)) + var err error + if alert == 0 { + err = &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: message} + } else { + err = qerr.NewCryptoError(alert, message) + } + h.runner.OnError(err) } // Close closes the crypto setup. @@ -554,8 +572,8 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph panic("Received 0-RTT read key for the client") } h.zeroRTTOpener = newLongHeaderOpener( - createAEAD(suite, trafficSecret), - newHeaderProtector(suite, trafficSecret, true), + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), ) h.mutex.Unlock() h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) @@ -566,8 +584,8 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph case qtls.EncryptionHandshake: h.readEncLevel = protocol.EncryptionHandshake h.handshakeOpener = newHandshakeOpener( - createAEAD(suite, trafficSecret), - newHeaderProtector(suite, trafficSecret, true), + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), h.dropInitialKeys, h.perspective, ) @@ -594,8 +612,8 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip panic("Received 0-RTT write key for the server") } h.zeroRTTSealer = newLongHeaderSealer( - createAEAD(suite, trafficSecret), - newHeaderProtector(suite, trafficSecret, true), + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), ) h.mutex.Unlock() h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) @@ -606,8 +624,8 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip case qtls.EncryptionHandshake: h.writeEncLevel = protocol.EncryptionHandshake h.handshakeSealer = newHandshakeSealer( - createAEAD(suite, trafficSecret), - newHeaderProtector(suite, trafficSecret, true), + createAEAD(suite, trafficSecret, h.version), + newHeaderProtector(suite, trafficSecret, true, h.version), h.dropInitialKeys, h.perspective, ) @@ -645,12 +663,13 @@ func (h *cryptoSetup) WriteRecord(p []byte) (int, error) { n, err := h.initialStream.Write(p) if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient { h.clientHelloWritten = true + close(h.clientHelloWrittenChan) if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { h.logger.Debugf("Doing 0-RTT.") - h.clientHelloWrittenChan <- h.zeroRTTParameters + h.zeroRTTParametersChan <- h.zeroRTTParameters } else { h.logger.Debugf("Not doing 0-RTT.") - h.clientHelloWrittenChan <- nil + h.zeroRTTParametersChan <- nil } } return n, err diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go index e1c72c3b..1f800c50 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/header_protector.go @@ -9,6 +9,7 @@ import ( "golang.org/x/crypto/chacha20" + "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" ) @@ -17,12 +18,20 @@ type headerProtector interface { DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) } -func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { +func hkdfHeaderProtectionLabel(v protocol.VersionNumber) string { + if v == protocol.Version2 { + return "quicv2 hp" + } + return "quic hp" +} + +func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector { + hkdfLabel := hkdfHeaderProtectionLabel(v) switch suite.ID { case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: - return newAESHeaderProtector(suite, trafficSecret, isLongHeader) + return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) case tls.TLS_CHACHA20_POLY1305_SHA256: - return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader) + return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) default: panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID)) } @@ -36,8 +45,8 @@ type aesHeaderProtector struct { var _ headerProtector = &aesHeaderProtector{} -func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { - hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", suite.KeyLen) +func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { + hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) block, err := aes.NewCipher(hpKey) if err != nil { panic(fmt.Sprintf("error creating new AES cipher: %s", err)) @@ -81,8 +90,8 @@ type chachaHeaderProtector struct { var _ headerProtector = &chachaHeaderProtector{} -func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { - hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", suite.KeyLen) +func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { + hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) p := &chachaHeaderProtector{ isLongHeader: isLongHeader, diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go index 2880acf3..00ed243c 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go @@ -12,12 +12,23 @@ import ( var ( quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} - quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} + quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} + quicSaltV2 = []byte{0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18, 0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, 0x06, 0xea, 0x7a, 0xe3, 0xe5, 0xd3} +) + +const ( + hkdfLabelKeyV1 = "quic key" + hkdfLabelKeyV2 = "quicv2 key" + hkdfLabelIVV1 = "quic iv" + hkdfLabelIVV2 = "quicv2 iv" ) func getSalt(v protocol.VersionNumber) []byte { + if v == protocol.Version2 { + return quicSaltV2 + } if v == protocol.Version1 { - return quicSalt + return quicSaltV1 } return quicSaltOld } @@ -40,14 +51,14 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p mySecret = serverSecret otherSecret = clientSecret } - myKey, myIV := computeInitialKeyAndIV(mySecret) - otherKey, otherIV := computeInitialKeyAndIV(otherSecret) + myKey, myIV := computeInitialKeyAndIV(mySecret, v) + otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v) encrypter := qtls.AEADAESGCMTLS13(myKey, myIV) decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV) - return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true)), - newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true)) + return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)), + newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v))) } func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) { @@ -57,8 +68,14 @@ func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (cli return } -func computeInitialKeyAndIV(secret []byte) (key, iv []byte) { - key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) - iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) +func computeInitialKeyAndIV(secret []byte, v protocol.VersionNumber) (key, iv []byte) { + keyLabel := hkdfLabelKeyV1 + ivLabel := hkdfLabelIVV1 + if v == protocol.Version2 { + keyLabel = hkdfLabelKeyV2 + ivLabel = hkdfLabelIVV2 + } + key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, keyLabel, 16) + iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, ivLabel, 12) return } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/updatable_aead.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/updatable_aead.go index 094e6504..1532e7b5 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/updatable_aead.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/updatable_aead.go @@ -55,8 +55,9 @@ type updatableAEAD struct { rttStats *utils.RTTStats - tracer logging.ConnectionTracer - logger utils.Logger + tracer logging.ConnectionTracer + logger utils.Logger + version protocol.VersionNumber // use a single slice to avoid allocations nonceBuf []byte @@ -67,7 +68,7 @@ var ( _ ShortHeaderSealer = &updatableAEAD{} ) -func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger) *updatableAEAD { +func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber) *updatableAEAD { return &updatableAEAD{ firstPacketNumber: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, @@ -77,6 +78,7 @@ func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, rttStats: rttStats, tracer: tracer, logger: logger, + version: version, } } @@ -100,8 +102,8 @@ func (a *updatableAEAD) rollKeys() { a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret) a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret) - a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret) - a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret) + a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret, a.version) + a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version) } func (a *updatableAEAD) startKeyDropTimer(now time.Time) { @@ -117,27 +119,27 @@ func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte // For the client, this function is called before SetWriteKey. // For the server, this function is called after SetWriteKey. func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { - a.rcvAEAD = createAEAD(suite, trafficSecret) - a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false) + a.rcvAEAD = createAEAD(suite, trafficSecret, a.version) + a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { a.setAEADParameters(a.rcvAEAD, suite) } a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) - a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret) + a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version) } // For the client, this function is called after SetReadKey. // For the server, this function is called before SetWriteKey. func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { - a.sendAEAD = createAEAD(suite, trafficSecret) - a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false) + a.sendAEAD = createAEAD(suite, trafficSecret, a.version) + a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { a.setAEADParameters(a.sendAEAD, suite) } a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) - a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret) + a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version) } func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) { diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go index 9c2a5e7f..684e5dec 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go @@ -137,8 +137,7 @@ const MaxAckFrameSize ByteCount = 1000 // The size is chosen such that a DATAGRAM frame fits into a QUIC packet. const DefaultMaxDatagramFrameSize ByteCount = 1220 -// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames. -// See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. +// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames (RFC 9221) const DatagramRcvQueueLen = 128 // MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame. diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go index b5276303..dd54dbd3 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go @@ -23,11 +23,12 @@ const ( VersionUnknown VersionNumber = math.MaxUint32 VersionDraft29 VersionNumber = 0xff00001d Version1 VersionNumber = 0x1 + Version2 VersionNumber = 0x709a50c4 ) // SupportedVersions lists the versions that the server supports // must be in sorted descending order -var SupportedVersions = []VersionNumber{Version1, VersionDraft29} +var SupportedVersions = []VersionNumber{Version1, Version2, VersionDraft29} // IsValidVersion says if the version is known to quic-go func IsValidVersion(v VersionNumber) bool { @@ -50,6 +51,8 @@ func (vn VersionNumber) String() string { return "draft-29" case Version1: return "v1" + case Version2: + return "v2" default: if vn.isGQUIC() { return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go118.go b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go118.go index 0e0e7966..5de030c7 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go118.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go118.go @@ -1,5 +1,5 @@ -//go:build go1.18 -// +build go1.18 +//go:build go1.18 && !go1.19 +// +build go1.18,!go1.19 package qtls diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go index 87e7132e..86dcaea3 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go119.go @@ -3,4 +3,98 @@ package qtls -var _ int = "The version of quic-go you're using can't be built on Go 1.19 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions." +import ( + "crypto" + "crypto/cipher" + "crypto/tls" + "net" + "unsafe" + + "github.com/marten-seemann/qtls-go1-19" +) + +type ( + // Alert is a TLS alert + Alert = qtls.Alert + // A Certificate is qtls.Certificate. + Certificate = qtls.Certificate + // CertificateRequestInfo contains information about a certificate request. + CertificateRequestInfo = qtls.CertificateRequestInfo + // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 + CipherSuiteTLS13 = qtls.CipherSuiteTLS13 + // ClientHelloInfo contains information about a ClientHello. + ClientHelloInfo = qtls.ClientHelloInfo + // ClientSessionCache is a cache used for session resumption. + ClientSessionCache = qtls.ClientSessionCache + // ClientSessionState is a state needed for session resumption. + ClientSessionState = qtls.ClientSessionState + // A Config is a qtls.Config. + Config = qtls.Config + // A Conn is a qtls.Conn. + Conn = qtls.Conn + // ConnectionState contains information about the state of the connection. + ConnectionState = qtls.ConnectionStateWith0RTT + // EncryptionLevel is the encryption level of a message. + EncryptionLevel = qtls.EncryptionLevel + // Extension is a TLS extension + Extension = qtls.Extension + // ExtraConfig is the qtls.ExtraConfig + ExtraConfig = qtls.ExtraConfig + // RecordLayer is a qtls RecordLayer. + RecordLayer = qtls.RecordLayer +) + +const ( + // EncryptionHandshake is the Handshake encryption level + EncryptionHandshake = qtls.EncryptionHandshake + // Encryption0RTT is the 0-RTT encryption level + Encryption0RTT = qtls.Encryption0RTT + // EncryptionApplication is the application data encryption level + EncryptionApplication = qtls.EncryptionApplication +) + +// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 +func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { + return qtls.AEADAESGCMTLS13(key, fixedNonce) +} + +// Client returns a new TLS client side connection. +func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { + return qtls.Client(conn, config, extraConfig) +} + +// Server returns a new TLS server side connection. +func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { + return qtls.Server(conn, config, extraConfig) +} + +func GetConnectionState(conn *Conn) ConnectionState { + return conn.ConnectionStateWith0RTT() +} + +// ToTLSConnectionState extracts the tls.ConnectionState +func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { + return cs.ConnectionState +} + +type cipherSuiteTLS13 struct { + ID uint16 + KeyLen int + AEAD func(key, fixedNonce []byte) cipher.AEAD + Hash crypto.Hash +} + +//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-19.cipherSuiteTLS13ByID +func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 + +// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. +func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { + val := cipherSuiteTLS13ByID(id) + cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) + return &qtls.CipherSuiteTLS13{ + ID: cs.ID, + KeyLen: cs.KeyLen, + AEAD: cs.AEAD, + Hash: cs.Hash, + } +} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go120.go b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go120.go new file mode 100644 index 00000000..f8d59d8b --- /dev/null +++ b/vendor/github.com/lucas-clemente/quic-go/internal/qtls/go120.go @@ -0,0 +1,6 @@ +//go:build go1.20 +// +build go1.20 + +package qtls + +var _ int = "The version of quic-go you're using can't be built on Go 1.20 yet. For more details, please see https://github.com/lucas-clemente/quic-go/wiki/quic-go-and-Go-versions." diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go index b8938aca..9d9edab2 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go @@ -127,18 +127,32 @@ func (h *ExtendedHeader) Write(b *bytes.Buffer, ver protocol.VersionNumber) erro return h.writeShortHeader(b, ver) } -func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, _ protocol.VersionNumber) error { +func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, version protocol.VersionNumber) error { var packetType uint8 - //nolint:exhaustive - switch h.Type { - case protocol.PacketTypeInitial: - packetType = 0x0 - case protocol.PacketType0RTT: - packetType = 0x1 - case protocol.PacketTypeHandshake: - packetType = 0x2 - case protocol.PacketTypeRetry: - packetType = 0x3 + if version == protocol.Version2 { + //nolint:exhaustive + switch h.Type { + case protocol.PacketTypeInitial: + packetType = 0b01 + case protocol.PacketType0RTT: + packetType = 0b10 + case protocol.PacketTypeHandshake: + packetType = 0b11 + case protocol.PacketTypeRetry: + packetType = 0b00 + } + } else { + //nolint:exhaustive + switch h.Type { + case protocol.PacketTypeInitial: + packetType = 0b00 + case protocol.PacketType0RTT: + packetType = 0b01 + case protocol.PacketTypeHandshake: + packetType = 0b10 + case protocol.PacketTypeRetry: + packetType = 0b11 + } } firstByte := 0xc0 | packetType<<4 if h.Type != protocol.PacketTypeRetry { diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go index 07ca9f05..f6a31ee0 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go @@ -53,10 +53,14 @@ func Is0RTTPacket(b []byte) bool { if b[0]&0x80 == 0 { return false } - if !protocol.IsSupportedVersion(protocol.SupportedVersions, protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5]))) { + version := protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5])) + if !protocol.IsSupportedVersion(protocol.SupportedVersions, version) { return false } - return b[0]&0x30>>4 == 0x1 + if version == protocol.Version2 { + return b[0]>>4&0b11 == 0b10 + } + return b[0]>>4&0b11 == 0b01 } var ErrUnsupportedVersion = errors.New("unsupported version") @@ -179,15 +183,28 @@ func (h *Header) parseLongHeader(b *bytes.Reader) error { return ErrUnsupportedVersion } - switch (h.typeByte & 0x30) >> 4 { - case 0x0: - h.Type = protocol.PacketTypeInitial - case 0x1: - h.Type = protocol.PacketType0RTT - case 0x2: - h.Type = protocol.PacketTypeHandshake - case 0x3: - h.Type = protocol.PacketTypeRetry + if h.Version == protocol.Version2 { + switch h.typeByte >> 4 & 0b11 { + case 0b00: + h.Type = protocol.PacketTypeRetry + case 0b01: + h.Type = protocol.PacketTypeInitial + case 0b10: + h.Type = protocol.PacketType0RTT + case 0b11: + h.Type = protocol.PacketTypeHandshake + } + } else { + switch h.typeByte >> 4 & 0b11 { + case 0b00: + h.Type = protocol.PacketTypeInitial + case 0b01: + h.Type = protocol.PacketType0RTT + case 0b10: + h.Type = protocol.PacketTypeHandshake + case 0b11: + h.Type = protocol.PacketTypeRetry + } } if h.Type == protocol.PacketTypeRetry { diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/transport_parameters.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/transport_parameters.go index b7e0a8c9..e1f83cd6 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/transport_parameters.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/wire/transport_parameters.go @@ -42,7 +42,7 @@ const ( activeConnectionIDLimitParameterID transportParameterID = 0xe initialSourceConnectionIDParameterID transportParameterID = 0xf retrySourceConnectionIDParameterID transportParameterID = 0x10 - // https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/ + // RFC 9221 maxDatagramFrameSizeParameterID transportParameterID = 0x20 ) diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go index bcae87d1..196853e0 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go @@ -35,7 +35,7 @@ func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.Version } // ComposeVersionNegotiation composes a Version Negotiation -func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) { +func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) []byte { greasedVersions := protocol.GetGreasedVersions(versions) expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4 buf := bytes.NewBuffer(make([]byte, 0, expectedLen)) @@ -50,5 +50,5 @@ func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, vers for _, v := range greasedVersions { utils.BigEndian.WriteUint32(buf, uint32(v)) } - return buf.Bytes(), nil + return buf.Bytes() } diff --git a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go b/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go index 6975d5a2..2d55a95e 100644 --- a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go +++ b/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go @@ -104,6 +104,7 @@ func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error { } if size >= protocol.DesiredReceiveBufferSize { logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024) + return nil } if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { return fmt.Errorf("failed to increase receive buffer size: %w", err) diff --git a/vendor/github.com/lucas-clemente/quic-go/quicvarint/io.go b/vendor/github.com/lucas-clemente/quic-go/quicvarint/io.go index c4d976b5..9368d1c5 100644 --- a/vendor/github.com/lucas-clemente/quic-go/quicvarint/io.go +++ b/vendor/github.com/lucas-clemente/quic-go/quicvarint/io.go @@ -31,7 +31,10 @@ func NewReader(r io.Reader) Reader { func (r *byteReader) ReadByte() (byte, error) { var b [1]byte - _, err := r.Reader.Read(b[:]) + n, err := r.Reader.Read(b[:]) + if n == 1 && err == io.EOF { + err = nil + } return b[0], err } diff --git a/vendor/github.com/lucas-clemente/quic-go/receive_stream.go b/vendor/github.com/lucas-clemente/quic-go/receive_stream.go index f9a1e066..ae6a449b 100644 --- a/vendor/github.com/lucas-clemente/quic-go/receive_stream.go +++ b/vendor/github.com/lucas-clemente/quic-go/receive_stream.go @@ -47,6 +47,7 @@ type receiveStream struct { resetRemotely bool // set when HandleResetStreamFrame() is called readChan chan struct{} + readOnce chan struct{} // cap: 1, to protect against concurrent use of Read deadline time.Time flowController flowcontrol.StreamFlowController @@ -70,6 +71,7 @@ func newReceiveStream( flowController: flowController, frameQueue: newFrameSorter(), readChan: make(chan struct{}, 1), + readOnce: make(chan struct{}, 1), finalOffset: protocol.MaxByteCount, version: version, } @@ -81,6 +83,12 @@ func (s *receiveStream) StreamID() protocol.StreamID { // Read implements io.Reader. It is not thread safe! func (s *receiveStream) Read(p []byte) (int, error) { + // Concurrent use of Read is not permitted (and doesn't make any sense), + // but sometimes people do it anyway. + // Make sure that we only execute one call at any given time to avoid hard to debug failures. + s.readOnce <- struct{}{} + defer func() { <-s.readOnce }() + s.mutex.Lock() completed, n, err := s.readImpl(p) s.mutex.Unlock() @@ -105,7 +113,7 @@ func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, err return false, 0, s.closeForShutdownErr } - bytesRead := 0 + var bytesRead int var deadlineTimer *utils.Timer for bytesRead < len(p) { if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) { diff --git a/vendor/github.com/lucas-clemente/quic-go/send_stream.go b/vendor/github.com/lucas-clemente/quic-go/send_stream.go index 946243ca..b23df00b 100644 --- a/vendor/github.com/lucas-clemente/quic-go/send_stream.go +++ b/vendor/github.com/lucas-clemente/quic-go/send_stream.go @@ -50,6 +50,7 @@ type sendStream struct { nextFrame *wire.StreamFrame writeChan chan struct{} + writeOnce chan struct{} deadline time.Time flowController flowcontrol.StreamFlowController @@ -73,6 +74,7 @@ func newSendStream( sender: sender, flowController: flowController, writeChan: make(chan struct{}, 1), + writeOnce: make(chan struct{}, 1), // cap: 1, to protect against concurrent use of Write version: version, } s.ctx, s.ctxCancel = context.WithCancel(context.Background()) @@ -84,6 +86,12 @@ func (s *sendStream) StreamID() protocol.StreamID { } func (s *sendStream) Write(p []byte) (int, error) { + // Concurrent use of Write is not permitted (and doesn't make any sense), + // but sometimes people do it anyway. + // Make sure that we only execute one call at any given time to avoid hard to debug failures. + s.writeOnce <- struct{}{} + defer func() { <-s.writeOnce }() + s.mutex.Lock() defer s.mutex.Unlock() diff --git a/vendor/github.com/lucas-clemente/quic-go/server.go b/vendor/github.com/lucas-clemente/quic-go/server.go index 5bf568a6..0e642970 100644 --- a/vendor/github.com/lucas-clemente/quic-go/server.go +++ b/vendor/github.com/lucas-clemente/quic-go/server.go @@ -20,6 +20,9 @@ import ( "github.com/lucas-clemente/quic-go/logging" ) +// ErrServerClosed is returned by the Listener or EarlyListener's Accept method after a call to Close. +var ErrServerClosed = errors.New("quic: Server closed") + // packetHandler handles packets type packetHandler interface { handlePacket(*receivedPacket) @@ -284,7 +287,7 @@ func (s *baseServer) Close() error { return nil } if s.serverError == nil { - s.serverError = errors.New("server closed") + s.serverError = ErrServerClosed } // If the server was started with ListenAddr, we created the packet conn. // We need to close it in order to make the go routine reading from that conn return. @@ -648,11 +651,7 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) { s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version) - data, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions) - if err != nil { - s.logger.Debugf("Error composing Version Negotiation: %s", err) - return - } + data := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions) if s.config.Tracer != nil { s.config.Tracer.SentPacket( p.remoteAddr, diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn.go index d73b01d2..7cc05465 100644 --- a/vendor/github.com/lucas-clemente/quic-go/sys_conn.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn.go @@ -30,9 +30,13 @@ func wrapConn(pc net.PacketConn) (rawConn, error) { if err != nil { return nil, err } - err = setDF(rawConn) - if err != nil { - return nil, err + + if _, ok := pc.LocalAddr().(*net.UDPAddr); ok { + // Only set DF on sockets that we expect to be able to handle that configuration. + err = setDF(rawConn) + if err != nil { + return nil, err + } } } c, ok := pc.(OOBCapablePacketConn) diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go index 69f8fc93..17ac67f1 100644 --- a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go @@ -7,9 +7,8 @@ import ( "errors" "syscall" - "golang.org/x/sys/unix" - "github.com/lucas-clemente/quic-go/internal/utils" + "golang.org/x/sys/unix" ) func setDF(rawConn syscall.RawConn) error { @@ -30,7 +29,7 @@ func setDF(rawConn syscall.RawConn) error { case errDFIPv4 != nil && errDFIPv6 == nil: utils.DefaultLogger.Debugf("Setting DF for IPv6.") case errDFIPv4 != nil && errDFIPv6 != nil: - utils.DefaultLogger.Errorf("setting DF failed for both IPv4 and IPv6") + return errors.New("setting DF failed for both IPv4 and IPv6") } return nil } diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/conn.go b/vendor/github.com/marten-seemann/qtls-go1-17/conn.go index b86bb66a..70fad465 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-17/conn.go +++ b/vendor/github.com/marten-seemann/qtls-go1-17/conn.go @@ -32,6 +32,7 @@ type Conn struct { // handshakeStatus is 1 if the connection is currently transferring // application data (i.e. is not currently processing a handshake). + // handshakeStatus == 1 implies handshakeErr == nil. // This field is only to be accessed with sync/atomic. handshakeStatus uint32 // constant after handshake; protected by handshakeMutex @@ -1451,6 +1452,13 @@ func (c *Conn) HandshakeContext(ctx context.Context) error { } func (c *Conn) handshakeContext(ctx context.Context) (ret error) { + // Fast sync/atomic-based exit if there is no handshake in flight and the + // last one succeeded without an error. Avoids the expensive context setup + // and mutex for most Read and Write calls. + if c.handshakeComplete() { + return nil + } + handshakeCtx, cancel := context.WithCancel(ctx) // Note: defer this before starting the "interrupter" goroutine // so that we can tell the difference between the input being canceled and @@ -1509,6 +1517,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { if c.handshakeErr == nil && !c.handshakeComplete() { c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") } + if c.handshakeErr != nil && c.handshakeComplete() { + panic("tls: internal error: handshake returned an error but is marked successful") + } return c.handshakeErr } diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-17/handshake_server_tls13.go index ee2ce93d..0c200605 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-17/handshake_server_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-17/handshake_server_tls13.go @@ -777,6 +777,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { if err != nil { return err } + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { return err } diff --git a/vendor/github.com/marten-seemann/qtls-go1-17/ticket.go b/vendor/github.com/marten-seemann/qtls-go1-17/ticket.go index 006b8c1d..81e8a52e 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-17/ticket.go +++ b/vendor/github.com/marten-seemann/qtls-go1-17/ticket.go @@ -11,6 +11,7 @@ import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" + "encoding/binary" "errors" "io" "time" @@ -232,6 +233,20 @@ func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, e return nil, err } m.lifetime = uint32(maxSessionTicketLifetime / time.Second) + + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 + // The value is not stored anywhere; we never need to check the ticket age + // because 0-RTT is not supported. + ageAdd := make([]byte, 4) + _, err = c.config.rand().Read(ageAdd) + if err != nil { + return nil, err + } + m.ageAdd = binary.LittleEndian.Uint32(ageAdd) + + // ticket_nonce, which must be unique per connection, is always left at + // zero because we only ever send one ticket per connection. + if c.extraConfig != nil { m.maxEarlyData = c.extraConfig.MaxEarlyData } diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/conn.go b/vendor/github.com/marten-seemann/qtls-go1-18/conn.go index 31a87b59..90a27b5d 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/conn.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/conn.go @@ -32,6 +32,7 @@ type Conn struct { // handshakeStatus is 1 if the connection is currently transferring // application data (i.e. is not currently processing a handshake). + // handshakeStatus == 1 implies handshakeErr == nil. // This field is only to be accessed with sync/atomic. handshakeStatus uint32 // constant after handshake; protected by handshakeMutex @@ -1458,6 +1459,13 @@ func (c *Conn) HandshakeContext(ctx context.Context) error { } func (c *Conn) handshakeContext(ctx context.Context) (ret error) { + // Fast sync/atomic-based exit if there is no handshake in flight and the + // last one succeeded without an error. Avoids the expensive context setup + // and mutex for most Read and Write calls. + if c.handshakeComplete() { + return nil + } + handshakeCtx, cancel := context.WithCancel(ctx) // Note: defer this before starting the "interrupter" goroutine // so that we can tell the difference between the input being canceled and @@ -1516,6 +1524,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { if c.handshakeErr == nil && !c.handshakeComplete() { c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") } + if c.handshakeErr != nil && c.handshakeComplete() { + panic("tls: internal error: handshake returned an error but is marked successful") + } return c.handshakeErr } diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go index d26deb8f..dd8d801e 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go @@ -777,6 +777,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { if err != nil { return err } + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { return err } diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/ticket.go b/vendor/github.com/marten-seemann/qtls-go1-18/ticket.go index 006b8c1d..81e8a52e 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/ticket.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/ticket.go @@ -11,6 +11,7 @@ import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" + "encoding/binary" "errors" "io" "time" @@ -232,6 +233,20 @@ func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, e return nil, err } m.lifetime = uint32(maxSessionTicketLifetime / time.Second) + + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 + // The value is not stored anywhere; we never need to check the ticket age + // because 0-RTT is not supported. + ageAdd := make([]byte, 4) + _, err = c.config.rand().Read(ageAdd) + if err != nil { + return nil, err + } + m.ageAdd = binary.LittleEndian.Uint32(ageAdd) + + // ticket_nonce, which must be unique per connection, is always left at + // zero because we only ever send one ticket per connection. + if c.extraConfig != nil { m.maxEarlyData = c.extraConfig.MaxEarlyData } diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/LICENSE b/vendor/github.com/marten-seemann/qtls-go1-19/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/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/marten-seemann/qtls-go1-19/README.md b/vendor/github.com/marten-seemann/qtls-go1-19/README.md new file mode 100644 index 00000000..3e902212 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/README.md @@ -0,0 +1,6 @@ +# qtls + +[![Go Reference](https://pkg.go.dev/badge/github.com/marten-seemann/qtls-go1-17.svg)](https://pkg.go.dev/github.com/marten-seemann/qtls-go1-17) +[![.github/workflows/go-test.yml](https://github.com/marten-seemann/qtls-go1-17/actions/workflows/go-test.yml/badge.svg)](https://github.com/marten-seemann/qtls-go1-17/actions/workflows/go-test.yml) + +This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go). diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/alert.go b/vendor/github.com/marten-seemann/qtls-go1-19/alert.go new file mode 100644 index 00000000..3feac79b --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/alert.go @@ -0,0 +1,102 @@ +// 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 LICENSE file. + +package qtls + +import "strconv" + +type alert uint8 + +// Alert is a TLS alert +type Alert = alert + +const ( + // alert level + alertLevelWarning = 1 + alertLevelError = 2 +) + +const ( + alertCloseNotify alert = 0 + alertUnexpectedMessage alert = 10 + alertBadRecordMAC alert = 20 + alertDecryptionFailed alert = 21 + alertRecordOverflow alert = 22 + alertDecompressionFailure alert = 30 + alertHandshakeFailure alert = 40 + alertBadCertificate alert = 42 + alertUnsupportedCertificate alert = 43 + alertCertificateRevoked alert = 44 + alertCertificateExpired alert = 45 + alertCertificateUnknown alert = 46 + alertIllegalParameter alert = 47 + alertUnknownCA alert = 48 + alertAccessDenied alert = 49 + alertDecodeError alert = 50 + alertDecryptError alert = 51 + alertExportRestriction alert = 60 + alertProtocolVersion alert = 70 + alertInsufficientSecurity alert = 71 + alertInternalError alert = 80 + alertInappropriateFallback alert = 86 + alertUserCanceled alert = 90 + alertNoRenegotiation alert = 100 + alertMissingExtension alert = 109 + alertUnsupportedExtension alert = 110 + alertCertificateUnobtainable alert = 111 + alertUnrecognizedName alert = 112 + alertBadCertificateStatusResponse alert = 113 + alertBadCertificateHashValue alert = 114 + alertUnknownPSKIdentity alert = 115 + alertCertificateRequired alert = 116 + alertNoApplicationProtocol alert = 120 +) + +var alertText = map[alert]string{ + alertCloseNotify: "close notify", + alertUnexpectedMessage: "unexpected message", + alertBadRecordMAC: "bad record MAC", + alertDecryptionFailed: "decryption failed", + alertRecordOverflow: "record overflow", + alertDecompressionFailure: "decompression failure", + alertHandshakeFailure: "handshake failure", + alertBadCertificate: "bad certificate", + alertUnsupportedCertificate: "unsupported certificate", + alertCertificateRevoked: "revoked certificate", + alertCertificateExpired: "expired certificate", + alertCertificateUnknown: "unknown certificate", + alertIllegalParameter: "illegal parameter", + alertUnknownCA: "unknown certificate authority", + alertAccessDenied: "access denied", + alertDecodeError: "error decoding message", + alertDecryptError: "error decrypting message", + alertExportRestriction: "export restriction", + alertProtocolVersion: "protocol version not supported", + alertInsufficientSecurity: "insufficient security level", + alertInternalError: "internal error", + alertInappropriateFallback: "inappropriate fallback", + alertUserCanceled: "user canceled", + alertNoRenegotiation: "no renegotiation", + alertMissingExtension: "missing extension", + alertUnsupportedExtension: "unsupported extension", + alertCertificateUnobtainable: "certificate unobtainable", + alertUnrecognizedName: "unrecognized name", + alertBadCertificateStatusResponse: "bad certificate status response", + alertBadCertificateHashValue: "bad certificate hash value", + alertUnknownPSKIdentity: "unknown PSK identity", + alertCertificateRequired: "certificate required", + alertNoApplicationProtocol: "no application protocol", +} + +func (e alert) String() string { + s, ok := alertText[e] + if ok { + return "tls: " + s + } + return "tls: alert(" + strconv.Itoa(int(e)) + ")" +} + +func (e alert) Error() string { + return e.String() +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/auth.go b/vendor/github.com/marten-seemann/qtls-go1-19/auth.go new file mode 100644 index 00000000..effc9ace --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/auth.go @@ -0,0 +1,293 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rsa" + "errors" + "fmt" + "hash" + "io" +) + +// verifyHandshakeSignature verifies a signature against pre-hashed +// (if required) handshake contents. +func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error { + switch sigType { + case signatureECDSA: + pubKey, ok := pubkey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("expected an ECDSA public key, got %T", pubkey) + } + if !ecdsa.VerifyASN1(pubKey, signed, sig) { + return errors.New("ECDSA verification failure") + } + case signatureEd25519: + pubKey, ok := pubkey.(ed25519.PublicKey) + if !ok { + return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey) + } + if !ed25519.Verify(pubKey, signed, sig) { + return errors.New("Ed25519 verification failure") + } + case signaturePKCS1v15: + pubKey, ok := pubkey.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("expected an RSA public key, got %T", pubkey) + } + if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil { + return err + } + case signatureRSAPSS: + pubKey, ok := pubkey.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("expected an RSA public key, got %T", pubkey) + } + signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash} + if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil { + return err + } + default: + return errors.New("internal error: unknown signature type") + } + return nil +} + +const ( + serverSignatureContext = "TLS 1.3, server CertificateVerify\x00" + clientSignatureContext = "TLS 1.3, client CertificateVerify\x00" +) + +var signaturePadding = []byte{ + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +} + +// signedMessage returns the pre-hashed (if necessary) message to be signed by +// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3. +func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte { + if sigHash == directSigning { + b := &bytes.Buffer{} + b.Write(signaturePadding) + io.WriteString(b, context) + b.Write(transcript.Sum(nil)) + return b.Bytes() + } + h := sigHash.New() + h.Write(signaturePadding) + io.WriteString(h, context) + h.Write(transcript.Sum(nil)) + return h.Sum(nil) +} + +// typeAndHashFromSignatureScheme returns the corresponding signature type and +// crypto.Hash for a given TLS SignatureScheme. +func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) { + switch signatureAlgorithm { + case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512: + sigType = signaturePKCS1v15 + case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512: + sigType = signatureRSAPSS + case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512: + sigType = signatureECDSA + case Ed25519: + sigType = signatureEd25519 + default: + return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm) + } + switch signatureAlgorithm { + case PKCS1WithSHA1, ECDSAWithSHA1: + hash = crypto.SHA1 + case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256: + hash = crypto.SHA256 + case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384: + hash = crypto.SHA384 + case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512: + hash = crypto.SHA512 + case Ed25519: + hash = directSigning + default: + return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm) + } + return sigType, hash, nil +} + +// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for +// a given public key used with TLS 1.0 and 1.1, before the introduction of +// signature algorithm negotiation. +func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) { + switch pub.(type) { + case *rsa.PublicKey: + return signaturePKCS1v15, crypto.MD5SHA1, nil + case *ecdsa.PublicKey: + return signatureECDSA, crypto.SHA1, nil + case ed25519.PublicKey: + // RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1, + // but it requires holding on to a handshake transcript to do a + // full signature, and not even OpenSSL bothers with the + // complexity, so we can't even test it properly. + return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2") + default: + return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub) + } +} + +var rsaSignatureSchemes = []struct { + scheme SignatureScheme + minModulusBytes int + maxVersion uint16 +}{ + // RSA-PSS is used with PSSSaltLengthEqualsHash, and requires + // emLen >= hLen + sLen + 2 + {PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13}, + {PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13}, + {PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13}, + // PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires + // emLen >= len(prefix) + hLen + 11 + // TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS. + {PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12}, + {PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12}, + {PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12}, + {PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12}, +} + +// signatureSchemesForCertificate returns the list of supported SignatureSchemes +// for a given certificate, based on the public key and the protocol version, +// and optionally filtered by its explicit SupportedSignatureAlgorithms. +// +// This function must be kept in sync with supportedSignatureAlgorithms. +// FIPS filtering is applied in the caller, selectSignatureScheme. +func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme { + priv, ok := cert.PrivateKey.(crypto.Signer) + if !ok { + return nil + } + + var sigAlgs []SignatureScheme + switch pub := priv.Public().(type) { + case *ecdsa.PublicKey: + if version != VersionTLS13 { + // In TLS 1.2 and earlier, ECDSA algorithms are not + // constrained to a single curve. + sigAlgs = []SignatureScheme{ + ECDSAWithP256AndSHA256, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + ECDSAWithSHA1, + } + break + } + switch pub.Curve { + case elliptic.P256(): + sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256} + case elliptic.P384(): + sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384} + case elliptic.P521(): + sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512} + default: + return nil + } + case *rsa.PublicKey: + size := pub.Size() + sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes)) + for _, candidate := range rsaSignatureSchemes { + if size >= candidate.minModulusBytes && version <= candidate.maxVersion { + sigAlgs = append(sigAlgs, candidate.scheme) + } + } + case ed25519.PublicKey: + sigAlgs = []SignatureScheme{Ed25519} + default: + return nil + } + + if cert.SupportedSignatureAlgorithms != nil { + var filteredSigAlgs []SignatureScheme + for _, sigAlg := range sigAlgs { + if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) { + filteredSigAlgs = append(filteredSigAlgs, sigAlg) + } + } + return filteredSigAlgs + } + return sigAlgs +} + +// selectSignatureScheme picks a SignatureScheme from the peer's preference list +// that works with the selected certificate. It's only called for protocol +// versions that support signature algorithms, so TLS 1.2 and 1.3. +func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) { + supportedAlgs := signatureSchemesForCertificate(vers, c) + if len(supportedAlgs) == 0 { + return 0, unsupportedCertificateError(c) + } + if len(peerAlgs) == 0 && vers == VersionTLS12 { + // For TLS 1.2, if the client didn't send signature_algorithms then we + // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1. + peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1} + } + // Pick signature scheme in the peer's preference order, as our + // preference order is not configurable. + for _, preferredAlg := range peerAlgs { + if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) { + continue + } + if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { + return preferredAlg, nil + } + } + return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms") +} + +// unsupportedCertificateError returns a helpful error for certificates with +// an unsupported private key. +func unsupportedCertificateError(cert *Certificate) error { + switch cert.PrivateKey.(type) { + case rsa.PrivateKey, ecdsa.PrivateKey: + return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T", + cert.PrivateKey, cert.PrivateKey) + case *ed25519.PrivateKey: + return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey") + } + + signer, ok := cert.PrivateKey.(crypto.Signer) + if !ok { + return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer", + cert.PrivateKey) + } + + switch pub := signer.Public().(type) { + case *ecdsa.PublicKey: + switch pub.Curve { + case elliptic.P256(): + case elliptic.P384(): + case elliptic.P521(): + default: + return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name) + } + case *rsa.PublicKey: + return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms") + case ed25519.PublicKey: + default: + return fmt.Errorf("tls: unsupported certificate key (%T)", pub) + } + + if cert.SupportedSignatureAlgorithms != nil { + return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms") + } + + return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey) +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/cipher_suites.go b/vendor/github.com/marten-seemann/qtls-go1-19/cipher_suites.go new file mode 100644 index 00000000..56dd4543 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/cipher_suites.go @@ -0,0 +1,693 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/hmac" + "crypto/rc4" + "crypto/sha1" + "crypto/sha256" + "fmt" + "hash" + + "golang.org/x/crypto/chacha20poly1305" +) + +// CipherSuite is a TLS cipher suite. Note that most functions in this package +// accept and expose cipher suite IDs instead of this type. +type CipherSuite struct { + ID uint16 + Name string + + // Supported versions is the list of TLS protocol versions that can + // negotiate this cipher suite. + SupportedVersions []uint16 + + // Insecure is true if the cipher suite has known security issues + // due to its primitives, design, or implementation. + Insecure bool +} + +var ( + supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12} + supportedOnlyTLS12 = []uint16{VersionTLS12} + supportedOnlyTLS13 = []uint16{VersionTLS13} +) + +// CipherSuites returns a list of cipher suites currently implemented by this +// package, excluding those with security issues, which are returned by +// InsecureCipherSuites. +// +// The list is sorted by ID. Note that the default cipher suites selected by +// this package might depend on logic that can't be captured by a static list, +// and might not match those returned by this function. +func CipherSuites() []*CipherSuite { + return []*CipherSuite{ + {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, + {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, + {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, + {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, + + {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false}, + {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false}, + {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false}, + + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, + {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, + {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, + {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, + {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, + {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, + {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, + } +} + +// InsecureCipherSuites returns a list of cipher suites currently implemented by +// this package and which have security issues. +// +// Most applications should not use the cipher suites in this list, and should +// only use those returned by CipherSuites. +func InsecureCipherSuites() []*CipherSuite { + // This list includes RC4, CBC_SHA256, and 3DES cipher suites. See + // cipherSuitesPreferenceOrder for details. + return []*CipherSuite{ + {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, + {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, + } +} + +// CipherSuiteName returns the standard name for the passed cipher suite ID +// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation +// of the ID value if the cipher suite is not implemented by this package. +func CipherSuiteName(id uint16) string { + for _, c := range CipherSuites() { + if c.ID == id { + return c.Name + } + } + for _, c := range InsecureCipherSuites() { + if c.ID == id { + return c.Name + } + } + return fmt.Sprintf("0x%04X", id) +} + +const ( + // suiteECDHE indicates that the cipher suite involves elliptic curve + // Diffie-Hellman. This means that it should only be selected when the + // client indicates that it supports ECC with a curve and point format + // that we're happy with. + suiteECDHE = 1 << iota + // suiteECSign indicates that the cipher suite involves an ECDSA or + // EdDSA signature and therefore may only be selected when the server's + // certificate is ECDSA or EdDSA. If this is not set then the cipher suite + // is RSA based. + suiteECSign + // suiteTLS12 indicates that the cipher suite should only be advertised + // and accepted when using TLS 1.2. + suiteTLS12 + // suiteSHA384 indicates that the cipher suite uses SHA384 as the + // handshake hash. + suiteSHA384 +) + +// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange +// mechanism, as well as the cipher+MAC pair or the AEAD. +type cipherSuite struct { + id uint16 + // the lengths, in bytes, of the key material needed for each component. + keyLen int + macLen int + ivLen int + ka func(version uint16) keyAgreement + // flags is a bitmask of the suite* values, above. + flags int + cipher func(key, iv []byte, isRead bool) any + mac func(key []byte) hash.Hash + aead func(key, fixedNonce []byte) aead +} + +var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter. + {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, + {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, + {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, + {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil}, +} + +// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which +// is also in supportedIDs and passes the ok filter. +func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite { + for _, id := range ids { + candidate := cipherSuiteByID(id) + if candidate == nil || !ok(candidate) { + continue + } + + for _, suppID := range supportedIDs { + if id == suppID { + return candidate + } + } + } + return nil +} + +// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash +// algorithm to be used with HKDF. See RFC 8446, Appendix B.4. +type cipherSuiteTLS13 struct { + id uint16 + keyLen int + aead func(key, fixedNonce []byte) aead + hash crypto.Hash +} + +type CipherSuiteTLS13 struct { + ID uint16 + KeyLen int + Hash crypto.Hash + AEAD func(key, fixedNonce []byte) cipher.AEAD +} + +func (c *CipherSuiteTLS13) IVLen() int { + return aeadNonceLength +} + +var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map. + {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256}, + {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256}, + {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384}, +} + +// cipherSuitesPreferenceOrder is the order in which we'll select (on the +// server) or advertise (on the client) TLS 1.0–1.2 cipher suites. +// +// Cipher suites are filtered but not reordered based on the application and +// peer's preferences, meaning we'll never select a suite lower in this list if +// any higher one is available. This makes it more defensible to keep weaker +// cipher suites enabled, especially on the server side where we get the last +// word, since there are no known downgrade attacks on cipher suites selection. +// +// The list is sorted by applying the following priority rules, stopping at the +// first (most important) applicable one: +// +// - Anything else comes before RC4 +// +// RC4 has practically exploitable biases. See https://www.rc4nomore.com. +// +// - Anything else comes before CBC_SHA256 +// +// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13 +// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and +// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. +// +// - Anything else comes before 3DES +// +// 3DES has 64-bit blocks, which makes it fundamentally susceptible to +// birthday attacks. See https://sweet32.info. +// +// - ECDHE comes before anything else +// +// Once we got the broken stuff out of the way, the most important +// property a cipher suite can have is forward secrecy. We don't +// implement FFDHE, so that means ECDHE. +// +// - AEADs come before CBC ciphers +// +// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites +// are fundamentally fragile, and suffered from an endless sequence of +// padding oracle attacks. See https://eprint.iacr.org/2015/1129, +// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and +// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/. +// +// - AES comes before ChaCha20 +// +// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster +// than ChaCha20Poly1305. +// +// When AES hardware is not available, AES-128-GCM is one or more of: much +// slower, way more complex, and less safe (because not constant time) +// than ChaCha20Poly1305. +// +// We use this list if we think both peers have AES hardware, and +// cipherSuitesPreferenceOrderNoAES otherwise. +// +// - AES-128 comes before AES-256 +// +// The only potential advantages of AES-256 are better multi-target +// margins, and hypothetical post-quantum properties. Neither apply to +// TLS, and AES-256 is slower due to its four extra rounds (which don't +// contribute to the advantages above). +// +// - ECDSA comes before RSA +// +// The relative order of ECDSA and RSA cipher suites doesn't matter, +// as they depend on the certificate. Pick one to get a stable order. +var cipherSuitesPreferenceOrder = []uint16{ + // AEADs w/ ECDHE + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + + // CBC w/ ECDHE + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + + // AEADs w/o ECDHE + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + + // CBC w/o ECDHE + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + + // 3DES + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + + // CBC_SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + + // RC4 + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_SHA, +} + +var cipherSuitesPreferenceOrderNoAES = []uint16{ + // ChaCha20Poly1305 + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + + // AES-GCM w/ ECDHE + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + + // The rest of cipherSuitesPreferenceOrder. + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_SHA, +} + +// disabledCipherSuites are not used unless explicitly listed in +// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder. +var disabledCipherSuites = []uint16{ + // CBC_SHA256 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA256, + + // RC4 + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_SHA, +} + +var ( + defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites) + defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen] +) + +// defaultCipherSuitesTLS13 is also the preference order, since there are no +// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as +// cipherSuitesPreferenceOrder applies. +var defaultCipherSuitesTLS13 = []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, +} + +var defaultCipherSuitesTLS13NoAES = []uint16{ + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, +} + +var aesgcmCiphers = map[uint16]bool{ + // TLS 1.2 + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true, + // TLS 1.3 + TLS_AES_128_GCM_SHA256: true, + TLS_AES_256_GCM_SHA384: true, +} + +var nonAESGCMAEADCiphers = map[uint16]bool{ + // TLS 1.2 + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true, + // TLS 1.3 + TLS_CHACHA20_POLY1305_SHA256: true, +} + +// aesgcmPreferred returns whether the first known cipher in the preference list +// is an AES-GCM cipher, implying the peer has hardware support for it. +func aesgcmPreferred(ciphers []uint16) bool { + for _, cID := range ciphers { + if c := cipherSuiteByID(cID); c != nil { + return aesgcmCiphers[cID] + } + if c := cipherSuiteTLS13ByID(cID); c != nil { + return aesgcmCiphers[cID] + } + } + return false +} + +func cipherRC4(key, iv []byte, isRead bool) any { + cipher, _ := rc4.NewCipher(key) + return cipher +} + +func cipher3DES(key, iv []byte, isRead bool) any { + block, _ := des.NewTripleDESCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + +func cipherAES(key, iv []byte, isRead bool) any { + block, _ := aes.NewCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + +// macSHA1 returns a SHA-1 based constant time MAC. +func macSHA1(key []byte) hash.Hash { + h := sha1.New + h = newConstantTimeHash(h) + return hmac.New(h, key) +} + +// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and +// is currently only used in disabled-by-default cipher suites. +func macSHA256(key []byte) hash.Hash { + return hmac.New(sha256.New, key) +} + +type aead interface { + cipher.AEAD + + // explicitNonceLen returns the number of bytes of explicit nonce + // included in each record. This is eight for older AEADs and + // zero for modern ones. + explicitNonceLen() int +} + +const ( + aeadNonceLength = 12 + noncePrefixLength = 4 +) + +// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to +// each call. +type prefixNonceAEAD struct { + // nonce contains the fixed part of the nonce in the first four bytes. + nonce [aeadNonceLength]byte + aead cipher.AEAD +} + +func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength } +func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() } +func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() } + +func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + copy(f.nonce[4:], nonce) + return f.aead.Seal(out, f.nonce[:], plaintext, additionalData) +} + +func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { + copy(f.nonce[4:], nonce) + return f.aead.Open(out, f.nonce[:], ciphertext, additionalData) +} + +// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce +// before each call. +type xorNonceAEAD struct { + nonceMask [aeadNonceLength]byte + aead cipher.AEAD +} + +func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number +func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } +func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } + +func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result +} + +func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result, err +} + +func aeadAESGCM(key, noncePrefix []byte) aead { + if len(noncePrefix) != noncePrefixLength { + panic("tls: internal error: wrong nonce length") + } + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + var aead cipher.AEAD + aead, err = cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + ret := &prefixNonceAEAD{aead: aead} + copy(ret.nonce[:], noncePrefix) + return ret +} + +// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 +func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { + return aeadAESGCMTLS13(key, fixedNonce) +} + +func aeadAESGCMTLS13(key, nonceMask []byte) aead { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +func aeadChaCha20Poly1305(key, nonceMask []byte) aead { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aead, err := chacha20poly1305.New(key) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +type constantTimeHash interface { + hash.Hash + ConstantTimeSum(b []byte) []byte +} + +// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces +// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC. +type cthWrapper struct { + h constantTimeHash +} + +func (c *cthWrapper) Size() int { return c.h.Size() } +func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() } +func (c *cthWrapper) Reset() { c.h.Reset() } +func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) } +func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) } + +func newConstantTimeHash(h func() hash.Hash) func() hash.Hash { + return func() hash.Hash { + return &cthWrapper{h().(constantTimeHash)} + } +} + +// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3. +func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte { + h.Reset() + h.Write(seq) + h.Write(header) + h.Write(data) + res := h.Sum(out) + if extra != nil { + h.Write(extra) + } + return res +} + +func rsaKA(version uint16) keyAgreement { + return rsaKeyAgreement{} +} + +func ecdheECDSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + isRSA: false, + version: version, + } +} + +func ecdheRSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + isRSA: true, + version: version, + } +} + +// mutualCipherSuite returns a cipherSuite given a list of supported +// ciphersuites and the id requested by the peer. +func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { + for _, id := range have { + if id == want { + return cipherSuiteByID(id) + } + } + return nil +} + +func cipherSuiteByID(id uint16) *cipherSuite { + for _, cipherSuite := range cipherSuites { + if cipherSuite.id == id { + return cipherSuite + } + } + return nil +} + +func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 { + for _, id := range have { + if id == want { + return cipherSuiteTLS13ByID(id) + } + } + return nil +} + +func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 { + for _, cipherSuite := range cipherSuitesTLS13 { + if cipherSuite.id == id { + return cipherSuite + } + } + return nil +} + +// A list of cipher suite IDs that are, or have been, implemented by this +// package. +// +// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml +const ( + // TLS 1.0 - 1.2 cipher suites. + TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 + TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a + TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 + TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c + TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c + TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a + TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027 + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8 + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9 + + // TLS 1.3 cipher suites. + TLS_AES_128_GCM_SHA256 uint16 = 0x1301 + TLS_AES_256_GCM_SHA384 uint16 = 0x1302 + TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 + + // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator + // that the client is doing version fallback. See RFC 7507. + TLS_FALLBACK_SCSV uint16 = 0x5600 + + // Legacy names for the corresponding cipher suites with the correct _SHA256 + // suffix, retained for backward compatibility. + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/common.go b/vendor/github.com/marten-seemann/qtls-go1-19/common.go new file mode 100644 index 00000000..6670ce2f --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/common.go @@ -0,0 +1,1512 @@ +// 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 LICENSE file. + +package qtls + +import ( + "bytes" + "container/list" + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha512" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + "net" + "strings" + "sync" + "time" +) + +const ( + VersionTLS10 = 0x0301 + VersionTLS11 = 0x0302 + VersionTLS12 = 0x0303 + VersionTLS13 = 0x0304 + + // Deprecated: SSLv3 is cryptographically broken, and is no longer + // supported by this package. See golang.org/issue/32716. + VersionSSL30 = 0x0300 +) + +const ( + maxPlaintext = 16384 // maximum plaintext payload length + maxCiphertext = 16384 + 2048 // maximum ciphertext payload length + maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3 + recordHeaderLen = 5 // record header length + maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) + maxUselessRecords = 16 // maximum number of consecutive non-advancing records +) + +// TLS record types. +type recordType uint8 + +const ( + recordTypeChangeCipherSpec recordType = 20 + recordTypeAlert recordType = 21 + recordTypeHandshake recordType = 22 + recordTypeApplicationData recordType = 23 +) + +// TLS handshake message types. +const ( + typeHelloRequest uint8 = 0 + typeClientHello uint8 = 1 + typeServerHello uint8 = 2 + typeNewSessionTicket uint8 = 4 + typeEndOfEarlyData uint8 = 5 + typeEncryptedExtensions uint8 = 8 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeKeyUpdate uint8 = 24 + typeNextProtocol uint8 = 67 // Not IANA assigned + typeMessageHash uint8 = 254 // synthetic message +) + +// TLS compression types. +const ( + compressionNone uint8 = 0 +) + +type Extension struct { + Type uint16 + Data []byte +} + +// TLS extension numbers +const ( + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionALPN uint16 = 16 + extensionSCT uint16 = 18 + extensionSessionTicket uint16 = 35 + extensionPreSharedKey uint16 = 41 + extensionEarlyData uint16 = 42 + extensionSupportedVersions uint16 = 43 + extensionCookie uint16 = 44 + extensionPSKModes uint16 = 45 + extensionCertificateAuthorities uint16 = 47 + extensionSignatureAlgorithmsCert uint16 = 50 + extensionKeyShare uint16 = 51 + extensionRenegotiationInfo uint16 = 0xff01 +) + +// TLS signaling cipher suite values +const ( + scsvRenegotiation uint16 = 0x00ff +) + +type EncryptionLevel uint8 + +const ( + EncryptionHandshake EncryptionLevel = iota + Encryption0RTT + EncryptionApplication +) + +// CurveID is a tls.CurveID +type CurveID = tls.CurveID + +const ( + CurveP256 CurveID = 23 + CurveP384 CurveID = 24 + CurveP521 CurveID = 25 + X25519 CurveID = 29 +) + +// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8. +type keyShare struct { + group CurveID + data []byte +} + +// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9. +const ( + pskModePlain uint8 = 0 + pskModeDHE uint8 = 1 +) + +// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved +// session. See RFC 8446, Section 4.2.11. +type pskIdentity struct { + label []byte + obfuscatedTicketAge uint32 +} + +// TLS Elliptic Curve Point Formats +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 +const ( + pointFormatUncompressed uint8 = 0 +) + +// TLS CertificateStatusType (RFC 3546) +const ( + statusTypeOCSP uint8 = 1 +) + +// Certificate types (for certificateRequestMsg) +const ( + certTypeRSASign = 1 + certTypeECDSASign = 64 // ECDSA or EdDSA keys, see RFC 8422, Section 3. +) + +// Signature algorithms (for internal signaling use). Starting at 225 to avoid overlap with +// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do. +const ( + signaturePKCS1v15 uint8 = iota + 225 + signatureRSAPSS + signatureECDSA + signatureEd25519 +) + +// directSigning is a standard Hash value that signals that no pre-hashing +// should be performed, and that the input should be signed directly. It is the +// hash function associated with the Ed25519 signature scheme. +var directSigning crypto.Hash = 0 + +// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that +// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+ +// CertificateRequest. The two fields are merged to match with TLS 1.3. +// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc. +var defaultSupportedSignatureAlgorithms = []SignatureScheme{ + PSSWithSHA256, + ECDSAWithP256AndSHA256, + Ed25519, + PSSWithSHA384, + PSSWithSHA512, + PKCS1WithSHA256, + PKCS1WithSHA384, + PKCS1WithSHA512, + ECDSAWithP384AndSHA384, + ECDSAWithP521AndSHA512, + PKCS1WithSHA1, + ECDSAWithSHA1, +} + +// helloRetryRequestRandom is set as the Random value of a ServerHello +// to signal that the message is actually a HelloRetryRequest. +var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3. + 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, + 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, + 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, + 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, +} + +const ( + // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server + // random as a downgrade protection if the server would be capable of + // negotiating a higher version. See RFC 8446, Section 4.1.3. + downgradeCanaryTLS12 = "DOWNGRD\x01" + downgradeCanaryTLS11 = "DOWNGRD\x00" +) + +// testingOnlyForceDowngradeCanary is set in tests to force the server side to +// include downgrade canaries even if it's using its highers supported version. +var testingOnlyForceDowngradeCanary bool + +type ConnectionState = tls.ConnectionState + +// ConnectionState records basic TLS details about the connection. +type connectionState struct { + // Version is the TLS version used by the connection (e.g. VersionTLS12). + Version uint16 + + // HandshakeComplete is true if the handshake has concluded. + HandshakeComplete bool + + // DidResume is true if this connection was successfully resumed from a + // previous session with a session ticket or similar mechanism. + DidResume bool + + // CipherSuite is the cipher suite negotiated for the connection (e.g. + // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256). + CipherSuite uint16 + + // NegotiatedProtocol is the application protocol negotiated with ALPN. + NegotiatedProtocol string + + // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation. + // + // Deprecated: this value is always true. + NegotiatedProtocolIsMutual bool + + // ServerName is the value of the Server Name Indication extension sent by + // the client. It's available both on the server and on the client side. + ServerName string + + // PeerCertificates are the parsed certificates sent by the peer, in the + // order in which they were sent. The first element is the leaf certificate + // that the connection is verified against. + // + // On the client side, it can't be empty. On the server side, it can be + // empty if Config.ClientAuth is not RequireAnyClientCert or + // RequireAndVerifyClientCert. + PeerCertificates []*x509.Certificate + + // VerifiedChains is a list of one or more chains where the first element is + // PeerCertificates[0] and the last element is from Config.RootCAs (on the + // client side) or Config.ClientCAs (on the server side). + // + // On the client side, it's set if Config.InsecureSkipVerify is false. On + // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven + // (and the peer provided a certificate) or RequireAndVerifyClientCert. + VerifiedChains [][]*x509.Certificate + + // SignedCertificateTimestamps is a list of SCTs provided by the peer + // through the TLS handshake for the leaf certificate, if any. + SignedCertificateTimestamps [][]byte + + // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP) + // response provided by the peer for the leaf certificate, if any. + OCSPResponse []byte + + // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929, + // Section 3). This value will be nil for TLS 1.3 connections and for all + // resumed connections. + // + // Deprecated: there are conditions in which this value might not be unique + // to a connection. See the Security Considerations sections of RFC 5705 and + // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings. + TLSUnique []byte + + // ekm is a closure exposed via ExportKeyingMaterial. + ekm func(label string, context []byte, length int) ([]byte, error) +} + +type ConnectionStateWith0RTT struct { + ConnectionState + + Used0RTT bool // true if 0-RTT was both offered and accepted +} + +// ClientAuthType is tls.ClientAuthType +type ClientAuthType = tls.ClientAuthType + +const ( + NoClientCert = tls.NoClientCert + RequestClientCert = tls.RequestClientCert + RequireAnyClientCert = tls.RequireAnyClientCert + VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven + RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert +) + +// requiresClientCert reports whether the ClientAuthType requires a client +// certificate to be provided. +func requiresClientCert(c ClientAuthType) bool { + switch c { + case RequireAnyClientCert, RequireAndVerifyClientCert: + return true + default: + return false + } +} + +// ClientSessionState contains the state needed by clients to resume TLS +// sessions. +type ClientSessionState = tls.ClientSessionState + +type clientSessionState struct { + sessionTicket []uint8 // Encrypted ticket used for session resumption with server + vers uint16 // TLS version negotiated for the session + cipherSuite uint16 // Ciphersuite negotiated for the session + masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret + serverCertificates []*x509.Certificate // Certificate chain presented by the server + verifiedChains [][]*x509.Certificate // Certificate chains we built for verification + receivedAt time.Time // When the session ticket was received from the server + ocspResponse []byte // Stapled OCSP response presented by the server + scts [][]byte // SCTs presented by the server + + // TLS 1.3 fields. + nonce []byte // Ticket nonce sent by the server, to derive PSK + useBy time.Time // Expiration of the ticket lifetime as set by the server + ageAdd uint32 // Random obfuscation factor for sending the ticket age +} + +// ClientSessionCache is a cache of ClientSessionState objects that can be used +// by a client to resume a TLS session with a given server. ClientSessionCache +// implementations should expect to be called concurrently from different +// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not +// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which +// are supported via this interface. +//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/marten-seemann/qtls-go1-17 ClientSessionCache" +type ClientSessionCache = tls.ClientSessionCache + +// SignatureScheme is a tls.SignatureScheme +type SignatureScheme = tls.SignatureScheme + +const ( + // RSASSA-PKCS1-v1_5 algorithms. + PKCS1WithSHA256 SignatureScheme = 0x0401 + PKCS1WithSHA384 SignatureScheme = 0x0501 + PKCS1WithSHA512 SignatureScheme = 0x0601 + + // RSASSA-PSS algorithms with public key OID rsaEncryption. + PSSWithSHA256 SignatureScheme = 0x0804 + PSSWithSHA384 SignatureScheme = 0x0805 + PSSWithSHA512 SignatureScheme = 0x0806 + + // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3. + ECDSAWithP256AndSHA256 SignatureScheme = 0x0403 + ECDSAWithP384AndSHA384 SignatureScheme = 0x0503 + ECDSAWithP521AndSHA512 SignatureScheme = 0x0603 + + // EdDSA algorithms. + Ed25519 SignatureScheme = 0x0807 + + // Legacy signature and hash algorithms for TLS 1.2. + PKCS1WithSHA1 SignatureScheme = 0x0201 + ECDSAWithSHA1 SignatureScheme = 0x0203 +) + +// ClientHelloInfo contains information from a ClientHello message in order to +// guide application logic in the GetCertificate and GetConfigForClient callbacks. +type ClientHelloInfo = tls.ClientHelloInfo + +type clientHelloInfo struct { + // CipherSuites lists the CipherSuites supported by the client (e.g. + // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256). + CipherSuites []uint16 + + // ServerName indicates the name of the server requested by the client + // in order to support virtual hosting. ServerName is only set if the + // client is using SNI (see RFC 4366, Section 3.1). + ServerName string + + // SupportedCurves lists the elliptic curves supported by the client. + // SupportedCurves is set only if the Supported Elliptic Curves + // Extension is being used (see RFC 4492, Section 5.1.1). + SupportedCurves []CurveID + + // SupportedPoints lists the point formats supported by the client. + // SupportedPoints is set only if the Supported Point Formats Extension + // is being used (see RFC 4492, Section 5.1.2). + SupportedPoints []uint8 + + // SignatureSchemes lists the signature and hash schemes that the client + // is willing to verify. SignatureSchemes is set only if the Signature + // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1). + SignatureSchemes []SignatureScheme + + // SupportedProtos lists the application protocols supported by the client. + // SupportedProtos is set only if the Application-Layer Protocol + // Negotiation Extension is being used (see RFC 7301, Section 3.1). + // + // Servers can select a protocol by setting Config.NextProtos in a + // GetConfigForClient return value. + SupportedProtos []string + + // SupportedVersions lists the TLS versions supported by the client. + // For TLS versions less than 1.3, this is extrapolated from the max + // version advertised by the client, so values other than the greatest + // might be rejected if used. + SupportedVersions []uint16 + + // Conn is the underlying net.Conn for the connection. Do not read + // from, or write to, this connection; that will cause the TLS + // connection to fail. + Conn net.Conn + + // config is embedded by the GetCertificate or GetConfigForClient caller, + // for use with SupportsCertificate. + config *Config + + // ctx is the context of the handshake that is in progress. + ctx context.Context +} + +// Context returns the context of the handshake that is in progress. +// This context is a child of the context passed to HandshakeContext, +// if any, and is canceled when the handshake concludes. +func (c *clientHelloInfo) Context() context.Context { + return c.ctx +} + +// CertificateRequestInfo contains information from a server's +// CertificateRequest message, which is used to demand a certificate and proof +// of control from a client. +type CertificateRequestInfo = tls.CertificateRequestInfo + +type certificateRequestInfo struct { + // AcceptableCAs contains zero or more, DER-encoded, X.501 + // Distinguished Names. These are the names of root or intermediate CAs + // that the server wishes the returned certificate to be signed by. An + // empty slice indicates that the server has no preference. + AcceptableCAs [][]byte + + // SignatureSchemes lists the signature schemes that the server is + // willing to verify. + SignatureSchemes []SignatureScheme + + // Version is the TLS version that was negotiated for this connection. + Version uint16 + + // ctx is the context of the handshake that is in progress. + ctx context.Context +} + +// Context returns the context of the handshake that is in progress. +// This context is a child of the context passed to HandshakeContext, +// if any, and is canceled when the handshake concludes. +func (c *certificateRequestInfo) Context() context.Context { + return c.ctx +} + +// RenegotiationSupport enumerates the different levels of support for TLS +// renegotiation. TLS renegotiation is the act of performing subsequent +// handshakes on a connection after the first. This significantly complicates +// the state machine and has been the source of numerous, subtle security +// issues. Initiating a renegotiation is not supported, but support for +// accepting renegotiation requests may be enabled. +// +// Even when enabled, the server may not change its identity between handshakes +// (i.e. the leaf certificate must be the same). Additionally, concurrent +// handshake and application data flow is not permitted so renegotiation can +// only be used with protocols that synchronise with the renegotiation, such as +// HTTPS. +// +// Renegotiation is not defined in TLS 1.3. +type RenegotiationSupport = tls.RenegotiationSupport + +const ( + // RenegotiateNever disables renegotiation. + RenegotiateNever = tls.RenegotiateNever + + // RenegotiateOnceAsClient allows a remote server to request + // renegotiation once per connection. + RenegotiateOnceAsClient = tls.RenegotiateOnceAsClient + + // RenegotiateFreelyAsClient allows a remote server to repeatedly + // request renegotiation. + RenegotiateFreelyAsClient = tls.RenegotiateFreelyAsClient +) + +// A Config structure is used to configure a TLS client or server. +// After one has been passed to a TLS function it must not be +// modified. A Config may be reused; the tls package will also not +// modify it. +type Config = tls.Config + +type config struct { + // Rand provides the source of entropy for nonces and RSA blinding. + // If Rand is nil, TLS uses the cryptographic random reader in package + // crypto/rand. + // The Reader must be safe for use by multiple goroutines. + Rand io.Reader + + // Time returns the current time as the number of seconds since the epoch. + // If Time is nil, TLS uses time.Now. + Time func() time.Time + + // Certificates contains one or more certificate chains to present to the + // other side of the connection. The first certificate compatible with the + // peer's requirements is selected automatically. + // + // Server configurations must set one of Certificates, GetCertificate or + // GetConfigForClient. Clients doing client-authentication may set either + // Certificates or GetClientCertificate. + // + // Note: if there are multiple Certificates, and they don't have the + // optional field Leaf set, certificate selection will incur a significant + // per-handshake performance cost. + Certificates []Certificate + + // NameToCertificate maps from a certificate name to an element of + // Certificates. Note that a certificate name can be of the form + // '*.example.com' and so doesn't have to be a domain name as such. + // + // Deprecated: NameToCertificate only allows associating a single + // certificate with a given name. Leave this field nil to let the library + // select the first compatible chain from Certificates. + NameToCertificate map[string]*Certificate + + // GetCertificate returns a Certificate based on the given + // ClientHelloInfo. It will only be called if the client supplies SNI + // information or if Certificates is empty. + // + // If GetCertificate is nil or returns nil, then the certificate is + // retrieved from NameToCertificate. If NameToCertificate is nil, the + // best element of Certificates will be used. + GetCertificate func(*ClientHelloInfo) (*Certificate, error) + + // GetClientCertificate, if not nil, is called when a server requests a + // certificate from a client. If set, the contents of Certificates will + // be ignored. + // + // If GetClientCertificate returns an error, the handshake will be + // aborted and that error will be returned. Otherwise + // GetClientCertificate must return a non-nil Certificate. If + // Certificate.Certificate is empty then no certificate will be sent to + // the server. If this is unacceptable to the server then it may abort + // the handshake. + // + // GetClientCertificate may be called multiple times for the same + // connection if renegotiation occurs or if TLS 1.3 is in use. + GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) + + // GetConfigForClient, if not nil, is called after a ClientHello is + // received from a client. It may return a non-nil Config in order to + // change the Config that will be used to handle this connection. If + // the returned Config is nil, the original Config will be used. The + // Config returned by this callback may not be subsequently modified. + // + // If GetConfigForClient is nil, the Config passed to Server() will be + // used for all connections. + // + // If SessionTicketKey was explicitly set on the returned Config, or if + // SetSessionTicketKeys was called on the returned Config, those keys will + // be used. Otherwise, the original Config keys will be used (and possibly + // rotated if they are automatically managed). + GetConfigForClient func(*ClientHelloInfo) (*Config, error) + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification by either a TLS client or server. It + // receives the raw ASN.1 certificates provided by the peer and also + // any verified chains that normal processing found. If it returns a + // non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled by + // setting InsecureSkipVerify, or (for a server) when ClientAuth is + // RequestClientCert or RequireAnyClientCert, then this callback will + // be considered but the verifiedChains argument will always be nil. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + + // VerifyConnection, if not nil, is called after normal certificate + // verification and after VerifyPeerCertificate by either a TLS client + // or server. If it returns a non-nil error, the handshake is aborted + // and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. This callback will run for all connections + // regardless of InsecureSkipVerify or ClientAuth settings. + VerifyConnection func(ConnectionState) error + + // RootCAs defines the set of root certificate authorities + // that clients use when verifying server certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *x509.CertPool + + // NextProtos is a list of supported application level protocols, in + // order of preference. If both peers support ALPN, the selected + // protocol will be one from this list, and the connection will fail + // if there is no mutually supported protocol. If NextProtos is empty + // or the peer doesn't support ALPN, the connection will succeed and + // ConnectionState.NegotiatedProtocol will be empty. + NextProtos []string + + // ServerName is used to verify the hostname on the returned + // certificates unless InsecureSkipVerify is given. It is also included + // in the client's handshake to support virtual hosting unless it is + // an IP address. + ServerName string + + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // InsecureSkipVerify controls whether a client verifies the server's + // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls + // accepts any certificate presented by the server and any host name in that + // certificate. In this mode, TLS is susceptible to machine-in-the-middle + // attacks unless custom verification is used. This should be used only for + // testing or in combination with VerifyConnection or VerifyPeerCertificate. + InsecureSkipVerify bool + + // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of + // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable. + // + // If CipherSuites is nil, a safe default list is used. The default cipher + // suites might change over time. + CipherSuites []uint16 + + // PreferServerCipherSuites is a legacy field and has no effect. + // + // It used to control whether the server would follow the client's or the + // server's preference. Servers now select the best mutually supported + // cipher suite based on logic that takes into account inferred client + // hardware, server hardware, and security. + // + // Deprecated: PreferServerCipherSuites is ignored. + PreferServerCipherSuites bool + + // SessionTicketsDisabled may be set to true to disable session ticket and + // PSK (resumption) support. Note that on clients, session ticket support is + // also disabled if ClientSessionCache is nil. + SessionTicketsDisabled bool + + // SessionTicketKey is used by TLS servers to provide session resumption. + // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled + // with random data before the first server handshake. + // + // Deprecated: if this field is left at zero, session ticket keys will be + // automatically rotated every day and dropped after seven days. For + // customizing the rotation schedule or synchronizing servers that are + // terminating connections for the same host, use SetSessionTicketKeys. + SessionTicketKey [32]byte + + // ClientSessionCache is a cache of ClientSessionState entries for TLS + // session resumption. It is only used by clients. + ClientSessionCache ClientSessionCache + + // MinVersion contains the minimum TLS version that is acceptable. + // + // By default, TLS 1.2 is currently used as the minimum when acting as a + // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum + // supported by this package, both as a client and as a server. + // + // The client-side default can temporarily be reverted to TLS 1.0 by + // including the value "x509sha1=1" in the GODEBUG environment variable. + // Note that this option will be removed in Go 1.19 (but it will still be + // possible to set this field to VersionTLS10 explicitly). + MinVersion uint16 + + // MaxVersion contains the maximum TLS version that is acceptable. + // + // By default, the maximum version supported by this package is used, + // which is currently TLS 1.3. + MaxVersion uint16 + + // CurvePreferences contains the elliptic curves that will be used in + // an ECDHE handshake, in preference order. If empty, the default will + // be used. The client will use the first preference as the type for + // its key share in TLS 1.3. This may change in the future. + CurvePreferences []CurveID + + // DynamicRecordSizingDisabled disables adaptive sizing of TLS records. + // When true, the largest possible TLS record size is always used. When + // false, the size of TLS records may be adjusted in an attempt to + // improve latency. + DynamicRecordSizingDisabled bool + + // Renegotiation controls what types of renegotiation are supported. + // The default, none, is correct for the vast majority of applications. + Renegotiation RenegotiationSupport + + // KeyLogWriter optionally specifies a destination for TLS master secrets + // in NSS key log format that can be used to allow external programs + // such as Wireshark to decrypt TLS connections. + // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + // Use of KeyLogWriter compromises security and should only be + // used for debugging. + KeyLogWriter io.Writer + + // mutex protects sessionTicketKeys and autoSessionTicketKeys. + mutex sync.RWMutex + // sessionTicketKeys contains zero or more ticket keys. If set, it means the + // the keys were set with SessionTicketKey or SetSessionTicketKeys. The + // first key is used for new tickets and any subsequent keys can be used to + // decrypt old tickets. The slice contents are not protected by the mutex + // and are immutable. + sessionTicketKeys []ticketKey + // autoSessionTicketKeys is like sessionTicketKeys but is owned by the + // auto-rotation logic. See Config.ticketKeys. + autoSessionTicketKeys []ticketKey +} + +// A RecordLayer handles encrypting and decrypting of TLS messages. +type RecordLayer interface { + SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) + SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) + ReadHandshakeMessage() ([]byte, error) + WriteRecord([]byte) (int, error) + SendAlert(uint8) +} + +type ExtraConfig struct { + // GetExtensions, if not nil, is called before a message that allows + // sending of extensions is sent. + // Currently only implemented for the ClientHello message (for the client) + // and for the EncryptedExtensions message (for the server). + // Only valid for TLS 1.3. + GetExtensions func(handshakeMessageType uint8) []Extension + + // ReceivedExtensions, if not nil, is called when a message that allows the + // inclusion of extensions is received. + // It is called with an empty slice of extensions, if the message didn't + // contain any extensions. + // Currently only implemented for the ClientHello message (sent by the + // client) and for the EncryptedExtensions message (sent by the server). + // Only valid for TLS 1.3. + ReceivedExtensions func(handshakeMessageType uint8, exts []Extension) + + // AlternativeRecordLayer is used by QUIC + AlternativeRecordLayer RecordLayer + + // Enforce the selection of a supported application protocol. + // Only works for TLS 1.3. + // If enabled, client and server have to agree on an application protocol. + // Otherwise, connection establishment fails. + EnforceNextProtoSelection bool + + // If MaxEarlyData is greater than 0, the client will be allowed to send early + // data when resuming a session. + // Requires the AlternativeRecordLayer to be set. + // + // It has no meaning on the client. + MaxEarlyData uint32 + + // The Accept0RTT callback is called when the client offers 0-RTT. + // The server then has to decide if it wants to accept or reject 0-RTT. + // It is only used for servers. + Accept0RTT func(appData []byte) bool + + // 0RTTRejected is called when the server rejectes 0-RTT. + // It is only used for clients. + Rejected0RTT func() + + // If set, the client will export the 0-RTT key when resuming a session that + // allows sending of early data. + // Requires the AlternativeRecordLayer to be set. + // + // It has no meaning to the server. + Enable0RTT bool + + // Is called when the client saves a session ticket to the session ticket. + // This gives the application the opportunity to save some data along with the ticket, + // which can be restored when the session ticket is used. + GetAppDataForSessionState func() []byte + + // Is called when the client uses a session ticket. + // Restores the application data that was saved earlier on GetAppDataForSessionTicket. + SetAppDataFromSessionState func([]byte) +} + +// Clone clones. +func (c *ExtraConfig) Clone() *ExtraConfig { + return &ExtraConfig{ + GetExtensions: c.GetExtensions, + ReceivedExtensions: c.ReceivedExtensions, + AlternativeRecordLayer: c.AlternativeRecordLayer, + EnforceNextProtoSelection: c.EnforceNextProtoSelection, + MaxEarlyData: c.MaxEarlyData, + Enable0RTT: c.Enable0RTT, + Accept0RTT: c.Accept0RTT, + Rejected0RTT: c.Rejected0RTT, + GetAppDataForSessionState: c.GetAppDataForSessionState, + SetAppDataFromSessionState: c.SetAppDataFromSessionState, + } +} + +func (c *ExtraConfig) usesAlternativeRecordLayer() bool { + return c != nil && c.AlternativeRecordLayer != nil +} + +const ( + // ticketKeyNameLen is the number of bytes of identifier that is prepended to + // an encrypted session ticket in order to identify the key used to encrypt it. + ticketKeyNameLen = 16 + + // ticketKeyLifetime is how long a ticket key remains valid and can be used to + // resume a client connection. + ticketKeyLifetime = 7 * 24 * time.Hour // 7 days + + // ticketKeyRotation is how often the server should rotate the session ticket key + // that is used for new tickets. + ticketKeyRotation = 24 * time.Hour +) + +// ticketKey is the internal representation of a session ticket key. +type ticketKey struct { + // keyName is an opaque byte string that serves to identify the session + // ticket key. It's exposed as plaintext in every session ticket. + keyName [ticketKeyNameLen]byte + aesKey [16]byte + hmacKey [16]byte + // created is the time at which this ticket key was created. See Config.ticketKeys. + created time.Time +} + +// ticketKeyFromBytes converts from the external representation of a session +// ticket key to a ticketKey. Externally, session ticket keys are 32 random +// bytes and this function expands that into sufficient name and key material. +func (c *config) ticketKeyFromBytes(b [32]byte) (key ticketKey) { + hashed := sha512.Sum512(b[:]) + copy(key.keyName[:], hashed[:ticketKeyNameLen]) + copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16]) + copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32]) + key.created = c.time() + return key +} + +// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session +// ticket, and the lifetime we set for tickets we send. +const maxSessionTicketLifetime = 7 * 24 * time.Hour + +// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a Config that is +// being used concurrently by a TLS client or server. +func (c *config) Clone() *config { + if c == nil { + return nil + } + c.mutex.RLock() + defer c.mutex.RUnlock() + return &config{ + Rand: c.Rand, + Time: c.Time, + Certificates: c.Certificates, + NameToCertificate: c.NameToCertificate, + GetCertificate: c.GetCertificate, + GetClientCertificate: c.GetClientCertificate, + GetConfigForClient: c.GetConfigForClient, + VerifyPeerCertificate: c.VerifyPeerCertificate, + VerifyConnection: c.VerifyConnection, + RootCAs: c.RootCAs, + NextProtos: c.NextProtos, + ServerName: c.ServerName, + ClientAuth: c.ClientAuth, + ClientCAs: c.ClientCAs, + InsecureSkipVerify: c.InsecureSkipVerify, + CipherSuites: c.CipherSuites, + PreferServerCipherSuites: c.PreferServerCipherSuites, + SessionTicketsDisabled: c.SessionTicketsDisabled, + SessionTicketKey: c.SessionTicketKey, + ClientSessionCache: c.ClientSessionCache, + MinVersion: c.MinVersion, + MaxVersion: c.MaxVersion, + CurvePreferences: c.CurvePreferences, + DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, + Renegotiation: c.Renegotiation, + KeyLogWriter: c.KeyLogWriter, + sessionTicketKeys: c.sessionTicketKeys, + autoSessionTicketKeys: c.autoSessionTicketKeys, + } +} + +// deprecatedSessionTicketKey is set as the prefix of SessionTicketKey if it was +// randomized for backwards compatibility but is not in use. +var deprecatedSessionTicketKey = []byte("DEPRECATED") + +// initLegacySessionTicketKeyRLocked ensures the legacy SessionTicketKey field is +// randomized if empty, and that sessionTicketKeys is populated from it otherwise. +func (c *config) initLegacySessionTicketKeyRLocked() { + // Don't write if SessionTicketKey is already defined as our deprecated string, + // or if it is defined by the user but sessionTicketKeys is already set. + if c.SessionTicketKey != [32]byte{} && + (bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) || len(c.sessionTicketKeys) > 0) { + return + } + + // We need to write some data, so get an exclusive lock and re-check any conditions. + c.mutex.RUnlock() + defer c.mutex.RLock() + c.mutex.Lock() + defer c.mutex.Unlock() + if c.SessionTicketKey == [32]byte{} { + if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { + panic(fmt.Sprintf("tls: unable to generate random session ticket key: %v", err)) + } + // Write the deprecated prefix at the beginning so we know we created + // it. This key with the DEPRECATED prefix isn't used as an actual + // session ticket key, and is only randomized in case the application + // reuses it for some reason. + copy(c.SessionTicketKey[:], deprecatedSessionTicketKey) + } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 { + c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)} + } + +} + +// ticketKeys returns the ticketKeys for this connection. +// If configForClient has explicitly set keys, those will +// be returned. Otherwise, the keys on c will be used and +// may be rotated if auto-managed. +// During rotation, any expired session ticket keys are deleted from +// c.sessionTicketKeys. If the session ticket key that is currently +// encrypting tickets (ie. the first ticketKey in c.sessionTicketKeys) +// is not fresh, then a new session ticket key will be +// created and prepended to c.sessionTicketKeys. +func (c *config) ticketKeys(configForClient *config) []ticketKey { + // If the ConfigForClient callback returned a Config with explicitly set + // keys, use those, otherwise just use the original Config. + if configForClient != nil { + configForClient.mutex.RLock() + if configForClient.SessionTicketsDisabled { + return nil + } + configForClient.initLegacySessionTicketKeyRLocked() + if len(configForClient.sessionTicketKeys) != 0 { + ret := configForClient.sessionTicketKeys + configForClient.mutex.RUnlock() + return ret + } + configForClient.mutex.RUnlock() + } + + c.mutex.RLock() + defer c.mutex.RUnlock() + if c.SessionTicketsDisabled { + return nil + } + c.initLegacySessionTicketKeyRLocked() + if len(c.sessionTicketKeys) != 0 { + return c.sessionTicketKeys + } + // Fast path for the common case where the key is fresh enough. + if len(c.autoSessionTicketKeys) > 0 && c.time().Sub(c.autoSessionTicketKeys[0].created) < ticketKeyRotation { + return c.autoSessionTicketKeys + } + + // autoSessionTicketKeys are managed by auto-rotation. + c.mutex.RUnlock() + defer c.mutex.RLock() + c.mutex.Lock() + defer c.mutex.Unlock() + // Re-check the condition in case it changed since obtaining the new lock. + if len(c.autoSessionTicketKeys) == 0 || c.time().Sub(c.autoSessionTicketKeys[0].created) >= ticketKeyRotation { + var newKey [32]byte + if _, err := io.ReadFull(c.rand(), newKey[:]); err != nil { + panic(fmt.Sprintf("unable to generate random session ticket key: %v", err)) + } + valid := make([]ticketKey, 0, len(c.autoSessionTicketKeys)+1) + valid = append(valid, c.ticketKeyFromBytes(newKey)) + for _, k := range c.autoSessionTicketKeys { + // While rotating the current key, also remove any expired ones. + if c.time().Sub(k.created) < ticketKeyLifetime { + valid = append(valid, k) + } + } + c.autoSessionTicketKeys = valid + } + return c.autoSessionTicketKeys +} + +// SetSessionTicketKeys updates the session ticket keys for a server. +// +// The first key will be used when creating new tickets, while all keys can be +// used for decrypting tickets. It is safe to call this function while the +// server is running in order to rotate the session ticket keys. The function +// will panic if keys is empty. +// +// Calling this function will turn off automatic session ticket key rotation. +// +// If multiple servers are terminating connections for the same host they should +// all have the same session ticket keys. If the session ticket keys leaks, +// previously recorded and future TLS connections using those keys might be +// compromised. +func (c *config) SetSessionTicketKeys(keys [][32]byte) { + if len(keys) == 0 { + panic("tls: keys must have at least one key") + } + + newKeys := make([]ticketKey, len(keys)) + for i, bytes := range keys { + newKeys[i] = c.ticketKeyFromBytes(bytes) + } + + c.mutex.Lock() + c.sessionTicketKeys = newKeys + c.mutex.Unlock() +} + +func (c *config) rand() io.Reader { + r := c.Rand + if r == nil { + return rand.Reader + } + return r +} + +func (c *config) time() time.Time { + t := c.Time + if t == nil { + t = time.Now + } + return t() +} + +func (c *config) cipherSuites() []uint16 { + if needFIPS() { + return fipsCipherSuites(c) + } + if c.CipherSuites != nil { + return c.CipherSuites + } + return defaultCipherSuites +} + +var supportedVersions = []uint16{ + VersionTLS13, + VersionTLS12, + VersionTLS11, + VersionTLS10, +} + +// roleClient and roleServer are meant to call supportedVersions and parents +// with more readability at the callsite. +const roleClient = true +const roleServer = false + +func (c *config) supportedVersions(isClient bool) []uint16 { + versions := make([]uint16, 0, len(supportedVersions)) + for _, v := range supportedVersions { + if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) { + continue + } + if (c == nil || c.MinVersion == 0) && + isClient && v < VersionTLS12 { + continue + } + if c != nil && c.MinVersion != 0 && v < c.MinVersion { + continue + } + if c != nil && c.MaxVersion != 0 && v > c.MaxVersion { + continue + } + versions = append(versions, v) + } + return versions +} + +func (c *config) maxSupportedVersion(isClient bool) uint16 { + supportedVersions := c.supportedVersions(isClient) + if len(supportedVersions) == 0 { + return 0 + } + return supportedVersions[0] +} + +// supportedVersionsFromMax returns a list of supported versions derived from a +// legacy maximum version value. Note that only versions supported by this +// library are returned. Any newer peer will use supportedVersions anyway. +func supportedVersionsFromMax(maxVersion uint16) []uint16 { + versions := make([]uint16, 0, len(supportedVersions)) + for _, v := range supportedVersions { + if v > maxVersion { + continue + } + versions = append(versions, v) + } + return versions +} + +var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} + +func (c *config) curvePreferences() []CurveID { + if needFIPS() { + return fipsCurvePreferences(c) + } + if c == nil || len(c.CurvePreferences) == 0 { + return defaultCurvePreferences + } + return c.CurvePreferences +} + +func (c *config) supportsCurve(curve CurveID) bool { + for _, cc := range c.curvePreferences() { + if cc == curve { + return true + } + } + return false +} + +// mutualVersion returns the protocol version to use given the advertised +// versions of the peer. Priority is given to the peer preference order. +func (c *config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) { + supportedVersions := c.supportedVersions(isClient) + for _, peerVersion := range peerVersions { + for _, v := range supportedVersions { + if v == peerVersion { + return v, true + } + } + } + return 0, false +} + +var errNoCertificates = errors.New("tls: no certificates configured") + +// getCertificate returns the best certificate for the given ClientHelloInfo, +// defaulting to the first element of c.Certificates. +func (c *config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) { + if c.GetCertificate != nil && + (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) { + cert, err := c.GetCertificate(clientHello) + if cert != nil || err != nil { + return cert, err + } + } + + if len(c.Certificates) == 0 { + return nil, errNoCertificates + } + + if len(c.Certificates) == 1 { + // There's only one choice, so no point doing any work. + return &c.Certificates[0], nil + } + + if c.NameToCertificate != nil { + name := strings.ToLower(clientHello.ServerName) + if cert, ok := c.NameToCertificate[name]; ok { + return cert, nil + } + if len(name) > 0 { + labels := strings.Split(name, ".") + labels[0] = "*" + wildcardName := strings.Join(labels, ".") + if cert, ok := c.NameToCertificate[wildcardName]; ok { + return cert, nil + } + } + } + + for _, cert := range c.Certificates { + if err := clientHello.SupportsCertificate(&cert); err == nil { + return &cert, nil + } + } + + // If nothing matches, return the first certificate. + return &c.Certificates[0], nil +} + +// SupportsCertificate returns nil if the provided certificate is supported by +// the client that sent the ClientHello. Otherwise, it returns an error +// describing the reason for the incompatibility. +// +// If this ClientHelloInfo was passed to a GetConfigForClient or GetCertificate +// callback, this method will take into account the associated Config. Note that +// if GetConfigForClient returns a different Config, the change can't be +// accounted for by this method. +// +// This function will call x509.ParseCertificate unless c.Leaf is set, which can +// incur a significant performance cost. +func (chi *clientHelloInfo) SupportsCertificate(c *Certificate) error { + // Note we don't currently support certificate_authorities nor + // signature_algorithms_cert, and don't check the algorithms of the + // signatures on the chain (which anyway are a SHOULD, see RFC 8446, + // Section 4.4.2.2). + + config := chi.config + if config == nil { + config = &Config{} + } + conf := fromConfig(config) + vers, ok := conf.mutualVersion(roleServer, chi.SupportedVersions) + if !ok { + return errors.New("no mutually supported protocol versions") + } + + // If the client specified the name they are trying to connect to, the + // certificate needs to be valid for it. + if chi.ServerName != "" { + x509Cert, err := leafCertificate(c) + if err != nil { + return fmt.Errorf("failed to parse certificate: %w", err) + } + if err := x509Cert.VerifyHostname(chi.ServerName); err != nil { + return fmt.Errorf("certificate is not valid for requested server name: %w", err) + } + } + + // supportsRSAFallback returns nil if the certificate and connection support + // the static RSA key exchange, and unsupported otherwise. The logic for + // supporting static RSA is completely disjoint from the logic for + // supporting signed key exchanges, so we just check it as a fallback. + supportsRSAFallback := func(unsupported error) error { + // TLS 1.3 dropped support for the static RSA key exchange. + if vers == VersionTLS13 { + return unsupported + } + // The static RSA key exchange works by decrypting a challenge with the + // RSA private key, not by signing, so check the PrivateKey implements + // crypto.Decrypter, like *rsa.PrivateKey does. + if priv, ok := c.PrivateKey.(crypto.Decrypter); ok { + if _, ok := priv.Public().(*rsa.PublicKey); !ok { + return unsupported + } + } else { + return unsupported + } + // Finally, there needs to be a mutual cipher suite that uses the static + // RSA key exchange instead of ECDHE. + rsaCipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool { + if c.flags&suiteECDHE != 0 { + return false + } + if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { + return false + } + return true + }) + if rsaCipherSuite == nil { + return unsupported + } + return nil + } + + // If the client sent the signature_algorithms extension, ensure it supports + // schemes we can use with this certificate and TLS version. + if len(chi.SignatureSchemes) > 0 { + if _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil { + return supportsRSAFallback(err) + } + } + + // In TLS 1.3 we are done because supported_groups is only relevant to the + // ECDHE computation, point format negotiation is removed, cipher suites are + // only relevant to the AEAD choice, and static RSA does not exist. + if vers == VersionTLS13 { + return nil + } + + // The only signed key exchange we support is ECDHE. + if !supportsECDHE(conf, chi.SupportedCurves, chi.SupportedPoints) { + return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange")) + } + + var ecdsaCipherSuite bool + if priv, ok := c.PrivateKey.(crypto.Signer); ok { + switch pub := priv.Public().(type) { + case *ecdsa.PublicKey: + var curve CurveID + switch pub.Curve { + case elliptic.P256(): + curve = CurveP256 + case elliptic.P384(): + curve = CurveP384 + case elliptic.P521(): + curve = CurveP521 + default: + return supportsRSAFallback(unsupportedCertificateError(c)) + } + var curveOk bool + for _, c := range chi.SupportedCurves { + if c == curve && conf.supportsCurve(c) { + curveOk = true + break + } + } + if !curveOk { + return errors.New("client doesn't support certificate curve") + } + ecdsaCipherSuite = true + case ed25519.PublicKey: + if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 { + return errors.New("connection doesn't support Ed25519") + } + ecdsaCipherSuite = true + case *rsa.PublicKey: + default: + return supportsRSAFallback(unsupportedCertificateError(c)) + } + } else { + return supportsRSAFallback(unsupportedCertificateError(c)) + } + + // Make sure that there is a mutually supported cipher suite that works with + // this certificate. Cipher suite selection will then apply the logic in + // reverse to pick it. See also serverHandshakeState.cipherSuiteOk. + cipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool { + if c.flags&suiteECDHE == 0 { + return false + } + if c.flags&suiteECSign != 0 { + if !ecdsaCipherSuite { + return false + } + } else { + if ecdsaCipherSuite { + return false + } + } + if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { + return false + } + return true + }) + if cipherSuite == nil { + return supportsRSAFallback(errors.New("client doesn't support any cipher suites compatible with the certificate")) + } + + return nil +} + +// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate +// from the CommonName and SubjectAlternateName fields of each of the leaf +// certificates. +// +// Deprecated: NameToCertificate only allows associating a single certificate +// with a given name. Leave that field nil to let the library select the first +// compatible chain from Certificates. +func (c *config) BuildNameToCertificate() { + c.NameToCertificate = make(map[string]*Certificate) + for i := range c.Certificates { + cert := &c.Certificates[i] + x509Cert, err := leafCertificate(cert) + if err != nil { + continue + } + // If SANs are *not* present, some clients will consider the certificate + // valid for the name in the Common Name. + if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 { + c.NameToCertificate[x509Cert.Subject.CommonName] = cert + } + for _, san := range x509Cert.DNSNames { + c.NameToCertificate[san] = cert + } + } +} + +const ( + keyLogLabelTLS12 = "CLIENT_RANDOM" + keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET" + keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET" + keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET" + keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0" + keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0" +) + +func (c *config) writeKeyLog(label string, clientRandom, secret []byte) error { + if c.KeyLogWriter == nil { + return nil + } + + logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret)) + + writerMutex.Lock() + _, err := c.KeyLogWriter.Write(logLine) + writerMutex.Unlock() + + return err +} + +// writerMutex protects all KeyLogWriters globally. It is rarely enabled, +// and is only for debugging, so a global mutex saves space. +var writerMutex sync.Mutex + +// A Certificate is a chain of one or more certificates, leaf first. +type Certificate = tls.Certificate + +// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing +// the corresponding c.Certificate[0]. +func leafCertificate(c *Certificate) (*x509.Certificate, error) { + if c.Leaf != nil { + return c.Leaf, nil + } + return x509.ParseCertificate(c.Certificate[0]) +} + +type handshakeMessage interface { + marshal() []byte + unmarshal([]byte) bool +} + +// lruSessionCache is a ClientSessionCache implementation that uses an LRU +// caching strategy. +type lruSessionCache struct { + sync.Mutex + + m map[string]*list.Element + q *list.List + capacity int +} + +type lruSessionCacheEntry struct { + sessionKey string + state *ClientSessionState +} + +// NewLRUClientSessionCache returns a ClientSessionCache with the given +// capacity that uses an LRU strategy. If capacity is < 1, a default capacity +// is used instead. +func NewLRUClientSessionCache(capacity int) ClientSessionCache { + const defaultSessionCacheCapacity = 64 + + if capacity < 1 { + capacity = defaultSessionCacheCapacity + } + return &lruSessionCache{ + m: make(map[string]*list.Element), + q: list.New(), + capacity: capacity, + } +} + +// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry +// corresponding to sessionKey is removed from the cache instead. +func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) { + c.Lock() + defer c.Unlock() + + if elem, ok := c.m[sessionKey]; ok { + if cs == nil { + c.q.Remove(elem) + delete(c.m, sessionKey) + } else { + entry := elem.Value.(*lruSessionCacheEntry) + entry.state = cs + c.q.MoveToFront(elem) + } + return + } + + if c.q.Len() < c.capacity { + entry := &lruSessionCacheEntry{sessionKey, cs} + c.m[sessionKey] = c.q.PushFront(entry) + return + } + + elem := c.q.Back() + entry := elem.Value.(*lruSessionCacheEntry) + delete(c.m, entry.sessionKey) + entry.sessionKey = sessionKey + entry.state = cs + c.q.MoveToFront(elem) + c.m[sessionKey] = elem +} + +// Get returns the ClientSessionState value associated with a given key. It +// returns (nil, false) if no value is found. +func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) { + c.Lock() + defer c.Unlock() + + if elem, ok := c.m[sessionKey]; ok { + c.q.MoveToFront(elem) + return elem.Value.(*lruSessionCacheEntry).state, true + } + return nil, false +} + +var emptyConfig Config + +func defaultConfig() *Config { + return &emptyConfig +} + +func unexpectedMessageError(wanted, got any) error { + return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted) +} + +func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool { + for _, s := range supportedSignatureAlgorithms { + if s == sigAlg { + return true + } + } + return false +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/conn.go b/vendor/github.com/marten-seemann/qtls-go1-19/conn.go new file mode 100644 index 00000000..1b275a9f --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/conn.go @@ -0,0 +1,1610 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TLS low level connection and record layer + +package qtls + +import ( + "bytes" + "context" + "crypto/cipher" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "hash" + "io" + "net" + "sync" + "sync/atomic" + "time" +) + +// A Conn represents a secured connection. +// It implements the net.Conn interface. +type Conn struct { + // constant + conn net.Conn + isClient bool + handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake + + // handshakeStatus is 1 if the connection is currently transferring + // application data (i.e. is not currently processing a handshake). + // handshakeStatus == 1 implies handshakeErr == nil. + // This field is only to be accessed with sync/atomic. + handshakeStatus uint32 + // constant after handshake; protected by handshakeMutex + handshakeMutex sync.Mutex + handshakeErr error // error resulting from handshake + vers uint16 // TLS version + haveVers bool // version has been negotiated + config *config // configuration passed to constructor + // handshakes counts the number of handshakes performed on the + // connection so far. If renegotiation is disabled then this is either + // zero or one. + extraConfig *ExtraConfig + + handshakes int + didResume bool // whether this connection was a session resumption + cipherSuite uint16 + ocspResponse []byte // stapled OCSP response + scts [][]byte // signed certificate timestamps from server + peerCertificates []*x509.Certificate + // verifiedChains contains the certificate chains that we built, as + // opposed to the ones presented by the server. + verifiedChains [][]*x509.Certificate + // serverName contains the server name indicated by the client, if any. + serverName string + // secureRenegotiation is true if the server echoed the secure + // renegotiation extension. (This is meaningless as a server because + // renegotiation is not supported in that case.) + secureRenegotiation bool + // ekm is a closure for exporting keying material. + ekm func(label string, context []byte, length int) ([]byte, error) + // For the client: + // resumptionSecret is the resumption_master_secret for handling + // NewSessionTicket messages. nil if config.SessionTicketsDisabled. + // For the server: + // resumptionSecret is the resumption_master_secret for generating + // NewSessionTicket messages. Only used when the alternative record + // layer is set. nil if config.SessionTicketsDisabled. + resumptionSecret []byte + + // ticketKeys is the set of active session ticket keys for this + // connection. The first one is used to encrypt new tickets and + // all are tried to decrypt tickets. + ticketKeys []ticketKey + + // clientFinishedIsFirst is true if the client sent the first Finished + // message during the most recent handshake. This is recorded because + // the first transmitted Finished message is the tls-unique + // channel-binding value. + clientFinishedIsFirst bool + + // closeNotifyErr is any error from sending the alertCloseNotify record. + closeNotifyErr error + // closeNotifySent is true if the Conn attempted to send an + // alertCloseNotify record. + closeNotifySent bool + + // clientFinished and serverFinished contain the Finished message sent + // by the client or server in the most recent handshake. This is + // retained to support the renegotiation extension and tls-unique + // channel-binding. + clientFinished [12]byte + serverFinished [12]byte + + // clientProtocol is the negotiated ALPN protocol. + clientProtocol string + + // input/output + in, out halfConn + rawInput bytes.Buffer // raw input, starting with a record header + input bytes.Reader // application data waiting to be read, from rawInput.Next + hand bytes.Buffer // handshake data waiting to be read + buffering bool // whether records are buffered in sendBuf + sendBuf []byte // a buffer of records waiting to be sent + + // bytesSent counts the bytes of application data sent. + // packetsSent counts packets. + bytesSent int64 + packetsSent int64 + + // retryCount counts the number of consecutive non-advancing records + // received by Conn.readRecord. That is, records that neither advance the + // handshake, nor deliver application data. Protected by in.Mutex. + retryCount int + + // activeCall is an atomic int32; the low bit is whether Close has + // been called. the rest of the bits are the number of goroutines + // in Conn.Write. + activeCall int32 + + used0RTT bool + + tmp [16]byte +} + +// Access to net.Conn methods. +// Cannot just embed net.Conn because that would +// export the struct field too. + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// SetDeadline sets the read and write deadlines associated with the connection. +// A zero value for t means Read and Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline on the underlying connection. +// A zero value for t means Read will not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline on the underlying connection. +// A zero value for t means Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +// NetConn returns the underlying connection that is wrapped by c. +// Note that writing to or reading from this connection directly will corrupt the +// TLS session. +func (c *Conn) NetConn() net.Conn { + return c.conn +} + +// A halfConn represents one direction of the record layer +// connection, either sending or receiving. +type halfConn struct { + sync.Mutex + + err error // first permanent error + version uint16 // protocol version + cipher any // cipher algorithm + mac hash.Hash + seq [8]byte // 64-bit sequence number + + scratchBuf [13]byte // to avoid allocs; interface method args escape + + nextCipher any // next encryption state + nextMac hash.Hash // next MAC algorithm + + trafficSecret []byte // current TLS 1.3 traffic secret + + setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) +} + +type permanentError struct { + err net.Error +} + +func (e *permanentError) Error() string { return e.err.Error() } +func (e *permanentError) Unwrap() error { return e.err } +func (e *permanentError) Timeout() bool { return e.err.Timeout() } +func (e *permanentError) Temporary() bool { return false } + +func (hc *halfConn) setErrorLocked(err error) error { + if e, ok := err.(net.Error); ok { + hc.err = &permanentError{err: e} + } else { + hc.err = err + } + return hc.err +} + +// prepareCipherSpec sets the encryption and MAC states +// that a subsequent changeCipherSpec will use. +func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) { + hc.version = version + hc.nextCipher = cipher + hc.nextMac = mac +} + +// changeCipherSpec changes the encryption and MAC states +// to the ones previously passed to prepareCipherSpec. +func (hc *halfConn) changeCipherSpec() error { + if hc.nextCipher == nil || hc.version == VersionTLS13 { + return alertInternalError + } + hc.cipher = hc.nextCipher + hc.mac = hc.nextMac + hc.nextCipher = nil + hc.nextMac = nil + for i := range hc.seq { + hc.seq[i] = 0 + } + return nil +} + +func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) { + if hc.setKeyCallback != nil { + s := &CipherSuiteTLS13{ + ID: suite.id, + KeyLen: suite.keyLen, + Hash: suite.hash, + AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) }, + } + hc.setKeyCallback(encLevel, s, trafficSecret) + } +} + +func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) { + hc.trafficSecret = secret + key, iv := suite.trafficKey(secret) + hc.cipher = suite.aead(key, iv) + for i := range hc.seq { + hc.seq[i] = 0 + } +} + +// incSeq increments the sequence number. +func (hc *halfConn) incSeq() { + for i := 7; i >= 0; i-- { + hc.seq[i]++ + if hc.seq[i] != 0 { + return + } + } + + // Not allowed to let sequence number wrap. + // Instead, must renegotiate before it does. + // Not likely enough to bother. + panic("TLS: sequence number wraparound") +} + +// explicitNonceLen returns the number of bytes of explicit nonce or IV included +// in each record. Explicit nonces are present only in CBC modes after TLS 1.0 +// and in certain AEAD modes in TLS 1.2. +func (hc *halfConn) explicitNonceLen() int { + if hc.cipher == nil { + return 0 + } + + switch c := hc.cipher.(type) { + case cipher.Stream: + return 0 + case aead: + return c.explicitNonceLen() + case cbcMode: + // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack. + if hc.version >= VersionTLS11 { + return c.BlockSize() + } + return 0 + default: + panic("unknown cipher type") + } +} + +// extractPadding returns, in constant time, the length of the padding to remove +// from the end of payload. It also returns a byte which is equal to 255 if the +// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2. +func extractPadding(payload []byte) (toRemove int, good byte) { + if len(payload) < 1 { + return 0, 0 + } + + paddingLen := payload[len(payload)-1] + t := uint(len(payload)-1) - uint(paddingLen) + // if len(payload) >= (paddingLen - 1) then the MSB of t is zero + good = byte(int32(^t) >> 31) + + // The maximum possible padding length plus the actual length field + toCheck := 256 + // The length of the padded data is public, so we can use an if here + if toCheck > len(payload) { + toCheck = len(payload) + } + + for i := 0; i < toCheck; i++ { + t := uint(paddingLen) - uint(i) + // if i <= paddingLen then the MSB of t is zero + mask := byte(int32(^t) >> 31) + b := payload[len(payload)-1-i] + good &^= mask&paddingLen ^ mask&b + } + + // We AND together the bits of good and replicate the result across + // all the bits. + good &= good << 4 + good &= good << 2 + good &= good << 1 + good = uint8(int8(good) >> 7) + + // Zero the padding length on error. This ensures any unchecked bytes + // are included in the MAC. Otherwise, an attacker that could + // distinguish MAC failures from padding failures could mount an attack + // similar to POODLE in SSL 3.0: given a good ciphertext that uses a + // full block's worth of padding, replace the final block with another + // block. If the MAC check passed but the padding check failed, the + // last byte of that block decrypted to the block size. + // + // See also macAndPaddingGood logic below. + paddingLen &= good + + toRemove = int(paddingLen) + 1 + return +} + +func roundUp(a, b int) int { + return a + (b-a%b)%b +} + +// cbcMode is an interface for block ciphers using cipher block chaining. +type cbcMode interface { + cipher.BlockMode + SetIV([]byte) +} + +// decrypt authenticates and decrypts the record if protection is active at +// this stage. The returned plaintext might overlap with the input. +func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) { + var plaintext []byte + typ := recordType(record[0]) + payload := record[recordHeaderLen:] + + // In TLS 1.3, change_cipher_spec messages are to be ignored without being + // decrypted. See RFC 8446, Appendix D.4. + if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec { + return payload, typ, nil + } + + paddingGood := byte(255) + paddingLen := 0 + + explicitNonceLen := hc.explicitNonceLen() + + if hc.cipher != nil { + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case aead: + if len(payload) < explicitNonceLen { + return nil, 0, alertBadRecordMAC + } + nonce := payload[:explicitNonceLen] + if len(nonce) == 0 { + nonce = hc.seq[:] + } + payload = payload[explicitNonceLen:] + + var additionalData []byte + if hc.version == VersionTLS13 { + additionalData = record[:recordHeaderLen] + } else { + additionalData = append(hc.scratchBuf[:0], hc.seq[:]...) + additionalData = append(additionalData, record[:3]...) + n := len(payload) - c.Overhead() + additionalData = append(additionalData, byte(n>>8), byte(n)) + } + + var err error + plaintext, err = c.Open(payload[:0], nonce, payload, additionalData) + if err != nil { + return nil, 0, alertBadRecordMAC + } + case cbcMode: + blockSize := c.BlockSize() + minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize) + if len(payload)%blockSize != 0 || len(payload) < minPayload { + return nil, 0, alertBadRecordMAC + } + + if explicitNonceLen > 0 { + c.SetIV(payload[:explicitNonceLen]) + payload = payload[explicitNonceLen:] + } + c.CryptBlocks(payload, payload) + + // In a limited attempt to protect against CBC padding oracles like + // Lucky13, the data past paddingLen (which is secret) is passed to + // the MAC function as extra data, to be fed into the HMAC after + // computing the digest. This makes the MAC roughly constant time as + // long as the digest computation is constant time and does not + // affect the subsequent write, modulo cache effects. + paddingLen, paddingGood = extractPadding(payload) + default: + panic("unknown cipher type") + } + + if hc.version == VersionTLS13 { + if typ != recordTypeApplicationData { + return nil, 0, alertUnexpectedMessage + } + if len(plaintext) > maxPlaintext+1 { + return nil, 0, alertRecordOverflow + } + // Remove padding and find the ContentType scanning from the end. + for i := len(plaintext) - 1; i >= 0; i-- { + if plaintext[i] != 0 { + typ = recordType(plaintext[i]) + plaintext = plaintext[:i] + break + } + if i == 0 { + return nil, 0, alertUnexpectedMessage + } + } + } + } else { + plaintext = payload + } + + if hc.mac != nil { + macSize := hc.mac.Size() + if len(payload) < macSize { + return nil, 0, alertBadRecordMAC + } + + n := len(payload) - macSize - paddingLen + n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 } + record[3] = byte(n >> 8) + record[4] = byte(n) + remoteMAC := payload[n : n+macSize] + localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:]) + + // This is equivalent to checking the MACs and paddingGood + // separately, but in constant-time to prevent distinguishing + // padding failures from MAC failures. Depending on what value + // of paddingLen was returned on bad padding, distinguishing + // bad MAC from bad padding can lead to an attack. + // + // See also the logic at the end of extractPadding. + macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood) + if macAndPaddingGood != 1 { + return nil, 0, alertBadRecordMAC + } + + plaintext = payload[:n] + } + + hc.incSeq() + return plaintext, typ, nil +} + +func (c *Conn) setAlternativeRecordLayer() { + if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { + c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey + c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey + } +} + +// sliceForAppend extends the input slice by n bytes. head is the full extended +// slice, while tail is the appended part. If the original slice has sufficient +// capacity no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and +// appends it to record, which must already contain the record header. +func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) { + if hc.cipher == nil { + return append(record, payload...), nil + } + + var explicitNonce []byte + if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 { + record, explicitNonce = sliceForAppend(record, explicitNonceLen) + if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 { + // The AES-GCM construction in TLS has an explicit nonce so that the + // nonce can be random. However, the nonce is only 8 bytes which is + // too small for a secure, random nonce. Therefore we use the + // sequence number as the nonce. The 3DES-CBC construction also has + // an 8 bytes nonce but its nonces must be unpredictable (see RFC + // 5246, Appendix F.3), forcing us to use randomness. That's not + // 3DES' biggest problem anyway because the birthday bound on block + // collision is reached first due to its similarly small block size + // (see the Sweet32 attack). + copy(explicitNonce, hc.seq[:]) + } else { + if _, err := io.ReadFull(rand, explicitNonce); err != nil { + return nil, err + } + } + } + + var dst []byte + switch c := hc.cipher.(type) { + case cipher.Stream: + mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil) + record, dst = sliceForAppend(record, len(payload)+len(mac)) + c.XORKeyStream(dst[:len(payload)], payload) + c.XORKeyStream(dst[len(payload):], mac) + case aead: + nonce := explicitNonce + if len(nonce) == 0 { + nonce = hc.seq[:] + } + + if hc.version == VersionTLS13 { + record = append(record, payload...) + + // Encrypt the actual ContentType and replace the plaintext one. + record = append(record, record[0]) + record[0] = byte(recordTypeApplicationData) + + n := len(payload) + 1 + c.Overhead() + record[3] = byte(n >> 8) + record[4] = byte(n) + + record = c.Seal(record[:recordHeaderLen], + nonce, record[recordHeaderLen:], record[:recordHeaderLen]) + } else { + additionalData := append(hc.scratchBuf[:0], hc.seq[:]...) + additionalData = append(additionalData, record[:recordHeaderLen]...) + record = c.Seal(record, nonce, payload, additionalData) + } + case cbcMode: + mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil) + blockSize := c.BlockSize() + plaintextLen := len(payload) + len(mac) + paddingLen := blockSize - plaintextLen%blockSize + record, dst = sliceForAppend(record, plaintextLen+paddingLen) + copy(dst, payload) + copy(dst[len(payload):], mac) + for i := plaintextLen; i < len(dst); i++ { + dst[i] = byte(paddingLen - 1) + } + if len(explicitNonce) > 0 { + c.SetIV(explicitNonce) + } + c.CryptBlocks(dst, dst) + default: + panic("unknown cipher type") + } + + // Update length to include nonce, MAC and any block padding needed. + n := len(record) - recordHeaderLen + record[3] = byte(n >> 8) + record[4] = byte(n) + hc.incSeq() + + return record, nil +} + +// RecordHeaderError is returned when a TLS record header is invalid. +type RecordHeaderError struct { + // Msg contains a human readable string that describes the error. + Msg string + // RecordHeader contains the five bytes of TLS record header that + // triggered the error. + RecordHeader [5]byte + // Conn provides the underlying net.Conn in the case that a client + // sent an initial handshake that didn't look like TLS. + // It is nil if there's already been a handshake or a TLS alert has + // been written to the connection. + Conn net.Conn +} + +func (e RecordHeaderError) Error() string { return "tls: " + e.Msg } + +func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) { + err.Msg = msg + err.Conn = conn + copy(err.RecordHeader[:], c.rawInput.Bytes()) + return err +} + +func (c *Conn) readRecord() error { + return c.readRecordOrCCS(false) +} + +func (c *Conn) readChangeCipherSpec() error { + return c.readRecordOrCCS(true) +} + +// readRecordOrCCS reads one or more TLS records from the connection and +// updates the record layer state. Some invariants: +// - c.in must be locked +// - c.input must be empty +// +// During the handshake one and only one of the following will happen: +// - c.hand grows +// - c.in.changeCipherSpec is called +// - an error is returned +// +// After the handshake one and only one of the following will happen: +// - c.hand grows +// - c.input is set +// - an error is returned +func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { + if c.in.err != nil { + return c.in.err + } + handshakeComplete := c.handshakeComplete() + + // This function modifies c.rawInput, which owns the c.input memory. + if c.input.Len() != 0 { + return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data")) + } + c.input.Reset(nil) + + // Read header, payload. + if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { + // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify + // is an error, but popular web sites seem to do this, so we accept it + // if and only if at the record boundary. + if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 { + err = io.EOF + } + if e, ok := err.(net.Error); !ok || !e.Temporary() { + c.in.setErrorLocked(err) + } + return err + } + hdr := c.rawInput.Bytes()[:recordHeaderLen] + typ := recordType(hdr[0]) + + // No valid TLS record has a type of 0x80, however SSLv2 handshakes + // start with a uint16 length where the MSB is set and the first record + // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests + // an SSLv2 client. + if !handshakeComplete && typ == 0x80 { + c.sendAlert(alertProtocolVersion) + return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received")) + } + + vers := uint16(hdr[1])<<8 | uint16(hdr[2]) + n := int(hdr[3])<<8 | int(hdr[4]) + if c.haveVers && c.vers != VersionTLS13 && vers != c.vers { + c.sendAlert(alertProtocolVersion) + msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers) + return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) + } + if !c.haveVers { + // First message, be extra suspicious: this might not be a TLS + // client. Bail out before reading a full 'body', if possible. + // The current max version is 3.3 so if the version is >= 16.0, + // it's probably not real. + if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 { + return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake")) + } + } + if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext { + c.sendAlert(alertRecordOverflow) + msg := fmt.Sprintf("oversized record received with length %d", n) + return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) + } + if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil { + if e, ok := err.(net.Error); !ok || !e.Temporary() { + c.in.setErrorLocked(err) + } + return err + } + + // Process message. + record := c.rawInput.Next(recordHeaderLen + n) + data, typ, err := c.in.decrypt(record) + if err != nil { + return c.in.setErrorLocked(c.sendAlert(err.(alert))) + } + if len(data) > maxPlaintext { + return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow)) + } + + // Application Data messages are always protected. + if c.in.cipher == nil && typ == recordTypeApplicationData { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 { + // This is a state-advancing message: reset the retry count. + c.retryCount = 0 + } + + // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3. + if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + switch typ { + default: + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + + case recordTypeAlert: + if len(data) != 2 { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + if alert(data[1]) == alertCloseNotify { + return c.in.setErrorLocked(io.EOF) + } + if c.vers == VersionTLS13 { + return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) + } + switch data[0] { + case alertLevelWarning: + // Drop the record on the floor and retry. + return c.retryReadRecord(expectChangeCipherSpec) + case alertLevelError: + return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) + default: + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + case recordTypeChangeCipherSpec: + if len(data) != 1 || data[0] != 1 { + return c.in.setErrorLocked(c.sendAlert(alertDecodeError)) + } + // Handshake messages are not allowed to fragment across the CCS. + if c.hand.Len() > 0 { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + // In TLS 1.3, change_cipher_spec records are ignored until the + // Finished. See RFC 8446, Appendix D.4. Note that according to Section + // 5, a server can send a ChangeCipherSpec before its ServerHello, when + // c.vers is still unset. That's not useful though and suspicious if the + // server then selects a lower protocol version, so don't allow that. + if c.vers == VersionTLS13 { + return c.retryReadRecord(expectChangeCipherSpec) + } + if !expectChangeCipherSpec { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + if err := c.in.changeCipherSpec(); err != nil { + return c.in.setErrorLocked(c.sendAlert(err.(alert))) + } + + case recordTypeApplicationData: + if !handshakeComplete || expectChangeCipherSpec { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + // Some OpenSSL servers send empty records in order to randomize the + // CBC IV. Ignore a limited number of empty records. + if len(data) == 0 { + return c.retryReadRecord(expectChangeCipherSpec) + } + // Note that data is owned by c.rawInput, following the Next call above, + // to avoid copying the plaintext. This is safe because c.rawInput is + // not read from or written to until c.input is drained. + c.input.Reset(data) + + case recordTypeHandshake: + if len(data) == 0 || expectChangeCipherSpec { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + c.hand.Write(data) + } + + return nil +} + +// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like +// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3. +func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error { + c.retryCount++ + if c.retryCount > maxUselessRecords { + c.sendAlert(alertUnexpectedMessage) + return c.in.setErrorLocked(errors.New("tls: too many ignored records")) + } + return c.readRecordOrCCS(expectChangeCipherSpec) +} + +// atLeastReader reads from R, stopping with EOF once at least N bytes have been +// read. It is different from an io.LimitedReader in that it doesn't cut short +// the last Read call, and in that it considers an early EOF an error. +type atLeastReader struct { + R io.Reader + N int64 +} + +func (r *atLeastReader) Read(p []byte) (int, error) { + if r.N <= 0 { + return 0, io.EOF + } + n, err := r.R.Read(p) + r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809 + if r.N > 0 && err == io.EOF { + return n, io.ErrUnexpectedEOF + } + if r.N <= 0 && err == nil { + return n, io.EOF + } + return n, err +} + +// readFromUntil reads from r into c.rawInput until c.rawInput contains +// at least n bytes or else returns an error. +func (c *Conn) readFromUntil(r io.Reader, n int) error { + if c.rawInput.Len() >= n { + return nil + } + needs := n - c.rawInput.Len() + // There might be extra input waiting on the wire. Make a best effort + // attempt to fetch it so that it can be used in (*Conn).Read to + // "predict" closeNotify alerts. + c.rawInput.Grow(needs + bytes.MinRead) + _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)}) + return err +} + +// sendAlert sends a TLS alert message. +func (c *Conn) sendAlertLocked(err alert) error { + switch err { + case alertNoRenegotiation, alertCloseNotify: + c.tmp[0] = alertLevelWarning + default: + c.tmp[0] = alertLevelError + } + c.tmp[1] = byte(err) + + _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2]) + if err == alertCloseNotify { + // closeNotify is a special case in that it isn't an error. + return writeErr + } + + return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) +} + +// sendAlert sends a TLS alert message. +func (c *Conn) sendAlert(err alert) error { + if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { + c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err)) + return &net.OpError{Op: "local error", Err: err} + } + + c.out.Lock() + defer c.out.Unlock() + return c.sendAlertLocked(err) +} + +const ( + // tcpMSSEstimate is a conservative estimate of the TCP maximum segment + // size (MSS). A constant is used, rather than querying the kernel for + // the actual MSS, to avoid complexity. The value here is the IPv6 + // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40 + // bytes) and a TCP header with timestamps (32 bytes). + tcpMSSEstimate = 1208 + + // recordSizeBoostThreshold is the number of bytes of application data + // sent after which the TLS record size will be increased to the + // maximum. + recordSizeBoostThreshold = 128 * 1024 +) + +// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the +// next application data record. There is the following trade-off: +// +// - For latency-sensitive applications, such as web browsing, each TLS +// record should fit in one TCP segment. +// - For throughput-sensitive applications, such as large file transfers, +// larger TLS records better amortize framing and encryption overheads. +// +// A simple heuristic that works well in practice is to use small records for +// the first 1MB of data, then use larger records for subsequent data, and +// reset back to smaller records after the connection becomes idle. See "High +// Performance Web Networking", Chapter 4, or: +// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/ +// +// In the interests of simplicity and determinism, this code does not attempt +// to reset the record size once the connection is idle, however. +func (c *Conn) maxPayloadSizeForWrite(typ recordType) int { + if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData { + return maxPlaintext + } + + if c.bytesSent >= recordSizeBoostThreshold { + return maxPlaintext + } + + // Subtract TLS overheads to get the maximum payload size. + payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen() + if c.out.cipher != nil { + switch ciph := c.out.cipher.(type) { + case cipher.Stream: + payloadBytes -= c.out.mac.Size() + case cipher.AEAD: + payloadBytes -= ciph.Overhead() + case cbcMode: + blockSize := ciph.BlockSize() + // The payload must fit in a multiple of blockSize, with + // room for at least one padding byte. + payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1 + // The MAC is appended before padding so affects the + // payload size directly. + payloadBytes -= c.out.mac.Size() + default: + panic("unknown cipher type") + } + } + if c.vers == VersionTLS13 { + payloadBytes-- // encrypted ContentType + } + + // Allow packet growth in arithmetic progression up to max. + pkt := c.packetsSent + c.packetsSent++ + if pkt > 1000 { + return maxPlaintext // avoid overflow in multiply below + } + + n := payloadBytes * int(pkt+1) + if n > maxPlaintext { + n = maxPlaintext + } + return n +} + +func (c *Conn) write(data []byte) (int, error) { + if c.buffering { + c.sendBuf = append(c.sendBuf, data...) + return len(data), nil + } + + n, err := c.conn.Write(data) + c.bytesSent += int64(n) + return n, err +} + +func (c *Conn) flush() (int, error) { + if len(c.sendBuf) == 0 { + return 0, nil + } + + n, err := c.conn.Write(c.sendBuf) + c.bytesSent += int64(n) + c.sendBuf = nil + c.buffering = false + return n, err +} + +// outBufPool pools the record-sized scratch buffers used by writeRecordLocked. +var outBufPool = sync.Pool{ + New: func() any { + return new([]byte) + }, +} + +// writeRecordLocked writes a TLS record with the given type and payload to the +// connection and updates the record layer state. +func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { + outBufPtr := outBufPool.Get().(*[]byte) + outBuf := *outBufPtr + defer func() { + // You might be tempted to simplify this by just passing &outBuf to Put, + // but that would make the local copy of the outBuf slice header escape + // to the heap, causing an allocation. Instead, we keep around the + // pointer to the slice header returned by Get, which is already on the + // heap, and overwrite and return that. + *outBufPtr = outBuf + outBufPool.Put(outBufPtr) + }() + + var n int + for len(data) > 0 { + m := len(data) + if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload { + m = maxPayload + } + + _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen) + outBuf[0] = byte(typ) + vers := c.vers + if vers == 0 { + // Some TLS servers fail if the record version is + // greater than TLS 1.0 for the initial ClientHello. + vers = VersionTLS10 + } else if vers == VersionTLS13 { + // TLS 1.3 froze the record layer version to 1.2. + // See RFC 8446, Section 5.1. + vers = VersionTLS12 + } + outBuf[1] = byte(vers >> 8) + outBuf[2] = byte(vers) + outBuf[3] = byte(m >> 8) + outBuf[4] = byte(m) + + var err error + outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand()) + if err != nil { + return n, err + } + if _, err := c.write(outBuf); err != nil { + return n, err + } + n += m + data = data[m:] + } + + if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 { + if err := c.out.changeCipherSpec(); err != nil { + return n, c.sendAlertLocked(err.(alert)) + } + } + + return n, nil +} + +// writeRecord writes a TLS record with the given type and payload to the +// connection and updates the record layer state. +func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) { + if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { + if typ == recordTypeChangeCipherSpec { + return len(data), nil + } + return c.extraConfig.AlternativeRecordLayer.WriteRecord(data) + } + + c.out.Lock() + defer c.out.Unlock() + + return c.writeRecordLocked(typ, data) +} + +// readHandshake reads the next handshake message from +// the record layer. +func (c *Conn) readHandshake() (any, error) { + var data []byte + if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { + var err error + data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage() + if err != nil { + return nil, err + } + } else { + for c.hand.Len() < 4 { + if err := c.readRecord(); err != nil { + return nil, err + } + } + + data = c.hand.Bytes() + n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if n > maxHandshake { + c.sendAlertLocked(alertInternalError) + return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)) + } + for c.hand.Len() < 4+n { + if err := c.readRecord(); err != nil { + return nil, err + } + } + data = c.hand.Next(4 + n) + } + var m handshakeMessage + switch data[0] { + case typeHelloRequest: + m = new(helloRequestMsg) + case typeClientHello: + m = new(clientHelloMsg) + case typeServerHello: + m = new(serverHelloMsg) + case typeNewSessionTicket: + if c.vers == VersionTLS13 { + m = new(newSessionTicketMsgTLS13) + } else { + m = new(newSessionTicketMsg) + } + case typeCertificate: + if c.vers == VersionTLS13 { + m = new(certificateMsgTLS13) + } else { + m = new(certificateMsg) + } + case typeCertificateRequest: + if c.vers == VersionTLS13 { + m = new(certificateRequestMsgTLS13) + } else { + m = &certificateRequestMsg{ + hasSignatureAlgorithm: c.vers >= VersionTLS12, + } + } + case typeCertificateStatus: + m = new(certificateStatusMsg) + case typeServerKeyExchange: + m = new(serverKeyExchangeMsg) + case typeServerHelloDone: + m = new(serverHelloDoneMsg) + case typeClientKeyExchange: + m = new(clientKeyExchangeMsg) + case typeCertificateVerify: + m = &certificateVerifyMsg{ + hasSignatureAlgorithm: c.vers >= VersionTLS12, + } + case typeFinished: + m = new(finishedMsg) + case typeEncryptedExtensions: + m = new(encryptedExtensionsMsg) + case typeEndOfEarlyData: + m = new(endOfEarlyDataMsg) + case typeKeyUpdate: + m = new(keyUpdateMsg) + default: + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + // The handshake message unmarshalers + // expect to be able to keep references to data, + // so pass in a fresh copy that won't be overwritten. + data = append([]byte(nil), data...) + + if !m.unmarshal(data) { + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + return m, nil +} + +var ( + errShutdown = errors.New("tls: protocol is shutdown") +) + +// Write writes data to the connection. +// +// As Write calls Handshake, in order to prevent indefinite blocking a deadline +// must be set for both Read and Write before Write is called when the handshake +// has not yet completed. See SetDeadline, SetReadDeadline, and +// SetWriteDeadline. +func (c *Conn) Write(b []byte) (int, error) { + // interlock with Close below + for { + x := atomic.LoadInt32(&c.activeCall) + if x&1 != 0 { + return 0, net.ErrClosed + } + if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) { + break + } + } + defer atomic.AddInt32(&c.activeCall, -2) + + if err := c.Handshake(); err != nil { + return 0, err + } + + c.out.Lock() + defer c.out.Unlock() + + if err := c.out.err; err != nil { + return 0, err + } + + if !c.handshakeComplete() { + return 0, alertInternalError + } + + if c.closeNotifySent { + return 0, errShutdown + } + + // TLS 1.0 is susceptible to a chosen-plaintext + // attack when using block mode ciphers due to predictable IVs. + // This can be prevented by splitting each Application Data + // record into two records, effectively randomizing the IV. + // + // https://www.openssl.org/~bodo/tls-cbc.txt + // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 + // https://www.imperialviolet.org/2012/01/15/beastfollowup.html + + var m int + if len(b) > 1 && c.vers == VersionTLS10 { + if _, ok := c.out.cipher.(cipher.BlockMode); ok { + n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1]) + if err != nil { + return n, c.out.setErrorLocked(err) + } + m, b = 1, b[1:] + } + } + + n, err := c.writeRecordLocked(recordTypeApplicationData, b) + return n + m, c.out.setErrorLocked(err) +} + +// handleRenegotiation processes a HelloRequest handshake message. +func (c *Conn) handleRenegotiation() error { + if c.vers == VersionTLS13 { + return errors.New("tls: internal error: unexpected renegotiation") + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + helloReq, ok := msg.(*helloRequestMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(helloReq, msg) + } + + if !c.isClient { + return c.sendAlert(alertNoRenegotiation) + } + + switch c.config.Renegotiation { + case RenegotiateNever: + return c.sendAlert(alertNoRenegotiation) + case RenegotiateOnceAsClient: + if c.handshakes > 1 { + return c.sendAlert(alertNoRenegotiation) + } + case RenegotiateFreelyAsClient: + // Ok. + default: + c.sendAlert(alertInternalError) + return errors.New("tls: unknown Renegotiation value") + } + + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + atomic.StoreUint32(&c.handshakeStatus, 0) + if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil { + c.handshakes++ + } + return c.handshakeErr +} + +func (c *Conn) HandlePostHandshakeMessage() error { + return c.handlePostHandshakeMessage() +} + +// handlePostHandshakeMessage processes a handshake message arrived after the +// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation. +func (c *Conn) handlePostHandshakeMessage() error { + if c.vers != VersionTLS13 { + return c.handleRenegotiation() + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + c.retryCount++ + if c.retryCount > maxUselessRecords { + c.sendAlert(alertUnexpectedMessage) + return c.in.setErrorLocked(errors.New("tls: too many non-advancing records")) + } + + switch msg := msg.(type) { + case *newSessionTicketMsgTLS13: + return c.handleNewSessionTicket(msg) + case *keyUpdateMsg: + return c.handleKeyUpdate(msg) + default: + c.sendAlert(alertUnexpectedMessage) + return fmt.Errorf("tls: received unexpected handshake message of type %T", msg) + } +} + +func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { + cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) + if cipherSuite == nil { + return c.in.setErrorLocked(c.sendAlert(alertInternalError)) + } + + newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret) + c.in.setTrafficSecret(cipherSuite, newSecret) + + if keyUpdate.updateRequested { + c.out.Lock() + defer c.out.Unlock() + + msg := &keyUpdateMsg{} + _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal()) + if err != nil { + // Surface the error at the next write. + c.out.setErrorLocked(err) + return nil + } + + newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret) + c.out.setTrafficSecret(cipherSuite, newSecret) + } + + return nil +} + +// Read reads data from the connection. +// +// As Read calls Handshake, in order to prevent indefinite blocking a deadline +// must be set for both Read and Write before Read is called when the handshake +// has not yet completed. See SetDeadline, SetReadDeadline, and +// SetWriteDeadline. +func (c *Conn) Read(b []byte) (int, error) { + if err := c.Handshake(); err != nil { + return 0, err + } + if len(b) == 0 { + // Put this after Handshake, in case people were calling + // Read(nil) for the side effect of the Handshake. + return 0, nil + } + + c.in.Lock() + defer c.in.Unlock() + + for c.input.Len() == 0 { + if err := c.readRecord(); err != nil { + return 0, err + } + for c.hand.Len() > 0 { + if err := c.handlePostHandshakeMessage(); err != nil { + return 0, err + } + } + } + + n, _ := c.input.Read(b) + + // If a close-notify alert is waiting, read it so that we can return (n, + // EOF) instead of (n, nil), to signal to the HTTP response reading + // goroutine that the connection is now closed. This eliminates a race + // where the HTTP response reading goroutine would otherwise not observe + // the EOF until its next read, by which time a client goroutine might + // have already tried to reuse the HTTP connection for a new request. + // See https://golang.org/cl/76400046 and https://golang.org/issue/3514 + if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 && + recordType(c.rawInput.Bytes()[0]) == recordTypeAlert { + if err := c.readRecord(); err != nil { + return n, err // will be io.EOF on closeNotify + } + } + + return n, nil +} + +// Close closes the connection. +func (c *Conn) Close() error { + // Interlock with Conn.Write above. + var x int32 + for { + x = atomic.LoadInt32(&c.activeCall) + if x&1 != 0 { + return net.ErrClosed + } + if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) { + break + } + } + if x != 0 { + // io.Writer and io.Closer should not be used concurrently. + // If Close is called while a Write is currently in-flight, + // interpret that as a sign that this Close is really just + // being used to break the Write and/or clean up resources and + // avoid sending the alertCloseNotify, which may block + // waiting on handshakeMutex or the c.out mutex. + return c.conn.Close() + } + + var alertErr error + if c.handshakeComplete() { + if err := c.closeNotify(); err != nil { + alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err) + } + } + + if err := c.conn.Close(); err != nil { + return err + } + return alertErr +} + +var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete") + +// CloseWrite shuts down the writing side of the connection. It should only be +// called once the handshake has completed and does not call CloseWrite on the +// underlying connection. Most callers should just use Close. +func (c *Conn) CloseWrite() error { + if !c.handshakeComplete() { + return errEarlyCloseWrite + } + + return c.closeNotify() +} + +func (c *Conn) closeNotify() error { + c.out.Lock() + defer c.out.Unlock() + + if !c.closeNotifySent { + // Set a Write Deadline to prevent possibly blocking forever. + c.SetWriteDeadline(time.Now().Add(time.Second * 5)) + c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify) + c.closeNotifySent = true + // Any subsequent writes will fail. + c.SetWriteDeadline(time.Now()) + } + return c.closeNotifyErr +} + +// Handshake runs the client or server handshake +// protocol if it has not yet been run. +// +// Most uses of this package need not call Handshake explicitly: the +// first Read or Write will call it automatically. +// +// For control over canceling or setting a timeout on a handshake, use +// HandshakeContext or the Dialer's DialContext method instead. +func (c *Conn) Handshake() error { + return c.HandshakeContext(context.Background()) +} + +// HandshakeContext runs the client or server handshake +// protocol if it has not yet been run. +// +// The provided Context must be non-nil. If the context is canceled before +// the handshake is complete, the handshake is interrupted and an error is returned. +// Once the handshake has completed, cancellation of the context will not affect the +// connection. +// +// Most uses of this package need not call HandshakeContext explicitly: the +// first Read or Write will call it automatically. +func (c *Conn) HandshakeContext(ctx context.Context) error { + // Delegate to unexported method for named return + // without confusing documented signature. + return c.handshakeContext(ctx) +} + +func (c *Conn) handshakeContext(ctx context.Context) (ret error) { + // Fast sync/atomic-based exit if there is no handshake in flight and the + // last one succeeded without an error. Avoids the expensive context setup + // and mutex for most Read and Write calls. + if c.handshakeComplete() { + return nil + } + + handshakeCtx, cancel := context.WithCancel(ctx) + // Note: defer this before starting the "interrupter" goroutine + // so that we can tell the difference between the input being canceled and + // this cancellation. In the former case, we need to close the connection. + defer cancel() + + // Start the "interrupter" goroutine, if this context might be canceled. + // (The background context cannot). + // + // The interrupter goroutine waits for the input context to be done and + // closes the connection if this happens before the function returns. + if ctx.Done() != nil { + done := make(chan struct{}) + interruptRes := make(chan error, 1) + defer func() { + close(done) + if ctxErr := <-interruptRes; ctxErr != nil { + // Return context error to user. + ret = ctxErr + } + }() + go func() { + select { + case <-handshakeCtx.Done(): + // Close the connection, discarding the error + _ = c.conn.Close() + interruptRes <- handshakeCtx.Err() + case <-done: + interruptRes <- nil + } + }() + } + + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + if err := c.handshakeErr; err != nil { + return err + } + if c.handshakeComplete() { + return nil + } + + c.in.Lock() + defer c.in.Unlock() + + c.handshakeErr = c.handshakeFn(handshakeCtx) + if c.handshakeErr == nil { + c.handshakes++ + } else { + // If an error occurred during the handshake try to flush the + // alert that might be left in the buffer. + c.flush() + } + + if c.handshakeErr == nil && !c.handshakeComplete() { + c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") + } + if c.handshakeErr != nil && c.handshakeComplete() { + panic("tls: internal error: handshake returned an error but is marked successful") + } + + return c.handshakeErr +} + +// ConnectionState returns basic TLS details about the connection. +func (c *Conn) ConnectionState() ConnectionState { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + return c.connectionStateLocked() +} + +// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection. +func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + return ConnectionStateWith0RTT{ + ConnectionState: c.connectionStateLocked(), + Used0RTT: c.used0RTT, + } +} + +func (c *Conn) connectionStateLocked() ConnectionState { + var state connectionState + state.HandshakeComplete = c.handshakeComplete() + state.Version = c.vers + state.NegotiatedProtocol = c.clientProtocol + state.DidResume = c.didResume + state.NegotiatedProtocolIsMutual = true + state.ServerName = c.serverName + state.CipherSuite = c.cipherSuite + state.PeerCertificates = c.peerCertificates + state.VerifiedChains = c.verifiedChains + state.SignedCertificateTimestamps = c.scts + state.OCSPResponse = c.ocspResponse + if !c.didResume && c.vers != VersionTLS13 { + if c.clientFinishedIsFirst { + state.TLSUnique = c.clientFinished[:] + } else { + state.TLSUnique = c.serverFinished[:] + } + } + if c.config.Renegotiation != RenegotiateNever { + state.ekm = noExportedKeyingMaterial + } else { + state.ekm = c.ekm + } + return toConnectionState(state) +} + +// OCSPResponse returns the stapled OCSP response from the TLS server, if +// any. (Only valid for client connections.) +func (c *Conn) OCSPResponse() []byte { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + return c.ocspResponse +} + +// VerifyHostname checks that the peer certificate chain is valid for +// connecting to host. If so, it returns nil; if not, it returns an error +// describing the problem. +func (c *Conn) VerifyHostname(host string) error { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if !c.isClient { + return errors.New("tls: VerifyHostname called on TLS server connection") + } + if !c.handshakeComplete() { + return errors.New("tls: handshake has not yet been performed") + } + if len(c.verifiedChains) == 0 { + return errors.New("tls: handshake did not verify certificate chain") + } + return c.peerCertificates[0].VerifyHostname(host) +} + +func (c *Conn) handshakeComplete() bool { + return atomic.LoadUint32(&c.handshakeStatus) == 1 +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/cpu.go b/vendor/github.com/marten-seemann/qtls-go1-19/cpu.go new file mode 100644 index 00000000..12194508 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/cpu.go @@ -0,0 +1,22 @@ +//go:build !js +// +build !js + +package qtls + +import ( + "runtime" + + "golang.org/x/sys/cpu" +) + +var ( + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && + (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || + runtime.GOARCH == "arm64" && hasGCMAsmARM64 || + runtime.GOARCH == "s390x" && hasGCMAsmS390X +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/cpu_other.go b/vendor/github.com/marten-seemann/qtls-go1-19/cpu_other.go new file mode 100644 index 00000000..33f7d219 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/cpu_other.go @@ -0,0 +1,12 @@ +//go:build js +// +build js + +package qtls + +var ( + hasGCMAsmAMD64 = false + hasGCMAsmARM64 = false + hasGCMAsmS390X = false + + hasAESGCMHardwareSupport = false +) diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go new file mode 100644 index 00000000..4407683a --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go @@ -0,0 +1,1117 @@ +// 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 LICENSE file. + +package qtls + +import ( + "bytes" + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "hash" + "io" + "net" + "strings" + "sync/atomic" + "time" + + "golang.org/x/crypto/cryptobyte" +) + +const clientSessionStateVersion = 1 + +type clientHandshakeState struct { + c *Conn + ctx context.Context + serverHello *serverHelloMsg + hello *clientHelloMsg + suite *cipherSuite + finishedHash finishedHash + masterSecret []byte + session *clientSessionState +} + +var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme + +func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { + config := c.config + if len(config.ServerName) == 0 && !config.InsecureSkipVerify { + return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") + } + + nextProtosLength := 0 + for _, proto := range config.NextProtos { + if l := len(proto); l == 0 || l > 255 { + return nil, nil, errors.New("tls: invalid NextProtos value") + } else { + nextProtosLength += 1 + l + } + } + if nextProtosLength > 0xffff { + return nil, nil, errors.New("tls: NextProtos values too large") + } + + var supportedVersions []uint16 + var clientHelloVersion uint16 + if c.extraConfig.usesAlternativeRecordLayer() { + if config.maxSupportedVersion(roleClient) < VersionTLS13 { + return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") + } + // Only offer TLS 1.3 when QUIC is used. + supportedVersions = []uint16{VersionTLS13} + clientHelloVersion = VersionTLS13 + } else { + supportedVersions = config.supportedVersions(roleClient) + if len(supportedVersions) == 0 { + return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") + } + clientHelloVersion = config.maxSupportedVersion(roleClient) + } + + // The version at the beginning of the ClientHello was capped at TLS 1.2 + // for compatibility reasons. The supported_versions extension is used + // to negotiate versions now. See RFC 8446, Section 4.2.1. + if clientHelloVersion > VersionTLS12 { + clientHelloVersion = VersionTLS12 + } + + hello := &clientHelloMsg{ + vers: clientHelloVersion, + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), + ocspStapling: true, + scts: true, + serverName: hostnameInSNI(config.ServerName), + supportedCurves: config.curvePreferences(), + supportedPoints: []uint8{pointFormatUncompressed}, + secureRenegotiationSupported: true, + alpnProtocols: config.NextProtos, + supportedVersions: supportedVersions, + } + + if c.handshakes > 0 { + hello.secureRenegotiation = c.clientFinished[:] + } + + preferenceOrder := cipherSuitesPreferenceOrder + if !hasAESGCMHardwareSupport { + preferenceOrder = cipherSuitesPreferenceOrderNoAES + } + configCipherSuites := config.cipherSuites() + hello.cipherSuites = make([]uint16, 0, len(configCipherSuites)) + + for _, suiteId := range preferenceOrder { + suite := mutualCipherSuite(configCipherSuites, suiteId) + if suite == nil { + continue + } + // Don't advertise TLS 1.2-only cipher suites unless + // we're attempting TLS 1.2. + if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 { + continue + } + hello.cipherSuites = append(hello.cipherSuites, suiteId) + } + + _, err := io.ReadFull(config.rand(), hello.random) + if err != nil { + return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) + } + + // A random session ID is used to detect when the server accepted a ticket + // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as + // a compatibility measure (see RFC 8446, Section 4.1.2). + if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { + hello.sessionId = make([]byte, 32) + if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { + return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) + } + } + + if hello.vers >= VersionTLS12 { + hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms() + } + if testingOnlyForceClientHelloSignatureAlgorithms != nil { + hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms + } + + var params ecdheParameters + if hello.supportedVersions[0] == VersionTLS13 { + var suites []uint16 + for _, suiteID := range configCipherSuites { + for _, suite := range cipherSuitesTLS13 { + if suite.id == suiteID { + suites = append(suites, suiteID) + } + } + } + if len(suites) > 0 { + hello.cipherSuites = suites + } else { + if hasAESGCMHardwareSupport { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...) + } else { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...) + } + } + + curveID := config.curvePreferences()[0] + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err = generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, nil, err + } + hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + } + + if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil { + hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello) + } + + return hello, params, nil +} + +func (c *Conn) clientHandshake(ctx context.Context) (err error) { + if c.config == nil { + c.config = fromConfig(defaultConfig()) + } + c.setAlternativeRecordLayer() + + // This may be a renegotiation handshake, in which case some fields + // need to be reset. + c.didResume = false + + hello, ecdheParams, err := c.makeClientHello() + if err != nil { + return err + } + c.serverName = hello.serverName + + cacheKey, session, earlySecret, binderKey := c.loadSession(hello) + if cacheKey != "" && session != nil { + var deletedTicket bool + if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT { + // don't reuse a session ticket that enabled 0-RTT + c.config.ClientSessionCache.Put(cacheKey, nil) + deletedTicket = true + + if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil { + h := suite.hash.New() + h.Write(hello.marshal()) + clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h) + c.out.exportKey(Encryption0RTT, suite, clientEarlySecret) + if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil { + c.sendAlert(alertInternalError) + return err + } + } + } + if !deletedTicket { + defer func() { + // If we got a handshake failure when resuming a session, throw away + // the session ticket. See RFC 5077, Section 3.2. + // + // RFC 8446 makes no mention of dropping tickets on failure, but it + // does require servers to abort on invalid binders, so we need to + // delete tickets to recover from a corrupted PSK. + if err != nil { + c.config.ClientSessionCache.Put(cacheKey, nil) + } + }() + } + } + + if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } + + if err := c.pickTLSVersion(serverHello); err != nil { + return err + } + + // If we are negotiating a protocol version that's lower than what we + // support, check for the server downgrade canaries. + // See RFC 8446, Section 4.1.3. + maxVers := c.config.maxSupportedVersion(roleClient) + tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12 + tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11 + if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) || + maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox") + } + + if c.vers == VersionTLS13 { + hs := &clientHandshakeStateTLS13{ + c: c, + ctx: ctx, + serverHello: serverHello, + hello: hello, + ecdheParams: ecdheParams, + session: session, + earlySecret: earlySecret, + binderKey: binderKey, + } + + // In TLS 1.3, session tickets are delivered after the handshake. + return hs.handshake() + } + + hs := &clientHandshakeState{ + c: c, + ctx: ctx, + serverHello: serverHello, + hello: hello, + session: session, + } + + if err := hs.handshake(); err != nil { + return err + } + + // If we had a successful handshake and hs.session is different from + // the one already cached - cache a new one. + if cacheKey != "" && hs.session != nil && session != hs.session { + c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session)) + } + + return nil +} + +// extract the app data saved in the session.nonce, +// and set the session.nonce to the actual nonce value +func (c *Conn) decodeSessionState(session *clientSessionState) (uint32 /* max early data */, []byte /* app data */, bool /* ok */) { + s := cryptobyte.String(session.nonce) + var version uint16 + if !s.ReadUint16(&version) { + return 0, nil, false + } + if version != clientSessionStateVersion { + return 0, nil, false + } + var maxEarlyData uint32 + if !s.ReadUint32(&maxEarlyData) { + return 0, nil, false + } + var appData []byte + if !readUint16LengthPrefixed(&s, &appData) { + return 0, nil, false + } + var nonce []byte + if !readUint16LengthPrefixed(&s, &nonce) { + return 0, nil, false + } + session.nonce = nonce + return maxEarlyData, appData, true +} + +func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, + session *clientSessionState, earlySecret, binderKey []byte) { + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { + return "", nil, nil, nil + } + + hello.ticketSupported = true + + if hello.supportedVersions[0] == VersionTLS13 { + // Require DHE on resumption as it guarantees forward secrecy against + // compromise of the session ticket key. See RFC 8446, Section 4.2.9. + hello.pskModes = []uint8{pskModeDHE} + } + + // Session resumption is not allowed if renegotiating because + // renegotiation is primarily used to allow a client to send a client + // certificate, which would be skipped if session resumption occurred. + if c.handshakes != 0 { + return "", nil, nil, nil + } + + // Try to resume a previously negotiated TLS session, if available. + cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + sess, ok := c.config.ClientSessionCache.Get(cacheKey) + if !ok || sess == nil { + return cacheKey, nil, nil, nil + } + session = fromClientSessionState(sess) + + var appData []byte + var maxEarlyData uint32 + if session.vers == VersionTLS13 { + var ok bool + maxEarlyData, appData, ok = c.decodeSessionState(session) + if !ok { // delete it, if parsing failed + c.config.ClientSessionCache.Put(cacheKey, nil) + return cacheKey, nil, nil, nil + } + } + + // Check that version used for the previous session is still valid. + versOk := false + for _, v := range hello.supportedVersions { + if v == session.vers { + versOk = true + break + } + } + if !versOk { + return cacheKey, nil, nil, nil + } + + // Check that the cached server certificate is not expired, and that it's + // valid for the ServerName. This should be ensured by the cache key, but + // protect the application from a faulty ClientSessionCache implementation. + if !c.config.InsecureSkipVerify { + if len(session.verifiedChains) == 0 { + // The original connection had InsecureSkipVerify, while this doesn't. + return cacheKey, nil, nil, nil + } + serverCert := session.serverCertificates[0] + if c.config.time().After(serverCert.NotAfter) { + // Expired certificate, delete the entry. + c.config.ClientSessionCache.Put(cacheKey, nil) + return cacheKey, nil, nil, nil + } + if err := serverCert.VerifyHostname(c.config.ServerName); err != nil { + return cacheKey, nil, nil, nil + } + } + + if session.vers != VersionTLS13 { + // In TLS 1.2 the cipher suite must match the resumed session. Ensure we + // are still offering it. + if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil { + return cacheKey, nil, nil, nil + } + + hello.sessionTicket = session.sessionTicket + return + } + + // Check that the session ticket is not expired. + if c.config.time().After(session.useBy) { + c.config.ClientSessionCache.Put(cacheKey, nil) + return cacheKey, nil, nil, nil + } + + // In TLS 1.3 the KDF hash must match the resumed session. Ensure we + // offer at least one cipher suite with that hash. + cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) + if cipherSuite == nil { + return cacheKey, nil, nil, nil + } + cipherSuiteOk := false + for _, offeredID := range hello.cipherSuites { + offeredSuite := cipherSuiteTLS13ByID(offeredID) + if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash { + cipherSuiteOk = true + break + } + } + if !cipherSuiteOk { + return cacheKey, nil, nil, nil + } + + // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. + ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond) + identity := pskIdentity{ + label: session.sessionTicket, + obfuscatedTicketAge: ticketAge + session.ageAdd, + } + hello.pskIdentities = []pskIdentity{identity} + hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())} + + // Compute the PSK binders. See RFC 8446, Section 4.2.11.2. + psk := cipherSuite.expandLabel(session.masterSecret, "resumption", + session.nonce, cipherSuite.hash.Size()) + earlySecret = cipherSuite.extract(psk, nil) + binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) + if c.extraConfig != nil { + hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0 + } + transcript := cipherSuite.hash.New() + transcript.Write(hello.marshalWithoutBinders()) + pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)} + hello.updateBinders(pskBinders) + + if session.vers == VersionTLS13 && c.extraConfig != nil && c.extraConfig.SetAppDataFromSessionState != nil { + c.extraConfig.SetAppDataFromSessionState(appData) + } + return +} + +func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { + peerVersion := serverHello.vers + if serverHello.supportedVersion != 0 { + peerVersion = serverHello.supportedVersion + } + + vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion}) + if !ok { + c.sendAlert(alertProtocolVersion) + return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion) + } + + c.vers = vers + c.haveVers = true + c.in.version = vers + c.out.version = vers + + return nil +} + +// Does the handshake, either a full one or resumes old session. Requires hs.c, +// hs.hello, hs.serverHello, and, optionally, hs.session to be set. +func (hs *clientHandshakeState) handshake() error { + c := hs.c + + isResume, err := hs.processServerHello() + if err != nil { + return err + } + + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + + // No signatures of the handshake are needed in a resumption. + // Otherwise, in a full handshake, if we don't have any certificates + // configured then we will never send a CertificateVerify message and + // thus no signatures are needed in that case either. + if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) { + hs.finishedHash.discardHandshakeBuffer() + } + + hs.finishedHash.Write(hs.hello.marshal()) + hs.finishedHash.Write(hs.serverHello.marshal()) + + c.buffering = true + c.didResume = isResume + if isResume { + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.readSessionTicket(); err != nil { + return err + } + if err := hs.readFinished(c.serverFinished[:]); err != nil { + return err + } + c.clientFinishedIsFirst = false + // Make sure the connection is still being verified whether or not this + // is a resumption. Resumptions currently don't reverify certificates so + // they don't call verifyServerCertificate. See Issue 31641. + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + if err := hs.sendFinished(c.clientFinished[:]); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + } else { + if err := hs.doFullHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.sendFinished(c.clientFinished[:]); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + c.clientFinishedIsFirst = true + if err := hs.readSessionTicket(); err != nil { + return err + } + if err := hs.readFinished(c.serverFinished[:]); err != nil { + return err + } + } + + c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random) + atomic.StoreUint32(&c.handshakeStatus, 1) + + return nil +} + +func (hs *clientHandshakeState) pickCipherSuite() error { + if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil { + hs.c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server chose an unconfigured cipher suite") + } + + hs.c.cipherSuite = hs.suite.id + return nil +} + +func (hs *clientHandshakeState) doFullHandshake() error { + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return err + } + certMsg, ok := msg.(*certificateMsg) + if !ok || len(certMsg.certificates) == 0 { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.finishedHash.Write(certMsg.marshal()) + + msg, err = c.readHandshake() + if err != nil { + return err + } + + cs, ok := msg.(*certificateStatusMsg) + if ok { + // RFC4366 on Certificate Status Request: + // The server MAY return a "certificate_status" message. + + if !hs.serverHello.ocspStapling { + // If a server returns a "CertificateStatus" message, then the + // server MUST have included an extension of type "status_request" + // with empty "extension_data" in the extended server hello. + + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: received unexpected CertificateStatus message") + } + hs.finishedHash.Write(cs.marshal()) + + c.ocspResponse = cs.response + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + if c.handshakes == 0 { + // If this is the first handshake on a connection, process and + // (optionally) verify the server's certificates. + if err := c.verifyServerCertificate(certMsg.certificates); err != nil { + return err + } + } else { + // This is a renegotiation handshake. We require that the + // server's identity (i.e. leaf certificate) is unchanged and + // thus any previous trust decision is still valid. + // + // See https://mitls.org/pages/attacks/3SHAKE for the + // motivation behind this requirement. + if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { + c.sendAlert(alertBadCertificate) + return errors.New("tls: server's identity changed during renegotiation") + } + } + + keyAgreement := hs.suite.ka(c.vers) + + skx, ok := msg.(*serverKeyExchangeMsg) + if ok { + hs.finishedHash.Write(skx.marshal()) + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) + if err != nil { + c.sendAlert(alertUnexpectedMessage) + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + var chainToSend *Certificate + var certRequested bool + certReq, ok := msg.(*certificateRequestMsg) + if ok { + certRequested = true + hs.finishedHash.Write(certReq.marshal()) + + cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq) + if chainToSend, err = c.getClientCertificate(cri); err != nil { + c.sendAlert(alertInternalError) + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + shd, ok := msg.(*serverHelloDoneMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(shd, msg) + } + hs.finishedHash.Write(shd.marshal()) + + // If the server requested a certificate then we have to send a + // Certificate message, even if it's empty because we don't have a + // certificate to send. + if certRequested { + certMsg = new(certificateMsg) + certMsg.certificates = chainToSend.Certificate + hs.finishedHash.Write(certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { + return err + } + } + + preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + if ckx != nil { + hs.finishedHash.Write(ckx.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil { + return err + } + } + + if chainToSend != nil && len(chainToSend.Certificate) > 0 { + certVerify := &certificateVerifyMsg{} + + key, ok := chainToSend.PrivateKey.(crypto.Signer) + if !ok { + c.sendAlert(alertInternalError) + return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) + } + + var sigType uint8 + var sigHash crypto.Hash + if c.vers >= VersionTLS12 { + signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms) + if err != nil { + c.sendAlert(alertIllegalParameter) + return err + } + sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + certVerify.hasSignatureAlgorithm = true + certVerify.signatureAlgorithm = signatureAlgorithm + } else { + sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public()) + if err != nil { + c.sendAlert(alertIllegalParameter) + return err + } + } + + signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret) + signOpts := crypto.SignerOpts(sigHash) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} + } + certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + hs.finishedHash.Write(certVerify.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil { + return err + } + } + + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) + if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to write to key log: " + err.Error()) + } + + hs.finishedHash.discardHandshakeBuffer() + + return nil +} + +func (hs *clientHandshakeState) establishKeys() error { + c := hs.c + + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) + var clientCipher, serverCipher any + var clientHash, serverHash hash.Hash + if hs.suite.cipher != nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */) + clientHash = hs.suite.mac(clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */) + serverHash = hs.suite.mac(serverMAC) + } else { + clientCipher = hs.suite.aead(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) + return nil +} + +func (hs *clientHandshakeState) serverResumedSession() bool { + // If the server responded with the same sessionId then it means the + // sessionTicket is being used to resume a TLS session. + return hs.session != nil && hs.hello.sessionId != nil && + bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId) +} + +func (hs *clientHandshakeState) processServerHello() (bool, error) { + c := hs.c + + if err := hs.pickCipherSuite(); err != nil { + return false, err + } + + if hs.serverHello.compressionMethod != compressionNone { + c.sendAlert(alertUnexpectedMessage) + return false, errors.New("tls: server selected unsupported compression format") + } + + if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported { + c.secureRenegotiation = true + if len(hs.serverHello.secureRenegotiation) != 0 { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: initial handshake had non-empty renegotiation extension") + } + } + + if c.handshakes > 0 && c.secureRenegotiation { + var expectedSecureRenegotiation [24]byte + copy(expectedSecureRenegotiation[:], c.clientFinished[:]) + copy(expectedSecureRenegotiation[12:], c.serverFinished[:]) + if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: incorrect renegotiation extension contents") + } + } + + if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil { + c.sendAlert(alertUnsupportedExtension) + return false, err + } + c.clientProtocol = hs.serverHello.alpnProtocol + + c.scts = hs.serverHello.scts + + if !hs.serverResumedSession() { + return false, nil + } + + if hs.session.vers != c.vers { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different version") + } + + if hs.session.cipherSuite != hs.suite.id { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different cipher suite") + } + + // Restore masterSecret, peerCerts, and ocspResponse from previous state + hs.masterSecret = hs.session.masterSecret + c.peerCertificates = hs.session.serverCertificates + c.verifiedChains = hs.session.verifiedChains + c.ocspResponse = hs.session.ocspResponse + // Let the ServerHello SCTs override the session SCTs from the original + // connection, if any are provided + if len(c.scts) == 0 && len(hs.session.scts) != 0 { + c.scts = hs.session.scts + } + + return true, nil +} + +// checkALPN ensure that the server's choice of ALPN protocol is compatible with +// the protocols that we advertised in the Client Hello. +func checkALPN(clientProtos []string, serverProto string) error { + if serverProto == "" { + return nil + } + if len(clientProtos) == 0 { + return errors.New("tls: server advertised unrequested ALPN extension") + } + for _, proto := range clientProtos { + if proto == serverProto { + return nil + } + } + return errors.New("tls: server selected unadvertised ALPN protocol") +} + +func (hs *clientHandshakeState) readFinished(out []byte) error { + c := hs.c + + if err := c.readChangeCipherSpec(); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + serverFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverFinished, msg) + } + + verify := hs.finishedHash.serverSum(hs.masterSecret) + if len(verify) != len(serverFinished.verifyData) || + subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server's Finished message was incorrect") + } + hs.finishedHash.Write(serverFinished.marshal()) + copy(out, verify) + return nil +} + +func (hs *clientHandshakeState) readSessionTicket() error { + if !hs.serverHello.ticketSupported { + return nil + } + + c := hs.c + msg, err := c.readHandshake() + if err != nil { + return err + } + sessionTicketMsg, ok := msg.(*newSessionTicketMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(sessionTicketMsg, msg) + } + hs.finishedHash.Write(sessionTicketMsg.marshal()) + + hs.session = &clientSessionState{ + sessionTicket: sessionTicketMsg.ticket, + vers: c.vers, + cipherSuite: hs.suite.id, + masterSecret: hs.masterSecret, + serverCertificates: c.peerCertificates, + verifiedChains: c.verifiedChains, + receivedAt: c.config.time(), + ocspResponse: c.ocspResponse, + scts: c.scts, + } + + return nil +} + +func (hs *clientHandshakeState) sendFinished(out []byte) error { + c := hs.c + + if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { + return err + } + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) + hs.finishedHash.Write(finished.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { + return err + } + copy(out, finished.verifyData) + return nil +} + +// verifyServerCertificate parses and verifies the provided chain, setting +// c.verifiedChains and c.peerCertificates or sending the appropriate alert. +func (c *Conn) verifyServerCertificate(certificates [][]byte) error { + certs := make([]*x509.Certificate, len(certificates)) + for i, asn1Data := range certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert + } + + if !c.config.InsecureSkipVerify { + opts := x509.VerifyOptions{ + Roots: c.config.RootCAs, + CurrentTime: c.config.time(), + DNSName: c.config.ServerName, + Intermediates: x509.NewCertPool(), + } + + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + var err error + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: + break + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + } + + c.peerCertificates = certs + + if c.config.VerifyPeerCertificate != nil { + if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + return nil +} + +// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS +// <= 1.2 CertificateRequest, making an effort to fill in missing information. +func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo { + cri := &certificateRequestInfo{ + AcceptableCAs: certReq.certificateAuthorities, + Version: vers, + ctx: ctx, + } + + var rsaAvail, ecAvail bool + for _, certType := range certReq.certificateTypes { + switch certType { + case certTypeRSASign: + rsaAvail = true + case certTypeECDSASign: + ecAvail = true + } + } + + if !certReq.hasSignatureAlgorithm { + // Prior to TLS 1.2, signature schemes did not exist. In this case we + // make up a list based on the acceptable certificate types, to help + // GetClientCertificate and SupportsCertificate select the right certificate. + // The hash part of the SignatureScheme is a lie here, because + // TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA. + switch { + case rsaAvail && ecAvail: + cri.SignatureSchemes = []SignatureScheme{ + ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, + PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1, + } + case rsaAvail: + cri.SignatureSchemes = []SignatureScheme{ + PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1, + } + case ecAvail: + cri.SignatureSchemes = []SignatureScheme{ + ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, + } + } + return toCertificateRequestInfo(cri) + } + + // Filter the signature schemes based on the certificate types. + // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated"). + cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms)) + for _, sigScheme := range certReq.supportedSignatureAlgorithms { + sigType, _, err := typeAndHashFromSignatureScheme(sigScheme) + if err != nil { + continue + } + switch sigType { + case signatureECDSA, signatureEd25519: + if ecAvail { + cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) + } + case signatureRSAPSS, signaturePKCS1v15: + if rsaAvail { + cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) + } + } + } + + return toCertificateRequestInfo(cri) +} + +func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) { + if c.config.GetClientCertificate != nil { + return c.config.GetClientCertificate(cri) + } + + for _, chain := range c.config.Certificates { + if err := cri.SupportsCertificate(&chain); err != nil { + continue + } + return &chain, nil + } + + // No acceptable certificate found. Don't send a certificate. + return new(Certificate), nil +} + +const clientSessionCacheKeyPrefix = "qtls-" + +// clientSessionCacheKey returns a key used to cache sessionTickets that could +// be used to resume previously negotiated TLS sessions with a server. +func clientSessionCacheKey(serverAddr net.Addr, config *config) string { + if len(config.ServerName) > 0 { + return clientSessionCacheKeyPrefix + config.ServerName + } + return clientSessionCacheKeyPrefix + serverAddr.String() +} + +// hostnameInSNI converts name into an appropriate hostname for SNI. +// Literal IP addresses and absolute FQDNs are not permitted as SNI values. +// See RFC 6066, Section 3. +func hostnameInSNI(name string) string { + host := name + if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1 : len(host)-1] + } + if i := strings.LastIndex(host, "%"); i > 0 { + host = host[:i] + } + if net.ParseIP(host) != nil { + return "" + } + for len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + return name +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go new file mode 100644 index 00000000..7f05f2c6 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go @@ -0,0 +1,736 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "bytes" + "context" + "crypto" + "crypto/hmac" + "crypto/rsa" + "encoding/binary" + "errors" + "hash" + "sync/atomic" + "time" + + "golang.org/x/crypto/cryptobyte" +) + +type clientHandshakeStateTLS13 struct { + c *Conn + ctx context.Context + serverHello *serverHelloMsg + hello *clientHelloMsg + ecdheParams ecdheParameters + + session *clientSessionState + earlySecret []byte + binderKey []byte + + certReq *certificateRequestMsgTLS13 + usingPSK bool + sentDummyCCS bool + suite *cipherSuiteTLS13 + transcript hash.Hash + masterSecret []byte + trafficSecret []byte // client_application_traffic_secret_0 +} + +// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and, +// optionally, hs.session, hs.earlySecret and hs.binderKey to be set. +func (hs *clientHandshakeStateTLS13) handshake() error { + c := hs.c + + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + + // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, + // sections 4.1.2 and 4.1.3. + if c.handshakes > 0 { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: server selected TLS 1.3 in a renegotiation") + } + + // Consistency check on the presence of a keyShare and its parameters. + if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { + return c.sendAlert(alertInternalError) + } + + if err := hs.checkServerHelloOrHRR(); err != nil { + return err + } + + hs.transcript = hs.suite.hash.New() + hs.transcript.Write(hs.hello.marshal()) + + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + if err := hs.processHelloRetryRequest(); err != nil { + return err + } + } + + hs.transcript.Write(hs.serverHello.marshal()) + + c.buffering = true + if err := hs.processServerHello(); err != nil { + return err + } + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + if err := hs.establishHandshakeKeys(); err != nil { + return err + } + if err := hs.readServerParameters(); err != nil { + return err + } + if err := hs.readServerCertificate(); err != nil { + return err + } + if err := hs.readServerFinished(); err != nil { + return err + } + if err := hs.sendClientCertificate(); err != nil { + return err + } + if err := hs.sendClientFinished(); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + + atomic.StoreUint32(&c.handshakeStatus, 1) + + return nil +} + +// checkServerHelloOrHRR does validity checks that apply to both ServerHello and +// HelloRetryRequest messages. It sets hs.suite. +func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error { + c := hs.c + + if hs.serverHello.supportedVersion == 0 { + c.sendAlert(alertMissingExtension) + return errors.New("tls: server selected TLS 1.3 using the legacy version field") + } + + if hs.serverHello.supportedVersion != VersionTLS13 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected an invalid version after a HelloRetryRequest") + } + + if hs.serverHello.vers != VersionTLS12 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server sent an incorrect legacy version") + } + + if hs.serverHello.ocspStapling || + hs.serverHello.ticketSupported || + hs.serverHello.secureRenegotiationSupported || + len(hs.serverHello.secureRenegotiation) != 0 || + len(hs.serverHello.alpnProtocol) != 0 || + len(hs.serverHello.scts) != 0 { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3") + } + + if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server did not echo the legacy session ID") + } + + if hs.serverHello.compressionMethod != compressionNone { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected unsupported compression format") + } + + selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite) + if hs.suite != nil && selectedSuite != hs.suite { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server changed cipher suite after a HelloRetryRequest") + } + if selectedSuite == nil { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server chose an unconfigured cipher suite") + } + hs.suite = selectedSuite + c.cipherSuite = hs.suite.id + + return nil +} + +// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility +// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. +func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + if hs.sentDummyCCS { + return nil + } + hs.sentDummyCCS = true + + _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + return err +} + +// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and +// resends hs.hello, and reads the new ServerHello into hs.serverHello. +func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { + c := hs.c + + // The first ClientHello gets double-hashed into the transcript upon a + // HelloRetryRequest. (The idea is that the server might offload transcript + // storage to the client in the cookie.) See RFC 8446, Section 4.4.1. + chHash := hs.transcript.Sum(nil) + hs.transcript.Reset() + hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + hs.transcript.Write(chHash) + hs.transcript.Write(hs.serverHello.marshal()) + + // The only HelloRetryRequest extensions we support are key_share and + // cookie, and clients must abort the handshake if the HRR would not result + // in any change in the ClientHello. + if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server sent an unnecessary HelloRetryRequest message") + } + + if hs.serverHello.cookie != nil { + hs.hello.cookie = hs.serverHello.cookie + } + + if hs.serverHello.serverShare.group != 0 { + c.sendAlert(alertDecodeError) + return errors.New("tls: received malformed key_share extension") + } + + // If the server sent a key_share extension selecting a group, ensure it's + // a group we advertised but did not send a key share for, and send a key + // share for it this time. + if curveID := hs.serverHello.selectedGroup; curveID != 0 { + curveOK := false + for _, id := range hs.hello.supportedCurves { + if id == curveID { + curveOK = true + break + } + } + if !curveOK { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected unsupported group") + } + if hs.ecdheParams.CurveID() == curveID { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share") + } + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + c.sendAlert(alertInternalError) + return errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(c.config.rand(), curveID) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.ecdheParams = params + hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + } + + hs.hello.raw = nil + if len(hs.hello.pskIdentities) > 0 { + pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) + if pskSuite == nil { + return c.sendAlert(alertInternalError) + } + if pskSuite.hash == hs.suite.hash { + // Update binders and obfuscated_ticket_age. + ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond) + hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd + + transcript := hs.suite.hash.New() + transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + transcript.Write(chHash) + transcript.Write(hs.serverHello.marshal()) + transcript.Write(hs.hello.marshalWithoutBinders()) + pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} + hs.hello.updateBinders(pskBinders) + } else { + // Server selected a cipher suite incompatible with the PSK. + hs.hello.pskIdentities = nil + hs.hello.pskBinders = nil + } + } + + if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { + c.extraConfig.Rejected0RTT() + } + hs.hello.earlyData = false // disable 0-RTT + + hs.transcript.Write(hs.hello.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } + hs.serverHello = serverHello + + if err := hs.checkServerHelloOrHRR(); err != nil { + return err + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) processServerHello() error { + c := hs.c + + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: server sent two HelloRetryRequest messages") + } + + if len(hs.serverHello.cookie) != 0 { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent a cookie in a normal ServerHello") + } + + if hs.serverHello.selectedGroup != 0 { + c.sendAlert(alertDecodeError) + return errors.New("tls: malformed key_share extension") + } + + if hs.serverHello.serverShare.group == 0 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server did not send a key share") + } + if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected unsupported group") + } + + if !hs.serverHello.selectedIdentityPresent { + return nil + } + + if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected an invalid PSK") + } + + if len(hs.hello.pskIdentities) != 1 || hs.session == nil { + return c.sendAlert(alertInternalError) + } + pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) + if pskSuite == nil { + return c.sendAlert(alertInternalError) + } + if pskSuite.hash != hs.suite.hash { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: server selected an invalid PSK and cipher suite pair") + } + + hs.usingPSK = true + c.didResume = true + c.peerCertificates = hs.session.serverCertificates + c.verifiedChains = hs.session.verifiedChains + c.ocspResponse = hs.session.ocspResponse + c.scts = hs.session.scts + return nil +} + +func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { + c := hs.c + + sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) + if sharedKey == nil { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: invalid server key share") + } + + earlySecret := hs.earlySecret + if !hs.usingPSK { + earlySecret = hs.suite.extract(nil, nil) + } + handshakeSecret := hs.suite.extract(sharedKey, + hs.suite.deriveSecret(earlySecret, "derived", nil)) + + clientSecret := hs.suite.deriveSecret(handshakeSecret, + clientHandshakeTrafficLabel, hs.transcript) + c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret) + c.out.setTrafficSecret(hs.suite, clientSecret) + serverSecret := hs.suite.deriveSecret(handshakeSecret, + serverHandshakeTrafficLabel, hs.transcript) + c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret) + c.in.setTrafficSecret(hs.suite, serverSecret) + + err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + hs.masterSecret = hs.suite.extract(nil, + hs.suite.deriveSecret(handshakeSecret, "derived", nil)) + + return nil +} + +func (hs *clientHandshakeStateTLS13) readServerParameters() error { + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return err + } + + encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(encryptedExtensions, msg) + } + // Notify the caller if 0-RTT was rejected. + if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { + c.extraConfig.Rejected0RTT() + } + c.used0RTT = encryptedExtensions.earlyData + if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil { + hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions) + } + hs.transcript.Write(encryptedExtensions.marshal()) + + if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil { + c.sendAlert(alertUnsupportedExtension) + return err + } + c.clientProtocol = encryptedExtensions.alpnProtocol + + if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection { + if len(encryptedExtensions.alpnProtocol) == 0 { + // the server didn't select an ALPN + c.sendAlert(alertNoApplicationProtocol) + return errors.New("ALPN negotiation failed. Server didn't offer any protocols") + } + } + return nil +} + +func (hs *clientHandshakeStateTLS13) readServerCertificate() error { + c := hs.c + + // Either a PSK or a certificate is always used, but not both. + // See RFC 8446, Section 4.1.1. + if hs.usingPSK { + // Make sure the connection is still being verified whether or not this + // is a resumption. Resumptions currently don't reverify certificates so + // they don't call verifyServerCertificate. See Issue 31641. + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + return nil + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + certReq, ok := msg.(*certificateRequestMsgTLS13) + if ok { + hs.transcript.Write(certReq.marshal()) + + hs.certReq = certReq + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + certMsg, ok := msg.(*certificateMsgTLS13) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + if len(certMsg.certificate.Certificate) == 0 { + c.sendAlert(alertDecodeError) + return errors.New("tls: received empty certificates message") + } + hs.transcript.Write(certMsg.marshal()) + + c.scts = certMsg.certificate.SignedCertificateTimestamps + c.ocspResponse = certMsg.certificate.OCSPStaple + + if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil { + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + // See RFC 8446, Section 4.4.3. + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: certificate used with invalid signature algorithm") + } + sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: certificate used with invalid signature algorithm") + } + signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) + if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, + sigHash, signed, certVerify.signature); err != nil { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid signature by the server certificate: " + err.Error()) + } + + hs.transcript.Write(certVerify.marshal()) + + return nil +} + +func (hs *clientHandshakeStateTLS13) readServerFinished() error { + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return err + } + + finished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(finished, msg) + } + + expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) + if !hmac.Equal(expectedMAC, finished.verifyData) { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid server finished hash") + } + + hs.transcript.Write(finished.marshal()) + + // Derive secrets that take context through the server Finished. + + hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, + clientApplicationTrafficLabel, hs.transcript) + serverSecret := hs.suite.deriveSecret(hs.masterSecret, + serverApplicationTrafficLabel, hs.transcript) + c.in.exportKey(EncryptionApplication, hs.suite, serverSecret) + c.in.setTrafficSecret(hs.suite, serverSecret) + + err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) + + return nil +} + +func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { + c := hs.c + + if hs.certReq == nil { + return nil + } + + cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{ + AcceptableCAs: hs.certReq.certificateAuthorities, + SignatureSchemes: hs.certReq.supportedSignatureAlgorithms, + Version: c.vers, + ctx: hs.ctx, + })) + if err != nil { + return err + } + + certMsg := new(certificateMsgTLS13) + + certMsg.certificate = *cert + certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0 + certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0 + + hs.transcript.Write(certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { + return err + } + + // If we sent an empty certificate message, skip the CertificateVerify. + if len(cert.Certificate) == 0 { + return nil + } + + certVerifyMsg := new(certificateVerifyMsg) + certVerifyMsg.hasSignatureAlgorithm = true + + certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms) + if err != nil { + // getClientCertificate returned a certificate incompatible with the + // CertificateRequestInfo supported signature algorithms. + c.sendAlert(alertHandshakeFailure) + return err + } + + sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + + signed := signedMessage(sigHash, clientSignatureContext, hs.transcript) + signOpts := crypto.SignerOpts(sigHash) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} + } + sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts) + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to sign handshake: " + err.Error()) + } + certVerifyMsg.signature = sig + + hs.transcript.Write(certVerifyMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { + return err + } + + return nil +} + +func (hs *clientHandshakeStateTLS13) sendClientFinished() error { + c := hs.c + + finished := &finishedMsg{ + verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), + } + + hs.transcript.Write(finished.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { + return err + } + + c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) + c.out.setTrafficSecret(hs.suite, hs.trafficSecret) + + if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { + c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, + resumptionLabel, hs.transcript) + } + + return nil +} + +func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { + if !c.isClient { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: received new session ticket from a client") + } + + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { + return nil + } + + // See RFC 8446, Section 4.6.1. + if msg.lifetime == 0 { + return nil + } + lifetime := time.Duration(msg.lifetime) * time.Second + if lifetime > maxSessionTicketLifetime { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: received a session ticket with invalid lifetime") + } + + cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) + if cipherSuite == nil || c.resumptionSecret == nil { + return c.sendAlert(alertInternalError) + } + + // We need to save the max_early_data_size that the server sent us, in order + // to decide if we're going to try 0-RTT with this ticket. + // However, at the same time, the qtls.ClientSessionTicket needs to be equal to + // the tls.ClientSessionTicket, so we can't just add a new field to the struct. + // We therefore abuse the nonce field (which is a byte slice) + nonceWithEarlyData := make([]byte, len(msg.nonce)+4) + binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData) + copy(nonceWithEarlyData[4:], msg.nonce) + + var appData []byte + if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil { + appData = c.extraConfig.GetAppDataForSessionState() + } + var b cryptobyte.Builder + b.AddUint16(clientSessionStateVersion) // revision + b.AddUint32(msg.maxEarlyData) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(appData) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(msg.nonce) + }) + + // Save the resumption_master_secret and nonce instead of deriving the PSK + // to do the least amount of work on NewSessionTicket messages before we + // know if the ticket will be used. Forward secrecy of resumed connections + // is guaranteed by the requirement for pskModeDHE. + session := &clientSessionState{ + sessionTicket: msg.label, + vers: c.vers, + cipherSuite: c.cipherSuite, + masterSecret: c.resumptionSecret, + serverCertificates: c.peerCertificates, + verifiedChains: c.verifiedChains, + receivedAt: c.config.time(), + nonce: b.BytesOrPanic(), + useBy: c.config.time().Add(lifetime), + ageAdd: msg.ageAdd, + ocspResponse: c.ocspResponse, + scts: c.scts, + } + + cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session)) + + return nil +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go new file mode 100644 index 00000000..07193c8e --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_messages.go @@ -0,0 +1,1843 @@ +// 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 LICENSE file. + +package qtls + +import ( + "fmt" + "strings" + + "golang.org/x/crypto/cryptobyte" +) + +// The marshalingFunction type is an adapter to allow the use of ordinary +// functions as cryptobyte.MarshalingValue. +type marshalingFunction func(b *cryptobyte.Builder) error + +func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error { + return f(b) +} + +// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If +// the length of the sequence is not the value specified, it produces an error. +func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) { + b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error { + if len(v) != n { + return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v)) + } + b.AddBytes(v) + return nil + })) +} + +// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder. +func addUint64(b *cryptobyte.Builder, v uint64) { + b.AddUint32(uint32(v >> 32)) + b.AddUint32(uint32(v)) +} + +// readUint64 decodes a big-endian, 64-bit value into out and advances over it. +// It reports whether the read was successful. +func readUint64(s *cryptobyte.String, out *uint64) bool { + var hi, lo uint32 + if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) { + return false + } + *out = uint64(hi)<<32 | uint64(lo) + return true +} + +// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a +// []byte instead of a cryptobyte.String. +func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { + return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out)) +} + +// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a +// []byte instead of a cryptobyte.String. +func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { + return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out)) +} + +// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a +// []byte instead of a cryptobyte.String. +func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { + return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out)) +} + +type clientHelloMsg struct { + raw []byte + vers uint16 + random []byte + sessionId []byte + cipherSuites []uint16 + compressionMethods []uint8 + serverName string + ocspStapling bool + supportedCurves []CurveID + supportedPoints []uint8 + ticketSupported bool + sessionTicket []uint8 + supportedSignatureAlgorithms []SignatureScheme + supportedSignatureAlgorithmsCert []SignatureScheme + secureRenegotiationSupported bool + secureRenegotiation []byte + alpnProtocols []string + scts bool + supportedVersions []uint16 + cookie []byte + keyShares []keyShare + earlyData bool + pskModes []uint8 + pskIdentities []pskIdentity + pskBinders [][]byte + additionalExtensions []Extension +} + +func (m *clientHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeClientHello) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(m.vers) + addBytesWithLength(b, m.random, 32) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.sessionId) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, suite := range m.cipherSuites { + b.AddUint16(suite) + } + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.compressionMethods) + }) + + // If extensions aren't present, omit them. + var extensionsPresent bool + bWithoutExtensions := *b + + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if len(m.serverName) > 0 { + // RFC 6066, Section 3 + b.AddUint16(extensionServerName) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(0) // name_type = host_name + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(m.serverName)) + }) + }) + }) + } + if m.ocspStapling { + // RFC 4366, Section 3.6 + b.AddUint16(extensionStatusRequest) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(1) // status_type = ocsp + b.AddUint16(0) // empty responder_id_list + b.AddUint16(0) // empty request_extensions + }) + } + if len(m.supportedCurves) > 0 { + // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 + b.AddUint16(extensionSupportedCurves) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, curve := range m.supportedCurves { + b.AddUint16(uint16(curve)) + } + }) + }) + } + if len(m.supportedPoints) > 0 { + // RFC 4492, Section 5.1.2 + b.AddUint16(extensionSupportedPoints) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.supportedPoints) + }) + }) + } + if m.ticketSupported { + // RFC 5077, Section 3.2 + b.AddUint16(extensionSessionTicket) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.sessionTicket) + }) + } + if len(m.supportedSignatureAlgorithms) > 0 { + // RFC 5246, Section 7.4.1.4.1 + b.AddUint16(extensionSignatureAlgorithms) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sigAlgo := range m.supportedSignatureAlgorithms { + b.AddUint16(uint16(sigAlgo)) + } + }) + }) + } + if len(m.supportedSignatureAlgorithmsCert) > 0 { + // RFC 8446, Section 4.2.3 + b.AddUint16(extensionSignatureAlgorithmsCert) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { + b.AddUint16(uint16(sigAlgo)) + } + }) + }) + } + if m.secureRenegotiationSupported { + // RFC 5746, Section 3.2 + b.AddUint16(extensionRenegotiationInfo) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.secureRenegotiation) + }) + }) + } + if len(m.alpnProtocols) > 0 { + // RFC 7301, Section 3.1 + b.AddUint16(extensionALPN) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, proto := range m.alpnProtocols { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(proto)) + }) + } + }) + }) + } + if m.scts { + // RFC 6962, Section 3.3.1 + b.AddUint16(extensionSCT) + b.AddUint16(0) // empty extension_data + } + if len(m.supportedVersions) > 0 { + // RFC 8446, Section 4.2.1 + b.AddUint16(extensionSupportedVersions) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + for _, vers := range m.supportedVersions { + b.AddUint16(vers) + } + }) + }) + } + if len(m.cookie) > 0 { + // RFC 8446, Section 4.2.2 + b.AddUint16(extensionCookie) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.cookie) + }) + }) + } + if len(m.keyShares) > 0 { + // RFC 8446, Section 4.2.8 + b.AddUint16(extensionKeyShare) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, ks := range m.keyShares { + b.AddUint16(uint16(ks.group)) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(ks.data) + }) + } + }) + }) + } + if m.earlyData { + // RFC 8446, Section 4.2.10 + b.AddUint16(extensionEarlyData) + b.AddUint16(0) // empty extension_data + } + if len(m.pskModes) > 0 { + // RFC 8446, Section 4.2.9 + b.AddUint16(extensionPSKModes) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.pskModes) + }) + }) + } + for _, ext := range m.additionalExtensions { + b.AddUint16(ext.Type) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(ext.Data) + }) + } + if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension + // RFC 8446, Section 4.2.11 + b.AddUint16(extensionPreSharedKey) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, psk := range m.pskIdentities { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(psk.label) + }) + b.AddUint32(psk.obfuscatedTicketAge) + } + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, binder := range m.pskBinders { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(binder) + }) + } + }) + }) + } + + extensionsPresent = len(b.BytesOrPanic()) > 2 + }) + + if !extensionsPresent { + *b = bWithoutExtensions + } + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +// marshalWithoutBinders returns the ClientHello through the +// PreSharedKeyExtension.identities field, according to RFC 8446, Section +// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length. +func (m *clientHelloMsg) marshalWithoutBinders() []byte { + bindersLen := 2 // uint16 length prefix + for _, binder := range m.pskBinders { + bindersLen += 1 // uint8 length prefix + bindersLen += len(binder) + } + + fullMessage := m.marshal() + return fullMessage[:len(fullMessage)-bindersLen] +} + +// updateBinders updates the m.pskBinders field, if necessary updating the +// cached marshaled representation. The supplied binders must have the same +// length as the current m.pskBinders. +func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) { + if len(pskBinders) != len(m.pskBinders) { + panic("tls: internal error: pskBinders length mismatch") + } + for i := range m.pskBinders { + if len(pskBinders[i]) != len(m.pskBinders[i]) { + panic("tls: internal error: pskBinders length mismatch") + } + } + m.pskBinders = pskBinders + if m.raw != nil { + lenWithoutBinders := len(m.marshalWithoutBinders()) + b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders]) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, binder := range m.pskBinders { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(binder) + }) + } + }) + if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) { + panic("tls: internal error: failed to update binders") + } + } +} + +func (m *clientHelloMsg) unmarshal(data []byte) bool { + *m = clientHelloMsg{raw: data} + s := cryptobyte.String(data) + + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || + !readUint8LengthPrefixed(&s, &m.sessionId) { + return false + } + + var cipherSuites cryptobyte.String + if !s.ReadUint16LengthPrefixed(&cipherSuites) { + return false + } + m.cipherSuites = []uint16{} + m.secureRenegotiationSupported = false + for !cipherSuites.Empty() { + var suite uint16 + if !cipherSuites.ReadUint16(&suite) { + return false + } + if suite == scsvRenegotiation { + m.secureRenegotiationSupported = true + } + m.cipherSuites = append(m.cipherSuites, suite) + } + + if !readUint8LengthPrefixed(&s, &m.compressionMethods) { + return false + } + + if s.Empty() { + // ClientHello is optionally followed by extension data + return true + } + + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return false + } + + seenExts := make(map[uint16]bool) + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + if seenExts[extension] { + return false + } + seenExts[extension] = true + + switch extension { + case extensionServerName: + // RFC 6066, Section 3 + var nameList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() { + return false + } + for !nameList.Empty() { + var nameType uint8 + var serverName cryptobyte.String + if !nameList.ReadUint8(&nameType) || + !nameList.ReadUint16LengthPrefixed(&serverName) || + serverName.Empty() { + return false + } + if nameType != 0 { + continue + } + if len(m.serverName) != 0 { + // Multiple names of the same name_type are prohibited. + return false + } + m.serverName = string(serverName) + // An SNI value may not include a trailing dot. + if strings.HasSuffix(m.serverName, ".") { + return false + } + } + case extensionStatusRequest: + // RFC 4366, Section 3.6 + var statusType uint8 + var ignored cryptobyte.String + if !extData.ReadUint8(&statusType) || + !extData.ReadUint16LengthPrefixed(&ignored) || + !extData.ReadUint16LengthPrefixed(&ignored) { + return false + } + m.ocspStapling = statusType == statusTypeOCSP + case extensionSupportedCurves: + // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 + var curves cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() { + return false + } + for !curves.Empty() { + var curve uint16 + if !curves.ReadUint16(&curve) { + return false + } + m.supportedCurves = append(m.supportedCurves, CurveID(curve)) + } + case extensionSupportedPoints: + // RFC 4492, Section 5.1.2 + if !readUint8LengthPrefixed(&extData, &m.supportedPoints) || + len(m.supportedPoints) == 0 { + return false + } + case extensionSessionTicket: + // RFC 5077, Section 3.2 + m.ticketSupported = true + extData.ReadBytes(&m.sessionTicket, len(extData)) + case extensionSignatureAlgorithms: + // RFC 5246, Section 7.4.1.4.1 + var sigAndAlgs cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { + return false + } + for !sigAndAlgs.Empty() { + var sigAndAlg uint16 + if !sigAndAlgs.ReadUint16(&sigAndAlg) { + return false + } + m.supportedSignatureAlgorithms = append( + m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) + } + case extensionSignatureAlgorithmsCert: + // RFC 8446, Section 4.2.3 + var sigAndAlgs cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { + return false + } + for !sigAndAlgs.Empty() { + var sigAndAlg uint16 + if !sigAndAlgs.ReadUint16(&sigAndAlg) { + return false + } + m.supportedSignatureAlgorithmsCert = append( + m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) + } + case extensionRenegotiationInfo: + // RFC 5746, Section 3.2 + if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { + return false + } + m.secureRenegotiationSupported = true + case extensionALPN: + // RFC 7301, Section 3.1 + var protoList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { + return false + } + for !protoList.Empty() { + var proto cryptobyte.String + if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() { + return false + } + m.alpnProtocols = append(m.alpnProtocols, string(proto)) + } + case extensionSCT: + // RFC 6962, Section 3.3.1 + m.scts = true + case extensionSupportedVersions: + // RFC 8446, Section 4.2.1 + var versList cryptobyte.String + if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() { + return false + } + for !versList.Empty() { + var vers uint16 + if !versList.ReadUint16(&vers) { + return false + } + m.supportedVersions = append(m.supportedVersions, vers) + } + case extensionCookie: + // RFC 8446, Section 4.2.2 + if !readUint16LengthPrefixed(&extData, &m.cookie) || + len(m.cookie) == 0 { + return false + } + case extensionKeyShare: + // RFC 8446, Section 4.2.8 + var clientShares cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&clientShares) { + return false + } + for !clientShares.Empty() { + var ks keyShare + if !clientShares.ReadUint16((*uint16)(&ks.group)) || + !readUint16LengthPrefixed(&clientShares, &ks.data) || + len(ks.data) == 0 { + return false + } + m.keyShares = append(m.keyShares, ks) + } + case extensionEarlyData: + // RFC 8446, Section 4.2.10 + m.earlyData = true + case extensionPSKModes: + // RFC 8446, Section 4.2.9 + if !readUint8LengthPrefixed(&extData, &m.pskModes) { + return false + } + case extensionPreSharedKey: + // RFC 8446, Section 4.2.11 + if !extensions.Empty() { + return false // pre_shared_key must be the last extension + } + var identities cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() { + return false + } + for !identities.Empty() { + var psk pskIdentity + if !readUint16LengthPrefixed(&identities, &psk.label) || + !identities.ReadUint32(&psk.obfuscatedTicketAge) || + len(psk.label) == 0 { + return false + } + m.pskIdentities = append(m.pskIdentities, psk) + } + var binders cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() { + return false + } + for !binders.Empty() { + var binder []byte + if !readUint8LengthPrefixed(&binders, &binder) || + len(binder) == 0 { + return false + } + m.pskBinders = append(m.pskBinders, binder) + } + default: + m.additionalExtensions = append(m.additionalExtensions, Extension{Type: extension, Data: extData}) + continue + } + + if !extData.Empty() { + return false + } + } + + return true +} + +type serverHelloMsg struct { + raw []byte + vers uint16 + random []byte + sessionId []byte + cipherSuite uint16 + compressionMethod uint8 + ocspStapling bool + ticketSupported bool + secureRenegotiationSupported bool + secureRenegotiation []byte + alpnProtocol string + scts [][]byte + supportedVersion uint16 + serverShare keyShare + selectedIdentityPresent bool + selectedIdentity uint16 + supportedPoints []uint8 + + // HelloRetryRequest extensions + cookie []byte + selectedGroup CurveID +} + +func (m *serverHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeServerHello) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(m.vers) + addBytesWithLength(b, m.random, 32) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.sessionId) + }) + b.AddUint16(m.cipherSuite) + b.AddUint8(m.compressionMethod) + + // If extensions aren't present, omit them. + var extensionsPresent bool + bWithoutExtensions := *b + + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if m.ocspStapling { + b.AddUint16(extensionStatusRequest) + b.AddUint16(0) // empty extension_data + } + if m.ticketSupported { + b.AddUint16(extensionSessionTicket) + b.AddUint16(0) // empty extension_data + } + if m.secureRenegotiationSupported { + b.AddUint16(extensionRenegotiationInfo) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.secureRenegotiation) + }) + }) + } + if len(m.alpnProtocol) > 0 { + b.AddUint16(extensionALPN) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(m.alpnProtocol)) + }) + }) + }) + } + if len(m.scts) > 0 { + b.AddUint16(extensionSCT) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sct := range m.scts { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(sct) + }) + } + }) + }) + } + if m.supportedVersion != 0 { + b.AddUint16(extensionSupportedVersions) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(m.supportedVersion) + }) + } + if m.serverShare.group != 0 { + b.AddUint16(extensionKeyShare) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(uint16(m.serverShare.group)) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.serverShare.data) + }) + }) + } + if m.selectedIdentityPresent { + b.AddUint16(extensionPreSharedKey) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(m.selectedIdentity) + }) + } + + if len(m.cookie) > 0 { + b.AddUint16(extensionCookie) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.cookie) + }) + }) + } + if m.selectedGroup != 0 { + b.AddUint16(extensionKeyShare) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(uint16(m.selectedGroup)) + }) + } + if len(m.supportedPoints) > 0 { + b.AddUint16(extensionSupportedPoints) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.supportedPoints) + }) + }) + } + + extensionsPresent = len(b.BytesOrPanic()) > 2 + }) + + if !extensionsPresent { + *b = bWithoutExtensions + } + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *serverHelloMsg) unmarshal(data []byte) bool { + *m = serverHelloMsg{raw: data} + s := cryptobyte.String(data) + + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || + !readUint8LengthPrefixed(&s, &m.sessionId) || + !s.ReadUint16(&m.cipherSuite) || + !s.ReadUint8(&m.compressionMethod) { + return false + } + + if s.Empty() { + // ServerHello is optionally followed by extension data + return true + } + + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return false + } + + seenExts := make(map[uint16]bool) + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + if seenExts[extension] { + return false + } + seenExts[extension] = true + + switch extension { + case extensionStatusRequest: + m.ocspStapling = true + case extensionSessionTicket: + m.ticketSupported = true + case extensionRenegotiationInfo: + if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { + return false + } + m.secureRenegotiationSupported = true + case extensionALPN: + var protoList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { + return false + } + var proto cryptobyte.String + if !protoList.ReadUint8LengthPrefixed(&proto) || + proto.Empty() || !protoList.Empty() { + return false + } + m.alpnProtocol = string(proto) + case extensionSCT: + var sctList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { + return false + } + for !sctList.Empty() { + var sct []byte + if !readUint16LengthPrefixed(&sctList, &sct) || + len(sct) == 0 { + return false + } + m.scts = append(m.scts, sct) + } + case extensionSupportedVersions: + if !extData.ReadUint16(&m.supportedVersion) { + return false + } + case extensionCookie: + if !readUint16LengthPrefixed(&extData, &m.cookie) || + len(m.cookie) == 0 { + return false + } + case extensionKeyShare: + // This extension has different formats in SH and HRR, accept either + // and let the handshake logic decide. See RFC 8446, Section 4.2.8. + if len(extData) == 2 { + if !extData.ReadUint16((*uint16)(&m.selectedGroup)) { + return false + } + } else { + if !extData.ReadUint16((*uint16)(&m.serverShare.group)) || + !readUint16LengthPrefixed(&extData, &m.serverShare.data) { + return false + } + } + case extensionPreSharedKey: + m.selectedIdentityPresent = true + if !extData.ReadUint16(&m.selectedIdentity) { + return false + } + case extensionSupportedPoints: + // RFC 4492, Section 5.1.2 + if !readUint8LengthPrefixed(&extData, &m.supportedPoints) || + len(m.supportedPoints) == 0 { + return false + } + default: + // Ignore unknown extensions. + continue + } + + if !extData.Empty() { + return false + } + } + + return true +} + +type encryptedExtensionsMsg struct { + raw []byte + alpnProtocol string + earlyData bool + + additionalExtensions []Extension +} + +func (m *encryptedExtensionsMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeEncryptedExtensions) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if len(m.alpnProtocol) > 0 { + b.AddUint16(extensionALPN) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(m.alpnProtocol)) + }) + }) + }) + } + if m.earlyData { + // RFC 8446, Section 4.2.10 + b.AddUint16(extensionEarlyData) + b.AddUint16(0) // empty extension_data + } + for _, ext := range m.additionalExtensions { + b.AddUint16(ext.Type) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(ext.Data) + }) + } + }) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { + *m = encryptedExtensionsMsg{raw: data} + s := cryptobyte.String(data) + + var extensions cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return false + } + + for !extensions.Empty() { + var ext uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + switch ext { + case extensionALPN: + var protoList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { + return false + } + var proto cryptobyte.String + if !protoList.ReadUint8LengthPrefixed(&proto) || + proto.Empty() || !protoList.Empty() { + return false + } + m.alpnProtocol = string(proto) + case extensionEarlyData: + m.earlyData = true + default: + m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData}) + continue + } + + if !extData.Empty() { + return false + } + } + + return true +} + +type endOfEarlyDataMsg struct{} + +func (m *endOfEarlyDataMsg) marshal() []byte { + x := make([]byte, 4) + x[0] = typeEndOfEarlyData + return x +} + +func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + +type keyUpdateMsg struct { + raw []byte + updateRequested bool +} + +func (m *keyUpdateMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeKeyUpdate) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + if m.updateRequested { + b.AddUint8(1) + } else { + b.AddUint8(0) + } + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *keyUpdateMsg) unmarshal(data []byte) bool { + m.raw = data + s := cryptobyte.String(data) + + var updateRequested uint8 + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint8(&updateRequested) || !s.Empty() { + return false + } + switch updateRequested { + case 0: + m.updateRequested = false + case 1: + m.updateRequested = true + default: + return false + } + return true +} + +type newSessionTicketMsgTLS13 struct { + raw []byte + lifetime uint32 + ageAdd uint32 + nonce []byte + label []byte + maxEarlyData uint32 +} + +func (m *newSessionTicketMsgTLS13) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeNewSessionTicket) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint32(m.lifetime) + b.AddUint32(m.ageAdd) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.nonce) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.label) + }) + + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if m.maxEarlyData > 0 { + b.AddUint16(extensionEarlyData) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint32(m.maxEarlyData) + }) + } + }) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool { + *m = newSessionTicketMsgTLS13{raw: data} + s := cryptobyte.String(data) + + var extensions cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint32(&m.lifetime) || + !s.ReadUint32(&m.ageAdd) || + !readUint8LengthPrefixed(&s, &m.nonce) || + !readUint16LengthPrefixed(&s, &m.label) || + !s.ReadUint16LengthPrefixed(&extensions) || + !s.Empty() { + return false + } + + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + switch extension { + case extensionEarlyData: + if !extData.ReadUint32(&m.maxEarlyData) { + return false + } + default: + // Ignore unknown extensions. + continue + } + + if !extData.Empty() { + return false + } + } + + return true +} + +type certificateRequestMsgTLS13 struct { + raw []byte + ocspStapling bool + scts bool + supportedSignatureAlgorithms []SignatureScheme + supportedSignatureAlgorithmsCert []SignatureScheme + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsgTLS13) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeCertificateRequest) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + // certificate_request_context (SHALL be zero length unless used for + // post-handshake authentication) + b.AddUint8(0) + + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if m.ocspStapling { + b.AddUint16(extensionStatusRequest) + b.AddUint16(0) // empty extension_data + } + if m.scts { + // RFC 8446, Section 4.4.2.1 makes no mention of + // signed_certificate_timestamp in CertificateRequest, but + // "Extensions in the Certificate message from the client MUST + // correspond to extensions in the CertificateRequest message + // from the server." and it appears in the table in Section 4.2. + b.AddUint16(extensionSCT) + b.AddUint16(0) // empty extension_data + } + if len(m.supportedSignatureAlgorithms) > 0 { + b.AddUint16(extensionSignatureAlgorithms) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sigAlgo := range m.supportedSignatureAlgorithms { + b.AddUint16(uint16(sigAlgo)) + } + }) + }) + } + if len(m.supportedSignatureAlgorithmsCert) > 0 { + b.AddUint16(extensionSignatureAlgorithmsCert) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { + b.AddUint16(uint16(sigAlgo)) + } + }) + }) + } + if len(m.certificateAuthorities) > 0 { + b.AddUint16(extensionCertificateAuthorities) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, ca := range m.certificateAuthorities { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(ca) + }) + } + }) + }) + } + }) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool { + *m = certificateRequestMsgTLS13{raw: data} + s := cryptobyte.String(data) + + var context, extensions cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || + !s.ReadUint16LengthPrefixed(&extensions) || + !s.Empty() { + return false + } + + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + + switch extension { + case extensionStatusRequest: + m.ocspStapling = true + case extensionSCT: + m.scts = true + case extensionSignatureAlgorithms: + var sigAndAlgs cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { + return false + } + for !sigAndAlgs.Empty() { + var sigAndAlg uint16 + if !sigAndAlgs.ReadUint16(&sigAndAlg) { + return false + } + m.supportedSignatureAlgorithms = append( + m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) + } + case extensionSignatureAlgorithmsCert: + var sigAndAlgs cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { + return false + } + for !sigAndAlgs.Empty() { + var sigAndAlg uint16 + if !sigAndAlgs.ReadUint16(&sigAndAlg) { + return false + } + m.supportedSignatureAlgorithmsCert = append( + m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) + } + case extensionCertificateAuthorities: + var auths cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() { + return false + } + for !auths.Empty() { + var ca []byte + if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 { + return false + } + m.certificateAuthorities = append(m.certificateAuthorities, ca) + } + default: + // Ignore unknown extensions. + continue + } + + if !extData.Empty() { + return false + } + } + + return true +} + +type certificateMsg struct { + raw []byte + certificates [][]byte +} + +func (m *certificateMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + var i int + for _, slice := range m.certificates { + i += len(slice) + } + + length := 3 + 3*len(m.certificates) + i + x = make([]byte, 4+length) + x[0] = typeCertificate + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + certificateOctets := length - 3 + x[4] = uint8(certificateOctets >> 16) + x[5] = uint8(certificateOctets >> 8) + x[6] = uint8(certificateOctets) + + y := x[7:] + for _, slice := range m.certificates { + y[0] = uint8(len(slice) >> 16) + y[1] = uint8(len(slice) >> 8) + y[2] = uint8(len(slice)) + copy(y[3:], slice) + y = y[3+len(slice):] + } + + m.raw = x + return +} + +func (m *certificateMsg) unmarshal(data []byte) bool { + if len(data) < 7 { + return false + } + + m.raw = data + certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]) + if uint32(len(data)) != certsLen+7 { + return false + } + + numCerts := 0 + d := data[7:] + for certsLen > 0 { + if len(d) < 4 { + return false + } + certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) + if uint32(len(d)) < 3+certLen { + return false + } + d = d[3+certLen:] + certsLen -= 3 + certLen + numCerts++ + } + + m.certificates = make([][]byte, numCerts) + d = data[7:] + for i := 0; i < numCerts; i++ { + certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) + m.certificates[i] = d[3 : 3+certLen] + d = d[3+certLen:] + } + + return true +} + +type certificateMsgTLS13 struct { + raw []byte + certificate Certificate + ocspStapling bool + scts bool +} + +func (m *certificateMsgTLS13) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeCertificate) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(0) // certificate_request_context + + certificate := m.certificate + if !m.ocspStapling { + certificate.OCSPStaple = nil + } + if !m.scts { + certificate.SignedCertificateTimestamps = nil + } + marshalCertificate(b, certificate) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) { + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + for i, cert := range certificate.Certificate { + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(cert) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if i > 0 { + // This library only supports OCSP and SCT for leaf certificates. + return + } + if certificate.OCSPStaple != nil { + b.AddUint16(extensionStatusRequest) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(statusTypeOCSP) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(certificate.OCSPStaple) + }) + }) + } + if certificate.SignedCertificateTimestamps != nil { + b.AddUint16(extensionSCT) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, sct := range certificate.SignedCertificateTimestamps { + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(sct) + }) + } + }) + }) + } + }) + } + }) +} + +func (m *certificateMsgTLS13) unmarshal(data []byte) bool { + *m = certificateMsgTLS13{raw: data} + s := cryptobyte.String(data) + + var context cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || + !unmarshalCertificate(&s, &m.certificate) || + !s.Empty() { + return false + } + + m.scts = m.certificate.SignedCertificateTimestamps != nil + m.ocspStapling = m.certificate.OCSPStaple != nil + + return true +} + +func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool { + var certList cryptobyte.String + if !s.ReadUint24LengthPrefixed(&certList) { + return false + } + for !certList.Empty() { + var cert []byte + var extensions cryptobyte.String + if !readUint24LengthPrefixed(&certList, &cert) || + !certList.ReadUint16LengthPrefixed(&extensions) { + return false + } + certificate.Certificate = append(certificate.Certificate, cert) + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + if len(certificate.Certificate) > 1 { + // This library only supports OCSP and SCT for leaf certificates. + continue + } + + switch extension { + case extensionStatusRequest: + var statusType uint8 + if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP || + !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) || + len(certificate.OCSPStaple) == 0 { + return false + } + case extensionSCT: + var sctList cryptobyte.String + if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { + return false + } + for !sctList.Empty() { + var sct []byte + if !readUint16LengthPrefixed(&sctList, &sct) || + len(sct) == 0 { + return false + } + certificate.SignedCertificateTimestamps = append( + certificate.SignedCertificateTimestamps, sct) + } + default: + // Ignore unknown extensions. + continue + } + + if !extData.Empty() { + return false + } + } + } + return true +} + +type serverKeyExchangeMsg struct { + raw []byte + key []byte +} + +func (m *serverKeyExchangeMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + length := len(m.key) + x := make([]byte, length+4) + x[0] = typeServerKeyExchange + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + copy(x[4:], m.key) + + m.raw = x + return x +} + +func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + m.key = data[4:] + return true +} + +type certificateStatusMsg struct { + raw []byte + response []byte +} + +func (m *certificateStatusMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeCertificateStatus) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8(statusTypeOCSP) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.response) + }) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *certificateStatusMsg) unmarshal(data []byte) bool { + m.raw = data + s := cryptobyte.String(data) + + var statusType uint8 + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint8(&statusType) || statusType != statusTypeOCSP || + !readUint24LengthPrefixed(&s, &m.response) || + len(m.response) == 0 || !s.Empty() { + return false + } + return true +} + +type serverHelloDoneMsg struct{} + +func (m *serverHelloDoneMsg) marshal() []byte { + x := make([]byte, 4) + x[0] = typeServerHelloDone + return x +} + +func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + +type clientKeyExchangeMsg struct { + raw []byte + ciphertext []byte +} + +func (m *clientKeyExchangeMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + length := len(m.ciphertext) + x := make([]byte, length+4) + x[0] = typeClientKeyExchange + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + copy(x[4:], m.ciphertext) + + m.raw = x + return x +} + +func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if l != len(data)-4 { + return false + } + m.ciphertext = data[4:] + return true +} + +type finishedMsg struct { + raw []byte + verifyData []byte +} + +func (m *finishedMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeFinished) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.verifyData) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *finishedMsg) unmarshal(data []byte) bool { + m.raw = data + s := cryptobyte.String(data) + return s.Skip(1) && + readUint24LengthPrefixed(&s, &m.verifyData) && + s.Empty() +} + +type certificateRequestMsg struct { + raw []byte + // hasSignatureAlgorithm indicates whether this message includes a list of + // supported signature algorithms. This change was introduced with TLS 1.2. + hasSignatureAlgorithm bool + + certificateTypes []byte + supportedSignatureAlgorithms []SignatureScheme + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See RFC 4346, Section 7.4.4. + length := 1 + len(m.certificateTypes) + 2 + casLength := 0 + for _, ca := range m.certificateAuthorities { + casLength += 2 + len(ca) + } + length += casLength + + if m.hasSignatureAlgorithm { + length += 2 + 2*len(m.supportedSignatureAlgorithms) + } + + x = make([]byte, 4+length) + x[0] = typeCertificateRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + x[4] = uint8(len(m.certificateTypes)) + + copy(x[5:], m.certificateTypes) + y := x[5+len(m.certificateTypes):] + + if m.hasSignatureAlgorithm { + n := len(m.supportedSignatureAlgorithms) * 2 + y[0] = uint8(n >> 8) + y[1] = uint8(n) + y = y[2:] + for _, sigAlgo := range m.supportedSignatureAlgorithms { + y[0] = uint8(sigAlgo >> 8) + y[1] = uint8(sigAlgo) + y = y[2:] + } + } + + y[0] = uint8(casLength >> 8) + y[1] = uint8(casLength) + y = y[2:] + for _, ca := range m.certificateAuthorities { + y[0] = uint8(len(ca) >> 8) + y[1] = uint8(len(ca)) + y = y[2:] + copy(y, ca) + y = y[len(ca):] + } + + m.raw = x + return +} + +func (m *certificateRequestMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 5 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + numCertTypes := int(data[4]) + data = data[5:] + if numCertTypes == 0 || len(data) <= numCertTypes { + return false + } + + m.certificateTypes = make([]byte, numCertTypes) + if copy(m.certificateTypes, data) != numCertTypes { + return false + } + + data = data[numCertTypes:] + + if m.hasSignatureAlgorithm { + if len(data) < 2 { + return false + } + sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if sigAndHashLen&1 != 0 { + return false + } + if len(data) < int(sigAndHashLen) { + return false + } + numSigAlgos := sigAndHashLen / 2 + m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos) + for i := range m.supportedSignatureAlgorithms { + m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1]) + data = data[2:] + } + } + + if len(data) < 2 { + return false + } + casLength := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if len(data) < int(casLength) { + return false + } + cas := make([]byte, casLength) + copy(cas, data) + data = data[casLength:] + + m.certificateAuthorities = nil + for len(cas) > 0 { + if len(cas) < 2 { + return false + } + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] + + if len(cas) < int(caLen) { + return false + } + + m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] + } + + return len(data) == 0 +} + +type certificateVerifyMsg struct { + raw []byte + hasSignatureAlgorithm bool // format change introduced in TLS 1.2 + signatureAlgorithm SignatureScheme + signature []byte +} + +func (m *certificateVerifyMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + var b cryptobyte.Builder + b.AddUint8(typeCertificateVerify) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + if m.hasSignatureAlgorithm { + b.AddUint16(uint16(m.signatureAlgorithm)) + } + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.signature) + }) + }) + + m.raw = b.BytesOrPanic() + return m.raw +} + +func (m *certificateVerifyMsg) unmarshal(data []byte) bool { + m.raw = data + s := cryptobyte.String(data) + + if !s.Skip(4) { // message type and uint24 length field + return false + } + if m.hasSignatureAlgorithm { + if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) { + return false + } + } + return readUint16LengthPrefixed(&s, &m.signature) && s.Empty() +} + +type newSessionTicketMsg struct { + raw []byte + ticket []byte +} + +func (m *newSessionTicketMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See RFC 5077, Section 3.3. + ticketLen := len(m.ticket) + length := 2 + 4 + ticketLen + x = make([]byte, 4+length) + x[0] = typeNewSessionTicket + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + x[8] = uint8(ticketLen >> 8) + x[9] = uint8(ticketLen) + copy(x[10:], m.ticket) + + m.raw = x + + return +} + +func (m *newSessionTicketMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 10 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + ticketLen := int(data[8])<<8 + int(data[9]) + if len(data)-10 != ticketLen { + return false + } + + m.ticket = data[10:] + + return true +} + +type helloRequestMsg struct { +} + +func (*helloRequestMsg) marshal() []byte { + return []byte{typeHelloRequest, 0, 0, 0} +} + +func (*helloRequestMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go new file mode 100644 index 00000000..e93874af --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go @@ -0,0 +1,905 @@ +// 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 LICENSE file. + +package qtls + +import ( + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "hash" + "io" + "sync/atomic" + "time" +) + +// serverHandshakeState contains details of a server handshake in progress. +// It's discarded once the handshake has completed. +type serverHandshakeState struct { + c *Conn + ctx context.Context + clientHello *clientHelloMsg + hello *serverHelloMsg + suite *cipherSuite + ecdheOk bool + ecSignOk bool + rsaDecryptOk bool + rsaSignOk bool + sessionState *sessionState + finishedHash finishedHash + masterSecret []byte + cert *Certificate +} + +// serverHandshake performs a TLS handshake as a server. +func (c *Conn) serverHandshake(ctx context.Context) error { + c.setAlternativeRecordLayer() + + clientHello, err := c.readClientHello(ctx) + if err != nil { + return err + } + + if c.vers == VersionTLS13 { + hs := serverHandshakeStateTLS13{ + c: c, + ctx: ctx, + clientHello: clientHello, + } + return hs.handshake() + } else if c.extraConfig.usesAlternativeRecordLayer() { + // This should already have been caught by the check that the ClientHello doesn't + // offer any (supported) versions older than TLS 1.3. + // Check again to make sure we can't be tricked into using an older version. + c.sendAlert(alertProtocolVersion) + return errors.New("tls: negotiated TLS < 1.3 when using QUIC") + } + + hs := serverHandshakeState{ + c: c, + ctx: ctx, + clientHello: clientHello, + } + return hs.handshake() +} + +func (hs *serverHandshakeState) handshake() error { + c := hs.c + + if err := hs.processClientHello(); err != nil { + return err + } + + // For an overview of TLS handshaking, see RFC 5246, Section 7.3. + c.buffering = true + if hs.checkForResumption() { + // The client has included a session ticket and so we do an abbreviated handshake. + c.didResume = true + if err := hs.doResumeHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.sendSessionTicket(); err != nil { + return err + } + if err := hs.sendFinished(c.serverFinished[:]); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + c.clientFinishedIsFirst = false + if err := hs.readFinished(nil); err != nil { + return err + } + } else { + // The client didn't include a session ticket, or it wasn't + // valid so we do a full handshake. + if err := hs.pickCipherSuite(); err != nil { + return err + } + if err := hs.doFullHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.readFinished(c.clientFinished[:]); err != nil { + return err + } + c.clientFinishedIsFirst = true + c.buffering = true + if err := hs.sendSessionTicket(); err != nil { + return err + } + if err := hs.sendFinished(nil); err != nil { + return err + } + if _, err := c.flush(); err != nil { + return err + } + } + + c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random) + atomic.StoreUint32(&c.handshakeStatus, 1) + + return nil +} + +// readClientHello reads a ClientHello message and selects the protocol version. +func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { + msg, err := c.readHandshake() + if err != nil { + return nil, err + } + clientHello, ok := msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return nil, unexpectedMessageError(clientHello, msg) + } + + var configForClient *config + originalConfig := c.config + if c.config.GetConfigForClient != nil { + chi := newClientHelloInfo(ctx, c, clientHello) + if cfc, err := c.config.GetConfigForClient(chi); err != nil { + c.sendAlert(alertInternalError) + return nil, err + } else if cfc != nil { + configForClient = fromConfig(cfc) + c.config = configForClient + } + } + c.ticketKeys = originalConfig.ticketKeys(configForClient) + + clientVersions := clientHello.supportedVersions + if len(clientHello.supportedVersions) == 0 { + clientVersions = supportedVersionsFromMax(clientHello.vers) + } + if c.extraConfig.usesAlternativeRecordLayer() { + // In QUIC, the client MUST NOT offer any old TLS versions. + // Here, we can only check that none of the other supported versions of this library + // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here. + for _, ver := range clientVersions { + if ver == VersionTLS13 { + continue + } + for _, v := range supportedVersions { + if ver == v { + c.sendAlert(alertProtocolVersion) + return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver) + } + } + } + // Make the config we're using allows us to use TLS 1.3. + if c.config.maxSupportedVersion(roleServer) < VersionTLS13 { + c.sendAlert(alertInternalError) + return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") + } + } + c.vers, ok = c.config.mutualVersion(roleServer, clientVersions) + if !ok { + c.sendAlert(alertProtocolVersion) + return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions) + } + c.haveVers = true + c.in.version = c.vers + c.out.version = c.vers + + return clientHello, nil +} + +func (hs *serverHandshakeState) processClientHello() error { + c := hs.c + + hs.hello = new(serverHelloMsg) + hs.hello.vers = c.vers + + foundCompression := false + // We only support null compression, so check that the client offered it. + for _, compression := range hs.clientHello.compressionMethods { + if compression == compressionNone { + foundCompression = true + break + } + } + + if !foundCompression { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: client does not support uncompressed connections") + } + + hs.hello.random = make([]byte, 32) + serverRandom := hs.hello.random + // Downgrade protection canaries. See RFC 8446, Section 4.1.3. + maxVers := c.config.maxSupportedVersion(roleServer) + if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary { + if c.vers == VersionTLS12 { + copy(serverRandom[24:], downgradeCanaryTLS12) + } else { + copy(serverRandom[24:], downgradeCanaryTLS11) + } + serverRandom = serverRandom[:24] + } + _, err := io.ReadFull(c.config.rand(), serverRandom) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + if len(hs.clientHello.secureRenegotiation) != 0 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: initial handshake had non-empty renegotiation extension") + } + + hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported + hs.hello.compressionMethod = compressionNone + if len(hs.clientHello.serverName) > 0 { + c.serverName = hs.clientHello.serverName + } + + selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) + if err != nil { + c.sendAlert(alertNoApplicationProtocol) + return err + } + hs.hello.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + + hs.cert, err = c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello)) + if err != nil { + if err == errNoCertificates { + c.sendAlert(alertUnrecognizedName) + } else { + c.sendAlert(alertInternalError) + } + return err + } + if hs.clientHello.scts { + hs.hello.scts = hs.cert.SignedCertificateTimestamps + } + + hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints) + + if hs.ecdheOk { + // Although omitting the ec_point_formats extension is permitted, some + // old OpenSSL version will refuse to handshake if not present. + // + // Per RFC 4492, section 5.1.2, implementations MUST support the + // uncompressed point format. See golang.org/issue/31943. + hs.hello.supportedPoints = []uint8{pointFormatUncompressed} + } + + if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok { + switch priv.Public().(type) { + case *ecdsa.PublicKey: + hs.ecSignOk = true + case ed25519.PublicKey: + hs.ecSignOk = true + case *rsa.PublicKey: + hs.rsaSignOk = true + default: + c.sendAlert(alertInternalError) + return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public()) + } + } + if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok { + switch priv.Public().(type) { + case *rsa.PublicKey: + hs.rsaDecryptOk = true + default: + c.sendAlert(alertInternalError) + return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public()) + } + } + + return nil +} + +// negotiateALPN picks a shared ALPN protocol that both sides support in server +// preference order. If ALPN is not configured or the peer doesn't support it, +// it returns "" and no error. +func negotiateALPN(serverProtos, clientProtos []string) (string, error) { + if len(serverProtos) == 0 || len(clientProtos) == 0 { + return "", nil + } + var http11fallback bool + for _, s := range serverProtos { + for _, c := range clientProtos { + if s == c { + return s, nil + } + if s == "h2" && c == "http/1.1" { + http11fallback = true + } + } + } + // As a special case, let http/1.1 clients connect to h2 servers as if they + // didn't support ALPN. We used not to enforce protocol overlap, so over + // time a number of HTTP servers were configured with only "h2", but + // expected to accept connections from "http/1.1" clients. See Issue 46310. + if http11fallback { + return "", nil + } + return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos) +} + +// supportsECDHE returns whether ECDHE key exchanges can be used with this +// pre-TLS 1.3 client. +func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8) bool { + supportsCurve := false + for _, curve := range supportedCurves { + if c.supportsCurve(curve) { + supportsCurve = true + break + } + } + + supportsPointFormat := false + for _, pointFormat := range supportedPoints { + if pointFormat == pointFormatUncompressed { + supportsPointFormat = true + break + } + } + + return supportsCurve && supportsPointFormat +} + +func (hs *serverHandshakeState) pickCipherSuite() error { + c := hs.c + + preferenceOrder := cipherSuitesPreferenceOrder + if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { + preferenceOrder = cipherSuitesPreferenceOrderNoAES + } + + configCipherSuites := c.config.cipherSuites() + preferenceList := make([]uint16, 0, len(configCipherSuites)) + for _, suiteID := range preferenceOrder { + for _, id := range configCipherSuites { + if id == suiteID { + preferenceList = append(preferenceList, id) + break + } + } + } + + hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk) + if hs.suite == nil { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no cipher suite supported by both client and server") + } + c.cipherSuite = hs.suite.id + + for _, id := range hs.clientHello.cipherSuites { + if id == TLS_FALLBACK_SCSV { + // The client is doing a fallback connection. See RFC 7507. + if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) { + c.sendAlert(alertInappropriateFallback) + return errors.New("tls: client using inappropriate protocol fallback") + } + break + } + } + + return nil +} + +func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool { + if c.flags&suiteECDHE != 0 { + if !hs.ecdheOk { + return false + } + if c.flags&suiteECSign != 0 { + if !hs.ecSignOk { + return false + } + } else if !hs.rsaSignOk { + return false + } + } else if !hs.rsaDecryptOk { + return false + } + if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { + return false + } + return true +} + +// checkForResumption reports whether we should perform resumption on this connection. +func (hs *serverHandshakeState) checkForResumption() bool { + c := hs.c + + if c.config.SessionTicketsDisabled { + return false + } + + plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket) + if plaintext == nil { + return false + } + hs.sessionState = &sessionState{usedOldKey: usedOldKey} + ok := hs.sessionState.unmarshal(plaintext) + if !ok { + return false + } + + createdAt := time.Unix(int64(hs.sessionState.createdAt), 0) + if c.config.time().Sub(createdAt) > maxSessionTicketLifetime { + return false + } + + // Never resume a session for a different TLS version. + if c.vers != hs.sessionState.vers { + return false + } + + cipherSuiteOk := false + // Check that the client is still offering the ciphersuite in the session. + for _, id := range hs.clientHello.cipherSuites { + if id == hs.sessionState.cipherSuite { + cipherSuiteOk = true + break + } + } + if !cipherSuiteOk { + return false + } + + // Check that we also support the ciphersuite from the session. + hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite}, + c.config.cipherSuites(), hs.cipherSuiteOk) + if hs.suite == nil { + return false + } + + sessionHasClientCerts := len(hs.sessionState.certificates) != 0 + needClientCerts := requiresClientCert(c.config.ClientAuth) + if needClientCerts && !sessionHasClientCerts { + return false + } + if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { + return false + } + + return true +} + +func (hs *serverHandshakeState) doResumeHandshake() error { + c := hs.c + + hs.hello.cipherSuite = hs.suite.id + c.cipherSuite = hs.suite.id + // We echo the client's session ID in the ServerHello to let it know + // that we're doing a resumption. + hs.hello.sessionId = hs.clientHello.sessionId + hs.hello.ticketSupported = hs.sessionState.usedOldKey + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash.discardHandshakeBuffer() + hs.finishedHash.Write(hs.clientHello.marshal()) + hs.finishedHash.Write(hs.hello.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { + return err + } + + if err := c.processCertsFromClient(Certificate{ + Certificate: hs.sessionState.certificates, + }); err != nil { + return err + } + + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + hs.masterSecret = hs.sessionState.masterSecret + + return nil +} + +func (hs *serverHandshakeState) doFullHandshake() error { + c := hs.c + + if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { + hs.hello.ocspStapling = true + } + + hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled + hs.hello.cipherSuite = hs.suite.id + + hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) + if c.config.ClientAuth == NoClientCert { + // No need to keep a full record of the handshake if client + // certificates won't be used. + hs.finishedHash.discardHandshakeBuffer() + } + hs.finishedHash.Write(hs.clientHello.marshal()) + hs.finishedHash.Write(hs.hello.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { + return err + } + + certMsg := new(certificateMsg) + certMsg.certificates = hs.cert.Certificate + hs.finishedHash.Write(certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { + return err + } + + if hs.hello.ocspStapling { + certStatus := new(certificateStatusMsg) + certStatus.response = hs.cert.OCSPStaple + hs.finishedHash.Write(certStatus.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil { + return err + } + } + + keyAgreement := hs.suite.ka(c.vers) + skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + if skx != nil { + hs.finishedHash.Write(skx.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil { + return err + } + } + + var certReq *certificateRequestMsg + if c.config.ClientAuth >= RequestClientCert { + // Request a client certificate + certReq = new(certificateRequestMsg) + certReq.certificateTypes = []byte{ + byte(certTypeRSASign), + byte(certTypeECDSASign), + } + if c.vers >= VersionTLS12 { + certReq.hasSignatureAlgorithm = true + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() + } + + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if c.config.ClientCAs != nil { + certReq.certificateAuthorities = c.config.ClientCAs.Subjects() + } + hs.finishedHash.Write(certReq.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { + return err + } + } + + helloDone := new(serverHelloDoneMsg) + hs.finishedHash.Write(helloDone.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil { + return err + } + + if _, err := c.flush(); err != nil { + return err + } + + var pub crypto.PublicKey // public key for client auth, if any + + msg, err := c.readHandshake() + if err != nil { + return err + } + + // If we requested a client certificate, then the client must send a + // certificate message, even if it's empty. + if c.config.ClientAuth >= RequestClientCert { + certMsg, ok := msg.(*certificateMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.finishedHash.Write(certMsg.marshal()) + + if err := c.processCertsFromClient(Certificate{ + Certificate: certMsg.certificates, + }); err != nil { + return err + } + if len(certMsg.certificates) != 0 { + pub = c.peerCertificates[0].PublicKey + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + // Get client key exchange + ckx, ok := msg.(*clientKeyExchangeMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(ckx, msg) + } + hs.finishedHash.Write(ckx.marshal()) + + preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) + if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil { + c.sendAlert(alertInternalError) + return err + } + + // If we received a client cert in response to our certificate request message, + // the client will send us a certificateVerifyMsg immediately after the + // clientKeyExchangeMsg. This message is a digest of all preceding + // handshake-layer messages that is signed using the private key corresponding + // to the client's certificate. This allows us to verify that the client is in + // possession of the private key of the certificate. + if len(c.peerCertificates) > 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + var sigType uint8 + var sigHash crypto.Hash + if c.vers >= VersionTLS12 { + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client certificate used with invalid signature algorithm") + } + sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + } else { + sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub) + if err != nil { + c.sendAlert(alertIllegalParameter) + return err + } + } + + signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret) + if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid signature by the client certificate: " + err.Error()) + } + + hs.finishedHash.Write(certVerify.marshal()) + } + + hs.finishedHash.discardHandshakeBuffer() + + return nil +} + +func (hs *serverHandshakeState) establishKeys() error { + c := hs.c + + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) + + var clientCipher, serverCipher any + var clientHash, serverHash hash.Hash + + if hs.suite.aead == nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash = hs.suite.mac(clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash = hs.suite.mac(serverMAC) + } else { + clientCipher = hs.suite.aead(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) + + return nil +} + +func (hs *serverHandshakeState) readFinished(out []byte) error { + c := hs.c + + if err := c.readChangeCipherSpec(); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + clientFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(clientFinished, msg) + } + + verify := hs.finishedHash.clientSum(hs.masterSecret) + if len(verify) != len(clientFinished.verifyData) || + subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: client's Finished message is incorrect") + } + + hs.finishedHash.Write(clientFinished.marshal()) + copy(out, verify) + return nil +} + +func (hs *serverHandshakeState) sendSessionTicket() error { + // ticketSupported is set in a resumption handshake if the + // ticket from the client was encrypted with an old session + // ticket key and thus a refreshed ticket should be sent. + if !hs.hello.ticketSupported { + return nil + } + + c := hs.c + m := new(newSessionTicketMsg) + + createdAt := uint64(c.config.time().Unix()) + if hs.sessionState != nil { + // If this is re-wrapping an old key, then keep + // the original time it was created. + createdAt = hs.sessionState.createdAt + } + + var certsFromClient [][]byte + for _, cert := range c.peerCertificates { + certsFromClient = append(certsFromClient, cert.Raw) + } + state := sessionState{ + vers: c.vers, + cipherSuite: hs.suite.id, + createdAt: createdAt, + masterSecret: hs.masterSecret, + certificates: certsFromClient, + } + var err error + m.ticket, err = c.encryptTicket(state.marshal()) + if err != nil { + return err + } + + hs.finishedHash.Write(m.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { + return err + } + + return nil +} + +func (hs *serverHandshakeState) sendFinished(out []byte) error { + c := hs.c + + if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { + return err + } + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) + hs.finishedHash.Write(finished.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { + return err + } + + copy(out, finished.verifyData) + + return nil +} + +// processCertsFromClient takes a chain of client certificates either from a +// Certificates message or from a sessionState and verifies them. It returns +// the public key of the leaf certificate. +func (c *Conn) processCertsFromClient(certificate Certificate) error { + certificates := certificate.Certificate + certs := make([]*x509.Certificate, len(certificates)) + var err error + for i, asn1Data := range certificates { + if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse client certificate: " + err.Error()) + } + } + + if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) { + c.sendAlert(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + + if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { + opts := x509.VerifyOptions{ + Roots: c.config.ClientCAs, + CurrentTime: c.config.time(), + Intermediates: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + + chains, err := certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to verify client certificate: " + err.Error()) + } + + c.verifiedChains = chains + } + + c.peerCertificates = certs + c.ocspResponse = certificate.OCSPStaple + c.scts = certificate.SignedCertificateTimestamps + + if len(certs) > 0 { + switch certs[0].PublicKey.(type) { + case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey: + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey) + } + } + + if c.config.VerifyPeerCertificate != nil { + if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + return nil +} + +func newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo { + supportedVersions := clientHello.supportedVersions + if len(clientHello.supportedVersions) == 0 { + supportedVersions = supportedVersionsFromMax(clientHello.vers) + } + + return toClientHelloInfo(&clientHelloInfo{ + CipherSuites: clientHello.cipherSuites, + ServerName: clientHello.serverName, + SupportedCurves: clientHello.supportedCurves, + SupportedPoints: clientHello.supportedPoints, + SignatureSchemes: clientHello.supportedSignatureAlgorithms, + SupportedProtos: clientHello.alpnProtocols, + SupportedVersions: supportedVersions, + Conn: c.conn, + config: toConfig(c.config), + ctx: ctx, + }) +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go new file mode 100644 index 00000000..e3db8063 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go @@ -0,0 +1,900 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "bytes" + "context" + "crypto" + "crypto/hmac" + "crypto/rsa" + "errors" + "hash" + "io" + "sync/atomic" + "time" +) + +// maxClientPSKIdentities is the number of client PSK identities the server will +// attempt to validate. It will ignore the rest not to let cheap ClientHello +// messages cause too much work in session ticket decryption attempts. +const maxClientPSKIdentities = 5 + +type serverHandshakeStateTLS13 struct { + c *Conn + ctx context.Context + clientHello *clientHelloMsg + hello *serverHelloMsg + alpnNegotiationErr error + encryptedExtensions *encryptedExtensionsMsg + sentDummyCCS bool + usingPSK bool + suite *cipherSuiteTLS13 + cert *Certificate + sigAlg SignatureScheme + earlySecret []byte + sharedKey []byte + handshakeSecret []byte + masterSecret []byte + trafficSecret []byte // client_application_traffic_secret_0 + transcript hash.Hash + clientFinished []byte +} + +func (hs *serverHandshakeStateTLS13) handshake() error { + c := hs.c + + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + + // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. + if err := hs.processClientHello(); err != nil { + return err + } + if err := hs.checkForResumption(); err != nil { + return err + } + if err := hs.pickCertificate(); err != nil { + return err + } + c.buffering = true + if err := hs.sendServerParameters(); err != nil { + return err + } + if err := hs.sendServerCertificate(); err != nil { + return err + } + if err := hs.sendServerFinished(); err != nil { + return err + } + // Note that at this point we could start sending application data without + // waiting for the client's second flight, but the application might not + // expect the lack of replay protection of the ClientHello parameters. + if _, err := c.flush(); err != nil { + return err + } + if err := hs.readClientCertificate(); err != nil { + return err + } + if err := hs.readClientFinished(); err != nil { + return err + } + + atomic.StoreUint32(&c.handshakeStatus, 1) + + return nil +} + +func (hs *serverHandshakeStateTLS13) processClientHello() error { + c := hs.c + + hs.hello = new(serverHelloMsg) + hs.encryptedExtensions = new(encryptedExtensionsMsg) + + // TLS 1.3 froze the ServerHello.legacy_version field, and uses + // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1. + hs.hello.vers = VersionTLS12 + hs.hello.supportedVersion = c.vers + + if len(hs.clientHello.supportedVersions) == 0 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client used the legacy version field to negotiate TLS 1.3") + } + + // Abort if the client is doing a fallback and landing lower than what we + // support. See RFC 7507, which however does not specify the interaction + // with supported_versions. The only difference is that with + // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4] + // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case, + // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to + // TLS 1.2, because a TLS 1.3 server would abort here. The situation before + // supported_versions was not better because there was just no way to do a + // TLS 1.4 handshake without risking the server selecting TLS 1.3. + for _, id := range hs.clientHello.cipherSuites { + if id == TLS_FALLBACK_SCSV { + // Use c.vers instead of max(supported_versions) because an attacker + // could defeat this by adding an arbitrary high version otherwise. + if c.vers < c.config.maxSupportedVersion(roleServer) { + c.sendAlert(alertInappropriateFallback) + return errors.New("tls: client using inappropriate protocol fallback") + } + break + } + } + + if len(hs.clientHello.compressionMethods) != 1 || + hs.clientHello.compressionMethods[0] != compressionNone { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: TLS 1.3 client supports illegal compression methods") + } + + hs.hello.random = make([]byte, 32) + if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil { + c.sendAlert(alertInternalError) + return err + } + + if len(hs.clientHello.secureRenegotiation) != 0 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: initial handshake had non-empty renegotiation extension") + } + + hs.hello.sessionId = hs.clientHello.sessionId + hs.hello.compressionMethod = compressionNone + + if hs.suite == nil { + var preferenceList []uint16 + for _, suiteID := range c.config.CipherSuites { + for _, suite := range cipherSuitesTLS13 { + if suite.id == suiteID { + preferenceList = append(preferenceList, suiteID) + break + } + } + } + if len(preferenceList) == 0 { + preferenceList = defaultCipherSuitesTLS13 + if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { + preferenceList = defaultCipherSuitesTLS13NoAES + } + } + for _, suiteID := range preferenceList { + hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID) + if hs.suite != nil { + break + } + } + } + if hs.suite == nil { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no cipher suite supported by both client and server") + } + c.cipherSuite = hs.suite.id + hs.hello.cipherSuite = hs.suite.id + hs.transcript = hs.suite.hash.New() + + // Pick the ECDHE group in server preference order, but give priority to + // groups with a key share, to avoid a HelloRetryRequest round-trip. + var selectedGroup CurveID + var clientKeyShare *keyShare +GroupSelection: + for _, preferredGroup := range c.config.curvePreferences() { + for _, ks := range hs.clientHello.keyShares { + if ks.group == preferredGroup { + selectedGroup = ks.group + clientKeyShare = &ks + break GroupSelection + } + } + if selectedGroup != 0 { + continue + } + for _, group := range hs.clientHello.supportedCurves { + if group == preferredGroup { + selectedGroup = group + break + } + } + } + if selectedGroup == 0 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no ECDHE curve supported by both client and server") + } + if clientKeyShare == nil { + if err := hs.doHelloRetryRequest(selectedGroup); err != nil { + return err + } + clientKeyShare = &hs.clientHello.keyShares[0] + } + + if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok { + c.sendAlert(alertInternalError) + return errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(c.config.rand(), selectedGroup) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} + hs.sharedKey = params.SharedKey(clientKeyShare.data) + if hs.sharedKey == nil { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: invalid client key share") + } + + c.serverName = hs.clientHello.serverName + + if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil { + c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions) + } + + selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) + if err != nil { + hs.alpnNegotiationErr = err + } + hs.encryptedExtensions.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + + return nil +} + +func (hs *serverHandshakeStateTLS13) checkForResumption() error { + c := hs.c + + if c.config.SessionTicketsDisabled { + return nil + } + + modeOK := false + for _, mode := range hs.clientHello.pskModes { + if mode == pskModeDHE { + modeOK = true + break + } + } + if !modeOK { + return nil + } + + if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: invalid or missing PSK binders") + } + if len(hs.clientHello.pskIdentities) == 0 { + return nil + } + + for i, identity := range hs.clientHello.pskIdentities { + if i >= maxClientPSKIdentities { + break + } + + plaintext, _ := c.decryptTicket(identity.label) + if plaintext == nil { + continue + } + sessionState := new(sessionStateTLS13) + if ok := sessionState.unmarshal(plaintext); !ok { + continue + } + + if hs.clientHello.earlyData { + if sessionState.maxEarlyData == 0 { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: client sent unexpected early data") + } + + if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol && + c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 && + c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) { + hs.encryptedExtensions.earlyData = true + c.used0RTT = true + } + } + + createdAt := time.Unix(int64(sessionState.createdAt), 0) + if c.config.time().Sub(createdAt) > maxSessionTicketLifetime { + continue + } + + // We don't check the obfuscated ticket age because it's affected by + // clock skew and it's only a freshness signal useful for shrinking the + // window for replay attacks, which don't affect us as we don't do 0-RTT. + + pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite) + if pskSuite == nil || pskSuite.hash != hs.suite.hash { + continue + } + + // PSK connections don't re-establish client certificates, but carry + // them over in the session ticket. Ensure the presence of client certs + // in the ticket is consistent with the configured requirements. + sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0 + needClientCerts := requiresClientCert(c.config.ClientAuth) + if needClientCerts && !sessionHasClientCerts { + continue + } + if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { + continue + } + + psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption", + nil, hs.suite.hash.Size()) + hs.earlySecret = hs.suite.extract(psk, nil) + binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil) + // Clone the transcript in case a HelloRetryRequest was recorded. + transcript := cloneHash(hs.transcript, hs.suite.hash) + if transcript == nil { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } + transcript.Write(hs.clientHello.marshalWithoutBinders()) + pskBinder := hs.suite.finishedHash(binderKey, transcript) + if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid PSK binder") + } + + c.didResume = true + if err := c.processCertsFromClient(sessionState.certificate); err != nil { + return err + } + + h := cloneHash(hs.transcript, hs.suite.hash) + h.Write(hs.clientHello.marshal()) + if hs.encryptedExtensions.earlyData { + clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h) + c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret) + if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil { + c.sendAlert(alertInternalError) + return err + } + } + + hs.hello.selectedIdentityPresent = true + hs.hello.selectedIdentity = uint16(i) + hs.usingPSK = true + return nil + } + + return nil +} + +// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler +// interfaces implemented by standard library hashes to clone the state of in +// to a new instance of h. It returns nil if the operation fails. +func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash { + // Recreate the interface to avoid importing encoding. + type binaryMarshaler interface { + MarshalBinary() (data []byte, err error) + UnmarshalBinary(data []byte) error + } + marshaler, ok := in.(binaryMarshaler) + if !ok { + return nil + } + state, err := marshaler.MarshalBinary() + if err != nil { + return nil + } + out := h.New() + unmarshaler, ok := out.(binaryMarshaler) + if !ok { + return nil + } + if err := unmarshaler.UnmarshalBinary(state); err != nil { + return nil + } + return out +} + +func (hs *serverHandshakeStateTLS13) pickCertificate() error { + c := hs.c + + // Only one of PSK and certificates are used at a time. + if hs.usingPSK { + return nil + } + + // signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3. + if len(hs.clientHello.supportedSignatureAlgorithms) == 0 { + return c.sendAlert(alertMissingExtension) + } + + certificate, err := c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello)) + if err != nil { + if err == errNoCertificates { + c.sendAlert(alertUnrecognizedName) + } else { + c.sendAlert(alertInternalError) + } + return err + } + hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms) + if err != nil { + // getCertificate returned a certificate that is unsupported or + // incompatible with the client's signature algorithms. + c.sendAlert(alertHandshakeFailure) + return err + } + hs.cert = certificate + + return nil +} + +// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility +// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. +func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + if hs.sentDummyCCS { + return nil + } + hs.sentDummyCCS = true + + _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + return err +} + +func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { + c := hs.c + + // The first ClientHello gets double-hashed into the transcript upon a + // HelloRetryRequest. See RFC 8446, Section 4.4.1. + hs.transcript.Write(hs.clientHello.marshal()) + chHash := hs.transcript.Sum(nil) + hs.transcript.Reset() + hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + hs.transcript.Write(chHash) + + helloRetryRequest := &serverHelloMsg{ + vers: hs.hello.vers, + random: helloRetryRequestRandom, + sessionId: hs.hello.sessionId, + cipherSuite: hs.hello.cipherSuite, + compressionMethod: hs.hello.compressionMethod, + supportedVersion: hs.hello.supportedVersion, + selectedGroup: selectedGroup, + } + + hs.transcript.Write(helloRetryRequest.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil { + return err + } + + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + clientHello, ok := msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(clientHello, msg) + } + + if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client sent invalid key share in second ClientHello") + } + + if clientHello.earlyData { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client indicated early data in second ClientHello") + } + + if illegalClientHelloChange(clientHello, hs.clientHello) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client illegally modified second ClientHello") + } + + if clientHello.earlyData { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client offered 0-RTT data in second ClientHello") + } + + hs.clientHello = clientHello + return nil +} + +// illegalClientHelloChange reports whether the two ClientHello messages are +// different, with the exception of the changes allowed before and after a +// HelloRetryRequest. See RFC 8446, Section 4.1.2. +func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { + if len(ch.supportedVersions) != len(ch1.supportedVersions) || + len(ch.cipherSuites) != len(ch1.cipherSuites) || + len(ch.supportedCurves) != len(ch1.supportedCurves) || + len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) || + len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) || + len(ch.alpnProtocols) != len(ch1.alpnProtocols) { + return true + } + for i := range ch.supportedVersions { + if ch.supportedVersions[i] != ch1.supportedVersions[i] { + return true + } + } + for i := range ch.cipherSuites { + if ch.cipherSuites[i] != ch1.cipherSuites[i] { + return true + } + } + for i := range ch.supportedCurves { + if ch.supportedCurves[i] != ch1.supportedCurves[i] { + return true + } + } + for i := range ch.supportedSignatureAlgorithms { + if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] { + return true + } + } + for i := range ch.supportedSignatureAlgorithmsCert { + if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] { + return true + } + } + for i := range ch.alpnProtocols { + if ch.alpnProtocols[i] != ch1.alpnProtocols[i] { + return true + } + } + return ch.vers != ch1.vers || + !bytes.Equal(ch.random, ch1.random) || + !bytes.Equal(ch.sessionId, ch1.sessionId) || + !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) || + ch.serverName != ch1.serverName || + ch.ocspStapling != ch1.ocspStapling || + !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) || + ch.ticketSupported != ch1.ticketSupported || + !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) || + ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported || + !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) || + ch.scts != ch1.scts || + !bytes.Equal(ch.cookie, ch1.cookie) || + !bytes.Equal(ch.pskModes, ch1.pskModes) +} + +func (hs *serverHandshakeStateTLS13) sendServerParameters() error { + c := hs.c + + hs.transcript.Write(hs.clientHello.marshal()) + hs.transcript.Write(hs.hello.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { + return err + } + + if err := hs.sendDummyChangeCipherSpec(); err != nil { + return err + } + + earlySecret := hs.earlySecret + if earlySecret == nil { + earlySecret = hs.suite.extract(nil, nil) + } + hs.handshakeSecret = hs.suite.extract(hs.sharedKey, + hs.suite.deriveSecret(earlySecret, "derived", nil)) + + clientSecret := hs.suite.deriveSecret(hs.handshakeSecret, + clientHandshakeTrafficLabel, hs.transcript) + c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret) + c.in.setTrafficSecret(hs.suite, clientSecret) + serverSecret := hs.suite.deriveSecret(hs.handshakeSecret, + serverHandshakeTrafficLabel, hs.transcript) + c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret) + c.out.setTrafficSecret(hs.suite, serverSecret) + + err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + if hs.alpnNegotiationErr != nil { + c.sendAlert(alertNoApplicationProtocol) + return hs.alpnNegotiationErr + } + if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil { + hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions) + } + + hs.transcript.Write(hs.encryptedExtensions.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, hs.encryptedExtensions.marshal()); err != nil { + return err + } + + return nil +} + +func (hs *serverHandshakeStateTLS13) requestClientCert() bool { + return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK +} + +func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { + c := hs.c + + // Only one of PSK and certificates are used at a time. + if hs.usingPSK { + return nil + } + + if hs.requestClientCert() { + // Request a client certificate + certReq := new(certificateRequestMsgTLS13) + certReq.ocspStapling = true + certReq.scts = true + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() + if c.config.ClientCAs != nil { + certReq.certificateAuthorities = c.config.ClientCAs.Subjects() + } + + hs.transcript.Write(certReq.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { + return err + } + } + + certMsg := new(certificateMsgTLS13) + + certMsg.certificate = *hs.cert + certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0 + certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 + + hs.transcript.Write(certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { + return err + } + + certVerifyMsg := new(certificateVerifyMsg) + certVerifyMsg.hasSignatureAlgorithm = true + certVerifyMsg.signatureAlgorithm = hs.sigAlg + + sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg) + if err != nil { + return c.sendAlert(alertInternalError) + } + + signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) + signOpts := crypto.SignerOpts(sigHash) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} + } + sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts) + if err != nil { + public := hs.cert.PrivateKey.(crypto.Signer).Public() + if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS && + rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS + c.sendAlert(alertHandshakeFailure) + } else { + c.sendAlert(alertInternalError) + } + return errors.New("tls: failed to sign handshake: " + err.Error()) + } + certVerifyMsg.signature = sig + + hs.transcript.Write(certVerifyMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { + return err + } + + return nil +} + +func (hs *serverHandshakeStateTLS13) sendServerFinished() error { + c := hs.c + + finished := &finishedMsg{ + verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), + } + + hs.transcript.Write(finished.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { + return err + } + + // Derive secrets that take context through the server Finished. + + hs.masterSecret = hs.suite.extract(nil, + hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil)) + + hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, + clientApplicationTrafficLabel, hs.transcript) + serverSecret := hs.suite.deriveSecret(hs.masterSecret, + serverApplicationTrafficLabel, hs.transcript) + c.out.exportKey(EncryptionApplication, hs.suite, serverSecret) + c.out.setTrafficSecret(hs.suite, serverSecret) + + err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) + + // If we did not request client certificates, at this point we can + // precompute the client finished and roll the transcript forward to send + // session tickets in our first flight. + if !hs.requestClientCert() { + if err := hs.sendSessionTickets(); err != nil { + return err + } + } + + return nil +} + +func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { + if hs.c.config.SessionTicketsDisabled { + return false + } + + // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9. + for _, pskMode := range hs.clientHello.pskModes { + if pskMode == pskModeDHE { + return true + } + } + return false +} + +func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { + c := hs.c + + hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) + finishedMsg := &finishedMsg{ + verifyData: hs.clientFinished, + } + hs.transcript.Write(finishedMsg.marshal()) + + if !hs.shouldSendSessionTickets() { + return nil + } + + c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, + resumptionLabel, hs.transcript) + + // Don't send session tickets when the alternative record layer is set. + // Instead, save the resumption secret on the Conn. + // Session tickets can then be generated by calling Conn.GetSessionTicket(). + if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil { + return nil + } + + m, err := hs.c.getSessionTicketMsg(nil) + if err != nil { + return err + } + + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { + return err + } + + return nil +} + +func (hs *serverHandshakeStateTLS13) readClientCertificate() error { + c := hs.c + + if !hs.requestClientCert() { + // Make sure the connection is still being verified whether or not + // the server requested a client certificate. + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + return nil + } + + // If we requested a client certificate, then the client must send a + // certificate message. If it's empty, no CertificateVerify is sent. + + msg, err := c.readHandshake() + if err != nil { + return err + } + + certMsg, ok := msg.(*certificateMsgTLS13) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.transcript.Write(certMsg.marshal()) + + if err := c.processCertsFromClient(certMsg.certificate); err != nil { + return err + } + + if c.config.VerifyConnection != nil { + if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + if len(certMsg.certificate.Certificate) != 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + // See RFC 8446, Section 4.4.3. + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client certificate used with invalid signature algorithm") + } + sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) + if err != nil { + return c.sendAlert(alertInternalError) + } + if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: client certificate used with invalid signature algorithm") + } + signed := signedMessage(sigHash, clientSignatureContext, hs.transcript) + if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, + sigHash, signed, certVerify.signature); err != nil { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid signature by the client certificate: " + err.Error()) + } + + hs.transcript.Write(certVerify.marshal()) + } + + // If we waited until the client certificates to send session tickets, we + // are ready to do it now. + if err := hs.sendSessionTickets(); err != nil { + return err + } + + return nil +} + +func (hs *serverHandshakeStateTLS13) readClientFinished() error { + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return err + } + + finished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(finished, msg) + } + + if !hmac.Equal(hs.clientFinished, finished.verifyData) { + c.sendAlert(alertDecryptError) + return errors.New("tls: invalid client finished hash") + } + + c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) + c.in.setTrafficSecret(hs.suite, hs.trafficSecret) + + return nil +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go b/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go new file mode 100644 index 00000000..453a8dcf --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go @@ -0,0 +1,357 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "crypto" + "crypto/md5" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "errors" + "fmt" + "io" +) + +// a keyAgreement implements the client and server side of a TLS key agreement +// protocol by generating and processing key exchange messages. +type keyAgreement interface { + // On the server side, the first two methods are called in order. + + // In the case that the key agreement protocol doesn't use a + // ServerKeyExchange message, generateServerKeyExchange can return nil, + // nil. + generateServerKeyExchange(*config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) + processClientKeyExchange(*config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error) + + // On the client side, the next two methods are called in order. + + // This method may not be called if the server doesn't send a + // ServerKeyExchange message. + processServerKeyExchange(*config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error + generateClientKeyExchange(*config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) +} + +var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") +var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message") + +// rsaKeyAgreement implements the standard TLS key agreement where the client +// encrypts the pre-master secret to the server's public key. +type rsaKeyAgreement struct{} + +func (ka rsaKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + return nil, nil +} + +func (ka rsaKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) < 2 { + return nil, errClientKeyExchange + } + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, errClientKeyExchange + } + ciphertext := ckx.ciphertext[2:] + + priv, ok := cert.PrivateKey.(crypto.Decrypter) + if !ok { + return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter") + } + // Perform constant time RSA PKCS #1 v1.5 decryption + preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48}) + if err != nil { + return nil, err + } + // We don't check the version number in the premaster secret. For one, + // by checking it, we would leak information about the validity of the + // encrypted pre-master secret. Secondly, it provides only a small + // benefit against a downgrade attack and some implementations send the + // wrong version anyway. See the discussion at the end of section + // 7.4.7.1 of RFC 4346. + return preMasterSecret, nil +} + +func (ka rsaKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + return errors.New("tls: unexpected ServerKeyExchange") +} + +func (ka rsaKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + preMasterSecret := make([]byte, 48) + preMasterSecret[0] = byte(clientHello.vers >> 8) + preMasterSecret[1] = byte(clientHello.vers) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, nil, err + } + + rsaKey, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite") + } + encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret) + if err != nil { + return nil, nil, err + } + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, len(encrypted)+2) + ckx.ciphertext[0] = byte(len(encrypted) >> 8) + ckx.ciphertext[1] = byte(len(encrypted)) + copy(ckx.ciphertext[2:], encrypted) + return preMasterSecret, ckx, nil +} + +// sha1Hash calculates a SHA1 hash over the given byte slices. +func sha1Hash(slices [][]byte) []byte { + hsha1 := sha1.New() + for _, slice := range slices { + hsha1.Write(slice) + } + return hsha1.Sum(nil) +} + +// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the +// concatenation of an MD5 and SHA1 hash. +func md5SHA1Hash(slices [][]byte) []byte { + md5sha1 := make([]byte, md5.Size+sha1.Size) + hmd5 := md5.New() + for _, slice := range slices { + hmd5.Write(slice) + } + copy(md5sha1, hmd5.Sum(nil)) + copy(md5sha1[md5.Size:], sha1Hash(slices)) + return md5sha1 +} + +// hashForServerKeyExchange hashes the given slices and returns their digest +// using the given hash function (for >= TLS 1.2) or using a default based on +// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't +// do pre-hashing, it returns the concatenation of the slices. +func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte { + if sigType == signatureEd25519 { + var signed []byte + for _, slice := range slices { + signed = append(signed, slice...) + } + return signed + } + if version >= VersionTLS12 { + h := hashFunc.New() + for _, slice := range slices { + h.Write(slice) + } + digest := h.Sum(nil) + return digest + } + if sigType == signatureECDSA { + return sha1Hash(slices) + } + return md5SHA1Hash(slices) +} + +// ecdheKeyAgreement implements a TLS key agreement where the server +// generates an ephemeral EC public/private key pair and signs it. The +// pre-master secret is then calculated using ECDH. The signature may +// be ECDSA, Ed25519 or RSA. +type ecdheKeyAgreement struct { + version uint16 + isRSA bool + params ecdheParameters + + // ckx and preMasterSecret are generated in processServerKeyExchange + // and returned in generateClientKeyExchange. + ckx *clientKeyExchangeMsg + preMasterSecret []byte +} + +func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + var curveID CurveID + for _, c := range clientHello.supportedCurves { + if config.supportsCurve(c) { + curveID = c + break + } + } + + if curveID == 0 { + return nil, errors.New("tls: no supported elliptic curves offered") + } + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, err + } + ka.params = params + + // See RFC 4492, Section 5.4. + ecdhePublic := params.PublicKey() + serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic)) + serverECDHEParams[0] = 3 // named curve + serverECDHEParams[1] = byte(curveID >> 8) + serverECDHEParams[2] = byte(curveID) + serverECDHEParams[3] = byte(len(ecdhePublic)) + copy(serverECDHEParams[4:], ecdhePublic) + + priv, ok := cert.PrivateKey.(crypto.Signer) + if !ok { + return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey) + } + + var signatureAlgorithm SignatureScheme + var sigType uint8 + var sigHash crypto.Hash + if ka.version >= VersionTLS12 { + signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms) + if err != nil { + return nil, err + } + sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) + if err != nil { + return nil, err + } + } else { + sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public()) + if err != nil { + return nil, err + } + } + if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { + return nil, errors.New("tls: certificate cannot be used with the selected cipher suite") + } + + signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams) + + signOpts := crypto.SignerOpts(sigHash) + if sigType == signatureRSAPSS { + signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} + } + sig, err := priv.Sign(config.rand(), signed, signOpts) + if err != nil { + return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error()) + } + + skx := new(serverKeyExchangeMsg) + sigAndHashLen := 0 + if ka.version >= VersionTLS12 { + sigAndHashLen = 2 + } + skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig)) + copy(skx.key, serverECDHEParams) + k := skx.key[len(serverECDHEParams):] + if ka.version >= VersionTLS12 { + k[0] = byte(signatureAlgorithm >> 8) + k[1] = byte(signatureAlgorithm) + k = k[2:] + } + k[0] = byte(len(sig) >> 8) + k[1] = byte(len(sig)) + copy(k[2:], sig) + + return skx, nil +} + +func (ka *ecdheKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { + return nil, errClientKeyExchange + } + + preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:]) + if preMasterSecret == nil { + return nil, errClientKeyExchange + } + + return preMasterSecret, nil +} + +func (ka *ecdheKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + if len(skx.key) < 4 { + return errServerKeyExchange + } + if skx.key[0] != 3 { // named curve + return errors.New("tls: server selected unsupported curve") + } + curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2]) + + publicLen := int(skx.key[3]) + if publicLen+4 > len(skx.key) { + return errServerKeyExchange + } + serverECDHEParams := skx.key[:4+publicLen] + publicKey := serverECDHEParams[4:] + + sig := skx.key[4+publicLen:] + if len(sig) < 2 { + return errServerKeyExchange + } + + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return errors.New("tls: server selected unsupported curve") + } + + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return err + } + ka.params = params + + ka.preMasterSecret = params.SharedKey(publicKey) + if ka.preMasterSecret == nil { + return errServerKeyExchange + } + + ourPublicKey := params.PublicKey() + ka.ckx = new(clientKeyExchangeMsg) + ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey)) + ka.ckx.ciphertext[0] = byte(len(ourPublicKey)) + copy(ka.ckx.ciphertext[1:], ourPublicKey) + + var sigType uint8 + var sigHash crypto.Hash + if ka.version >= VersionTLS12 { + signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1]) + sig = sig[2:] + if len(sig) < 2 { + return errServerKeyExchange + } + + if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) { + return errors.New("tls: certificate used with invalid signature algorithm") + } + sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) + if err != nil { + return err + } + } else { + sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey) + if err != nil { + return err + } + } + if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { + return errServerKeyExchange + } + + sigLen := int(sig[0])<<8 | int(sig[1]) + if sigLen+2 != len(sig) { + return errServerKeyExchange + } + sig = sig[2:] + + signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams) + if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil { + return errors.New("tls: invalid signature by the server certificate: " + err.Error()) + } + return nil +} + +func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + if ka.ckx == nil { + return nil, nil, errors.New("tls: missing ServerKeyExchange message") + } + + return ka.preMasterSecret, ka.ckx, nil +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/key_schedule.go b/vendor/github.com/marten-seemann/qtls-go1-19/key_schedule.go new file mode 100644 index 00000000..da13904a --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/key_schedule.go @@ -0,0 +1,199 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "crypto/elliptic" + "crypto/hmac" + "errors" + "hash" + "io" + "math/big" + + "golang.org/x/crypto/cryptobyte" + "golang.org/x/crypto/curve25519" + "golang.org/x/crypto/hkdf" +) + +// This file contains the functions necessary to compute the TLS 1.3 key +// schedule. See RFC 8446, Section 7. + +const ( + resumptionBinderLabel = "res binder" + clientHandshakeTrafficLabel = "c hs traffic" + serverHandshakeTrafficLabel = "s hs traffic" + clientApplicationTrafficLabel = "c ap traffic" + serverApplicationTrafficLabel = "s ap traffic" + exporterLabel = "exp master" + resumptionLabel = "res master" + trafficUpdateLabel = "traffic upd" +) + +// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. +func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte { + var hkdfLabel cryptobyte.Builder + hkdfLabel.AddUint16(uint16(length)) + hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte("tls13 ")) + b.AddBytes([]byte(label)) + }) + hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(context) + }) + out := make([]byte, length) + n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out) + if err != nil || n != length { + panic("tls: HKDF-Expand-Label invocation failed unexpectedly") + } + return out +} + +// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1. +func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte { + if transcript == nil { + transcript = c.hash.New() + } + return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size()) +} + +// extract implements HKDF-Extract with the cipher suite hash. +func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte { + if newSecret == nil { + newSecret = make([]byte, c.hash.Size()) + } + return hkdf.Extract(c.hash.New, newSecret, currentSecret) +} + +// nextTrafficSecret generates the next traffic secret, given the current one, +// according to RFC 8446, Section 7.2. +func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte { + return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size()) +} + +// trafficKey generates traffic keys according to RFC 8446, Section 7.3. +func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { + key = c.expandLabel(trafficSecret, "key", nil, c.keyLen) + iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength) + return +} + +// finishedHash generates the Finished verify_data or PskBinderEntry according +// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey +// selection. +func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte { + finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size()) + verifyData := hmac.New(c.hash.New, finishedKey) + verifyData.Write(transcript.Sum(nil)) + return verifyData.Sum(nil) +} + +// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to +// RFC 8446, Section 7.5. +func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) { + expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript) + return func(label string, context []byte, length int) ([]byte, error) { + secret := c.deriveSecret(expMasterSecret, label, nil) + h := c.hash.New() + h.Write(context) + return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil + } +} + +// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519, +// according to RFC 8446, Section 4.2.8.2. +type ecdheParameters interface { + CurveID() CurveID + PublicKey() []byte + SharedKey(peerPublicKey []byte) []byte +} + +func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) { + if curveID == X25519 { + privateKey := make([]byte, curve25519.ScalarSize) + if _, err := io.ReadFull(rand, privateKey); err != nil { + return nil, err + } + publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint) + if err != nil { + return nil, err + } + return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil + } + + curve, ok := curveForCurveID(curveID) + if !ok { + return nil, errors.New("tls: internal error: unsupported curve") + } + + p := &nistParameters{curveID: curveID} + var err error + p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand) + if err != nil { + return nil, err + } + return p, nil +} + +func curveForCurveID(id CurveID) (elliptic.Curve, bool) { + switch id { + case CurveP256: + return elliptic.P256(), true + case CurveP384: + return elliptic.P384(), true + case CurveP521: + return elliptic.P521(), true + default: + return nil, false + } +} + +type nistParameters struct { + privateKey []byte + x, y *big.Int // public key + curveID CurveID +} + +func (p *nistParameters) CurveID() CurveID { + return p.curveID +} + +func (p *nistParameters) PublicKey() []byte { + curve, _ := curveForCurveID(p.curveID) + return elliptic.Marshal(curve, p.x, p.y) +} + +func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte { + curve, _ := curveForCurveID(p.curveID) + // Unmarshal also checks whether the given point is on the curve. + x, y := elliptic.Unmarshal(curve, peerPublicKey) + if x == nil { + return nil + } + + xShared, _ := curve.ScalarMult(x, y, p.privateKey) + sharedKey := make([]byte, (curve.Params().BitSize+7)/8) + return xShared.FillBytes(sharedKey) +} + +type x25519Parameters struct { + privateKey []byte + publicKey []byte +} + +func (p *x25519Parameters) CurveID() CurveID { + return X25519 +} + +func (p *x25519Parameters) PublicKey() []byte { + return p.publicKey[:] +} + +func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte { + sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey) + if err != nil { + return nil + } + return sharedKey +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/notboring.go b/vendor/github.com/marten-seemann/qtls-go1-19/notboring.go new file mode 100644 index 00000000..f292e4f0 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/notboring.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +func needFIPS() bool { return false } + +func supportedSignatureAlgorithms() []SignatureScheme { + return defaultSupportedSignatureAlgorithms +} + +func fipsMinVersion(c *config) uint16 { panic("fipsMinVersion") } +func fipsMaxVersion(c *config) uint16 { panic("fipsMaxVersion") } +func fipsCurvePreferences(c *config) []CurveID { panic("fipsCurvePreferences") } +func fipsCipherSuites(c *config) []uint16 { panic("fipsCipherSuites") } + +var fipsSupportedSignatureAlgorithms []SignatureScheme diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/prf.go b/vendor/github.com/marten-seemann/qtls-go1-19/prf.go new file mode 100644 index 00000000..9eb0221a --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/prf.go @@ -0,0 +1,283 @@ +// 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 LICENSE file. + +package qtls + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "errors" + "fmt" + "hash" +) + +// Split a premaster secret in two as specified in RFC 4346, Section 5. +func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { + s1 = secret[0 : (len(secret)+1)/2] + s2 = secret[len(secret)/2:] + return +} + +// pHash implements the P_hash function, as defined in RFC 4346, Section 5. +func pHash(result, secret, seed []byte, hash func() hash.Hash) { + h := hmac.New(hash, secret) + h.Write(seed) + a := h.Sum(nil) + + j := 0 + for j < len(result) { + h.Reset() + h.Write(a) + h.Write(seed) + b := h.Sum(nil) + copy(result[j:], b) + j += len(b) + + h.Reset() + h.Write(a) + a = h.Sum(nil) + } +} + +// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. +func prf10(result, secret, label, seed []byte) { + hashSHA1 := sha1.New + hashMD5 := md5.New + + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + s1, s2 := splitPreMasterSecret(secret) + pHash(result, s1, labelAndSeed, hashMD5) + result2 := make([]byte, len(result)) + pHash(result2, s2, labelAndSeed, hashSHA1) + + for i, b := range result2 { + result[i] ^= b + } +} + +// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. +func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) { + return func(result, secret, label, seed []byte) { + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + pHash(result, secret, labelAndSeed, hashFunc) + } +} + +const ( + masterSecretLength = 48 // Length of a master secret in TLS 1.1. + finishedVerifyLength = 12 // Length of verify_data in a Finished message. +) + +var masterSecretLabel = []byte("master secret") +var keyExpansionLabel = []byte("key expansion") +var clientFinishedLabel = []byte("client finished") +var serverFinishedLabel = []byte("server finished") + +func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { + switch version { + case VersionTLS10, VersionTLS11: + return prf10, crypto.Hash(0) + case VersionTLS12: + if suite.flags&suiteSHA384 != 0 { + return prf12(sha512.New384), crypto.SHA384 + } + return prf12(sha256.New), crypto.SHA256 + default: + panic("unknown version") + } +} + +func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { + prf, _ := prfAndHashForVersion(version, suite) + return prf +} + +// masterFromPreMasterSecret generates the master secret from the pre-master +// secret. See RFC 5246, Section 8.1. +func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte { + seed := make([]byte, 0, len(clientRandom)+len(serverRandom)) + seed = append(seed, clientRandom...) + seed = append(seed, serverRandom...) + + masterSecret := make([]byte, masterSecretLength) + prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed) + return masterSecret +} + +// keysFromMasterSecret generates the connection keys from the master +// secret, given the lengths of the MAC key, cipher key and IV, as defined in +// RFC 2246, Section 6.3. +func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + seed := make([]byte, 0, len(serverRandom)+len(clientRandom)) + seed = append(seed, serverRandom...) + seed = append(seed, clientRandom...) + + n := 2*macLen + 2*keyLen + 2*ivLen + keyMaterial := make([]byte, n) + prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed) + clientMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + serverMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + clientKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + serverKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + clientIV = keyMaterial[:ivLen] + keyMaterial = keyMaterial[ivLen:] + serverIV = keyMaterial[:ivLen] + return +} + +func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { + var buffer []byte + if version >= VersionTLS12 { + buffer = []byte{} + } + + prf, hash := prfAndHashForVersion(version, cipherSuite) + if hash != 0 { + return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf} + } + + return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf} +} + +// A finishedHash calculates the hash of a set of handshake messages suitable +// for including in a Finished message. +type finishedHash struct { + client hash.Hash + server hash.Hash + + // Prior to TLS 1.2, an additional MD5 hash is required. + clientMD5 hash.Hash + serverMD5 hash.Hash + + // In TLS 1.2, a full buffer is sadly required. + buffer []byte + + version uint16 + prf func(result, secret, label, seed []byte) +} + +func (h *finishedHash) Write(msg []byte) (n int, err error) { + h.client.Write(msg) + h.server.Write(msg) + + if h.version < VersionTLS12 { + h.clientMD5.Write(msg) + h.serverMD5.Write(msg) + } + + if h.buffer != nil { + h.buffer = append(h.buffer, msg...) + } + + return len(msg), nil +} + +func (h finishedHash) Sum() []byte { + if h.version >= VersionTLS12 { + return h.client.Sum(nil) + } + + out := make([]byte, 0, md5.Size+sha1.Size) + out = h.clientMD5.Sum(out) + return h.client.Sum(out) +} + +// clientSum returns the contents of the verify_data member of a client's +// Finished message. +func (h finishedHash) clientSum(masterSecret []byte) []byte { + out := make([]byte, finishedVerifyLength) + h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) + return out +} + +// serverSum returns the contents of the verify_data member of a server's +// Finished message. +func (h finishedHash) serverSum(masterSecret []byte) []byte { + out := make([]byte, finishedVerifyLength) + h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) + return out +} + +// hashForClientCertificate returns the handshake messages so far, pre-hashed if +// necessary, suitable for signing by a TLS client certificate. +func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) []byte { + if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil { + panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer") + } + + if sigType == signatureEd25519 { + return h.buffer + } + + if h.version >= VersionTLS12 { + hash := hashAlg.New() + hash.Write(h.buffer) + return hash.Sum(nil) + } + + if sigType == signatureECDSA { + return h.server.Sum(nil) + } + + return h.Sum() +} + +// discardHandshakeBuffer is called when there is no more need to +// buffer the entirety of the handshake messages. +func (h *finishedHash) discardHandshakeBuffer() { + h.buffer = nil +} + +// noExportedKeyingMaterial is used as a value of +// ConnectionState.ekm when renegotiation is enabled and thus +// we wish to fail all key-material export requests. +func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { + return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") +} + +// ekmFromMasterSecret generates exported keying material as defined in RFC 5705. +func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { + return func(label string, context []byte, length int) ([]byte, error) { + switch label { + case "client finished", "server finished", "master secret", "key expansion": + // These values are reserved and may not be used. + return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label) + } + + seedLen := len(serverRandom) + len(clientRandom) + if context != nil { + seedLen += 2 + len(context) + } + seed := make([]byte, 0, seedLen) + + seed = append(seed, clientRandom...) + seed = append(seed, serverRandom...) + + if context != nil { + if len(context) >= 1<<16 { + return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long") + } + seed = append(seed, byte(len(context)>>8), byte(len(context))) + seed = append(seed, context...) + } + + keyMaterial := make([]byte, length) + prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) + return keyMaterial, nil + } +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/ticket.go b/vendor/github.com/marten-seemann/qtls-go1-19/ticket.go new file mode 100644 index 00000000..81e8a52e --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/ticket.go @@ -0,0 +1,274 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "encoding/binary" + "errors" + "io" + "time" + + "golang.org/x/crypto/cryptobyte" +) + +// sessionState contains the information that is serialized into a session +// ticket in order to later resume a connection. +type sessionState struct { + vers uint16 + cipherSuite uint16 + createdAt uint64 + masterSecret []byte // opaque master_secret<1..2^16-1>; + // struct { opaque certificate<1..2^24-1> } Certificate; + certificates [][]byte // Certificate certificate_list<0..2^24-1>; + + // usedOldKey is true if the ticket from which this session came from + // was encrypted with an older key and thus should be refreshed. + usedOldKey bool +} + +func (m *sessionState) marshal() []byte { + var b cryptobyte.Builder + b.AddUint16(m.vers) + b.AddUint16(m.cipherSuite) + addUint64(&b, m.createdAt) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.masterSecret) + }) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + for _, cert := range m.certificates { + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(cert) + }) + } + }) + return b.BytesOrPanic() +} + +func (m *sessionState) unmarshal(data []byte) bool { + *m = sessionState{usedOldKey: m.usedOldKey} + s := cryptobyte.String(data) + if ok := s.ReadUint16(&m.vers) && + s.ReadUint16(&m.cipherSuite) && + readUint64(&s, &m.createdAt) && + readUint16LengthPrefixed(&s, &m.masterSecret) && + len(m.masterSecret) != 0; !ok { + return false + } + var certList cryptobyte.String + if !s.ReadUint24LengthPrefixed(&certList) { + return false + } + for !certList.Empty() { + var cert []byte + if !readUint24LengthPrefixed(&certList, &cert) { + return false + } + m.certificates = append(m.certificates, cert) + } + return s.Empty() +} + +// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first +// version (revision = 0) doesn't carry any of the information needed for 0-RTT +// validation and the nonce is always empty. +// version (revision = 1) carries the max_early_data_size sent in the ticket. +// version (revision = 2) carries the ALPN sent in the ticket. +type sessionStateTLS13 struct { + // uint8 version = 0x0304; + // uint8 revision = 2; + cipherSuite uint16 + createdAt uint64 + resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>; + certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; + maxEarlyData uint32 + alpn string + + appData []byte +} + +func (m *sessionStateTLS13) marshal() []byte { + var b cryptobyte.Builder + b.AddUint16(VersionTLS13) + b.AddUint8(2) // revision + b.AddUint16(m.cipherSuite) + addUint64(&b, m.createdAt) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.resumptionSecret) + }) + marshalCertificate(&b, m.certificate) + b.AddUint32(m.maxEarlyData) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes([]byte(m.alpn)) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.appData) + }) + return b.BytesOrPanic() +} + +func (m *sessionStateTLS13) unmarshal(data []byte) bool { + *m = sessionStateTLS13{} + s := cryptobyte.String(data) + var version uint16 + var revision uint8 + var alpn []byte + ret := s.ReadUint16(&version) && + version == VersionTLS13 && + s.ReadUint8(&revision) && + revision == 2 && + s.ReadUint16(&m.cipherSuite) && + readUint64(&s, &m.createdAt) && + readUint8LengthPrefixed(&s, &m.resumptionSecret) && + len(m.resumptionSecret) != 0 && + unmarshalCertificate(&s, &m.certificate) && + s.ReadUint32(&m.maxEarlyData) && + readUint8LengthPrefixed(&s, &alpn) && + readUint16LengthPrefixed(&s, &m.appData) && + s.Empty() + m.alpn = string(alpn) + return ret +} + +func (c *Conn) encryptTicket(state []byte) ([]byte, error) { + if len(c.ticketKeys) == 0 { + return nil, errors.New("tls: internal error: session ticket keys unavailable") + } + + encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size) + keyName := encrypted[:ticketKeyNameLen] + iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + + if _, err := io.ReadFull(c.config.rand(), iv); err != nil { + return nil, err + } + key := c.ticketKeys[0] + copy(keyName, key.keyName[:]) + block, err := aes.NewCipher(key.aesKey[:]) + if err != nil { + return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) + } + cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state) + + mac := hmac.New(sha256.New, key.hmacKey[:]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + mac.Sum(macBytes[:0]) + + return encrypted, nil +} + +func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) { + if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size { + return nil, false + } + + keyName := encrypted[:ticketKeyNameLen] + iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size] + + keyIndex := -1 + for i, candidateKey := range c.ticketKeys { + if bytes.Equal(keyName, candidateKey.keyName[:]) { + keyIndex = i + break + } + } + if keyIndex == -1 { + return nil, false + } + key := &c.ticketKeys[keyIndex] + + mac := hmac.New(sha256.New, key.hmacKey[:]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + expected := mac.Sum(nil) + + if subtle.ConstantTimeCompare(macBytes, expected) != 1 { + return nil, false + } + + block, err := aes.NewCipher(key.aesKey[:]) + if err != nil { + return nil, false + } + plaintext = make([]byte, len(ciphertext)) + cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) + + return plaintext, keyIndex > 0 +} + +func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) { + m := new(newSessionTicketMsgTLS13) + + var certsFromClient [][]byte + for _, cert := range c.peerCertificates { + certsFromClient = append(certsFromClient, cert.Raw) + } + state := sessionStateTLS13{ + cipherSuite: c.cipherSuite, + createdAt: uint64(c.config.time().Unix()), + resumptionSecret: c.resumptionSecret, + certificate: Certificate{ + Certificate: certsFromClient, + OCSPStaple: c.ocspResponse, + SignedCertificateTimestamps: c.scts, + }, + appData: appData, + alpn: c.clientProtocol, + } + if c.extraConfig != nil { + state.maxEarlyData = c.extraConfig.MaxEarlyData + } + var err error + m.label, err = c.encryptTicket(state.marshal()) + if err != nil { + return nil, err + } + m.lifetime = uint32(maxSessionTicketLifetime / time.Second) + + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 + // The value is not stored anywhere; we never need to check the ticket age + // because 0-RTT is not supported. + ageAdd := make([]byte, 4) + _, err = c.config.rand().Read(ageAdd) + if err != nil { + return nil, err + } + m.ageAdd = binary.LittleEndian.Uint32(ageAdd) + + // ticket_nonce, which must be unique per connection, is always left at + // zero because we only ever send one ticket per connection. + + if c.extraConfig != nil { + m.maxEarlyData = c.extraConfig.MaxEarlyData + } + return m, nil +} + +// GetSessionTicket generates a new session ticket. +// It should only be called after the handshake completes. +// It can only be used for servers, and only if the alternative record layer is set. +// The ticket may be nil if config.SessionTicketsDisabled is set, +// or if the client isn't able to receive session tickets. +func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) { + if c.isClient || !c.handshakeComplete() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { + return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.") + } + if c.config.SessionTicketsDisabled { + return nil, nil + } + + m, err := c.getSessionTicketMsg(appData) + if err != nil { + return nil, err + } + return m.marshal(), nil +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/tls.go b/vendor/github.com/marten-seemann/qtls-go1-19/tls.go new file mode 100644 index 00000000..42207c23 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/tls.go @@ -0,0 +1,362 @@ +// 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 LICENSE file. + +// package qtls partially implements TLS 1.2, as specified in RFC 5246, +// and TLS 1.3, as specified in RFC 8446. +package qtls + +// BUG(agl): The crypto/tls package only implements some countermeasures +// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 +// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and +// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. + +import ( + "bytes" + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "net" + "os" + "strings" +) + +// Server returns a new TLS server side connection +// using conn as the underlying transport. +// The configuration config must be non-nil and must include +// at least one certificate or else set GetCertificate. +func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { + c := &Conn{ + conn: conn, + config: fromConfig(config), + extraConfig: extraConfig, + } + c.handshakeFn = c.serverHandshake + return c +} + +// Client returns a new TLS client side connection +// using conn as the underlying transport. +// The config cannot be nil: users must set either ServerName or +// InsecureSkipVerify in the config. +func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { + c := &Conn{ + conn: conn, + config: fromConfig(config), + extraConfig: extraConfig, + isClient: true, + } + c.handshakeFn = c.clientHandshake + return c +} + +// A listener implements a network listener (net.Listener) for TLS connections. +type listener struct { + net.Listener + config *Config + extraConfig *ExtraConfig +} + +// Accept waits for and returns the next incoming TLS connection. +// The returned connection is of type *Conn. +func (l *listener) Accept() (net.Conn, error) { + c, err := l.Listener.Accept() + if err != nil { + return nil, err + } + return Server(c, l.config, l.extraConfig), nil +} + +// NewListener creates a Listener which accepts connections from an inner +// Listener and wraps each connection with Server. +// The configuration config must be non-nil and must include +// at least one certificate or else set GetCertificate. +func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener { + l := new(listener) + l.Listener = inner + l.config = config + l.extraConfig = extraConfig + return l +} + +// Listen creates a TLS listener accepting connections on the +// given network address using net.Listen. +// The configuration config must be non-nil and must include +// at least one certificate or else set GetCertificate. +func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) { + if config == nil || len(config.Certificates) == 0 && + config.GetCertificate == nil && config.GetConfigForClient == nil { + return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config") + } + l, err := net.Listen(network, laddr) + if err != nil { + return nil, err + } + return NewListener(l, config, extraConfig), nil +} + +type timeoutError struct{} + +func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } +func (timeoutError) Timeout() bool { return true } +func (timeoutError) Temporary() bool { return true } + +// DialWithDialer connects to the given network address using dialer.Dial and +// then initiates a TLS handshake, returning the resulting TLS connection. Any +// timeout or deadline given in the dialer apply to connection and TLS +// handshake as a whole. +// +// DialWithDialer interprets a nil configuration as equivalent to the zero +// configuration; see the documentation of Config for the defaults. +// +// DialWithDialer uses context.Background internally; to specify the context, +// use Dialer.DialContext with NetDialer set to the desired dialer. +func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { + return dial(context.Background(), dialer, network, addr, config, extraConfig) +} + +func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { + if netDialer.Timeout != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout) + defer cancel() + } + + if !netDialer.Deadline.IsZero() { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline) + defer cancel() + } + + rawConn, err := netDialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + + colonPos := strings.LastIndex(addr, ":") + if colonPos == -1 { + colonPos = len(addr) + } + hostname := addr[:colonPos] + + if config == nil { + config = defaultConfig() + } + // If no ServerName is set, infer the ServerName + // from the hostname we're connecting to. + if config.ServerName == "" { + // Make a copy to avoid polluting argument or default. + c := config.Clone() + c.ServerName = hostname + config = c + } + + conn := Client(rawConn, config, extraConfig) + if err := conn.HandshakeContext(ctx); err != nil { + rawConn.Close() + return nil, err + } + return conn, nil +} + +// Dial connects to the given network address using net.Dial +// and then initiates a TLS handshake, returning the resulting +// TLS connection. +// Dial interprets a nil configuration as equivalent to +// the zero configuration; see the documentation of Config +// for the defaults. +func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { + return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig) +} + +// Dialer dials TLS connections given a configuration and a Dialer for the +// underlying connection. +type Dialer struct { + // NetDialer is the optional dialer to use for the TLS connections' + // underlying TCP connections. + // A nil NetDialer is equivalent to the net.Dialer zero value. + NetDialer *net.Dialer + + // Config is the TLS configuration to use for new connections. + // A nil configuration is equivalent to the zero + // configuration; see the documentation of Config for the + // defaults. + Config *Config + + ExtraConfig *ExtraConfig +} + +// Dial connects to the given network address and initiates a TLS +// handshake, returning the resulting TLS connection. +// +// The returned Conn, if any, will always be of type *Conn. +// +// Dial uses context.Background internally; to specify the context, +// use DialContext. +func (d *Dialer) Dial(network, addr string) (net.Conn, error) { + return d.DialContext(context.Background(), network, addr) +} + +func (d *Dialer) netDialer() *net.Dialer { + if d.NetDialer != nil { + return d.NetDialer + } + return new(net.Dialer) +} + +// DialContext connects to the given network address and initiates a TLS +// handshake, returning the resulting TLS connection. +// +// The provided Context must be non-nil. If the context expires before +// the connection is complete, an error is returned. Once successfully +// connected, any expiration of the context will not affect the +// connection. +// +// The returned Conn, if any, will always be of type *Conn. +func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { + c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig) + if err != nil { + // Don't return c (a typed nil) in an interface. + return nil, err + } + return c, nil +} + +// LoadX509KeyPair reads and parses a public/private key pair from a pair +// of files. The files must contain PEM encoded data. The certificate file +// may contain intermediate certificates following the leaf certificate to +// form a certificate chain. On successful return, Certificate.Leaf will +// be nil because the parsed form of the certificate is not retained. +func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { + certPEMBlock, err := os.ReadFile(certFile) + if err != nil { + return Certificate{}, err + } + keyPEMBlock, err := os.ReadFile(keyFile) + if err != nil { + return Certificate{}, err + } + return X509KeyPair(certPEMBlock, keyPEMBlock) +} + +// X509KeyPair parses a public/private key pair from a pair of +// PEM encoded data. On successful return, Certificate.Leaf will be nil because +// the parsed form of the certificate is not retained. +func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { + fail := func(err error) (Certificate, error) { return Certificate{}, err } + + var cert Certificate + var skippedBlockTypes []string + for { + var certDERBlock *pem.Block + certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) + if certDERBlock == nil { + break + } + if certDERBlock.Type == "CERTIFICATE" { + cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) + } else { + skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type) + } + } + + if len(cert.Certificate) == 0 { + if len(skippedBlockTypes) == 0 { + return fail(errors.New("tls: failed to find any PEM data in certificate input")) + } + if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") { + return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched")) + } + return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) + } + + skippedBlockTypes = skippedBlockTypes[:0] + var keyDERBlock *pem.Block + for { + keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) + if keyDERBlock == nil { + if len(skippedBlockTypes) == 0 { + return fail(errors.New("tls: failed to find any PEM data in key input")) + } + if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" { + return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key")) + } + return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) + } + if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { + break + } + skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) + } + + // We don't need to parse the public key for TLS, but we so do anyway + // to check that it looks sane and matches the private key. + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return fail(err) + } + + cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) + if err != nil { + return fail(err) + } + + switch pub := x509Cert.PublicKey.(type) { + case *rsa.PublicKey: + priv, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + return fail(errors.New("tls: private key type does not match public key type")) + } + if pub.N.Cmp(priv.N) != 0 { + return fail(errors.New("tls: private key does not match public key")) + } + case *ecdsa.PublicKey: + priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + return fail(errors.New("tls: private key type does not match public key type")) + } + if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { + return fail(errors.New("tls: private key does not match public key")) + } + case ed25519.PublicKey: + priv, ok := cert.PrivateKey.(ed25519.PrivateKey) + if !ok { + return fail(errors.New("tls: private key type does not match public key type")) + } + if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) { + return fail(errors.New("tls: private key does not match public key")) + } + default: + return fail(errors.New("tls: unknown public key algorithm")) + } + + return cert, nil +} + +// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates +// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys. +// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. +func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: + return key, nil + default: + return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + + return nil, errors.New("tls: failed to parse private key") +} diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/unsafe.go b/vendor/github.com/marten-seemann/qtls-go1-19/unsafe.go new file mode 100644 index 00000000..55fa01b3 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/unsafe.go @@ -0,0 +1,96 @@ +package qtls + +import ( + "crypto/tls" + "reflect" + "unsafe" +) + +func init() { + if !structsEqual(&tls.ConnectionState{}, &connectionState{}) { + panic("qtls.ConnectionState doesn't match") + } + if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) { + panic("qtls.ClientSessionState doesn't match") + } + if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) { + panic("qtls.CertificateRequestInfo doesn't match") + } + if !structsEqual(&tls.Config{}, &config{}) { + panic("qtls.Config doesn't match") + } + if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) { + panic("qtls.ClientHelloInfo doesn't match") + } +} + +func toConnectionState(c connectionState) ConnectionState { + return *(*ConnectionState)(unsafe.Pointer(&c)) +} + +func toClientSessionState(s *clientSessionState) *ClientSessionState { + return (*ClientSessionState)(unsafe.Pointer(s)) +} + +func fromClientSessionState(s *ClientSessionState) *clientSessionState { + return (*clientSessionState)(unsafe.Pointer(s)) +} + +func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo { + return (*CertificateRequestInfo)(unsafe.Pointer(i)) +} + +func toConfig(c *config) *Config { + return (*Config)(unsafe.Pointer(c)) +} + +func fromConfig(c *Config) *config { + return (*config)(unsafe.Pointer(c)) +} + +func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo { + return (*ClientHelloInfo)(unsafe.Pointer(chi)) +} + +func structsEqual(a, b interface{}) bool { + return compare(reflect.ValueOf(a), reflect.ValueOf(b)) +} + +func compare(a, b reflect.Value) bool { + sa := a.Elem() + sb := b.Elem() + if sa.NumField() != sb.NumField() { + return false + } + for i := 0; i < sa.NumField(); i++ { + fa := sa.Type().Field(i) + fb := sb.Type().Field(i) + if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) { + if fa.Type.Kind() != fb.Type.Kind() { + return false + } + if fa.Type.Kind() == reflect.Slice { + if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) { + return false + } + continue + } + return false + } + } + return true +} + +func compareStruct(a, b reflect.Type) bool { + if a.NumField() != b.NumField() { + return false + } + for i := 0; i < a.NumField(); i++ { + fa := a.Field(i) + fb := b.Field(i) + if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) { + return false + } + } + return true +} diff --git a/vendor/golang.org/x/net/bpf/doc.go b/vendor/golang.org/x/net/bpf/doc.go index ae62feb5..04ec1c8a 100644 --- a/vendor/golang.org/x/net/bpf/doc.go +++ b/vendor/golang.org/x/net/bpf/doc.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. /* - Package bpf implements marshaling and unmarshaling of programs for the Berkeley Packet Filter virtual machine, and provides a Go implementation of the virtual machine. @@ -21,7 +20,7 @@ access to kernel functions, and while conditional branches are allowed, they can only jump forwards, to guarantee that there are no infinite loops. -The virtual machine +# The virtual machine The BPF VM is an accumulator machine. Its main register, called register A, is an implicit source and destination in all arithmetic @@ -50,7 +49,7 @@ to extensions, which are essentially calls to kernel utility functions. Currently, the only extensions supported by this package are the Linux packet filter extensions. -Examples +# Examples This packet filter selects all ARP packets. @@ -77,6 +76,5 @@ This packet filter captures a random 1% sample of traffic. // Ignore. bpf.RetConstant{Val: 0}, }) - */ package bpf // import "golang.org/x/net/bpf" diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go index a3c021d3..cf66309c 100644 --- a/vendor/golang.org/x/net/context/context.go +++ b/vendor/golang.org/x/net/context/context.go @@ -21,9 +21,9 @@ // explicitly to each function that needs it. The Context should be the first // parameter, typically named ctx: // -// func DoSomething(ctx context.Context, arg Arg) error { -// // ... use ctx ... -// } +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } // // Do not pass a nil Context, even if a function permits it. Pass context.TODO // if you are unsure about which Context to use. diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index 344bd143..0a54bdbc 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -54,11 +54,11 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { // Canceling this context releases resources associated with it, so code should // call cancel as soon as the operations running in this Context complete: // -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) } diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 5270db5d..7b6b6851 100644 --- a/vendor/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -264,11 +264,11 @@ func (c *timerCtx) cancel(removeFromParent bool, err error) { // Canceling this context releases resources associated with it, so code should // call cancel as soon as the operations running in this Context complete: // -// func slowOperationWithTimeout(ctx context.Context) (Result, error) { -// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) -// defer cancel() // releases resources if slowOperation completes before timeout elapses -// return slowOperation(ctx) -// } +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) } diff --git a/vendor/golang.org/x/net/http/httpguts/httplex.go b/vendor/golang.org/x/net/http/httpguts/httplex.go index c79aa73f..6e071e85 100644 --- a/vendor/golang.org/x/net/http/httpguts/httplex.go +++ b/vendor/golang.org/x/net/http/httpguts/httplex.go @@ -173,13 +173,15 @@ func tokenEqual(t1, t2 string) bool { // isLWS reports whether b is linear white space, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 -// LWS = [CRLF] 1*( SP | HT ) +// +// LWS = [CRLF] 1*( SP | HT ) func isLWS(b byte) bool { return b == ' ' || b == '\t' } // isCTL reports whether b is a control byte, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 -// CTL = +// +// CTL = func isCTL(b byte) bool { const del = 0x7f // a CTL return b < ' ' || b == del @@ -189,12 +191,13 @@ func isCTL(b byte) bool { // HTTP/2 imposes the additional restriction that uppercase ASCII // letters are not allowed. // -// RFC 7230 says: -// header-field = field-name ":" OWS field-value OWS -// field-name = token -// token = 1*tchar -// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / -// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// RFC 7230 says: +// +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// token = 1*tchar +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA func ValidHeaderFieldName(v string) bool { if len(v) == 0 { return false @@ -267,27 +270,28 @@ var validHostByte = [256]bool{ // ValidHeaderFieldValue reports whether v is a valid "field-value" according to // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : // -// message-header = field-name ":" [ field-value ] -// field-value = *( field-content | LWS ) -// field-content = +// message-header = field-name ":" [ field-value ] +// field-value = *( field-content | LWS ) +// field-content = // // http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : // -// TEXT = -// LWS = [CRLF] 1*( SP | HT ) -// CTL = +// TEXT = +// LWS = [CRLF] 1*( SP | HT ) +// CTL = // // RFC 7230 says: -// field-value = *( field-content / obs-fold ) -// obj-fold = N/A to http2, and deprecated -// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] -// field-vchar = VCHAR / obs-text -// obs-text = %x80-FF -// VCHAR = "any visible [USASCII] character" +// +// field-value = *( field-content / obs-fold ) +// obj-fold = N/A to http2, and deprecated +// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +// field-vchar = VCHAR / obs-text +// obs-text = %x80-FF +// VCHAR = "any visible [USASCII] character" // // http2 further says: "Similarly, HTTP/2 allows header field values // that are not valid. While most of the values that can be encoded diff --git a/vendor/golang.org/x/net/http2/client_conn_pool.go b/vendor/golang.org/x/net/http2/client_conn_pool.go index c936843e..780968d6 100644 --- a/vendor/golang.org/x/net/http2/client_conn_pool.go +++ b/vendor/golang.org/x/net/http2/client_conn_pool.go @@ -139,7 +139,6 @@ func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *d func (c *dialCall) dial(ctx context.Context, addr string) { const singleUse = false // shared conn c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse) - close(c.done) c.p.mu.Lock() delete(c.p.dialing, addr) @@ -147,6 +146,8 @@ func (c *dialCall) dial(ctx context.Context, addr string) { c.p.addConnLocked(addr, c.res) } c.p.mu.Unlock() + + close(c.done) } // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't diff --git a/vendor/golang.org/x/net/http2/errors.go b/vendor/golang.org/x/net/http2/errors.go index 2663e5d2..f2067dab 100644 --- a/vendor/golang.org/x/net/http2/errors.go +++ b/vendor/golang.org/x/net/http2/errors.go @@ -136,7 +136,7 @@ func (e headerFieldNameError) Error() string { type headerFieldValueError string func (e headerFieldValueError) Error() string { - return fmt.Sprintf("invalid header field value %q", string(e)) + return fmt.Sprintf("invalid header field value for %q", string(e)) } var ( diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index 96a74790..0178647e 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -1532,7 +1532,8 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) { fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) } if !httpguts.ValidHeaderFieldValue(hf.Value) { - invalid = headerFieldValueError(hf.Value) + // Don't include the value in the error, because it may be sensitive. + invalid = headerFieldValueError(hf.Name) } isPseudo := strings.HasPrefix(hf.Name, ":") if isPseudo { diff --git a/vendor/golang.org/x/net/http2/go118.go b/vendor/golang.org/x/net/http2/go118.go new file mode 100644 index 00000000..aca4b2b3 --- /dev/null +++ b/vendor/golang.org/x/net/http2/go118.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.18 +// +build go1.18 + +package http2 + +import ( + "crypto/tls" + "net" +) + +func tlsUnderlyingConn(tc *tls.Conn) net.Conn { + return tc.NetConn() +} diff --git a/vendor/golang.org/x/net/http2/hpack/huffman.go b/vendor/golang.org/x/net/http2/hpack/huffman.go index fe0b84cc..20d083a7 100644 --- a/vendor/golang.org/x/net/http2/hpack/huffman.go +++ b/vendor/golang.org/x/net/http2/hpack/huffman.go @@ -169,25 +169,50 @@ func buildRootHuffmanNode() { // AppendHuffmanString appends s, as encoded in Huffman codes, to dst // and returns the extended buffer. func AppendHuffmanString(dst []byte, s string) []byte { - rembits := uint8(8) - + // This relies on the maximum huffman code length being 30 (See tables.go huffmanCodeLen array) + // So if a uint64 buffer has less than 32 valid bits can always accommodate another huffmanCode. + var ( + x uint64 // buffer + n uint // number valid of bits present in x + ) for i := 0; i < len(s); i++ { - if rembits == 8 { - dst = append(dst, 0) + c := s[i] + n += uint(huffmanCodeLen[c]) + x <<= huffmanCodeLen[c] % 64 + x |= uint64(huffmanCodes[c]) + if n >= 32 { + n %= 32 // Normally would be -= 32 but %= 32 informs compiler 0 <= n <= 31 for upcoming shift + y := uint32(x >> n) // Compiler doesn't combine memory writes if y isn't uint32 + dst = append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } - dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i]) } - - if rembits < 8 { - // special EOS symbol - code := uint32(0x3fffffff) - nbits := uint8(30) - - t := uint8(code >> (nbits - rembits)) - dst[len(dst)-1] |= t + // Add padding bits if necessary + if over := n % 8; over > 0 { + const ( + eosCode = 0x3fffffff + eosNBits = 30 + eosPadByte = eosCode >> (eosNBits - 8) + ) + pad := 8 - over + x = (x << pad) | (eosPadByte >> over) + n += pad // 8 now divides into n exactly } - - return dst + // n in (0, 8, 16, 24, 32) + switch n / 8 { + case 0: + return dst + case 1: + return append(dst, byte(x)) + case 2: + y := uint16(x) + return append(dst, byte(y>>8), byte(y)) + case 3: + y := uint16(x >> 8) + return append(dst, byte(y>>8), byte(y), byte(x)) + } + // case 4: + y := uint32(x) + return append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } // HuffmanEncodeLength returns the number of bytes required to encode @@ -199,35 +224,3 @@ func HuffmanEncodeLength(s string) uint64 { } return (n + 7) / 8 } - -// appendByteToHuffmanCode appends Huffman code for c to dst and -// returns the extended buffer and the remaining bits in the last -// element. The appending is not byte aligned and the remaining bits -// in the last element of dst is given in rembits. -func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) { - code := huffmanCodes[c] - nbits := huffmanCodeLen[c] - - for { - if rembits > nbits { - t := uint8(code << (rembits - nbits)) - dst[len(dst)-1] |= t - rembits -= nbits - break - } - - t := uint8(code >> (nbits - rembits)) - dst[len(dst)-1] |= t - - nbits -= rembits - rembits = 8 - - if nbits == 0 { - break - } - - dst = append(dst, 0) - } - - return dst, rembits -} diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index 5571ccfd..479ba4b2 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -13,7 +13,6 @@ // See https://http2.github.io/ for more information on HTTP/2. // // See https://http2.golang.org/ for a test server running this code. -// package http2 // import "golang.org/x/net/http2" import ( @@ -176,10 +175,11 @@ func (s SettingID) String() string { // name (key). See httpguts.ValidHeaderName for the base rules. // // Further, http2 says: -// "Just as in HTTP/1.x, header field names are strings of ASCII -// characters that are compared in a case-insensitive -// fashion. However, header field names MUST be converted to -// lowercase prior to their encoding in HTTP/2. " +// +// "Just as in HTTP/1.x, header field names are strings of ASCII +// characters that are compared in a case-insensitive +// fashion. However, header field names MUST be converted to +// lowercase prior to their encoding in HTTP/2. " func validWireHeaderFieldName(v string) bool { if len(v) == 0 { return false @@ -365,8 +365,8 @@ func (s *sorter) SortStrings(ss []string) { // validPseudoPath reports whether v is a valid :path pseudo-header // value. It must be either: // -// *) a non-empty string starting with '/' -// *) the string '*', for OPTIONS requests. +// - a non-empty string starting with '/' +// - the string '*', for OPTIONS requests. // // For now this is only used a quick check for deciding when to clean // up Opaque URLs before sending requests from the Transport. diff --git a/vendor/golang.org/x/net/http2/not_go118.go b/vendor/golang.org/x/net/http2/not_go118.go new file mode 100644 index 00000000..eab532c9 --- /dev/null +++ b/vendor/golang.org/x/net/http2/not_go118.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.18 +// +build !go1.18 + +package http2 + +import ( + "crypto/tls" + "net" +) + +func tlsUnderlyingConn(tc *tls.Conn) net.Conn { + return nil +} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index e644d9b2..47524a61 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -315,6 +315,20 @@ type ServeConnOpts struct { // requests. If nil, BaseConfig.Handler is used. If BaseConfig // or BaseConfig.Handler is nil, http.DefaultServeMux is used. Handler http.Handler + + // UpgradeRequest is an initial request received on a connection + // undergoing an h2c upgrade. The request body must have been + // completely read from the connection before calling ServeConn, + // and the 101 Switching Protocols response written. + UpgradeRequest *http.Request + + // Settings is the decoded contents of the HTTP2-Settings header + // in an h2c upgrade request. + Settings []byte + + // SawClientPreface is set if the HTTP/2 connection preface + // has already been read from the connection. + SawClientPreface bool } func (o *ServeConnOpts) context() context.Context { @@ -383,6 +397,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { headerTableSize: initialHeaderTableSize, serveG: newGoroutineLock(), pushEnabled: true, + sawClientPreface: opts.SawClientPreface, } s.state.registerConn(sc) @@ -400,7 +415,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { if s.NewWriteScheduler != nil { sc.writeSched = s.NewWriteScheduler() } else { - sc.writeSched = NewRandomWriteScheduler() + sc.writeSched = NewPriorityWriteScheduler(nil) } // These start at the RFC-specified defaults. If there is a higher @@ -465,9 +480,27 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) { } } + if opts.Settings != nil { + fr := &SettingsFrame{ + FrameHeader: FrameHeader{valid: true}, + p: opts.Settings, + } + if err := fr.ForeachSetting(sc.processSetting); err != nil { + sc.rejectConn(ErrCodeProtocol, "invalid settings") + return + } + opts.Settings = nil + } + if hook := testHookGetServerConn; hook != nil { hook(sc) } + + if opts.UpgradeRequest != nil { + sc.upgradeRequest(opts.UpgradeRequest) + opts.UpgradeRequest = nil + } + sc.serve() } @@ -512,6 +545,7 @@ type serverConn struct { // Everything following is owned by the serve loop; use serveG.check(): serveG goroutineLock // used to verify funcs are on serve() pushEnabled bool + sawClientPreface bool // preface has already been read, used in h2c upgrade sawFirstSettings bool // got the initial SETTINGS frame after the preface needToSendSettingsAck bool unackedSettings int // how many SETTINGS have we sent without ACKs? @@ -974,6 +1008,9 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface") // returns errPrefaceTimeout on timeout, or an error if the greeting // is invalid. func (sc *serverConn) readPreface() error { + if sc.sawClientPreface { + return nil + } errc := make(chan error, 1) go func() { // Read the client preface @@ -1915,6 +1952,26 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { return nil } +func (sc *serverConn) upgradeRequest(req *http.Request) { + sc.serveG.check() + id := uint32(1) + sc.maxClientStreamID = id + st := sc.newStream(id, 0, stateHalfClosedRemote) + st.reqTrailer = req.Trailer + if st.reqTrailer != nil { + st.trailer = make(http.Header) + } + rw := sc.newResponseWriter(st, req) + + // Disable any read deadline set by the net/http package + // prior to the upgrade. + if sc.hs.ReadTimeout != 0 { + sc.conn.SetReadDeadline(time.Time{}) + } + + go sc.runHandler(rw, req, sc.handler.ServeHTTP) +} + func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error { sc := st.sc sc.serveG.check() @@ -2145,6 +2202,11 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r } req = req.WithContext(st.ctx) + rw := sc.newResponseWriter(st, req) + return rw, req, nil +} + +func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *responseWriter { rws := responseWriterStatePool.Get().(*responseWriterState) bwSave := rws.bw *rws = responseWriterState{} // zero all the fields @@ -2153,10 +2215,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r rws.bw.Reset(chunkWriter{rws}) rws.stream = st rws.req = req - rws.body = body - - rw := &responseWriter{rws: rws} - return rw, req, nil + return &responseWriter{rws: rws} } // Run on its own goroutine. @@ -2316,17 +2375,18 @@ type requestBody struct { _ incomparable stream *stream conn *serverConn - closed bool // for use by Close only - sawEOF bool // for use by Read only - pipe *pipe // non-nil if we have a HTTP entity message body - needsContinue bool // need to send a 100-continue + closeOnce sync.Once // for use by Close only + sawEOF bool // for use by Read only + pipe *pipe // non-nil if we have a HTTP entity message body + needsContinue bool // need to send a 100-continue } func (b *requestBody) Close() error { - if b.pipe != nil && !b.closed { - b.pipe.BreakWithError(errClosedBody) - } - b.closed = true + b.closeOnce.Do(func() { + if b.pipe != nil { + b.pipe.BreakWithError(errClosedBody) + } + }) return nil } @@ -2370,7 +2430,6 @@ type responseWriterState struct { // immutable within a request: stream *stream req *http.Request - body *requestBody // to close at end of request, if DATA frames didn't conn *serverConn // TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc @@ -2546,8 +2605,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { // prior to the headers being written. If the set of trailers is fixed // or known before the header is written, the normal Go trailers mechanism // is preferred: -// https://golang.org/pkg/net/http/#ResponseWriter -// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers +// +// https://golang.org/pkg/net/http/#ResponseWriter +// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers const TrailerPrefix = "Trailer:" // promoteUndeclaredTrailers permits http.Handlers to set trailers @@ -2643,8 +2703,7 @@ func checkWriteHeaderCode(code int) { // Issue 22880: require valid WriteHeader status codes. // For now we only enforce that it's three digits. // In the future we might block things over 599 (600 and above aren't defined - // at http://httpwg.org/specs/rfc7231.html#status.codes) - // and we might block under 200 (once we have more mature 1xx support). + // at http://httpwg.org/specs/rfc7231.html#status.codes). // But for now any three digits. // // We used to send "HTTP/1.1 000 0" on the wire in responses but there's @@ -2665,13 +2724,41 @@ func (w *responseWriter) WriteHeader(code int) { } func (rws *responseWriterState) writeHeader(code int) { - if !rws.wroteHeader { - checkWriteHeaderCode(code) - rws.wroteHeader = true - rws.status = code - if len(rws.handlerHeader) > 0 { - rws.snapHeader = cloneHeader(rws.handlerHeader) + if rws.wroteHeader { + return + } + + checkWriteHeaderCode(code) + + // Handle informational headers + if code >= 100 && code <= 199 { + // Per RFC 8297 we must not clear the current header map + h := rws.handlerHeader + + _, cl := h["Content-Length"] + _, te := h["Transfer-Encoding"] + if cl || te { + h = h.Clone() + h.Del("Content-Length") + h.Del("Transfer-Encoding") } + + if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ + streamID: rws.stream.id, + httpResCode: code, + h: h, + endStream: rws.handlerDone && !rws.hasTrailers(), + }) != nil { + rws.dirty = true + } + + return + } + + rws.wroteHeader = true + rws.status = code + if len(rws.handlerHeader) > 0 { + rws.snapHeader = cloneHeader(rws.handlerHeader) } } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index f135b0f7..4ded4dfd 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -16,7 +16,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "math" mathrand "math/rand" @@ -501,12 +500,14 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res if req, err = shouldRetryRequest(req, err); err == nil { // After the first retry, do exponential backoff with 10% jitter. if retry == 0 { + t.vlogf("RoundTrip retrying after failure: %v", err) continue } backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) select { case <-time.After(time.Second * time.Duration(backoff)): + t.vlogf("RoundTrip retrying after failure: %v", err) continue case <-req.Context().Done(): err = req.Context().Err() @@ -732,11 +733,13 @@ func (cc *ClientConn) healthCheck() { // trigger the healthCheck again if there is no frame received. ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) defer cancel() + cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) if err != nil { + cc.vlogf("http2: Transport health check failure: %v", err) cc.closeForLostPing() - cc.t.connPool().MarkDead(cc) - return + } else { + cc.vlogf("http2: Transport health check success") } } @@ -907,6 +910,24 @@ func (cc *ClientConn) onIdleTimeout() { cc.closeIfIdle() } +func (cc *ClientConn) closeConn() error { + t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn) + defer t.Stop() + return cc.tconn.Close() +} + +// A tls.Conn.Close can hang for a long time if the peer is unresponsive. +// Try to shut it down more aggressively. +func (cc *ClientConn) forceCloseConn() { + tc, ok := cc.tconn.(*tls.Conn) + if !ok { + return + } + if nc := tlsUnderlyingConn(tc); nc != nil { + nc.Close() + } +} + func (cc *ClientConn) closeIfIdle() { cc.mu.Lock() if len(cc.streams) > 0 || cc.streamsReserved > 0 { @@ -921,7 +942,7 @@ func (cc *ClientConn) closeIfIdle() { if VerboseLogs { cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2) } - cc.tconn.Close() + cc.closeConn() } func (cc *ClientConn) isDoNotReuseAndIdle() bool { @@ -938,7 +959,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { return err } // Wait for all in-flight streams to complete or connection to close - done := make(chan error, 1) + done := make(chan struct{}) cancelled := false // guarded by cc.mu go func() { cc.mu.Lock() @@ -946,7 +967,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { for { if len(cc.streams) == 0 || cc.closed { cc.closed = true - done <- cc.tconn.Close() + close(done) break } if cancelled { @@ -957,8 +978,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error { }() shutdownEnterWaitStateHook() select { - case err := <-done: - return err + case <-done: + return cc.closeConn() case <-ctx.Done(): cc.mu.Lock() // Free the goroutine above @@ -1001,9 +1022,9 @@ func (cc *ClientConn) closeForError(err error) error { for _, cs := range cc.streams { cs.abortStreamLocked(err) } - defer cc.cond.Broadcast() - defer cc.mu.Unlock() - return cc.tconn.Close() + cc.cond.Broadcast() + cc.mu.Unlock() + return cc.closeConn() } // Close closes the client connection immediately. @@ -1748,7 +1769,8 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { - return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) + // Don't include the value in the error, because it may be sensitive. + return nil, fmt.Errorf("invalid HTTP header value for header %q", k) } } } @@ -1978,7 +2000,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) { cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2) } cc.closed = true - defer cc.tconn.Close() + defer cc.closeConn() } cc.mu.Unlock() @@ -2025,8 +2047,8 @@ func isEOFOrNetReadError(err error) bool { func (rl *clientConnReadLoop) cleanup() { cc := rl.cc - defer cc.tconn.Close() - defer cc.t.connPool().MarkDead(cc) + cc.t.connPool().MarkDead(cc) + defer cc.closeConn() defer close(cc.readerDone) if cc.idleTimer != nil { @@ -2881,7 +2903,12 @@ func (t *Transport) logf(format string, args ...interface{}) { log.Printf(format, args...) } -var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil)) +var noBody io.ReadCloser = noBodyReader{} + +type noBodyReader struct{} + +func (noBodyReader) Close() error { return nil } +func (noBodyReader) Read([]byte) (int, error) { return 0, io.EOF } type missingBody struct{} diff --git a/vendor/golang.org/x/net/http2/writesched_priority.go b/vendor/golang.org/x/net/http2/writesched_priority.go index 2618b2c1..0a242c66 100644 --- a/vendor/golang.org/x/net/http2/writesched_priority.go +++ b/vendor/golang.org/x/net/http2/writesched_priority.go @@ -383,16 +383,15 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) { var n *priorityNode - if id := wr.StreamID(); id == 0 { + if wr.isControl() { n = &ws.root } else { + id := wr.StreamID() n = ws.nodes[id] if n == nil { // id is an idle or closed stream. wr should not be a HEADERS or - // DATA frame. However, wr can be a RST_STREAM. In this case, we - // push wr onto the root, rather than creating a new priorityNode, - // since RST_STREAM is tiny and the stream's priority is unknown - // anyway. See issue #17919. + // DATA frame. In other case, we push wr onto the root, rather + // than creating a new priorityNode. if wr.DataSize() > 0 { panic("add DATA on non-open stream") } diff --git a/vendor/golang.org/x/net/idna/trieval.go b/vendor/golang.org/x/net/idna/trieval.go index 7a8cf889..9c070a44 100644 --- a/vendor/golang.org/x/net/idna/trieval.go +++ b/vendor/golang.org/x/net/idna/trieval.go @@ -17,23 +17,23 @@ package idna // // The per-rune values have the following format: // -// if mapped { -// if inlinedXOR { -// 15..13 inline XOR marker -// 12..11 unused -// 10..3 inline XOR mask -// } else { -// 15..3 index into xor or mapping table -// } -// } else { -// 15..14 unused -// 13 mayNeedNorm -// 12..11 attributes -// 10..8 joining type -// 7..3 category type -// } -// 2 use xor pattern -// 1..0 mapped category +// if mapped { +// if inlinedXOR { +// 15..13 inline XOR marker +// 12..11 unused +// 10..3 inline XOR mask +// } else { +// 15..3 index into xor or mapping table +// } +// } else { +// 15..14 unused +// 13 mayNeedNorm +// 12..11 attributes +// 10..8 joining type +// 7..3 category type +// } +// 2 use xor pattern +// 1..0 mapped category // // See the definitions below for a more detailed description of the various // bits. diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go index aa1b0620..19d46789 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_zos_s390x.go index 98be146b..68dc8ad6 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_zos_s390x.go @@ -4,22 +4,8 @@ package socket -import "syscall" - func (h *cmsghdr) set(l, lvl, typ int) { h.Len = int32(l) h.Level = int32(lvl) h.Type = int32(typ) } - -func controlHeaderLen() int { - return syscall.CmsgLen(0) -} - -func controlMessageLen(dataLen int) int { - return syscall.CmsgLen(dataLen) -} - -func controlMessageSpace(dataLen int) int { - return syscall.CmsgSpace(dataLen) -} diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go index 40ebedab..0bfcf7af 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go @@ -9,7 +9,9 @@ package socket import ( "net" + "os" "sync" + "syscall" ) type mmsghdrs []mmsghdr @@ -93,22 +95,86 @@ func (p *mmsghdrsPacker) pack(ms []Message, parseFn func([]byte, string) (net.Ad return hs } -var defaultMmsghdrsPool = mmsghdrsPool{ +// syscaller is a helper to invoke recvmmsg and sendmmsg via the RawConn.Read/Write interface. +// It is reusable, to amortize the overhead of allocating a closure for the function passed to +// RawConn.Read/Write. +type syscaller struct { + n int + operr error + hs mmsghdrs + flags int + + boundRecvmmsgF func(uintptr) bool + boundSendmmsgF func(uintptr) bool +} + +func (r *syscaller) init() { + r.boundRecvmmsgF = r.recvmmsgF + r.boundSendmmsgF = r.sendmmsgF +} + +func (r *syscaller) recvmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) { + r.n = 0 + r.operr = nil + r.hs = hs + r.flags = flags + if err := c.Read(r.boundRecvmmsgF); err != nil { + return r.n, err + } + if r.operr != nil { + return r.n, os.NewSyscallError("recvmmsg", r.operr) + } + return r.n, nil +} + +func (r *syscaller) recvmmsgF(s uintptr) bool { + r.n, r.operr = recvmmsg(s, r.hs, r.flags) + return ioComplete(r.flags, r.operr) +} + +func (r *syscaller) sendmmsg(c syscall.RawConn, hs mmsghdrs, flags int) (int, error) { + r.n = 0 + r.operr = nil + r.hs = hs + r.flags = flags + if err := c.Write(r.boundSendmmsgF); err != nil { + return r.n, err + } + if r.operr != nil { + return r.n, os.NewSyscallError("sendmmsg", r.operr) + } + return r.n, nil +} + +func (r *syscaller) sendmmsgF(s uintptr) bool { + r.n, r.operr = sendmmsg(s, r.hs, r.flags) + return ioComplete(r.flags, r.operr) +} + +// mmsgTmps holds reusable temporary helpers for recvmmsg and sendmmsg. +type mmsgTmps struct { + packer mmsghdrsPacker + syscaller syscaller +} + +var defaultMmsgTmpsPool = mmsgTmpsPool{ p: sync.Pool{ New: func() interface{} { - return new(mmsghdrsPacker) + tmps := new(mmsgTmps) + tmps.syscaller.init() + return tmps }, }, } -type mmsghdrsPool struct { +type mmsgTmpsPool struct { p sync.Pool } -func (p *mmsghdrsPool) Get() *mmsghdrsPacker { - return p.p.Get().(*mmsghdrsPacker) +func (p *mmsgTmpsPool) Get() *mmsgTmps { + return p.p.Get().(*mmsgTmps) } -func (p *mmsghdrsPool) Put(packer *mmsghdrsPacker) { - p.p.Put(packer) +func (p *mmsgTmpsPool) Put(tmps *mmsgTmps) { + p.p.Put(tmps) } diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go index 3fcb51b3..8f79b38f 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go @@ -9,32 +9,23 @@ package socket import ( "net" - "os" ) func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { for i := range ms { ms[i].raceWrite() } - packer := defaultMmsghdrsPool.Get() - defer defaultMmsghdrsPool.Put(packer) + tmps := defaultMmsgTmpsPool.Get() + defer defaultMmsgTmpsPool.Put(tmps) var parseFn func([]byte, string) (net.Addr, error) if c.network != "tcp" { parseFn = parseInetAddr } - hs := packer.pack(ms, parseFn, nil) - var operr error - var n int - fn := func(s uintptr) bool { - n, operr = recvmmsg(s, hs, flags) - return ioComplete(flags, operr) - } - if err := c.c.Read(fn); err != nil { + hs := tmps.packer.pack(ms, parseFn, nil) + n, err := tmps.syscaller.recvmmsg(c.c, hs, flags) + if err != nil { return n, err } - if operr != nil { - return n, os.NewSyscallError("recvmmsg", operr) - } if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil { return n, err } @@ -45,25 +36,17 @@ func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { for i := range ms { ms[i].raceRead() } - packer := defaultMmsghdrsPool.Get() - defer defaultMmsghdrsPool.Put(packer) + tmps := defaultMmsgTmpsPool.Get() + defer defaultMmsgTmpsPool.Put(tmps) var marshalFn func(net.Addr, []byte) int if c.network != "tcp" { marshalFn = marshalInetAddr } - hs := packer.pack(ms, nil, marshalFn) - var operr error - var n int - fn := func(s uintptr) bool { - n, operr = sendmmsg(s, hs, flags) - return ioComplete(flags, operr) - } - if err := c.c.Write(fn); err != nil { + hs := tmps.packer.pack(ms, nil, marshalFn) + n, err := tmps.syscaller.sendmmsg(c.c, hs, flags) + if err != nil { return n, err } - if operr != nil { - return n, os.NewSyscallError("sendmmsg", operr) - } if err := hs[:n].unpack(ms[:n], nil, ""); err != nil { return n, err } diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc.go index 59b71da5..4c19269b 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc.go @@ -4,32 +4,32 @@ package socket type iovec struct { - Base *byte - Len uint32 + Base *byte + Len uint32 } type msghdr struct { - Name *byte - Namelen uint32 - Iov *iovec - Iovlen uint32 - Control *byte - Controllen uint32 - Flags int32 + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 } type mmsghdr struct { - Hdr msghdr - Len uint32 + Hdr msghdr + Len uint32 } type cmsghdr struct { - Len uint32 - Level int32 - Type int32 + Len uint32 + Level int32 + Type int32 } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c ) diff --git a/vendor/golang.org/x/net/ipv4/doc.go b/vendor/golang.org/x/net/ipv4/doc.go index 24583497..6fbdc52b 100644 --- a/vendor/golang.org/x/net/ipv4/doc.go +++ b/vendor/golang.org/x/net/ipv4/doc.go @@ -16,8 +16,7 @@ // 3376. // Source-specific multicast is defined in RFC 4607. // -// -// Unicasting +// # Unicasting // // The options for unicasting are available for net.TCPConn, // net.UDPConn and net.IPConn which are created as network connections @@ -51,8 +50,7 @@ // }(c) // } // -// -// Multicasting +// # Multicasting // // The options for multicasting are available for net.UDPConn and // net.IPConn which are created as network connections that use the @@ -141,8 +139,7 @@ // } // } // -// -// More multicasting +// # More multicasting // // An application that uses PacketConn or RawConn may join multiple // multicast groups. For example, a UDP listener with port 1024 might @@ -200,8 +197,7 @@ // // error handling // } // -// -// Source-specific multicasting +// # Source-specific multicasting // // An application that uses PacketConn or RawConn on IGMPv3 supported // platform is able to join source-specific multicast groups. diff --git a/vendor/golang.org/x/net/ipv6/doc.go b/vendor/golang.org/x/net/ipv6/doc.go index e0be9d50..2148b814 100644 --- a/vendor/golang.org/x/net/ipv6/doc.go +++ b/vendor/golang.org/x/net/ipv6/doc.go @@ -17,8 +17,7 @@ // On Darwin, this package requires OS X Mavericks version 10.9 or // above, or equivalent. // -// -// Unicasting +// # Unicasting // // The options for unicasting are available for net.TCPConn, // net.UDPConn and net.IPConn which are created as network connections @@ -52,8 +51,7 @@ // }(c) // } // -// -// Multicasting +// # Multicasting // // The options for multicasting are available for net.UDPConn and // net.IPConn which are created as network connections that use the @@ -140,8 +138,7 @@ // } // } // -// -// More multicasting +// # More multicasting // // An application that uses PacketConn may join multiple multicast // groups. For example, a UDP listener with port 1024 might join two @@ -199,8 +196,7 @@ // // error handling // } // -// -// Source-specific multicasting +// # Source-specific multicasting // // An application that uses PacketConn on MLDv2 supported platform is // able to join source-specific multicast groups. diff --git a/vendor/golang.org/x/net/websocket/websocket.go b/vendor/golang.org/x/net/websocket/websocket.go index 6c45c735..ea422e11 100644 --- a/vendor/golang.org/x/net/websocket/websocket.go +++ b/vendor/golang.org/x/net/websocket/websocket.go @@ -8,8 +8,8 @@ // This package currently lacks some features found in alternative // and more actively maintained WebSocket packages: // -// https://godoc.org/github.com/gorilla/websocket -// https://godoc.org/nhooyr.io/websocket +// https://godoc.org/github.com/gorilla/websocket +// https://godoc.org/nhooyr.io/websocket package websocket // import "golang.org/x/net/websocket" import ( @@ -416,7 +416,6 @@ Trivial usage: // send binary frame data = []byte{0, 1, 2} websocket.Message.Send(ws, data) - */ var Message = Codec{marshal, unmarshal} diff --git a/vendor/golang.org/x/sys/cpu/byteorder.go b/vendor/golang.org/x/sys/cpu/byteorder.go index dcbb14ef..271055be 100644 --- a/vendor/golang.org/x/sys/cpu/byteorder.go +++ b/vendor/golang.org/x/sys/cpu/byteorder.go @@ -46,6 +46,7 @@ func hostByteOrder() byteOrder { case "386", "amd64", "amd64p32", "alpha", "arm", "arm64", + "loong64", "mipsle", "mips64le", "mips64p32le", "nios2", "ppc64le", diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go index b56886f2..83f112c4 100644 --- a/vendor/golang.org/x/sys/cpu/cpu.go +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -106,8 +106,8 @@ var ARM64 struct { // ARM contains the supported CPU features of the current ARM (32-bit) platform. // All feature flags are false if: -// 1. the current platform is not arm, or -// 2. the current operating system is not Linux. +// 1. the current platform is not arm, or +// 2. the current operating system is not Linux. var ARM struct { _ CacheLinePad HasSWP bool // SWP instruction support diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_loong64.go new file mode 100644 index 00000000..0f57b05b --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 +// +build loong64 + +package cpu + +const cacheLineSize = 64 + +func initOptions() { +} diff --git a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go index a864f24d..96134157 100644 --- a/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go +++ b/vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go @@ -5,7 +5,7 @@ // Recreate a getsystemcfg syscall handler instead of // using the one provided by x/sys/unix to avoid having // the dependency between them. (See golang.org/issue/32102) -// Morever, this file will be used during the building of +// Moreover, this file will be used during the building of // gccgo's libgo and thus must not used a CGo method. //go:build aix && gccgo diff --git a/vendor/golang.org/x/sys/execabs/execabs.go b/vendor/golang.org/x/sys/execabs/execabs.go index 78192498..b981cfbb 100644 --- a/vendor/golang.org/x/sys/execabs/execabs.go +++ b/vendor/golang.org/x/sys/execabs/execabs.go @@ -53,7 +53,7 @@ func relError(file, path string) error { // LookPath instead returns an error. func LookPath(file string) (string, error) { path, err := exec.LookPath(file) - if err != nil { + if err != nil && !isGo119ErrDot(err) { return "", err } if filepath.Base(file) == file && !filepath.IsAbs(path) { diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go new file mode 100644 index 00000000..6ab5f508 --- /dev/null +++ b/vendor/golang.org/x/sys/execabs/execabs_go118.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.19 +// +build !go1.19 + +package execabs + +func isGo119ErrDot(err error) bool { + return false +} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go new file mode 100644 index 00000000..1e7a9ada --- /dev/null +++ b/vendor/golang.org/x/sys/execabs/execabs_go119.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.19 +// +build go1.19 + +package execabs + +import "strings" + +func isGo119ErrDot(err error) bool { + // TODO: return errors.Is(err, exec.ErrDot) + return strings.Contains(err.Error(), "current directory") +} diff --git a/vendor/golang.org/x/sys/plan9/syscall.go b/vendor/golang.org/x/sys/plan9/syscall.go index 602473cb..a25223b8 100644 --- a/vendor/golang.org/x/sys/plan9/syscall.go +++ b/vendor/golang.org/x/sys/plan9/syscall.go @@ -113,5 +113,6 @@ func (tv *Timeval) Nano() int64 { // use is a no-op, but the compiler cannot see that it is. // Calling use(p) ensures that p is kept live until that point. +// //go:noescape func use(p unsafe.Pointer) diff --git a/vendor/golang.org/x/sys/plan9/syscall_plan9.go b/vendor/golang.org/x/sys/plan9/syscall_plan9.go index 723b1f40..d079d811 100644 --- a/vendor/golang.org/x/sys/plan9/syscall_plan9.go +++ b/vendor/golang.org/x/sys/plan9/syscall_plan9.go @@ -115,6 +115,7 @@ func Write(fd int, p []byte) (n int, err error) { var ioSync int64 //sys fd2path(fd int, buf []byte) (err error) + func Fd2path(fd int) (path string, err error) { var buf [512]byte @@ -126,6 +127,7 @@ func Fd2path(fd int) (path string, err error) { } //sys pipe(p *[2]int32) (err error) + func Pipe(p []int) (err error) { if len(p) != 2 { return syscall.ErrorString("bad arg in system call") @@ -180,6 +182,7 @@ func (w Waitmsg) ExitStatus() int { } //sys await(s []byte) (n int, err error) + func Await(w *Waitmsg) (err error) { var buf [512]byte var f [5][]byte @@ -301,42 +304,49 @@ func Getgroups() (gids []int, err error) { } //sys open(path string, mode int) (fd int, err error) + func Open(path string, mode int) (fd int, err error) { fixwd() return open(path, mode) } //sys create(path string, mode int, perm uint32) (fd int, err error) + func Create(path string, mode int, perm uint32) (fd int, err error) { fixwd() return create(path, mode, perm) } //sys remove(path string) (err error) + func Remove(path string) error { fixwd() return remove(path) } //sys stat(path string, edir []byte) (n int, err error) + func Stat(path string, edir []byte) (n int, err error) { fixwd() return stat(path, edir) } //sys bind(name string, old string, flag int) (err error) + func Bind(name string, old string, flag int) (err error) { fixwd() return bind(name, old, flag) } //sys mount(fd int, afd int, old string, flag int, aname string) (err error) + func Mount(fd int, afd int, old string, flag int, aname string) (err error) { fixwd() return mount(fd, afd, old, flag, aname) } //sys wstat(path string, edir []byte) (err error) + func Wstat(path string, edir []byte) (err error) { fixwd() return wstat(path, edir) diff --git a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s new file mode 100644 index 00000000..6abd48ee --- /dev/null +++ b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s @@ -0,0 +1,54 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && loong64 && gc +// +build linux +// +build loong64 +// +build gc + +#include "textflag.h" + + +// Just jump to package syscall's implementation for all these functions. +// The runtime may know about them. + +TEXT ·Syscall(SB),NOSPLIT,$0-56 + JMP syscall·Syscall(SB) + +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + JMP syscall·Syscall6(SB) + +TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 + JAL runtime·entersyscall(SB) + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV a3+24(FP), R6 + MOVV R0, R7 + MOVV R0, R8 + MOVV R0, R9 + MOVV trap+0(FP), R11 // syscall entry + SYSCALL + MOVV R4, r1+32(FP) + MOVV R5, r2+40(FP) + JAL runtime·exitsyscall(SB) + RET + +TEXT ·RawSyscall(SB),NOSPLIT,$0-56 + JMP syscall·RawSyscall(SB) + +TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 + JMP syscall·RawSyscall6(SB) + +TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 + MOVV a1+8(FP), R4 + MOVV a2+16(FP), R5 + MOVV a3+24(FP), R6 + MOVV R0, R7 + MOVV R0, R8 + MOVV R0, R9 + MOVV trap+0(FP), R11 // syscall entry + SYSCALL + MOVV R4, r1+32(FP) + MOVV R5, r2+40(FP) + RET diff --git a/vendor/golang.org/x/sys/unix/endian_little.go b/vendor/golang.org/x/sys/unix/endian_little.go index 4362f47e..b0f2bc4a 100644 --- a/vendor/golang.org/x/sys/unix/endian_little.go +++ b/vendor/golang.org/x/sys/unix/endian_little.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh -// +build 386 amd64 amd64p32 alpha arm arm64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh +// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh package unix diff --git a/vendor/golang.org/x/sys/unix/ifreq_linux.go b/vendor/golang.org/x/sys/unix/ifreq_linux.go index 934af313..15721a51 100644 --- a/vendor/golang.org/x/sys/unix/ifreq_linux.go +++ b/vendor/golang.org/x/sys/unix/ifreq_linux.go @@ -8,7 +8,6 @@ package unix import ( - "bytes" "unsafe" ) @@ -45,13 +44,7 @@ func NewIfreq(name string) (*Ifreq, error) { // Name returns the interface name associated with the Ifreq. func (ifr *Ifreq) Name() string { - // BytePtrToString requires a NULL terminator or the program may crash. If - // one is not present, just return the empty string. - if !bytes.Contains(ifr.raw.Ifrn[:], []byte{0x00}) { - return "" - } - - return BytePtrToString(&ifr.raw.Ifrn[0]) + return ByteSliceToString(ifr.raw.Ifrn[:]) } // According to netdevice(7), only AF_INET addresses are returned for numerous diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index 1dadead2..884430b8 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -194,3 +194,26 @@ func ioctlIfreqData(fd int, req uint, value *ifreqData) error { // identical so pass *IfreqData directly. return ioctlPtr(fd, req, unsafe.Pointer(value)) } + +// IoctlKCMClone attaches a new file descriptor to a multiplexor by cloning an +// existing KCM socket, returning a structure containing the file descriptor of +// the new socket. +func IoctlKCMClone(fd int) (*KCMClone, error) { + var info KCMClone + if err := ioctlPtr(fd, SIOCKCMCLONE, unsafe.Pointer(&info)); err != nil { + return nil, err + } + + return &info, nil +} + +// IoctlKCMAttach attaches a TCP socket and associated BPF program file +// descriptor to a multiplexor. +func IoctlKCMAttach(fd int, info KCMAttach) error { + return ioctlPtr(fd, SIOCKCMATTACH, unsafe.Pointer(&info)) +} + +// IoctlKCMUnattach unattaches a TCP socket file descriptor from a multiplexor. +func IoctlKCMUnattach(fd int, info KCMUnattach) error { + return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info)) +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index a47b035f..d888fb77 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -205,6 +205,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -214,6 +215,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -231,6 +233,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -503,6 +506,7 @@ ccflags="$@" $2 ~ /^O?XTABS$/ || $2 ~ /^TC[IO](ON|OFF)$/ || $2 ~ /^IN_/ || + $2 ~ /^KCM/ || $2 ~ /^LANDLOCK_/ || $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || @@ -597,8 +601,10 @@ ccflags="$@" $2 ~ /^DEVLINK_/ || $2 ~ /^ETHTOOL_/ || $2 ~ /^LWTUNNEL_IP/ || + $2 ~ /^ITIMER_/ || $2 !~ "WMESGLEN" && $2 ~ /^W[A-Z0-9]+$/ || + $2 ~ /^P_/ || $2 ~/^PPPIOC/ || $2 ~ /^FAN_|FANOTIFY_/ || $2 == "HID_MAX_DESCRIPTOR_SIZE" || @@ -608,6 +614,7 @@ ccflags="$@" $2 ~ /^OTP/ || $2 ~ /^MEM/ || $2 ~ /^WG/ || + $2 ~ /^FIB_RULE_/ || $2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)} $2 ~ /^__WCOREFLAG$/ {next} $2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)} diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index 4f55c8d9..ad22c33d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -37,6 +37,7 @@ func Creat(path string, mode uint32) (fd int, err error) { } //sys utimes(path string, times *[2]Timeval) (err error) + func Utimes(path string, tv []Timeval) error { if len(tv) != 2 { return EINVAL @@ -45,6 +46,7 @@ func Utimes(path string, tv []Timeval) error { } //sys utimensat(dirfd int, path string, times *[2]Timespec, flag int) (err error) + func UtimesNano(path string, ts []Timespec) error { if len(ts) != 2 { return EINVAL @@ -215,18 +217,12 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { return } -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { // Recvmsg not implemented on AIX - sa := new(SockaddrUnix) - return -1, -1, -1, sa, ENOSYS + return -1, -1, -1, ENOSYS } -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) - return -} - -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { // SendmsgN not implemented on AIX return -1, ENOSYS } @@ -306,11 +302,13 @@ func direntNamlen(buf []byte) (uint64, bool) { } //sys getdirent(fd int, buf []byte) (n int, err error) + func Getdents(fd int, buf []byte) (n int, err error) { return getdirent(fd, buf) } //sys wait4(pid Pid_t, status *_C_int, options int, rusage *Rusage) (wpid Pid_t, err error) + func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status _C_int var r Pid_t @@ -378,6 +376,7 @@ func (w WaitStatus) TrapCause() int { return -1 } //sys fcntl(fd int, cmd int, arg int) (val int, err error) //sys fsyncRange(fd int, how int, start int64, length int64) (err error) = fsync_range + func Fsync(fd int) error { return fsyncRange(fd, O_SYNC, 0, 0) } @@ -458,8 +457,8 @@ func Fsync(fd int) error { //sys Listen(s int, n int) (err error) //sys lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = pread64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = pwrite64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = pread64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = pwrite64 //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) //sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) //sysnb Setregid(rgid int, egid int) (err error) @@ -542,6 +541,7 @@ func Poll(fds []PollFd, timeout int) (n int, err error) { //sys Getsystemcfg(label int) (n uint64) //sys umount(target string) (err error) + func Unmount(target string, flags int) (err error) { if flags != 0 { // AIX doesn't have any flags for umount. diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index 0ce45232..9c87c5f0 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -325,10 +325,9 @@ func GetsockoptString(fd, level, opt int) (string, error) { //sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -352,29 +351,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } oobn = int(msg.Controllen) recvflags = int(msg.Flags) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(fd, &rsa) - } return } //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) - return -} - -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) @@ -571,12 +553,7 @@ func UtimesNano(path string, ts []Timespec) error { if len(ts) != 2 { return EINVAL } - // Darwin setattrlist can set nanosecond timestamps - err := setattrlistTimes(path, ts, 0) - if err != ENOSYS { - return err - } - err = utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) + err := utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) if err != ENOSYS { return err } @@ -596,10 +573,6 @@ func UtimesNanoAt(dirfd int, path string, ts []Timespec, flags int) error { if len(ts) != 2 { return EINVAL } - err := setattrlistTimes(path, ts, flags) - if err != ENOSYS { - return err - } return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), flags) } diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 0eaab913..e5448cc9 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -141,16 +141,6 @@ func direntNamlen(buf []byte) (uint64, bool) { func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) } func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) } -type attrList struct { - bitmapCount uint16 - _ uint16 - CommonAttr uint32 - VolAttr uint32 - DirAttr uint32 - FileAttr uint32 - Forkattr uint32 -} - //sysnb pipe(p *[2]int32) (err error) func Pipe(p []int) (err error) { @@ -282,36 +272,7 @@ func Flistxattr(fd int, dest []byte) (sz int, err error) { return flistxattr(fd, xattrPointer(dest), len(dest), 0) } -func setattrlistTimes(path string, times []Timespec, flags int) error { - _p0, err := BytePtrFromString(path) - if err != nil { - return err - } - - var attrList attrList - attrList.bitmapCount = ATTR_BIT_MAP_COUNT - attrList.CommonAttr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME - - // order is mtime, atime: the opposite of Chtimes - attributes := [2]Timespec{times[1], times[0]} - options := 0 - if flags&AT_SYMLINK_NOFOLLOW != 0 { - options |= FSOPT_NOFOLLOW - } - return setattrlist( - _p0, - unsafe.Pointer(&attrList), - unsafe.Pointer(&attributes), - unsafe.Sizeof(attributes), - options) -} - -//sys setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) - -func utimensat(dirfd int, path string, times *[2]Timespec, flags int) error { - // Darwin doesn't support SYS_UTIMENSAT - return ENOSYS -} +//sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) /* * Wrapped @@ -543,11 +504,12 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error) //sys Mknod(path string, mode uint32, dev int) (err error) +//sys Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error) @@ -611,7 +573,6 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { // Nfssvc // Getfh // Quotactl -// Mount // Csops // Waitid // Add_profil diff --git a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go index 2e37c316..61c0d0de 100644 --- a/vendor/golang.org/x/sys/unix/syscall_dragonfly.go +++ b/vendor/golang.org/x/sys/unix/syscall_dragonfly.go @@ -125,12 +125,14 @@ func Pipe2(p []int, flags int) (err error) { } //sys extpread(fd int, p []byte, flags int, offset int64) (n int, err error) -func Pread(fd int, p []byte, offset int64) (n int, err error) { + +func pread(fd int, p []byte, offset int64) (n int, err error) { return extpread(fd, p, 0, offset) } //sys extpwrite(fd int, p []byte, flags int, offset int64) (n int, err error) -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + +func pwrite(fd int, p []byte, offset int64) (n int, err error) { return extpwrite(fd, p, 0, offset) } @@ -169,11 +171,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec, flags int) error { - // used on Darwin for UtimesNano - return ENOSYS -} - //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go index 2f650ae6..6f6c510f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -194,11 +194,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec, flags int) error { - // used on Darwin for UtimesNano - return ENOSYS -} - //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL @@ -638,8 +633,8 @@ func PtraceSingleStep(pid int) (err error) { //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index f432b068..c8d20321 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -14,6 +14,7 @@ package unix import ( "encoding/binary" "syscall" + "time" "unsafe" ) @@ -249,6 +250,13 @@ func Getwd() (wd string, err error) { if n < 1 || n > len(buf) || buf[n-1] != 0 { return "", EINVAL } + // In some cases, Linux can return a path that starts with the + // "(unreachable)" prefix, which can potentially be a valid relative + // path. To work around that, return ENOENT if path is not absolute. + if buf[0] != '/' { + return "", ENOENT + } + return string(buf[0 : n-1]), nil } @@ -358,6 +366,8 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, return } +//sys Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error) + func Mkfifo(path string, mode uint32) error { return Mknod(path, mode|S_IFIFO, 0) } @@ -502,24 +512,24 @@ func (sa *SockaddrL2) sockaddr() (unsafe.Pointer, _Socklen, error) { // // Server example: // -// fd, _ := Socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM) -// _ = unix.Bind(fd, &unix.SockaddrRFCOMM{ -// Channel: 1, -// Addr: [6]uint8{0, 0, 0, 0, 0, 0}, // BDADDR_ANY or 00:00:00:00:00:00 -// }) -// _ = Listen(fd, 1) -// nfd, sa, _ := Accept(fd) -// fmt.Printf("conn addr=%v fd=%d", sa.(*unix.SockaddrRFCOMM).Addr, nfd) -// Read(nfd, buf) +// fd, _ := Socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM) +// _ = unix.Bind(fd, &unix.SockaddrRFCOMM{ +// Channel: 1, +// Addr: [6]uint8{0, 0, 0, 0, 0, 0}, // BDADDR_ANY or 00:00:00:00:00:00 +// }) +// _ = Listen(fd, 1) +// nfd, sa, _ := Accept(fd) +// fmt.Printf("conn addr=%v fd=%d", sa.(*unix.SockaddrRFCOMM).Addr, nfd) +// Read(nfd, buf) // // Client example: // -// fd, _ := Socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM) -// _ = Connect(fd, &SockaddrRFCOMM{ -// Channel: 1, -// Addr: [6]byte{0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc}, // CC:BB:AA:33:22:11 -// }) -// Write(fd, []byte(`hello`)) +// fd, _ := Socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM) +// _ = Connect(fd, &SockaddrRFCOMM{ +// Channel: 1, +// Addr: [6]byte{0x11, 0x22, 0x33, 0xaa, 0xbb, 0xcc}, // CC:BB:AA:33:22:11 +// }) +// Write(fd, []byte(`hello`)) type SockaddrRFCOMM struct { // Addr represents a bluetooth address, byte ordering is little-endian. Addr [6]uint8 @@ -546,12 +556,12 @@ func (sa *SockaddrRFCOMM) sockaddr() (unsafe.Pointer, _Socklen, error) { // The SockaddrCAN struct must be bound to the socket file descriptor // using Bind before the CAN socket can be used. // -// // Read one raw CAN frame -// fd, _ := Socket(AF_CAN, SOCK_RAW, CAN_RAW) -// addr := &SockaddrCAN{Ifindex: index} -// Bind(fd, addr) -// frame := make([]byte, 16) -// Read(fd, frame) +// // Read one raw CAN frame +// fd, _ := Socket(AF_CAN, SOCK_RAW, CAN_RAW) +// addr := &SockaddrCAN{Ifindex: index} +// Bind(fd, addr) +// frame := make([]byte, 16) +// Read(fd, frame) // // The full SocketCAN documentation can be found in the linux kernel // archives at: https://www.kernel.org/doc/Documentation/networking/can.txt @@ -622,13 +632,13 @@ func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) { // Here is an example of using an AF_ALG socket with SHA1 hashing. // The initial socket setup process is as follows: // -// // Open a socket to perform SHA1 hashing. -// fd, _ := unix.Socket(unix.AF_ALG, unix.SOCK_SEQPACKET, 0) -// addr := &unix.SockaddrALG{Type: "hash", Name: "sha1"} -// unix.Bind(fd, addr) -// // Note: unix.Accept does not work at this time; must invoke accept() -// // manually using unix.Syscall. -// hashfd, _, _ := unix.Syscall(unix.SYS_ACCEPT, uintptr(fd), 0, 0) +// // Open a socket to perform SHA1 hashing. +// fd, _ := unix.Socket(unix.AF_ALG, unix.SOCK_SEQPACKET, 0) +// addr := &unix.SockaddrALG{Type: "hash", Name: "sha1"} +// unix.Bind(fd, addr) +// // Note: unix.Accept does not work at this time; must invoke accept() +// // manually using unix.Syscall. +// hashfd, _, _ := unix.Syscall(unix.SYS_ACCEPT, uintptr(fd), 0, 0) // // Once a file descriptor has been returned from Accept, it may be used to // perform SHA1 hashing. The descriptor is not safe for concurrent use, but @@ -637,39 +647,39 @@ func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) { // When hashing a small byte slice or string, a single Write and Read may // be used: // -// // Assume hashfd is already configured using the setup process. -// hash := os.NewFile(hashfd, "sha1") -// // Hash an input string and read the results. Each Write discards -// // previous hash state. Read always reads the current state. -// b := make([]byte, 20) -// for i := 0; i < 2; i++ { -// io.WriteString(hash, "Hello, world.") -// hash.Read(b) -// fmt.Println(hex.EncodeToString(b)) -// } -// // Output: -// // 2ae01472317d1935a84797ec1983ae243fc6aa28 -// // 2ae01472317d1935a84797ec1983ae243fc6aa28 +// // Assume hashfd is already configured using the setup process. +// hash := os.NewFile(hashfd, "sha1") +// // Hash an input string and read the results. Each Write discards +// // previous hash state. Read always reads the current state. +// b := make([]byte, 20) +// for i := 0; i < 2; i++ { +// io.WriteString(hash, "Hello, world.") +// hash.Read(b) +// fmt.Println(hex.EncodeToString(b)) +// } +// // Output: +// // 2ae01472317d1935a84797ec1983ae243fc6aa28 +// // 2ae01472317d1935a84797ec1983ae243fc6aa28 // // For hashing larger byte slices, or byte streams such as those read from // a file or socket, use Sendto with MSG_MORE to instruct the kernel to update // the hash digest instead of creating a new one for a given chunk and finalizing it. // -// // Assume hashfd and addr are already configured using the setup process. -// hash := os.NewFile(hashfd, "sha1") -// // Hash the contents of a file. -// f, _ := os.Open("/tmp/linux-4.10-rc7.tar.xz") -// b := make([]byte, 4096) -// for { -// n, err := f.Read(b) -// if err == io.EOF { -// break -// } -// unix.Sendto(hashfd, b[:n], unix.MSG_MORE, addr) -// } -// hash.Read(b) -// fmt.Println(hex.EncodeToString(b)) -// // Output: 85cdcad0c06eef66f805ecce353bec9accbeecc5 +// // Assume hashfd and addr are already configured using the setup process. +// hash := os.NewFile(hashfd, "sha1") +// // Hash the contents of a file. +// f, _ := os.Open("/tmp/linux-4.10-rc7.tar.xz") +// b := make([]byte, 4096) +// for { +// n, err := f.Read(b) +// if err == io.EOF { +// break +// } +// unix.Sendto(hashfd, b[:n], unix.MSG_MORE, addr) +// } +// hash.Read(b) +// fmt.Println(hex.EncodeToString(b)) +// // Output: 85cdcad0c06eef66f805ecce353bec9accbeecc5 // // For more information, see: http://www.chronox.de/crypto-API/crypto/userspace-if.html. type SockaddrALG struct { @@ -1489,10 +1499,9 @@ func KeyctlRestrictKeyring(ringid int, keyType string, restriction string) error //sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL //sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -1523,28 +1532,10 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } oobn = int(msg.Controllen) recvflags = int(msg.Flags) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(fd, &rsa) - } return } -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) - return -} - -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - var err error - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(ptr) msg.Namelen = uint32(salen) @@ -1838,6 +1829,9 @@ func Dup2(oldfd, newfd int) error { //sys Fremovexattr(fd int, attr string) (err error) //sys Fsetxattr(fd int, attr string, dest []byte, flags int) (err error) //sys Fsync(fd int) (err error) +//sys Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) +//sys Fsopen(fsName string, flags int) (fd int, err error) +//sys Fspick(dirfd int, pathName string, flags int) (fd int, err error) //sys Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64 //sysnb Getpgid(pid int) (pgid int, err error) @@ -1868,7 +1862,9 @@ func Getpgrp() (pid int) { //sys MemfdCreate(name string, flags int) (fd int, err error) //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) +//sys MoveMount(fromDirfd int, fromPathName string, toDirfd int, toPathName string, flags int) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) +//sys OpenTree(dfd int, fileName string, flags uint) (r int, err error) //sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT //sysnb Prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64 @@ -2193,7 +2189,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { gid = Getgid() } - if uint32(gid) == st.Gid || isGroupMember(gid) { + if uint32(gid) == st.Gid || isGroupMember(int(st.Gid)) { fmode = (st.Mode >> 3) & 7 } else { fmode = st.Mode & 7 @@ -2308,17 +2304,63 @@ type RemoteIovec struct { //sys PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN //sys PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD +//sys PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) = SYS_PIDFD_SEND_SIGNAL //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) //sys shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error) //sys shmdt(addr uintptr) (err error) //sys shmget(key int, size int, flag int) (id int, err error) +//sys getitimer(which int, currValue *Itimerval) (err error) +//sys setitimer(which int, newValue *Itimerval, oldValue *Itimerval) (err error) + +// MakeItimerval creates an Itimerval from interval and value durations. +func MakeItimerval(interval, value time.Duration) Itimerval { + return Itimerval{ + Interval: NsecToTimeval(interval.Nanoseconds()), + Value: NsecToTimeval(value.Nanoseconds()), + } +} + +// A value which may be passed to the which parameter for Getitimer and +// Setitimer. +type ItimerWhich int + +// Possible which values for Getitimer and Setitimer. +const ( + ItimerReal ItimerWhich = ITIMER_REAL + ItimerVirtual ItimerWhich = ITIMER_VIRTUAL + ItimerProf ItimerWhich = ITIMER_PROF +) + +// Getitimer wraps getitimer(2) to return the current value of the timer +// specified by which. +func Getitimer(which ItimerWhich) (Itimerval, error) { + var it Itimerval + if err := getitimer(int(which), &it); err != nil { + return Itimerval{}, err + } + + return it, nil +} + +// Setitimer wraps setitimer(2) to arm or disarm the timer specified by which. +// It returns the previous value of the timer. +// +// If the Itimerval argument is the zero value, the timer will be disarmed. +func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) { + var prev Itimerval + if err := setitimer(int(which), &it, &prev); err != nil { + return Itimerval{}, err + } + + return prev, nil +} + /* * Unimplemented */ // AfsSyscall -// Alarm // ArchPrctl // Brk // ClockNanosleep @@ -2334,7 +2376,6 @@ type RemoteIovec struct { // GetMempolicy // GetRobustList // GetThreadArea -// Getitimer // Getpmsg // IoCancel // IoDestroy @@ -2412,5 +2453,4 @@ type RemoteIovec struct { // Vfork // Vhangup // Vserver -// Waitid // _Sysctl diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_386.go b/vendor/golang.org/x/sys/unix/syscall_linux_386.go index 5f757e8a..518e476e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_386.go @@ -35,8 +35,8 @@ func setTimeval(sec, usec int64) Timeval { //sys Iopl(level int) (err error) //sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32 //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 //sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32 @@ -173,14 +173,6 @@ const ( _SENDMMSG = 20 ) -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - fd, e := socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0) - if e != 0 { - err = e - } - return -} - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) if e != 0 { diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go new file mode 100644 index 00000000..08086ac6 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/syscall_linux_alarm.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && (386 || amd64 || mips || mipsle || mips64 || mipsle || ppc64 || ppc64le || ppc || s390x || sparc64) +// +build linux +// +build 386 amd64 mips mipsle mips64 mipsle ppc64 ppc64le ppc s390x sparc64 + +package unix + +// SYS_ALARM is not defined on arm or riscv, but is available for other GOARCH +// values. + +//sys Alarm(seconds uint) (remaining uint, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go index 4299125a..f5e9d6be 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_amd64.go @@ -28,9 +28,10 @@ func Lstat(path string, stat *Stat_t) (err error) { return Fstatat(AT_FDCWD, path, stat, AT_SYMLINK_NOFOLLOW) } +//sys MemfdSecret(flags int) (fd int, err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK @@ -62,7 +63,6 @@ func Stat(path string, stat *Stat_t) (err error) { //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go index 79edeb9c..c1a7778f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm.go @@ -27,7 +27,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { return newoffset, nil } -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) @@ -97,8 +96,8 @@ func Utime(path string, buf *Utimbuf) error { //sys utimes(path string, times *[2]Timeval) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Truncate(path string, length int64) (err error) = SYS_TRUNCATE64 //sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64 diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go index 862890de..d83e2c65 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go @@ -22,8 +22,9 @@ import "unsafe" //sysnb getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Listen(s int, n int) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys MemfdSecret(flags int) (fd int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK @@ -66,7 +67,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { return ENOSYS } -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go new file mode 100644 index 00000000..28ba7b8c --- /dev/null +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -0,0 +1,191 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 && linux +// +build loong64,linux + +package unix + +import "unsafe" + +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT +//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 +//sys Fchown(fd int, uid int, gid int) (err error) +//sys Fstat(fd int, stat *Stat_t) (err error) +//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) +//sys Fstatfs(fd int, buf *Statfs_t) (err error) +//sys Ftruncate(fd int, length int64) (err error) +//sysnb Getegid() (egid int) +//sysnb Geteuid() (euid int) +//sysnb Getgid() (gid int) +//sysnb Getuid() (uid int) +//sys Listen(s int, n int) (err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK + +func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) { + var ts *Timespec + if timeout != nil { + ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} + } + return Pselect(nfd, r, w, e, ts, nil) +} + +//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) +//sys setfsgid(gid int) (prev int, err error) +//sys setfsuid(uid int) (prev int, err error) +//sysnb Setregid(rgid int, egid int) (err error) +//sysnb Setresgid(rgid int, egid int, sgid int) (err error) +//sysnb Setresuid(ruid int, euid int, suid int) (err error) +//sysnb Setreuid(ruid int, euid int) (err error) +//sys Shutdown(fd int, how int) (err error) +//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) + +func Stat(path string, stat *Stat_t) (err error) { + return Fstatat(AT_FDCWD, path, stat, 0) +} + +func Lchown(path string, uid int, gid int) (err error) { + return Fchownat(AT_FDCWD, path, uid, gid, AT_SYMLINK_NOFOLLOW) +} + +func Lstat(path string, stat *Stat_t) (err error) { + return Fstatat(AT_FDCWD, path, stat, AT_SYMLINK_NOFOLLOW) +} + +//sys Statfs(path string, buf *Statfs_t) (err error) +//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) +//sys Truncate(path string, length int64) (err error) + +func Ustat(dev int, ubuf *Ustat_t) (err error) { + return ENOSYS +} + +//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) +//sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) +//sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) +//sysnb getgroups(n int, list *_Gid_t) (nn int, err error) +//sysnb setgroups(n int, list *_Gid_t) (err error) +//sys getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) +//sys setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) +//sysnb socket(domain int, typ int, proto int) (fd int, err error) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) +//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) +//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) +//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) +//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) +//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) +//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) + +//sysnb Gettimeofday(tv *Timeval) (err error) + +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} +} + +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} +} + +func Getrlimit(resource int, rlim *Rlimit) (err error) { + err = Prlimit(0, resource, nil, rlim) + return +} + +func Setrlimit(resource int, rlim *Rlimit) (err error) { + err = Prlimit(0, resource, rlim, nil) + return +} + +func futimesat(dirfd int, path string, tv *[2]Timeval) (err error) { + if tv == nil { + return utimensat(dirfd, path, nil, 0) + } + + ts := []Timespec{ + NsecToTimespec(TimevalToNsec(tv[0])), + NsecToTimespec(TimevalToNsec(tv[1])), + } + return utimensat(dirfd, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) +} + +func Time(t *Time_t) (Time_t, error) { + var tv Timeval + err := Gettimeofday(&tv) + if err != nil { + return 0, err + } + if t != nil { + *t = Time_t(tv.Sec) + } + return Time_t(tv.Sec), nil +} + +func Utime(path string, buf *Utimbuf) error { + tv := []Timeval{ + {Sec: buf.Actime}, + {Sec: buf.Modtime}, + } + return Utimes(path, tv) +} + +func utimes(path string, tv *[2]Timeval) (err error) { + if tv == nil { + return utimensat(AT_FDCWD, path, nil, 0) + } + + ts := []Timespec{ + NsecToTimespec(TimevalToNsec(tv[0])), + NsecToTimespec(TimevalToNsec(tv[1])), + } + return utimensat(AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) +} + +func (r *PtraceRegs) PC() uint64 { return r.Era } + +func (r *PtraceRegs) SetPC(era uint64) { r.Era = era } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint64(length) +} + +func (msghdr *Msghdr) SetIovlen(length int) { + msghdr.Iovlen = uint64(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint64(length) +} + +func (rsa *RawSockaddrNFCLLCP) SetServiceNameLen(length int) { + rsa.Service_name_len = uint64(length) +} + +func Pause() error { + _, err := ppoll(nil, 0, nil, nil) + return err +} + +func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) { + return Renameat2(olddirfd, oldpath, newdirfd, newpath, 0) +} + +//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error) + +func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error { + cmdlineLen := len(cmdline) + if cmdlineLen > 0 { + // Account for the additional NULL byte added by + // BytePtrFromString in kexecFileLoad. The kexec_file_load + // syscall expects a NULL-terminated string. + cmdlineLen++ + } + return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags) +} diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go index 8932e34a..98a2660b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go @@ -21,8 +21,8 @@ package unix //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK @@ -48,7 +48,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go index 7821c25d..b8a18c0a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go @@ -25,8 +25,8 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sysnb Getuid() (uid int) //sys Lchown(path string, uid int, gid int) (err error) //sys Listen(s int, n int) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 @@ -41,7 +41,6 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) = SYS_TRUNCATE64 //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go index c5053a0f..4ed9e67c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc.go @@ -27,8 +27,8 @@ import ( //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64 @@ -43,7 +43,6 @@ import ( //sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64 //sys Truncate(path string, length int64) (err error) = SYS_TRUNCATE64 //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go index 25786c42..db63d384 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go @@ -26,8 +26,8 @@ package unix //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT @@ -45,7 +45,6 @@ package unix //sys Statfs(path string, buf *Statfs_t) (err error) //sys Truncate(path string, length int64) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 6f9f7104..8ff7adba 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -22,8 +22,8 @@ import "unsafe" //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Listen(s int, n int) (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) { @@ -65,7 +65,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { return ENOSYS } -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go index 6aa59cb2..6fcf277b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_s390x.go @@ -26,8 +26,8 @@ import ( //sys Lchown(path string, uid int, gid int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) @@ -145,15 +145,6 @@ const ( netSendMMsg = 20 ) -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (int, error) { - args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))} - fd, _, err := Syscall(SYS_SOCKETCALL, netAccept, uintptr(unsafe.Pointer(&args)), 0) - if err != 0 { - return 0, err - } - return int(fd), nil -} - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (int, error) { args := [4]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags)} fd, _, err := Syscall(SYS_SOCKETCALL, netAccept4, uintptr(unsafe.Pointer(&args)), 0) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go index bbe8d174..02a45d9c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go @@ -23,8 +23,8 @@ package unix //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 +//sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK //sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) @@ -42,7 +42,6 @@ package unix //sys Statfs(path string, buf *Statfs_t) (err error) //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) //sys Truncate(path string, length int64) (err error) -//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) //sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) //sys bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) //sys connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go index 696fed49..666f0a1b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -163,11 +163,6 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e return -1, ENOSYS } -func setattrlistTimes(path string, times []Timespec, flags int) error { - // used on Darwin for UtimesNano - return ENOSYS -} - //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL @@ -313,8 +308,8 @@ func Statvfs(path string, buf *Statvfs_t) (err error) { //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 11b1d419..78daceb3 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -81,6 +81,7 @@ func Pipe(p []int) (err error) { } //sysnb pipe2(p *[2]_C_int, flags int) (err error) + func Pipe2(p []int, flags int) error { if len(p) != 2 { return EINVAL @@ -95,6 +96,7 @@ func Pipe2(p []int, flags int) error { } //sys Getdents(fd int, buf []byte) (n int, err error) + func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { n, err = Getdents(fd, buf) if err != nil || basep == nil { @@ -149,11 +151,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } -func setattrlistTimes(path string, times []Timespec, flags int) error { - // used on Darwin for UtimesNano - return ENOSYS -} - //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL @@ -274,8 +271,8 @@ func Uname(uname *Utsname) error { //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index 5c813921..5c2003ce 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -451,10 +451,9 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { +func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr - var rsa RawSockaddrAny - msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) var iov Iovec if len(p) > 0 { @@ -476,29 +475,12 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from return } oobn = int(msg.Accrightslen) - // source address is only specified if the socket is unconnected - if rsa.Addr.Family != AF_UNSPEC { - from, err = anyToSockaddr(fd, &rsa) - } - return -} - -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { - _, err = SendmsgN(fd, p, oob, to, flags) return } //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg -func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { - var ptr unsafe.Pointer - var salen _Socklen - if to != nil { - ptr, salen, err = to.sockaddr() - if err != nil { - return 0, err - } - } +func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) @@ -661,8 +643,8 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) //sys Pathconf(path string, name int) (val int, err error) //sys Pause() (err error) -//sys Pread(fd int, p []byte, offset int64) (n int, err error) -//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) +//sys pread(fd int, p []byte, offset int64) (n int, err error) +//sys pwrite(fd int, p []byte, offset int64) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error) //sys Rename(from string, to string) (err error) @@ -755,8 +737,20 @@ type fileObjCookie struct { type EventPort struct { port int mu sync.Mutex - fds map[uintptr]interface{} + fds map[uintptr]*fileObjCookie paths map[string]*fileObjCookie + // The user cookie presents an interesting challenge from a memory management perspective. + // There are two paths by which we can discover that it is no longer in use: + // 1. The user calls port_dissociate before any events fire + // 2. An event fires and we return it to the user + // The tricky situation is if the event has fired in the kernel but + // the user hasn't requested/received it yet. + // If the user wants to port_dissociate before the event has been processed, + // we should handle things gracefully. To do so, we need to keep an extra + // reference to the cookie around until the event is processed + // thus the otherwise seemingly extraneous "cookies" map + // The key of this map is a pointer to the corresponding &fCookie.cookie + cookies map[*interface{}]*fileObjCookie } // PortEvent is an abstraction of the port_event C struct. @@ -780,9 +774,10 @@ func NewEventPort() (*EventPort, error) { return nil, err } e := &EventPort{ - port: port, - fds: make(map[uintptr]interface{}), - paths: make(map[string]*fileObjCookie), + port: port, + fds: make(map[uintptr]*fileObjCookie), + paths: make(map[string]*fileObjCookie), + cookies: make(map[*interface{}]*fileObjCookie), } return e, nil } @@ -797,9 +792,13 @@ func NewEventPort() (*EventPort, error) { func (e *EventPort) Close() error { e.mu.Lock() defer e.mu.Unlock() + err := Close(e.port) + if err != nil { + return err + } e.fds = nil e.paths = nil - return Close(e.port) + return nil } // PathIsWatched checks to see if path is associated with this EventPort. @@ -836,6 +835,7 @@ func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, coo return err } e.paths[path] = fCookie + e.cookies[&fCookie.cookie] = fCookie return nil } @@ -848,11 +848,19 @@ func (e *EventPort) DissociatePath(path string) error { return fmt.Errorf("%v is not associated with this Event Port", path) } _, err := port_dissociate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(f.fobj))) - if err != nil { + // If the path is no longer associated with this event port (ENOENT) + // we should delete it from our map. We can still return ENOENT to the caller. + // But we need to save the cookie + if err != nil && err != ENOENT { return err } + if err == nil { + // dissociate was successful, safe to delete the cookie + fCookie := e.paths[path] + delete(e.cookies, &fCookie.cookie) + } delete(e.paths, path) - return nil + return err } // AssociateFd wraps calls to port_associate(3c) on file descriptors. @@ -862,12 +870,13 @@ func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) erro if _, found := e.fds[fd]; found { return fmt.Errorf("%v is already associated with this Event Port", fd) } - pcookie := &cookie - _, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(pcookie))) + fCookie := &fileObjCookie{nil, cookie} + _, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie))) if err != nil { return err } - e.fds[fd] = pcookie + e.fds[fd] = fCookie + e.cookies[&fCookie.cookie] = fCookie return nil } @@ -880,11 +889,16 @@ func (e *EventPort) DissociateFd(fd uintptr) error { return fmt.Errorf("%v is not associated with this Event Port", fd) } _, err := port_dissociate(e.port, PORT_SOURCE_FD, fd) - if err != nil { + if err != nil && err != ENOENT { return err } + if err == nil { + // dissociate was successful, safe to delete the cookie + fCookie := e.fds[fd] + delete(e.cookies, &fCookie.cookie) + } delete(e.fds, fd) - return nil + return err } func createFileObj(name string, stat os.FileInfo) (*fileObj, error) { @@ -912,26 +926,48 @@ func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) { return nil, err } p := new(PortEvent) - p.Events = pe.Events - p.Source = pe.Source e.mu.Lock() defer e.mu.Unlock() - switch pe.Source { - case PORT_SOURCE_FD: - p.Fd = uintptr(pe.Object) - cookie := (*interface{})(unsafe.Pointer(pe.User)) - p.Cookie = *cookie - delete(e.fds, p.Fd) - case PORT_SOURCE_FILE: - p.fobj = (*fileObj)(unsafe.Pointer(uintptr(pe.Object))) - p.Path = BytePtrToString((*byte)(unsafe.Pointer(p.fobj.Name))) - cookie := (*interface{})(unsafe.Pointer(pe.User)) - p.Cookie = *cookie - delete(e.paths, p.Path) - } + e.peIntToExt(pe, p) return p, nil } +// peIntToExt converts a cgo portEvent struct into the friendlier PortEvent +// NOTE: Always call this function while holding the e.mu mutex +func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) { + peExt.Events = peInt.Events + peExt.Source = peInt.Source + cookie := (*interface{})(unsafe.Pointer(peInt.User)) + peExt.Cookie = *cookie + switch peInt.Source { + case PORT_SOURCE_FD: + delete(e.cookies, cookie) + peExt.Fd = uintptr(peInt.Object) + // Only remove the fds entry if it exists and this cookie matches + if fobj, ok := e.fds[peExt.Fd]; ok { + if &fobj.cookie == cookie { + delete(e.fds, peExt.Fd) + } + } + case PORT_SOURCE_FILE: + if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) { + // Use our stashed reference rather than using unsafe on what we got back + // the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object))) + peExt.fobj = fCookie.fobj + } else { + panic("mismanaged memory") + } + delete(e.cookies, cookie) + peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name))) + // Only remove the paths entry if it exists and this cookie matches + if fobj, ok := e.paths[peExt.Path]; ok { + if &fobj.cookie == cookie { + delete(e.paths, peExt.Path) + } + } + } +} + // Pending wraps port_getn(3c) and returns how many events are pending. func (e *EventPort) Pending() (int, error) { var n uint32 = 0 @@ -962,21 +998,7 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error) e.mu.Lock() defer e.mu.Unlock() for i := 0; i < int(got); i++ { - s[i].Events = ps[i].Events - s[i].Source = ps[i].Source - switch ps[i].Source { - case PORT_SOURCE_FD: - s[i].Fd = uintptr(ps[i].Object) - cookie := (*interface{})(unsafe.Pointer(ps[i].User)) - s[i].Cookie = *cookie - delete(e.fds, s[i].Fd) - case PORT_SOURCE_FILE: - s[i].fobj = (*fileObj)(unsafe.Pointer(uintptr(ps[i].Object))) - s[i].Path = BytePtrToString((*byte)(unsafe.Pointer(s[i].fobj.Name))) - cookie := (*interface{})(unsafe.Pointer(ps[i].User)) - s[i].Cookie = *cookie - delete(e.paths, s[i].Path) - } + e.peIntToExt(&ps[i], &s[i]) } return int(got), err } diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index cf296a24..70508afc 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -177,6 +177,30 @@ func Write(fd int, p []byte) (n int, err error) { return } +func Pread(fd int, p []byte, offset int64) (n int, err error) { + n, err = pread(fd, p, offset) + if raceenabled { + if n > 0 { + raceWriteRange(unsafe.Pointer(&p[0]), n) + } + if err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } + } + return +} + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = pwrite(fd, p, offset) + if raceenabled && n > 0 { + raceReadRange(unsafe.Pointer(&p[0]), n) + } + return +} + // For testing: clients can set this flag to force // creation of IPv6 sockets to return EAFNOSUPPORT. var SocketDisableIPv6 bool @@ -313,6 +337,33 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { return } +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { + var rsa RawSockaddrAny + n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa) + // source address is only specified if the socket is unconnected + if rsa.Addr.Family != AF_UNSPEC { + from, err = anyToSockaddr(fd, &rsa) + } + return +} + +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { + _, err = SendmsgN(fd, p, oob, to, flags) + return +} + +func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { + var ptr unsafe.Pointer + var salen _Socklen + if to != nil { + ptr, salen, err = to.sockaddr() + if err != nil { + return 0, err + } + } + return sendmsgN(fd, p, oob, ptr, salen, flags) +} + func Send(s int, buf []byte, flags int) (err error) { return sendto(s, buf, flags, nil, 0) } diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 4e542058..c0a43f8b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -38,7 +38,8 @@ const ( AF_KEY = 0xf AF_LLC = 0x1a AF_LOCAL = 0x1 - AF_MAX = 0x2d + AF_MAX = 0x2e + AF_MCTP = 0x2d AF_MPLS = 0x1c AF_NETBEUI = 0xd AF_NETLINK = 0x10 @@ -259,6 +260,17 @@ const ( BUS_USB = 0x3 BUS_VIRTUAL = 0x6 CAN_BCM = 0x2 + CAN_CTRLMODE_3_SAMPLES = 0x4 + CAN_CTRLMODE_BERR_REPORTING = 0x10 + CAN_CTRLMODE_CC_LEN8_DLC = 0x100 + CAN_CTRLMODE_FD = 0x20 + CAN_CTRLMODE_FD_NON_ISO = 0x80 + CAN_CTRLMODE_LISTENONLY = 0x2 + CAN_CTRLMODE_LOOPBACK = 0x1 + CAN_CTRLMODE_ONE_SHOT = 0x8 + CAN_CTRLMODE_PRESUME_ACK = 0x40 + CAN_CTRLMODE_TDC_AUTO = 0x200 + CAN_CTRLMODE_TDC_MANUAL = 0x400 CAN_EFF_FLAG = 0x80000000 CAN_EFF_ID_BITS = 0x1d CAN_EFF_MASK = 0x1fffffff @@ -336,6 +348,7 @@ const ( CAN_RTR_FLAG = 0x40000000 CAN_SFF_ID_BITS = 0xb CAN_SFF_MASK = 0x7ff + CAN_TERMINATION_DISABLED = 0x0 CAN_TP16 = 0x3 CAN_TP20 = 0x4 CAP_AUDIT_CONTROL = 0x1e @@ -380,9 +393,11 @@ const ( CAP_SYS_TIME = 0x19 CAP_SYS_TTY_CONFIG = 0x1a CAP_WAKE_ALARM = 0x23 + CEPH_SUPER_MAGIC = 0xc36400 CFLUSH = 0xf CGROUP2_SUPER_MAGIC = 0x63677270 CGROUP_SUPER_MAGIC = 0x27e0eb + CIFS_SUPER_MAGIC = 0xff534d42 CLOCK_BOOTTIME = 0x7 CLOCK_BOOTTIME_ALARM = 0x9 CLOCK_DEFAULT = 0x0 @@ -771,6 +786,7 @@ const ( EV_SYN = 0x0 EV_VERSION = 0x10001 EXABYTE_ENABLE_NEST = 0xf0 + EXFAT_SUPER_MAGIC = 0x2011bab0 EXT2_SUPER_MAGIC = 0xef53 EXT3_SUPER_MAGIC = 0xef53 EXT4_SUPER_MAGIC = 0xef53 @@ -813,6 +829,8 @@ const ( FAN_EVENT_INFO_TYPE_DFID_NAME = 0x2 FAN_EVENT_INFO_TYPE_ERROR = 0x5 FAN_EVENT_INFO_TYPE_FID = 0x1 + FAN_EVENT_INFO_TYPE_NEW_DFID_NAME = 0xc + FAN_EVENT_INFO_TYPE_OLD_DFID_NAME = 0xa FAN_EVENT_INFO_TYPE_PIDFD = 0x4 FAN_EVENT_METADATA_LEN = 0x18 FAN_EVENT_ON_CHILD = 0x8000000 @@ -841,17 +859,27 @@ const ( FAN_OPEN_EXEC_PERM = 0x40000 FAN_OPEN_PERM = 0x10000 FAN_Q_OVERFLOW = 0x4000 + FAN_RENAME = 0x10000000 FAN_REPORT_DFID_NAME = 0xc00 + FAN_REPORT_DFID_NAME_TARGET = 0x1e00 FAN_REPORT_DIR_FID = 0x400 FAN_REPORT_FID = 0x200 FAN_REPORT_NAME = 0x800 FAN_REPORT_PIDFD = 0x80 + FAN_REPORT_TARGET_FID = 0x1000 FAN_REPORT_TID = 0x100 FAN_UNLIMITED_MARKS = 0x20 FAN_UNLIMITED_QUEUE = 0x10 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 FF0 = 0x0 + FIB_RULE_DEV_DETACHED = 0x8 + FIB_RULE_FIND_SADDR = 0x10000 + FIB_RULE_IIF_DETACHED = 0x8 + FIB_RULE_INVERT = 0x2 + FIB_RULE_OIF_DETACHED = 0x10 + FIB_RULE_PERMANENT = 0x1 + FIB_RULE_UNRESOLVED = 0x4 FIDEDUPERANGE = 0xc0189436 FSCRYPT_KEY_DESCRIPTOR_SIZE = 0x8 FSCRYPT_KEY_DESC_PREFIX = "fscrypt:" @@ -914,6 +942,7 @@ const ( FS_VERITY_METADATA_TYPE_DESCRIPTOR = 0x2 FS_VERITY_METADATA_TYPE_MERKLE_TREE = 0x1 FS_VERITY_METADATA_TYPE_SIGNATURE = 0x3 + FUSE_SUPER_MAGIC = 0x65735546 FUTEXFS_SUPER_MAGIC = 0xbad1dea F_ADD_SEALS = 0x409 F_DUPFD = 0x0 @@ -1267,15 +1296,21 @@ const ( IP_XFRM_POLICY = 0x11 ISOFS_SUPER_MAGIC = 0x9660 ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 IUTF8 = 0x4000 IXANY = 0x800 JFFS2_SUPER_MAGIC = 0x72b6 + KCMPROTO_CONNECTED = 0x0 + KCM_RECV_DISABLE = 0x1 KEXEC_ARCH_386 = 0x30000 KEXEC_ARCH_68K = 0x40000 KEXEC_ARCH_AARCH64 = 0xb70000 KEXEC_ARCH_ARM = 0x280000 KEXEC_ARCH_DEFAULT = 0x0 KEXEC_ARCH_IA_64 = 0x320000 + KEXEC_ARCH_LOONGARCH = 0x1020000 KEXEC_ARCH_MASK = 0xffff0000 KEXEC_ARCH_MIPS = 0x80000 KEXEC_ARCH_MIPS_LE = 0xa0000 @@ -1477,6 +1512,7 @@ const ( MNT_DETACH = 0x2 MNT_EXPIRE = 0x4 MNT_FORCE = 0x1 + MODULE_INIT_COMPRESSED_FILE = 0x4 MODULE_INIT_IGNORE_MODVERSIONS = 0x1 MODULE_INIT_IGNORE_VERMAGIC = 0x2 MOUNT_ATTR_IDMAP = 0x100000 @@ -1831,6 +1867,9 @@ const ( PERF_MEM_BLK_NA = 0x1 PERF_MEM_BLK_SHIFT = 0x28 PERF_MEM_HOPS_0 = 0x1 + PERF_MEM_HOPS_1 = 0x2 + PERF_MEM_HOPS_2 = 0x3 + PERF_MEM_HOPS_3 = 0x4 PERF_MEM_HOPS_SHIFT = 0x2b PERF_MEM_LOCK_LOCKED = 0x2 PERF_MEM_LOCK_NA = 0x1 @@ -2034,6 +2073,8 @@ const ( PR_SET_TIMING = 0xe PR_SET_TSC = 0x1a PR_SET_UNALIGN = 0x6 + PR_SET_VMA = 0x53564d41 + PR_SET_VMA_ANON_NAME = 0x0 PR_SPEC_DISABLE = 0x4 PR_SPEC_DISABLE_NOEXEC = 0x10 PR_SPEC_ENABLE = 0x2 @@ -2117,6 +2158,10 @@ const ( PTRACE_SYSCALL_INFO_NONE = 0x0 PTRACE_SYSCALL_INFO_SECCOMP = 0x3 PTRACE_TRACEME = 0x0 + P_ALL = 0x0 + P_PGID = 0x2 + P_PID = 0x1 + P_PIDFD = 0x3 QNX4_SUPER_MAGIC = 0x2f QNX6_SUPER_MAGIC = 0x68191122 RAMFS_MAGIC = 0x858458f6 @@ -2442,6 +2487,9 @@ const ( SIOCGSTAMPNS = 0x8907 SIOCGSTAMPNS_OLD = 0x8907 SIOCGSTAMP_OLD = 0x8906 + SIOCKCMATTACH = 0x89e0 + SIOCKCMCLONE = 0x89e2 + SIOCKCMUNATTACH = 0x89e1 SIOCOUTQNSD = 0x894b SIOCPROTOPRIVATE = 0x89e0 SIOCRTMSG = 0x890d @@ -2484,6 +2532,7 @@ const ( SMART_STATUS = 0xda SMART_WRITE_LOG_SECTOR = 0xd6 SMART_WRITE_THRESHOLDS = 0xd7 + SMB2_SUPER_MAGIC = 0xfe534d42 SMB_SUPER_MAGIC = 0x517b SOCKFS_MAGIC = 0x534f434b SOCK_BUF_LOCK_MASK = 0x3 @@ -2625,7 +2674,7 @@ const ( TASKSTATS_GENL_NAME = "TASKSTATS" TASKSTATS_GENL_VERSION = 0x1 TASKSTATS_TYPE_MAX = 0x6 - TASKSTATS_VERSION = 0xa + TASKSTATS_VERSION = 0xb TCIFLUSH = 0x0 TCIOFF = 0x2 TCIOFLUSH = 0x2 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 234fd4a5..1b305fab 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -5,7 +5,7 @@ // +build 386,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 58619b75..6bcdef5d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -5,7 +5,7 @@ // +build amd64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 3a64ff59..e65df0f8 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -5,7 +5,7 @@ // +build arm,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index abe0b925..c7021115 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -5,7 +5,7 @@ // +build arm64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go new file mode 100644 index 00000000..0d83a1cd --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -0,0 +1,818 @@ +// mkerrors.sh -Wall -Werror -static -I/tmp/include +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build loong64 && linux +// +build loong64,linux + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go + +package unix + +import "syscall" + +const ( + B1000000 = 0x1008 + B115200 = 0x1002 + B1152000 = 0x1009 + B1500000 = 0x100a + B2000000 = 0x100b + B230400 = 0x1003 + B2500000 = 0x100c + B3000000 = 0x100d + B3500000 = 0x100e + B4000000 = 0x100f + B460800 = 0x1004 + B500000 = 0x1005 + B57600 = 0x1001 + B576000 = 0x1006 + B921600 = 0x1007 + BLKBSZGET = 0x80081270 + BLKBSZSET = 0x40081271 + BLKFLSBUF = 0x1261 + BLKFRAGET = 0x1265 + BLKFRASET = 0x1264 + BLKGETSIZE = 0x1260 + BLKGETSIZE64 = 0x80081272 + BLKPBSZGET = 0x127b + BLKRAGET = 0x1263 + BLKRASET = 0x1262 + BLKROGET = 0x125e + BLKROSET = 0x125d + BLKRRPART = 0x125f + BLKSECTGET = 0x1267 + BLKSECTSET = 0x1266 + BLKSSZGET = 0x1268 + BOTHER = 0x1000 + BS1 = 0x2000 + BSDLY = 0x2000 + CBAUD = 0x100f + CBAUDEX = 0x1000 + CIBAUD = 0x100f0000 + CLOCAL = 0x800 + CR1 = 0x200 + CR2 = 0x400 + CR3 = 0x600 + CRDLY = 0x600 + CREAD = 0x80 + CS6 = 0x10 + CS7 = 0x20 + CS8 = 0x30 + CSIZE = 0x30 + CSTOPB = 0x40 + ECCGETLAYOUT = 0x81484d11 + ECCGETSTATS = 0x80104d12 + ECHOCTL = 0x200 + ECHOE = 0x10 + ECHOK = 0x20 + ECHOKE = 0x800 + ECHONL = 0x40 + ECHOPRT = 0x400 + EFD_CLOEXEC = 0x80000 + EFD_NONBLOCK = 0x800 + EPOLL_CLOEXEC = 0x80000 + EXTPROC = 0x10000 + FF1 = 0x8000 + FFDLY = 0x8000 + FICLONE = 0x40049409 + FICLONERANGE = 0x4020940d + FLUSHO = 0x1000 + FPU_CTX_MAGIC = 0x46505501 + FS_IOC_ENABLE_VERITY = 0x40806685 + FS_IOC_GETFLAGS = 0x80086601 + FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b + FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 + FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 + FS_IOC_SETFLAGS = 0x40086602 + FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 + F_GETLK = 0x5 + F_GETLK64 = 0x5 + F_GETOWN = 0x9 + F_RDLCK = 0x0 + F_SETLK = 0x6 + F_SETLK64 = 0x6 + F_SETLKW = 0x7 + F_SETLKW64 = 0x7 + F_SETOWN = 0x8 + F_UNLCK = 0x2 + F_WRLCK = 0x1 + HIDIOCGRAWINFO = 0x80084803 + HIDIOCGRDESC = 0x90044802 + HIDIOCGRDESCSIZE = 0x80044801 + HUPCL = 0x400 + ICANON = 0x2 + IEXTEN = 0x8000 + IN_CLOEXEC = 0x80000 + IN_NONBLOCK = 0x800 + IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + ISIG = 0x1 + IUCLC = 0x200 + IXOFF = 0x1000 + IXON = 0x400 + LASX_CTX_MAGIC = 0x41535801 + LSX_CTX_MAGIC = 0x53580001 + MAP_ANON = 0x20 + MAP_ANONYMOUS = 0x20 + MAP_DENYWRITE = 0x800 + MAP_EXECUTABLE = 0x1000 + MAP_GROWSDOWN = 0x100 + MAP_HUGETLB = 0x40000 + MAP_LOCKED = 0x2000 + MAP_NONBLOCK = 0x10000 + MAP_NORESERVE = 0x4000 + MAP_POPULATE = 0x8000 + MAP_STACK = 0x20000 + MAP_SYNC = 0x80000 + MCL_CURRENT = 0x1 + MCL_FUTURE = 0x2 + MCL_ONFAULT = 0x4 + MEMERASE = 0x40084d02 + MEMERASE64 = 0x40104d14 + MEMGETBADBLOCK = 0x40084d0b + MEMGETINFO = 0x80204d01 + MEMGETOOBSEL = 0x80c84d0a + MEMGETREGIONCOUNT = 0x80044d07 + MEMISLOCKED = 0x80084d17 + MEMLOCK = 0x40084d05 + MEMREADOOB = 0xc0104d04 + MEMSETBADBLOCK = 0x40084d0c + MEMUNLOCK = 0x40084d06 + MEMWRITEOOB = 0xc0104d03 + MTDFILEMODE = 0x4d13 + NFDBITS = 0x40 + NLDLY = 0x100 + NOFLSH = 0x80 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 + OLCUC = 0x2 + ONLCR = 0x4 + OTPERASE = 0x400c4d19 + OTPGETREGIONCOUNT = 0x40044d0e + OTPGETREGIONINFO = 0x400c4d0f + OTPLOCK = 0x800c4d10 + OTPSELECT = 0x80044d0d + O_APPEND = 0x400 + O_ASYNC = 0x2000 + O_CLOEXEC = 0x80000 + O_CREAT = 0x40 + O_DIRECT = 0x4000 + O_DIRECTORY = 0x10000 + O_DSYNC = 0x1000 + O_EXCL = 0x80 + O_FSYNC = 0x101000 + O_LARGEFILE = 0x0 + O_NDELAY = 0x800 + O_NOATIME = 0x40000 + O_NOCTTY = 0x100 + O_NOFOLLOW = 0x20000 + O_NONBLOCK = 0x800 + O_PATH = 0x200000 + O_RSYNC = 0x101000 + O_SYNC = 0x101000 + O_TMPFILE = 0x410000 + O_TRUNC = 0x200 + PARENB = 0x100 + PARODD = 0x200 + PENDIN = 0x4000 + PERF_EVENT_IOC_DISABLE = 0x2401 + PERF_EVENT_IOC_ENABLE = 0x2400 + PERF_EVENT_IOC_ID = 0x80082407 + PERF_EVENT_IOC_MODIFY_ATTRIBUTES = 0x4008240b + PERF_EVENT_IOC_PAUSE_OUTPUT = 0x40042409 + PERF_EVENT_IOC_PERIOD = 0x40082404 + PERF_EVENT_IOC_QUERY_BPF = 0xc008240a + PERF_EVENT_IOC_REFRESH = 0x2402 + PERF_EVENT_IOC_RESET = 0x2403 + PERF_EVENT_IOC_SET_BPF = 0x40042408 + PERF_EVENT_IOC_SET_FILTER = 0x40082406 + PERF_EVENT_IOC_SET_OUTPUT = 0x2405 + PPPIOCATTACH = 0x4004743d + PPPIOCATTCHAN = 0x40047438 + PPPIOCBRIDGECHAN = 0x40047435 + PPPIOCCONNECT = 0x4004743a + PPPIOCDETACH = 0x4004743c + PPPIOCDISCONN = 0x7439 + PPPIOCGASYNCMAP = 0x80047458 + PPPIOCGCHAN = 0x80047437 + PPPIOCGDEBUG = 0x80047441 + PPPIOCGFLAGS = 0x8004745a + PPPIOCGIDLE = 0x8010743f + PPPIOCGIDLE32 = 0x8008743f + PPPIOCGIDLE64 = 0x8010743f + PPPIOCGL2TPSTATS = 0x80487436 + PPPIOCGMRU = 0x80047453 + PPPIOCGRASYNCMAP = 0x80047455 + PPPIOCGUNIT = 0x80047456 + PPPIOCGXASYNCMAP = 0x80207450 + PPPIOCSACTIVE = 0x40107446 + PPPIOCSASYNCMAP = 0x40047457 + PPPIOCSCOMPRESS = 0x4010744d + PPPIOCSDEBUG = 0x40047440 + PPPIOCSFLAGS = 0x40047459 + PPPIOCSMAXCID = 0x40047451 + PPPIOCSMRRU = 0x4004743b + PPPIOCSMRU = 0x40047452 + PPPIOCSNPMODE = 0x4008744b + PPPIOCSPASS = 0x40107447 + PPPIOCSRASYNCMAP = 0x40047454 + PPPIOCSXASYNCMAP = 0x4020744f + PPPIOCUNBRIDGECHAN = 0x7434 + PPPIOCXFERUNIT = 0x744e + PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTRACE_SYSEMU = 0x1f + PTRACE_SYSEMU_SINGLESTEP = 0x20 + RLIMIT_AS = 0x9 + RLIMIT_MEMLOCK = 0x8 + RLIMIT_NOFILE = 0x7 + RLIMIT_NPROC = 0x6 + RLIMIT_RSS = 0x5 + RNDADDENTROPY = 0x40085203 + RNDADDTOENTCNT = 0x40045201 + RNDCLEARPOOL = 0x5206 + RNDGETENTCNT = 0x80045200 + RNDGETPOOL = 0x80085202 + RNDRESEEDCRNG = 0x5207 + RNDZAPENTCNT = 0x5204 + RTC_AIE_OFF = 0x7002 + RTC_AIE_ON = 0x7001 + RTC_ALM_READ = 0x80247008 + RTC_ALM_SET = 0x40247007 + RTC_EPOCH_READ = 0x8008700d + RTC_EPOCH_SET = 0x4008700e + RTC_IRQP_READ = 0x8008700b + RTC_IRQP_SET = 0x4008700c + RTC_PARAM_GET = 0x40187013 + RTC_PARAM_SET = 0x40187014 + RTC_PIE_OFF = 0x7006 + RTC_PIE_ON = 0x7005 + RTC_PLL_GET = 0x80207011 + RTC_PLL_SET = 0x40207012 + RTC_RD_TIME = 0x80247009 + RTC_SET_TIME = 0x4024700a + RTC_UIE_OFF = 0x7004 + RTC_UIE_ON = 0x7003 + RTC_VL_CLR = 0x7014 + RTC_VL_READ = 0x80047013 + RTC_WIE_OFF = 0x7010 + RTC_WIE_ON = 0x700f + RTC_WKALM_RD = 0x80287010 + RTC_WKALM_SET = 0x4028700f + SCM_TIMESTAMPING = 0x25 + SCM_TIMESTAMPING_OPT_STATS = 0x36 + SCM_TIMESTAMPING_PKTINFO = 0x3a + SCM_TIMESTAMPNS = 0x23 + SCM_TXTIME = 0x3d + SCM_WIFI_STATUS = 0x29 + SFD_CLOEXEC = 0x80000 + SFD_NONBLOCK = 0x800 + SIOCATMARK = 0x8905 + SIOCGPGRP = 0x8904 + SIOCGSTAMPNS_NEW = 0x80108907 + SIOCGSTAMP_NEW = 0x80108906 + SIOCINQ = 0x541b + SIOCOUTQ = 0x5411 + SIOCSPGRP = 0x8902 + SOCK_CLOEXEC = 0x80000 + SOCK_DGRAM = 0x2 + SOCK_NONBLOCK = 0x800 + SOCK_STREAM = 0x1 + SOL_SOCKET = 0x1 + SO_ACCEPTCONN = 0x1e + SO_ATTACH_BPF = 0x32 + SO_ATTACH_REUSEPORT_CBPF = 0x33 + SO_ATTACH_REUSEPORT_EBPF = 0x34 + SO_BINDTODEVICE = 0x19 + SO_BINDTOIFINDEX = 0x3e + SO_BPF_EXTENSIONS = 0x30 + SO_BROADCAST = 0x6 + SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 + SO_BUSY_POLL = 0x2e + SO_BUSY_POLL_BUDGET = 0x46 + SO_CNX_ADVICE = 0x35 + SO_COOKIE = 0x39 + SO_DETACH_REUSEPORT_BPF = 0x44 + SO_DOMAIN = 0x27 + SO_DONTROUTE = 0x5 + SO_ERROR = 0x4 + SO_INCOMING_CPU = 0x31 + SO_INCOMING_NAPI_ID = 0x38 + SO_KEEPALIVE = 0x9 + SO_LINGER = 0xd + SO_LOCK_FILTER = 0x2c + SO_MARK = 0x24 + SO_MAX_PACING_RATE = 0x2f + SO_MEMINFO = 0x37 + SO_NETNS_COOKIE = 0x47 + SO_NOFCS = 0x2b + SO_OOBINLINE = 0xa + SO_PASSCRED = 0x10 + SO_PASSSEC = 0x22 + SO_PEEK_OFF = 0x2a + SO_PEERCRED = 0x11 + SO_PEERGROUPS = 0x3b + SO_PEERSEC = 0x1f + SO_PREFER_BUSY_POLL = 0x45 + SO_PROTOCOL = 0x26 + SO_RCVBUF = 0x8 + SO_RCVBUFFORCE = 0x21 + SO_RCVLOWAT = 0x12 + SO_RCVTIMEO = 0x14 + SO_RCVTIMEO_NEW = 0x42 + SO_RCVTIMEO_OLD = 0x14 + SO_RESERVE_MEM = 0x49 + SO_REUSEADDR = 0x2 + SO_REUSEPORT = 0xf + SO_RXQ_OVFL = 0x28 + SO_SECURITY_AUTHENTICATION = 0x16 + SO_SECURITY_ENCRYPTION_NETWORK = 0x18 + SO_SECURITY_ENCRYPTION_TRANSPORT = 0x17 + SO_SELECT_ERR_QUEUE = 0x2d + SO_SNDBUF = 0x7 + SO_SNDBUFFORCE = 0x20 + SO_SNDLOWAT = 0x13 + SO_SNDTIMEO = 0x15 + SO_SNDTIMEO_NEW = 0x43 + SO_SNDTIMEO_OLD = 0x15 + SO_TIMESTAMPING = 0x25 + SO_TIMESTAMPING_NEW = 0x41 + SO_TIMESTAMPING_OLD = 0x25 + SO_TIMESTAMPNS = 0x23 + SO_TIMESTAMPNS_NEW = 0x40 + SO_TIMESTAMPNS_OLD = 0x23 + SO_TIMESTAMP_NEW = 0x3f + SO_TXTIME = 0x3d + SO_TYPE = 0x3 + SO_WIFI_STATUS = 0x29 + SO_ZEROCOPY = 0x3c + TAB1 = 0x800 + TAB2 = 0x1000 + TAB3 = 0x1800 + TABDLY = 0x1800 + TCFLSH = 0x540b + TCGETA = 0x5405 + TCGETS = 0x5401 + TCGETS2 = 0x802c542a + TCGETX = 0x5432 + TCSAFLUSH = 0x2 + TCSBRK = 0x5409 + TCSBRKP = 0x5425 + TCSETA = 0x5406 + TCSETAF = 0x5408 + TCSETAW = 0x5407 + TCSETS = 0x5402 + TCSETS2 = 0x402c542b + TCSETSF = 0x5404 + TCSETSF2 = 0x402c542d + TCSETSW = 0x5403 + TCSETSW2 = 0x402c542c + TCSETX = 0x5433 + TCSETXF = 0x5434 + TCSETXW = 0x5435 + TCXONC = 0x540a + TFD_CLOEXEC = 0x80000 + TFD_NONBLOCK = 0x800 + TIOCCBRK = 0x5428 + TIOCCONS = 0x541d + TIOCEXCL = 0x540c + TIOCGDEV = 0x80045432 + TIOCGETD = 0x5424 + TIOCGEXCL = 0x80045440 + TIOCGICOUNT = 0x545d + TIOCGISO7816 = 0x80285442 + TIOCGLCKTRMIOS = 0x5456 + TIOCGPGRP = 0x540f + TIOCGPKT = 0x80045438 + TIOCGPTLCK = 0x80045439 + TIOCGPTN = 0x80045430 + TIOCGPTPEER = 0x5441 + TIOCGRS485 = 0x542e + TIOCGSERIAL = 0x541e + TIOCGSID = 0x5429 + TIOCGSOFTCAR = 0x5419 + TIOCGWINSZ = 0x5413 + TIOCINQ = 0x541b + TIOCLINUX = 0x541c + TIOCMBIC = 0x5417 + TIOCMBIS = 0x5416 + TIOCMGET = 0x5415 + TIOCMIWAIT = 0x545c + TIOCMSET = 0x5418 + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x5422 + TIOCNXCL = 0x540d + TIOCOUTQ = 0x5411 + TIOCPKT = 0x5420 + TIOCSBRK = 0x5427 + TIOCSCTTY = 0x540e + TIOCSERCONFIG = 0x5453 + TIOCSERGETLSR = 0x5459 + TIOCSERGETMULTI = 0x545a + TIOCSERGSTRUCT = 0x5458 + TIOCSERGWILD = 0x5454 + TIOCSERSETMULTI = 0x545b + TIOCSERSWILD = 0x5455 + TIOCSER_TEMT = 0x1 + TIOCSETD = 0x5423 + TIOCSIG = 0x40045436 + TIOCSISO7816 = 0xc0285443 + TIOCSLCKTRMIOS = 0x5457 + TIOCSPGRP = 0x5410 + TIOCSPTLCK = 0x40045431 + TIOCSRS485 = 0x542f + TIOCSSERIAL = 0x541f + TIOCSSOFTCAR = 0x541a + TIOCSTI = 0x5412 + TIOCSWINSZ = 0x5414 + TIOCVHANGUP = 0x5437 + TOSTOP = 0x100 + TUNATTACHFILTER = 0x401054d5 + TUNDETACHFILTER = 0x401054d6 + TUNGETDEVNETNS = 0x54e3 + TUNGETFEATURES = 0x800454cf + TUNGETFILTER = 0x801054db + TUNGETIFF = 0x800454d2 + TUNGETSNDBUF = 0x800454d3 + TUNGETVNETBE = 0x800454df + TUNGETVNETHDRSZ = 0x800454d7 + TUNGETVNETLE = 0x800454dd + TUNSETCARRIER = 0x400454e2 + TUNSETDEBUG = 0x400454c9 + TUNSETFILTEREBPF = 0x800454e1 + TUNSETGROUP = 0x400454ce + TUNSETIFF = 0x400454ca + TUNSETIFINDEX = 0x400454da + TUNSETLINK = 0x400454cd + TUNSETNOCSUM = 0x400454c8 + TUNSETOFFLOAD = 0x400454d0 + TUNSETOWNER = 0x400454cc + TUNSETPERSIST = 0x400454cb + TUNSETQUEUE = 0x400454d9 + TUNSETSNDBUF = 0x400454d4 + TUNSETSTEERINGEBPF = 0x800454e0 + TUNSETTXFILTER = 0x400454d1 + TUNSETVNETBE = 0x400454de + TUNSETVNETHDRSZ = 0x400454d8 + TUNSETVNETLE = 0x400454dc + UBI_IOCATT = 0x40186f40 + UBI_IOCDET = 0x40046f41 + UBI_IOCEBCH = 0x40044f02 + UBI_IOCEBER = 0x40044f01 + UBI_IOCEBISMAP = 0x80044f05 + UBI_IOCEBMAP = 0x40084f03 + UBI_IOCEBUNMAP = 0x40044f04 + UBI_IOCMKVOL = 0x40986f00 + UBI_IOCRMVOL = 0x40046f01 + UBI_IOCRNVOL = 0x51106f03 + UBI_IOCRPEB = 0x40046f04 + UBI_IOCRSVOL = 0x400c6f02 + UBI_IOCSETVOLPROP = 0x40104f06 + UBI_IOCSPEB = 0x40046f05 + UBI_IOCVOLCRBLK = 0x40804f07 + UBI_IOCVOLRMBLK = 0x4f08 + UBI_IOCVOLUP = 0x40084f00 + VDISCARD = 0xd + VEOF = 0x4 + VEOL = 0xb + VEOL2 = 0x10 + VMIN = 0x6 + VREPRINT = 0xc + VSTART = 0x8 + VSTOP = 0x9 + VSUSP = 0xa + VSWTC = 0x7 + VT1 = 0x4000 + VTDLY = 0x4000 + VTIME = 0x5 + VWERASE = 0xe + WDIOC_GETBOOTSTATUS = 0x80045702 + WDIOC_GETPRETIMEOUT = 0x80045709 + WDIOC_GETSTATUS = 0x80045701 + WDIOC_GETSUPPORT = 0x80285700 + WDIOC_GETTEMP = 0x80045703 + WDIOC_GETTIMELEFT = 0x8004570a + WDIOC_GETTIMEOUT = 0x80045707 + WDIOC_KEEPALIVE = 0x80045705 + WDIOC_SETOPTIONS = 0x80045704 + WORDSIZE = 0x40 + XCASE = 0x4 + XTABS = 0x1800 + _HIDIOCGRAWNAME = 0x80804804 + _HIDIOCGRAWPHYS = 0x80404805 + _HIDIOCGRAWUNIQ = 0x80404808 +) + +// Errors +const ( + EADDRINUSE = syscall.Errno(0x62) + EADDRNOTAVAIL = syscall.Errno(0x63) + EADV = syscall.Errno(0x44) + EAFNOSUPPORT = syscall.Errno(0x61) + EALREADY = syscall.Errno(0x72) + EBADE = syscall.Errno(0x34) + EBADFD = syscall.Errno(0x4d) + EBADMSG = syscall.Errno(0x4a) + EBADR = syscall.Errno(0x35) + EBADRQC = syscall.Errno(0x38) + EBADSLT = syscall.Errno(0x39) + EBFONT = syscall.Errno(0x3b) + ECANCELED = syscall.Errno(0x7d) + ECHRNG = syscall.Errno(0x2c) + ECOMM = syscall.Errno(0x46) + ECONNABORTED = syscall.Errno(0x67) + ECONNREFUSED = syscall.Errno(0x6f) + ECONNRESET = syscall.Errno(0x68) + EDEADLK = syscall.Errno(0x23) + EDEADLOCK = syscall.Errno(0x23) + EDESTADDRREQ = syscall.Errno(0x59) + EDOTDOT = syscall.Errno(0x49) + EDQUOT = syscall.Errno(0x7a) + EHOSTDOWN = syscall.Errno(0x70) + EHOSTUNREACH = syscall.Errno(0x71) + EHWPOISON = syscall.Errno(0x85) + EIDRM = syscall.Errno(0x2b) + EILSEQ = syscall.Errno(0x54) + EINPROGRESS = syscall.Errno(0x73) + EISCONN = syscall.Errno(0x6a) + EISNAM = syscall.Errno(0x78) + EKEYEXPIRED = syscall.Errno(0x7f) + EKEYREJECTED = syscall.Errno(0x81) + EKEYREVOKED = syscall.Errno(0x80) + EL2HLT = syscall.Errno(0x33) + EL2NSYNC = syscall.Errno(0x2d) + EL3HLT = syscall.Errno(0x2e) + EL3RST = syscall.Errno(0x2f) + ELIBACC = syscall.Errno(0x4f) + ELIBBAD = syscall.Errno(0x50) + ELIBEXEC = syscall.Errno(0x53) + ELIBMAX = syscall.Errno(0x52) + ELIBSCN = syscall.Errno(0x51) + ELNRNG = syscall.Errno(0x30) + ELOOP = syscall.Errno(0x28) + EMEDIUMTYPE = syscall.Errno(0x7c) + EMSGSIZE = syscall.Errno(0x5a) + EMULTIHOP = syscall.Errno(0x48) + ENAMETOOLONG = syscall.Errno(0x24) + ENAVAIL = syscall.Errno(0x77) + ENETDOWN = syscall.Errno(0x64) + ENETRESET = syscall.Errno(0x66) + ENETUNREACH = syscall.Errno(0x65) + ENOANO = syscall.Errno(0x37) + ENOBUFS = syscall.Errno(0x69) + ENOCSI = syscall.Errno(0x32) + ENODATA = syscall.Errno(0x3d) + ENOKEY = syscall.Errno(0x7e) + ENOLCK = syscall.Errno(0x25) + ENOLINK = syscall.Errno(0x43) + ENOMEDIUM = syscall.Errno(0x7b) + ENOMSG = syscall.Errno(0x2a) + ENONET = syscall.Errno(0x40) + ENOPKG = syscall.Errno(0x41) + ENOPROTOOPT = syscall.Errno(0x5c) + ENOSR = syscall.Errno(0x3f) + ENOSTR = syscall.Errno(0x3c) + ENOSYS = syscall.Errno(0x26) + ENOTCONN = syscall.Errno(0x6b) + ENOTEMPTY = syscall.Errno(0x27) + ENOTNAM = syscall.Errno(0x76) + ENOTRECOVERABLE = syscall.Errno(0x83) + ENOTSOCK = syscall.Errno(0x58) + ENOTSUP = syscall.Errno(0x5f) + ENOTUNIQ = syscall.Errno(0x4c) + EOPNOTSUPP = syscall.Errno(0x5f) + EOVERFLOW = syscall.Errno(0x4b) + EOWNERDEAD = syscall.Errno(0x82) + EPFNOSUPPORT = syscall.Errno(0x60) + EPROTO = syscall.Errno(0x47) + EPROTONOSUPPORT = syscall.Errno(0x5d) + EPROTOTYPE = syscall.Errno(0x5b) + EREMCHG = syscall.Errno(0x4e) + EREMOTE = syscall.Errno(0x42) + EREMOTEIO = syscall.Errno(0x79) + ERESTART = syscall.Errno(0x55) + ERFKILL = syscall.Errno(0x84) + ESHUTDOWN = syscall.Errno(0x6c) + ESOCKTNOSUPPORT = syscall.Errno(0x5e) + ESRMNT = syscall.Errno(0x45) + ESTALE = syscall.Errno(0x74) + ESTRPIPE = syscall.Errno(0x56) + ETIME = syscall.Errno(0x3e) + ETIMEDOUT = syscall.Errno(0x6e) + ETOOMANYREFS = syscall.Errno(0x6d) + EUCLEAN = syscall.Errno(0x75) + EUNATCH = syscall.Errno(0x31) + EUSERS = syscall.Errno(0x57) + EXFULL = syscall.Errno(0x36) +) + +// Signals +const ( + SIGBUS = syscall.Signal(0x7) + SIGCHLD = syscall.Signal(0x11) + SIGCLD = syscall.Signal(0x11) + SIGCONT = syscall.Signal(0x12) + SIGIO = syscall.Signal(0x1d) + SIGPOLL = syscall.Signal(0x1d) + SIGPROF = syscall.Signal(0x1b) + SIGPWR = syscall.Signal(0x1e) + SIGSTKFLT = syscall.Signal(0x10) + SIGSTOP = syscall.Signal(0x13) + SIGSYS = syscall.Signal(0x1f) + SIGTSTP = syscall.Signal(0x14) + SIGTTIN = syscall.Signal(0x15) + SIGTTOU = syscall.Signal(0x16) + SIGURG = syscall.Signal(0x17) + SIGUSR1 = syscall.Signal(0xa) + SIGUSR2 = syscall.Signal(0xc) + SIGVTALRM = syscall.Signal(0x1a) + SIGWINCH = syscall.Signal(0x1c) + SIGXCPU = syscall.Signal(0x18) + SIGXFSZ = syscall.Signal(0x19) +) + +// Error table +var errorList = [...]struct { + num syscall.Errno + name string + desc string +}{ + {1, "EPERM", "operation not permitted"}, + {2, "ENOENT", "no such file or directory"}, + {3, "ESRCH", "no such process"}, + {4, "EINTR", "interrupted system call"}, + {5, "EIO", "input/output error"}, + {6, "ENXIO", "no such device or address"}, + {7, "E2BIG", "argument list too long"}, + {8, "ENOEXEC", "exec format error"}, + {9, "EBADF", "bad file descriptor"}, + {10, "ECHILD", "no child processes"}, + {11, "EAGAIN", "resource temporarily unavailable"}, + {12, "ENOMEM", "cannot allocate memory"}, + {13, "EACCES", "permission denied"}, + {14, "EFAULT", "bad address"}, + {15, "ENOTBLK", "block device required"}, + {16, "EBUSY", "device or resource busy"}, + {17, "EEXIST", "file exists"}, + {18, "EXDEV", "invalid cross-device link"}, + {19, "ENODEV", "no such device"}, + {20, "ENOTDIR", "not a directory"}, + {21, "EISDIR", "is a directory"}, + {22, "EINVAL", "invalid argument"}, + {23, "ENFILE", "too many open files in system"}, + {24, "EMFILE", "too many open files"}, + {25, "ENOTTY", "inappropriate ioctl for device"}, + {26, "ETXTBSY", "text file busy"}, + {27, "EFBIG", "file too large"}, + {28, "ENOSPC", "no space left on device"}, + {29, "ESPIPE", "illegal seek"}, + {30, "EROFS", "read-only file system"}, + {31, "EMLINK", "too many links"}, + {32, "EPIPE", "broken pipe"}, + {33, "EDOM", "numerical argument out of domain"}, + {34, "ERANGE", "numerical result out of range"}, + {35, "EDEADLK", "resource deadlock avoided"}, + {36, "ENAMETOOLONG", "file name too long"}, + {37, "ENOLCK", "no locks available"}, + {38, "ENOSYS", "function not implemented"}, + {39, "ENOTEMPTY", "directory not empty"}, + {40, "ELOOP", "too many levels of symbolic links"}, + {42, "ENOMSG", "no message of desired type"}, + {43, "EIDRM", "identifier removed"}, + {44, "ECHRNG", "channel number out of range"}, + {45, "EL2NSYNC", "level 2 not synchronized"}, + {46, "EL3HLT", "level 3 halted"}, + {47, "EL3RST", "level 3 reset"}, + {48, "ELNRNG", "link number out of range"}, + {49, "EUNATCH", "protocol driver not attached"}, + {50, "ENOCSI", "no CSI structure available"}, + {51, "EL2HLT", "level 2 halted"}, + {52, "EBADE", "invalid exchange"}, + {53, "EBADR", "invalid request descriptor"}, + {54, "EXFULL", "exchange full"}, + {55, "ENOANO", "no anode"}, + {56, "EBADRQC", "invalid request code"}, + {57, "EBADSLT", "invalid slot"}, + {59, "EBFONT", "bad font file format"}, + {60, "ENOSTR", "device not a stream"}, + {61, "ENODATA", "no data available"}, + {62, "ETIME", "timer expired"}, + {63, "ENOSR", "out of streams resources"}, + {64, "ENONET", "machine is not on the network"}, + {65, "ENOPKG", "package not installed"}, + {66, "EREMOTE", "object is remote"}, + {67, "ENOLINK", "link has been severed"}, + {68, "EADV", "advertise error"}, + {69, "ESRMNT", "srmount error"}, + {70, "ECOMM", "communication error on send"}, + {71, "EPROTO", "protocol error"}, + {72, "EMULTIHOP", "multihop attempted"}, + {73, "EDOTDOT", "RFS specific error"}, + {74, "EBADMSG", "bad message"}, + {75, "EOVERFLOW", "value too large for defined data type"}, + {76, "ENOTUNIQ", "name not unique on network"}, + {77, "EBADFD", "file descriptor in bad state"}, + {78, "EREMCHG", "remote address changed"}, + {79, "ELIBACC", "can not access a needed shared library"}, + {80, "ELIBBAD", "accessing a corrupted shared library"}, + {81, "ELIBSCN", ".lib section in a.out corrupted"}, + {82, "ELIBMAX", "attempting to link in too many shared libraries"}, + {83, "ELIBEXEC", "cannot exec a shared library directly"}, + {84, "EILSEQ", "invalid or incomplete multibyte or wide character"}, + {85, "ERESTART", "interrupted system call should be restarted"}, + {86, "ESTRPIPE", "streams pipe error"}, + {87, "EUSERS", "too many users"}, + {88, "ENOTSOCK", "socket operation on non-socket"}, + {89, "EDESTADDRREQ", "destination address required"}, + {90, "EMSGSIZE", "message too long"}, + {91, "EPROTOTYPE", "protocol wrong type for socket"}, + {92, "ENOPROTOOPT", "protocol not available"}, + {93, "EPROTONOSUPPORT", "protocol not supported"}, + {94, "ESOCKTNOSUPPORT", "socket type not supported"}, + {95, "ENOTSUP", "operation not supported"}, + {96, "EPFNOSUPPORT", "protocol family not supported"}, + {97, "EAFNOSUPPORT", "address family not supported by protocol"}, + {98, "EADDRINUSE", "address already in use"}, + {99, "EADDRNOTAVAIL", "cannot assign requested address"}, + {100, "ENETDOWN", "network is down"}, + {101, "ENETUNREACH", "network is unreachable"}, + {102, "ENETRESET", "network dropped connection on reset"}, + {103, "ECONNABORTED", "software caused connection abort"}, + {104, "ECONNRESET", "connection reset by peer"}, + {105, "ENOBUFS", "no buffer space available"}, + {106, "EISCONN", "transport endpoint is already connected"}, + {107, "ENOTCONN", "transport endpoint is not connected"}, + {108, "ESHUTDOWN", "cannot send after transport endpoint shutdown"}, + {109, "ETOOMANYREFS", "too many references: cannot splice"}, + {110, "ETIMEDOUT", "connection timed out"}, + {111, "ECONNREFUSED", "connection refused"}, + {112, "EHOSTDOWN", "host is down"}, + {113, "EHOSTUNREACH", "no route to host"}, + {114, "EALREADY", "operation already in progress"}, + {115, "EINPROGRESS", "operation now in progress"}, + {116, "ESTALE", "stale file handle"}, + {117, "EUCLEAN", "structure needs cleaning"}, + {118, "ENOTNAM", "not a XENIX named type file"}, + {119, "ENAVAIL", "no XENIX semaphores available"}, + {120, "EISNAM", "is a named type file"}, + {121, "EREMOTEIO", "remote I/O error"}, + {122, "EDQUOT", "disk quota exceeded"}, + {123, "ENOMEDIUM", "no medium found"}, + {124, "EMEDIUMTYPE", "wrong medium type"}, + {125, "ECANCELED", "operation canceled"}, + {126, "ENOKEY", "required key not available"}, + {127, "EKEYEXPIRED", "key has expired"}, + {128, "EKEYREVOKED", "key has been revoked"}, + {129, "EKEYREJECTED", "key was rejected by service"}, + {130, "EOWNERDEAD", "owner died"}, + {131, "ENOTRECOVERABLE", "state not recoverable"}, + {132, "ERFKILL", "operation not possible due to RF-kill"}, + {133, "EHWPOISON", "memory page has hardware error"}, +} + +// Signal table +var signalList = [...]struct { + num syscall.Signal + name string + desc string +}{ + {1, "SIGHUP", "hangup"}, + {2, "SIGINT", "interrupt"}, + {3, "SIGQUIT", "quit"}, + {4, "SIGILL", "illegal instruction"}, + {5, "SIGTRAP", "trace/breakpoint trap"}, + {6, "SIGABRT", "aborted"}, + {7, "SIGBUS", "bus error"}, + {8, "SIGFPE", "floating point exception"}, + {9, "SIGKILL", "killed"}, + {10, "SIGUSR1", "user defined signal 1"}, + {11, "SIGSEGV", "segmentation fault"}, + {12, "SIGUSR2", "user defined signal 2"}, + {13, "SIGPIPE", "broken pipe"}, + {14, "SIGALRM", "alarm clock"}, + {15, "SIGTERM", "terminated"}, + {16, "SIGSTKFLT", "stack fault"}, + {17, "SIGCHLD", "child exited"}, + {18, "SIGCONT", "continued"}, + {19, "SIGSTOP", "stopped (signal)"}, + {20, "SIGTSTP", "stopped"}, + {21, "SIGTTIN", "stopped (tty input)"}, + {22, "SIGTTOU", "stopped (tty output)"}, + {23, "SIGURG", "urgent I/O condition"}, + {24, "SIGXCPU", "CPU time limit exceeded"}, + {25, "SIGXFSZ", "file size limit exceeded"}, + {26, "SIGVTALRM", "virtual timer expired"}, + {27, "SIGPROF", "profiling timer expired"}, + {28, "SIGWINCH", "window changed"}, + {29, "SIGIO", "I/O possible"}, + {30, "SIGPWR", "power failure"}, + {31, "SIGSYS", "bad system call"}, +} diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 14d7a843..7f44a495 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -5,7 +5,7 @@ // +build mips,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 99e7c4ac..2f92b4e4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -5,7 +5,7 @@ // +build mips64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 496364c3..f5367a96 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -5,7 +5,7 @@ // +build mips64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 3e408308..2e22337d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -5,7 +5,7 @@ // +build mipsle,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 1151a7df..858c4f30 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -5,7 +5,7 @@ // +build ppc,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index ed17f249..af2a7ba6 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -5,7 +5,7 @@ // +build ppc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index d84a37c1..eaa2eb8e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -5,7 +5,7 @@ // +build ppc64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 5cafba83..faaa9f06 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -5,7 +5,7 @@ // +build riscv64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 6d122da4..0d161f0b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -5,7 +5,7 @@ // +build s390x,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 6bd19e51..4fd497a3 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -5,7 +5,7 @@ // +build sparc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/_const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go index 85e0cc38..870215d2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go @@ -975,7 +975,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] @@ -992,7 +992,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] diff --git a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go index f1d4a73b..a89b0bfa 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go @@ -931,7 +931,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] @@ -946,7 +946,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 0ae0ed4c..467deed7 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { - _, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) +func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0) if e1 != 0 { err = errnoErr(e1) } return } -var libc_setattrlist_trampoline_addr uintptr +var libc_utimensat_trampoline_addr uintptr -//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1638,6 +1643,30 @@ var libc_mknod_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Open(path string, mode int, perm uint32) (fd int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1698,7 +1727,7 @@ var libc_pathconf_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1719,7 +1748,7 @@ var libc_pread_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index eac6ca80..7e308a47 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8 DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB) -TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0 - JMP libc_setattrlist(SB) +TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_utimensat(SB) -GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8 -DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB) +GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) @@ -600,6 +600,12 @@ TEXT libc_mknod_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknod_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknod_trampoline_addr(SB)/8, $libc_mknod_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) + +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_open_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_open(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index cf71be3e..35938d34 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -643,17 +643,22 @@ var libc_flistxattr_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { - _, _, e1 := syscall_syscall6(libc_setattrlist_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) +func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_utimensat_trampoline_addr, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0) if e1 != 0 { err = errnoErr(e1) } return } -var libc_setattrlist_trampoline_addr uintptr +var libc_utimensat_trampoline_addr uintptr -//go:cgo_import_dynamic libc_setattrlist setattrlist "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_utimensat utimensat "/usr/lib/libSystem.B.dylib" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -1638,6 +1643,30 @@ var libc_mknod_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Mount(fsType string, dir string, flags int, data unsafe.Pointer) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsType) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(dir) + if err != nil { + return + } + _, _, e1 := syscall_syscall6(libc_mount_trampoline_addr, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flags), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_mount_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_mount mount "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Open(path string, mode int, perm uint32) (fd int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1698,7 +1727,7 @@ var libc_pathconf_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1719,7 +1748,7 @@ var libc_pread_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 4ebcf217..b09e5bb0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -228,11 +228,11 @@ TEXT libc_flistxattr_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_flistxattr_trampoline_addr(SB), RODATA, $8 DATA ·libc_flistxattr_trampoline_addr(SB)/8, $libc_flistxattr_trampoline<>(SB) -TEXT libc_setattrlist_trampoline<>(SB),NOSPLIT,$0-0 - JMP libc_setattrlist(SB) +TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_utimensat(SB) -GLOBL ·libc_setattrlist_trampoline_addr(SB), RODATA, $8 -DATA ·libc_setattrlist_trampoline_addr(SB)/8, $libc_setattrlist_trampoline<>(SB) +GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fcntl(SB) @@ -600,6 +600,12 @@ TEXT libc_mknod_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_mknod_trampoline_addr(SB), RODATA, $8 DATA ·libc_mknod_trampoline_addr(SB)/8, $libc_mknod_trampoline<>(SB) +TEXT libc_mount_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_mount(SB) + +GLOBL ·libc_mount_trampoline_addr(SB), RODATA, $8 +DATA ·libc_mount_trampoline_addr(SB)/8, $libc_mount_trampoline<>(SB) + TEXT libc_open_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_open(SB) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index 3e9bddb7..e9d9997e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -1420,7 +1420,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1437,7 +1437,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index c72a462b..edd373b1 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -1420,7 +1420,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1437,7 +1437,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index 530d5df9..82e9764b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -1420,7 +1420,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1437,7 +1437,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go index 71e7df9e..a6479acd 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go @@ -1420,7 +1420,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1437,7 +1437,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index 93edda4c..bc4a2753 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -231,6 +231,16 @@ func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Waitid(idType int, id int, info *Siginfo, options int, rusage *Rusage) (err error) { + _, _, e1 := Syscall6(SYS_WAITID, uintptr(idType), uintptr(id), uintptr(unsafe.Pointer(info)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func KeyctlInt(cmd int, arg2 int, arg3 int, arg4 int, arg5 int) (ret int, err error) { r0, _, e1 := Syscall6(SYS_KEYCTL, uintptr(cmd), uintptr(arg2), uintptr(arg3), uintptr(arg4), uintptr(arg5), 0) ret = int(r0) @@ -818,6 +828,49 @@ func Fsync(fd int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error) { + r0, _, e1 := Syscall(SYS_FSMOUNT, uintptr(fd), uintptr(flags), uintptr(mountAttrs)) + fsfd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsopen(fsName string, flags int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fsName) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_FSOPEN, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fspick(dirfd int, pathName string, flags int) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(pathName) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_FSPICK, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Getdents(fd int, buf []byte) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { @@ -1195,6 +1248,26 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func MoveMount(fromDirfd int, fromPathName string, toDirfd int, toPathName string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fromPathName) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(toPathName) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MOVE_MOUNT, uintptr(fromDirfd), uintptr(unsafe.Pointer(_p0)), uintptr(toDirfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { @@ -1205,6 +1278,22 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func OpenTree(dfd int, fileName string, flags uint) (r int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(fileName) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN_TREE, uintptr(dfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags)) + r = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_PERF_EVENT_OPEN, uintptr(unsafe.Pointer(attr)), uintptr(pid), uintptr(cpu), uintptr(groupFd), uintptr(flags), 0) fd = int(r0) @@ -1992,6 +2081,16 @@ func PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) { + _, _, e1 := Syscall6(SYS_PIDFD_SEND_SIGNAL, uintptr(pidfd), uintptr(sig), uintptr(unsafe.Pointer(info)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func shmat(id int, addr uintptr, flag int) (ret uintptr, err error) { r0, _, e1 := Syscall(SYS_SHMAT, uintptr(id), uintptr(addr), uintptr(flag)) ret = uintptr(r0) @@ -2032,3 +2131,23 @@ func shmget(key int, size int, flag int) (id int, err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getitimer(which int, currValue *Itimerval) (err error) { + _, _, e1 := Syscall(SYS_GETITIMER, uintptr(which), uintptr(unsafe.Pointer(currValue)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setitimer(which int, newValue *Itimerval, oldValue *Itimerval) (err error) { + _, _, e1 := Syscall(SYS_SETITIMER, uintptr(which), uintptr(unsafe.Pointer(newValue)), uintptr(unsafe.Pointer(oldValue))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go index ff90c81e..88af526b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_386.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -l32 -tags linux,386 syscall_linux.go syscall_linux_386.go +// go run mksyscall.go -l32 -tags linux,386 syscall_linux.go syscall_linux_386.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && 386 @@ -200,7 +200,7 @@ func Lstat(path string, stat *Stat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -217,7 +217,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -524,3 +524,14 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go index fa7d3dbe..2a0c4aa6 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,amd64 syscall_linux.go syscall_linux_amd64.go +// go run mksyscall.go -tags linux,amd64 syscall_linux.go syscall_linux_amd64.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && amd64 @@ -215,6 +215,17 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func MemfdSecret(flags int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_MEMFD_SECRET, uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Pause() (err error) { _, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0) if e1 != 0 { @@ -225,7 +236,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -242,7 +253,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -444,17 +455,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -691,3 +691,14 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go index 654f9153..4882bde3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go @@ -46,17 +46,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -549,7 +538,7 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -566,7 +555,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go index e893f987..9f8c24e4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go @@ -180,7 +180,18 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func MemfdSecret(flags int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_MEMFD_SECRET, uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -197,7 +208,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -389,17 +400,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go new file mode 100644 index 00000000..8cdfbe71 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go @@ -0,0 +1,552 @@ +// go run mksyscall.go -tags linux,loong64 syscall_linux.go syscall_linux_loong64.go +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build linux && loong64 +// +build linux,loong64 + +package unix + +import ( + "syscall" + "unsafe" +) + +var _ syscall.Errno + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname *byte) (err error) { + _, _, e1 := Syscall6(SYS_FANOTIFY_MARK, uintptr(fd), uintptr(flags), uintptr(mask), uintptr(dirFd), uintptr(unsafe.Pointer(pathname)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fallocate(fd int, mode uint32, off int64, len int64) (err error) { + _, _, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off), uintptr(len), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) { + r0, _, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0) + n = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + var _p0 unsafe.Pointer + if len(events) > 0 { + _p0 = unsafe.Pointer(&events[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fadvise(fd int, offset int64, length int64, advice int) (err error) { + _, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, stat *Stat_t) (err error) { + _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatfs(fd int, buf *Statfs_t) (err error) { + _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _ := RawSyscallNoError(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (euid int) { + r0, _ := RawSyscallNoError(SYS_GETEUID, 0, 0, 0) + euid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _ := RawSyscallNoError(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _ := RawSyscallNoError(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, n int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(n), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (off int64, err error) { + r0, _, e1 := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence)) + off = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0) + written = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setfsgid(gid int) (prev int, err error) { + r0, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0) + prev = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setfsuid(uid int) (prev int, err error) { + r0, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0) + prev = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setregid(rgid int, egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setresgid(rgid int, egid int, sgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setresuid(ruid int, euid int, suid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setreuid(ruid int, euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) { + r0, _, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags)) + n = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Statfs(path string, buf *Statfs_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func SyncFileRange(fd int, off int64, n int64, flags int) (err error) { + _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE, uintptr(fd), uintptr(off), uintptr(n), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { + r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(n int, list *_Gid_t) (nn int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0) + nn = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setgroups(n int, list *_Gid_t) (err error) { + _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { + r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)) + xaddr = uintptr(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(cmdline) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_KEXEC_FILE_LOAD, uintptr(kernelFd), uintptr(initrdFd), uintptr(cmdlineLen), uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go index 6d155288..d7d6f424 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -b32 -arm -tags linux,mips syscall_linux.go syscall_linux_mipsx.go +// go run mksyscall.go -b32 -arm -tags linux,mips syscall_linux.go syscall_linux_mipsx.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips @@ -150,7 +150,7 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -167,7 +167,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -344,17 +344,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -702,3 +691,14 @@ func setrlimit(resource int, rlim *rlimit32) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go index 1e20d72d..7f1f8e65 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,mips64 syscall_linux.go syscall_linux_mips64x.go +// go run mksyscall.go -tags linux,mips64 syscall_linux.go syscall_linux_mips64x.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mips64 @@ -180,7 +180,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -197,7 +197,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -399,17 +399,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -696,3 +685,14 @@ func stat(path string, st *stat_t) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go index 82b5e2d9..f933d0f5 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go @@ -180,7 +180,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -197,7 +197,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -399,17 +399,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go index a0440c1d..297d0a99 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -l32 -arm -tags linux,mipsle syscall_linux.go syscall_linux_mipsx.go +// go run mksyscall.go -l32 -arm -tags linux,mipsle syscall_linux.go syscall_linux_mipsx.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && mipsle @@ -150,7 +150,7 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -167,7 +167,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -344,17 +344,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -702,3 +691,14 @@ func setrlimit(resource int, rlim *rlimit32) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go index 5864b9ca..2e32e7a4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -b32 -tags linux,ppc syscall_linux.go syscall_linux_ppc.go +// go run mksyscall.go -b32 -tags linux,ppc syscall_linux.go syscall_linux_ppc.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc @@ -210,7 +210,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -227,7 +227,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -409,17 +409,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -707,3 +696,14 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go index beeb49e3..3c531704 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,ppc64 syscall_linux.go syscall_linux_ppc64x.go +// go run mksyscall.go -tags linux,ppc64 syscall_linux.go syscall_linux_ppc64x.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64 @@ -240,7 +240,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -257,7 +257,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -475,17 +475,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -753,3 +742,14 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go index 53139b82..a00c6744 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,ppc64le syscall_linux.go syscall_linux_ppc64x.go +// go run mksyscall.go -tags linux,ppc64le syscall_linux.go syscall_linux_ppc64x.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && ppc64le @@ -240,7 +240,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -257,7 +257,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -475,17 +475,6 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -753,3 +742,14 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go index 63b393b8..a1a9bcbb 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go @@ -180,7 +180,7 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -197,7 +197,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -369,17 +369,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go index 202add37..e0dabc60 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,s390x syscall_linux.go syscall_linux_s390x.go +// go run mksyscall.go -tags linux,s390x syscall_linux.go syscall_linux_s390x.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && s390x @@ -210,7 +210,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -227,7 +227,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -533,3 +533,14 @@ func kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, f } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go index 2ab268c3..368623c0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go @@ -1,4 +1,4 @@ -// go run mksyscall.go -tags linux,sparc64 syscall_linux.go syscall_linux_sparc64.go +// go run mksyscall.go -tags linux,sparc64 syscall_linux.go syscall_linux_sparc64.go syscall_linux_alarm.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build linux && sparc64 @@ -220,7 +220,7 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -237,7 +237,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -455,17 +455,6 @@ func Truncate(path string, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { - r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) { r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) fd = int(r0) @@ -697,3 +686,14 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Alarm(seconds uint) (remaining uint, err error) { + r0, _, e1 := Syscall(SYS_ALARM, uintptr(seconds), 0, 0) + remaining = uint(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go index 51d0c074..4af561a4 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go @@ -1330,7 +1330,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1347,7 +1347,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go index df2efb6d..3b90e944 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go @@ -1330,7 +1330,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1347,7 +1347,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go index c8536c2c..890f4ccd 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go @@ -1330,7 +1330,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1347,7 +1347,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go index 8b981bfc..c79f071f 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go @@ -1330,7 +1330,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1347,7 +1347,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 8f80f4ad..a057fc5d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -1128,7 +1128,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1145,7 +1145,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 3a47aca7..04db8fa2 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -1128,7 +1128,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1145,7 +1145,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 883a9b45..69f80300 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -1128,7 +1128,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1145,7 +1145,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index aac7fdc9..c96a5051 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -1128,7 +1128,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1145,7 +1145,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 87761874..016d959b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -1128,7 +1128,7 @@ func Pathconf(path string, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) @@ -1145,7 +1145,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { _p0 = unsafe.Pointer(&p[0]) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index b5f926ce..d12f4fbf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -227,8 +227,8 @@ import ( //go:linkname procOpenat libc_openat //go:linkname procPathconf libc_pathconf //go:linkname procPause libc_pause -//go:linkname procPread libc_pread -//go:linkname procPwrite libc_pwrite +//go:linkname procpread libc_pread +//go:linkname procpwrite libc_pwrite //go:linkname procread libc_read //go:linkname procReadlink libc_readlink //go:linkname procRename libc_rename @@ -364,8 +364,8 @@ var ( procOpenat, procPathconf, procPause, - procPread, - procPwrite, + procpread, + procpwrite, procread, procReadlink, procRename, @@ -1380,12 +1380,12 @@ func Pause() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pread(fd int, p []byte, offset int64) (n int, err error) { +func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procPread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procpread)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = e1 @@ -1395,12 +1395,12 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pwrite(fd int, p []byte, offset int64) (n int, err error) { +func pwrite(fd int, p []byte, offset int64) (n int, err error) { var _p0 *byte if len(p) > 0 { _p0 = &p[0] } - r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procPwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procpwrite)), 4, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0) n = int(r0) if e1 != 0 { err = e1 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index cac1f758..62192e1d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -446,4 +446,5 @@ const ( SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index f327e4a0..490aab5d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -368,4 +368,5 @@ const ( SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index fb06a08d..aca17b6f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -410,4 +410,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 58285646..54b4dfa5 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -313,4 +313,5 @@ const ( SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go new file mode 100644 index 00000000..e443f9a3 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -0,0 +1,313 @@ +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build loong64 && linux +// +build loong64,linux + +package unix + +const ( + SYS_IO_SETUP = 0 + SYS_IO_DESTROY = 1 + SYS_IO_SUBMIT = 2 + SYS_IO_CANCEL = 3 + SYS_IO_GETEVENTS = 4 + SYS_SETXATTR = 5 + SYS_LSETXATTR = 6 + SYS_FSETXATTR = 7 + SYS_GETXATTR = 8 + SYS_LGETXATTR = 9 + SYS_FGETXATTR = 10 + SYS_LISTXATTR = 11 + SYS_LLISTXATTR = 12 + SYS_FLISTXATTR = 13 + SYS_REMOVEXATTR = 14 + SYS_LREMOVEXATTR = 15 + SYS_FREMOVEXATTR = 16 + SYS_GETCWD = 17 + SYS_LOOKUP_DCOOKIE = 18 + SYS_EVENTFD2 = 19 + SYS_EPOLL_CREATE1 = 20 + SYS_EPOLL_CTL = 21 + SYS_EPOLL_PWAIT = 22 + SYS_DUP = 23 + SYS_DUP3 = 24 + SYS_FCNTL = 25 + SYS_INOTIFY_INIT1 = 26 + SYS_INOTIFY_ADD_WATCH = 27 + SYS_INOTIFY_RM_WATCH = 28 + SYS_IOCTL = 29 + SYS_IOPRIO_SET = 30 + SYS_IOPRIO_GET = 31 + SYS_FLOCK = 32 + SYS_MKNODAT = 33 + SYS_MKDIRAT = 34 + SYS_UNLINKAT = 35 + SYS_SYMLINKAT = 36 + SYS_LINKAT = 37 + SYS_UMOUNT2 = 39 + SYS_MOUNT = 40 + SYS_PIVOT_ROOT = 41 + SYS_NFSSERVCTL = 42 + SYS_STATFS = 43 + SYS_FSTATFS = 44 + SYS_TRUNCATE = 45 + SYS_FTRUNCATE = 46 + SYS_FALLOCATE = 47 + SYS_FACCESSAT = 48 + SYS_CHDIR = 49 + SYS_FCHDIR = 50 + SYS_CHROOT = 51 + SYS_FCHMOD = 52 + SYS_FCHMODAT = 53 + SYS_FCHOWNAT = 54 + SYS_FCHOWN = 55 + SYS_OPENAT = 56 + SYS_CLOSE = 57 + SYS_VHANGUP = 58 + SYS_PIPE2 = 59 + SYS_QUOTACTL = 60 + SYS_GETDENTS64 = 61 + SYS_LSEEK = 62 + SYS_READ = 63 + SYS_WRITE = 64 + SYS_READV = 65 + SYS_WRITEV = 66 + SYS_PREAD64 = 67 + SYS_PWRITE64 = 68 + SYS_PREADV = 69 + SYS_PWRITEV = 70 + SYS_SENDFILE = 71 + SYS_PSELECT6 = 72 + SYS_PPOLL = 73 + SYS_SIGNALFD4 = 74 + SYS_VMSPLICE = 75 + SYS_SPLICE = 76 + SYS_TEE = 77 + SYS_READLINKAT = 78 + SYS_FSTATAT = 79 + SYS_FSTAT = 80 + SYS_SYNC = 81 + SYS_FSYNC = 82 + SYS_FDATASYNC = 83 + SYS_SYNC_FILE_RANGE = 84 + SYS_TIMERFD_CREATE = 85 + SYS_TIMERFD_SETTIME = 86 + SYS_TIMERFD_GETTIME = 87 + SYS_UTIMENSAT = 88 + SYS_ACCT = 89 + SYS_CAPGET = 90 + SYS_CAPSET = 91 + SYS_PERSONALITY = 92 + SYS_EXIT = 93 + SYS_EXIT_GROUP = 94 + SYS_WAITID = 95 + SYS_SET_TID_ADDRESS = 96 + SYS_UNSHARE = 97 + SYS_FUTEX = 98 + SYS_SET_ROBUST_LIST = 99 + SYS_GET_ROBUST_LIST = 100 + SYS_NANOSLEEP = 101 + SYS_GETITIMER = 102 + SYS_SETITIMER = 103 + SYS_KEXEC_LOAD = 104 + SYS_INIT_MODULE = 105 + SYS_DELETE_MODULE = 106 + SYS_TIMER_CREATE = 107 + SYS_TIMER_GETTIME = 108 + SYS_TIMER_GETOVERRUN = 109 + SYS_TIMER_SETTIME = 110 + SYS_TIMER_DELETE = 111 + SYS_CLOCK_SETTIME = 112 + SYS_CLOCK_GETTIME = 113 + SYS_CLOCK_GETRES = 114 + SYS_CLOCK_NANOSLEEP = 115 + SYS_SYSLOG = 116 + SYS_PTRACE = 117 + SYS_SCHED_SETPARAM = 118 + SYS_SCHED_SETSCHEDULER = 119 + SYS_SCHED_GETSCHEDULER = 120 + SYS_SCHED_GETPARAM = 121 + SYS_SCHED_SETAFFINITY = 122 + SYS_SCHED_GETAFFINITY = 123 + SYS_SCHED_YIELD = 124 + SYS_SCHED_GET_PRIORITY_MAX = 125 + SYS_SCHED_GET_PRIORITY_MIN = 126 + SYS_SCHED_RR_GET_INTERVAL = 127 + SYS_RESTART_SYSCALL = 128 + SYS_KILL = 129 + SYS_TKILL = 130 + SYS_TGKILL = 131 + SYS_SIGALTSTACK = 132 + SYS_RT_SIGSUSPEND = 133 + SYS_RT_SIGACTION = 134 + SYS_RT_SIGPROCMASK = 135 + SYS_RT_SIGPENDING = 136 + SYS_RT_SIGTIMEDWAIT = 137 + SYS_RT_SIGQUEUEINFO = 138 + SYS_RT_SIGRETURN = 139 + SYS_SETPRIORITY = 140 + SYS_GETPRIORITY = 141 + SYS_REBOOT = 142 + SYS_SETREGID = 143 + SYS_SETGID = 144 + SYS_SETREUID = 145 + SYS_SETUID = 146 + SYS_SETRESUID = 147 + SYS_GETRESUID = 148 + SYS_SETRESGID = 149 + SYS_GETRESGID = 150 + SYS_SETFSUID = 151 + SYS_SETFSGID = 152 + SYS_TIMES = 153 + SYS_SETPGID = 154 + SYS_GETPGID = 155 + SYS_GETSID = 156 + SYS_SETSID = 157 + SYS_GETGROUPS = 158 + SYS_SETGROUPS = 159 + SYS_UNAME = 160 + SYS_SETHOSTNAME = 161 + SYS_SETDOMAINNAME = 162 + SYS_GETRUSAGE = 165 + SYS_UMASK = 166 + SYS_PRCTL = 167 + SYS_GETCPU = 168 + SYS_GETTIMEOFDAY = 169 + SYS_SETTIMEOFDAY = 170 + SYS_ADJTIMEX = 171 + SYS_GETPID = 172 + SYS_GETPPID = 173 + SYS_GETUID = 174 + SYS_GETEUID = 175 + SYS_GETGID = 176 + SYS_GETEGID = 177 + SYS_GETTID = 178 + SYS_SYSINFO = 179 + SYS_MQ_OPEN = 180 + SYS_MQ_UNLINK = 181 + SYS_MQ_TIMEDSEND = 182 + SYS_MQ_TIMEDRECEIVE = 183 + SYS_MQ_NOTIFY = 184 + SYS_MQ_GETSETATTR = 185 + SYS_MSGGET = 186 + SYS_MSGCTL = 187 + SYS_MSGRCV = 188 + SYS_MSGSND = 189 + SYS_SEMGET = 190 + SYS_SEMCTL = 191 + SYS_SEMTIMEDOP = 192 + SYS_SEMOP = 193 + SYS_SHMGET = 194 + SYS_SHMCTL = 195 + SYS_SHMAT = 196 + SYS_SHMDT = 197 + SYS_SOCKET = 198 + SYS_SOCKETPAIR = 199 + SYS_BIND = 200 + SYS_LISTEN = 201 + SYS_ACCEPT = 202 + SYS_CONNECT = 203 + SYS_GETSOCKNAME = 204 + SYS_GETPEERNAME = 205 + SYS_SENDTO = 206 + SYS_RECVFROM = 207 + SYS_SETSOCKOPT = 208 + SYS_GETSOCKOPT = 209 + SYS_SHUTDOWN = 210 + SYS_SENDMSG = 211 + SYS_RECVMSG = 212 + SYS_READAHEAD = 213 + SYS_BRK = 214 + SYS_MUNMAP = 215 + SYS_MREMAP = 216 + SYS_ADD_KEY = 217 + SYS_REQUEST_KEY = 218 + SYS_KEYCTL = 219 + SYS_CLONE = 220 + SYS_EXECVE = 221 + SYS_MMAP = 222 + SYS_FADVISE64 = 223 + SYS_SWAPON = 224 + SYS_SWAPOFF = 225 + SYS_MPROTECT = 226 + SYS_MSYNC = 227 + SYS_MLOCK = 228 + SYS_MUNLOCK = 229 + SYS_MLOCKALL = 230 + SYS_MUNLOCKALL = 231 + SYS_MINCORE = 232 + SYS_MADVISE = 233 + SYS_REMAP_FILE_PAGES = 234 + SYS_MBIND = 235 + SYS_GET_MEMPOLICY = 236 + SYS_SET_MEMPOLICY = 237 + SYS_MIGRATE_PAGES = 238 + SYS_MOVE_PAGES = 239 + SYS_RT_TGSIGQUEUEINFO = 240 + SYS_PERF_EVENT_OPEN = 241 + SYS_ACCEPT4 = 242 + SYS_RECVMMSG = 243 + SYS_ARCH_SPECIFIC_SYSCALL = 244 + SYS_WAIT4 = 260 + SYS_PRLIMIT64 = 261 + SYS_FANOTIFY_INIT = 262 + SYS_FANOTIFY_MARK = 263 + SYS_NAME_TO_HANDLE_AT = 264 + SYS_OPEN_BY_HANDLE_AT = 265 + SYS_CLOCK_ADJTIME = 266 + SYS_SYNCFS = 267 + SYS_SETNS = 268 + SYS_SENDMMSG = 269 + SYS_PROCESS_VM_READV = 270 + SYS_PROCESS_VM_WRITEV = 271 + SYS_KCMP = 272 + SYS_FINIT_MODULE = 273 + SYS_SCHED_SETATTR = 274 + SYS_SCHED_GETATTR = 275 + SYS_RENAMEAT2 = 276 + SYS_SECCOMP = 277 + SYS_GETRANDOM = 278 + SYS_MEMFD_CREATE = 279 + SYS_BPF = 280 + SYS_EXECVEAT = 281 + SYS_USERFAULTFD = 282 + SYS_MEMBARRIER = 283 + SYS_MLOCK2 = 284 + SYS_COPY_FILE_RANGE = 285 + SYS_PREADV2 = 286 + SYS_PWRITEV2 = 287 + SYS_PKEY_MPROTECT = 288 + SYS_PKEY_ALLOC = 289 + SYS_PKEY_FREE = 290 + SYS_STATX = 291 + SYS_IO_PGETEVENTS = 292 + SYS_RSEQ = 293 + SYS_KEXEC_FILE_LOAD = 294 + SYS_PIDFD_SEND_SIGNAL = 424 + SYS_IO_URING_SETUP = 425 + SYS_IO_URING_ENTER = 426 + SYS_IO_URING_REGISTER = 427 + SYS_OPEN_TREE = 428 + SYS_MOVE_MOUNT = 429 + SYS_FSOPEN = 430 + SYS_FSCONFIG = 431 + SYS_FSMOUNT = 432 + SYS_FSPICK = 433 + SYS_PIDFD_OPEN = 434 + SYS_CLONE3 = 435 + SYS_CLOSE_RANGE = 436 + SYS_OPENAT2 = 437 + SYS_PIDFD_GETFD = 438 + SYS_FACCESSAT2 = 439 + SYS_PROCESS_MADVISE = 440 + SYS_EPOLL_PWAIT2 = 441 + SYS_MOUNT_SETATTR = 442 + SYS_QUOTACTL_FD = 443 + SYS_LANDLOCK_CREATE_RULESET = 444 + SYS_LANDLOCK_ADD_RULE = 445 + SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 + SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 +) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 3b0418e6..65a99efc 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -430,4 +430,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 4446 SYS_PROCESS_MRELEASE = 4448 SYS_FUTEX_WAITV = 4449 + SYS_SET_MEMPOLICY_HOME_NODE = 4450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 314ebf16..841c8a66 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -360,4 +360,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 5446 SYS_PROCESS_MRELEASE = 5448 SYS_FUTEX_WAITV = 5449 + SYS_SET_MEMPOLICY_HOME_NODE = 5450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index b8fbb937..e26a7c76 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -360,4 +360,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 5446 SYS_PROCESS_MRELEASE = 5448 SYS_FUTEX_WAITV = 5449 + SYS_SET_MEMPOLICY_HOME_NODE = 5450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index ee309b2b..26447260 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -430,4 +430,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 4446 SYS_PROCESS_MRELEASE = 4448 SYS_FUTEX_WAITV = 4449 + SYS_SET_MEMPOLICY_HOME_NODE = 4450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index ac374810..26aefc18 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -437,4 +437,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 5aa47211..8d4cd9d9 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -409,4 +409,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index 0793ac1a..3b405d1f 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -409,4 +409,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index a520962e..c3a5af86 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -311,4 +311,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index d1738586..8ffa6646 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -374,4 +374,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index dfd5660f..6a39640e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -388,4 +388,5 @@ const ( SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 + SYS_SET_MEMPOLICY_HOME_NODE = 450 ) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 66788f15..9962d26b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -24,6 +24,11 @@ type ItimerSpec struct { Value Timespec } +type Itimerval struct { + Interval Timeval + Value Timeval +} + const ( TIME_OK = 0x0 TIME_INS = 0x1 @@ -749,6 +754,25 @@ const ( AT_SYMLINK_NOFOLLOW = 0x100 AT_EACCESS = 0x200 + + OPEN_TREE_CLONE = 0x1 + + MOVE_MOUNT_F_SYMLINKS = 0x1 + MOVE_MOUNT_F_AUTOMOUNTS = 0x2 + MOVE_MOUNT_F_EMPTY_PATH = 0x4 + MOVE_MOUNT_T_SYMLINKS = 0x10 + MOVE_MOUNT_T_AUTOMOUNTS = 0x20 + MOVE_MOUNT_T_EMPTY_PATH = 0x40 + MOVE_MOUNT_SET_GROUP = 0x100 + + FSOPEN_CLOEXEC = 0x1 + + FSPICK_CLOEXEC = 0x1 + FSPICK_SYMLINK_NOFOLLOW = 0x2 + FSPICK_NO_AUTOMOUNT = 0x4 + FSPICK_EMPTY_PATH = 0x8 + + FSMOUNT_CLOEXEC = 0x1 ) type OpenHow struct { @@ -3614,7 +3638,7 @@ const ( ETHTOOL_A_RINGS_RX_MINI = 0x7 ETHTOOL_A_RINGS_RX_JUMBO = 0x8 ETHTOOL_A_RINGS_TX = 0x9 - ETHTOOL_A_RINGS_MAX = 0x9 + ETHTOOL_A_RINGS_MAX = 0xa ETHTOOL_A_CHANNELS_UNSPEC = 0x0 ETHTOOL_A_CHANNELS_HEADER = 0x1 ETHTOOL_A_CHANNELS_RX_MAX = 0x2 @@ -3766,6 +3790,8 @@ const ( ETHTOOL_A_TUNNEL_INFO_MAX = 0x2 ) +const SPEED_UNKNOWN = -0x1 + type EthtoolDrvinfo struct { Cmd uint32 Driver [32]byte @@ -4065,3 +4091,1500 @@ const ( NL_POLICY_TYPE_ATTR_MASK = 0xc NL_POLICY_TYPE_ATTR_MAX = 0xc ) + +type CANBitTiming struct { + Bitrate uint32 + Sample_point uint32 + Tq uint32 + Prop_seg uint32 + Phase_seg1 uint32 + Phase_seg2 uint32 + Sjw uint32 + Brp uint32 +} + +type CANBitTimingConst struct { + Name [16]uint8 + Tseg1_min uint32 + Tseg1_max uint32 + Tseg2_min uint32 + Tseg2_max uint32 + Sjw_max uint32 + Brp_min uint32 + Brp_max uint32 + Brp_inc uint32 +} + +type CANClock struct { + Freq uint32 +} + +type CANBusErrorCounters struct { + Txerr uint16 + Rxerr uint16 +} + +type CANCtrlMode struct { + Mask uint32 + Flags uint32 +} + +type CANDeviceStats struct { + Bus_error uint32 + Error_warning uint32 + Error_passive uint32 + Bus_off uint32 + Arbitration_lost uint32 + Restarts uint32 +} + +const ( + CAN_STATE_ERROR_ACTIVE = 0x0 + CAN_STATE_ERROR_WARNING = 0x1 + CAN_STATE_ERROR_PASSIVE = 0x2 + CAN_STATE_BUS_OFF = 0x3 + CAN_STATE_STOPPED = 0x4 + CAN_STATE_SLEEPING = 0x5 + CAN_STATE_MAX = 0x6 +) + +const ( + IFLA_CAN_UNSPEC = 0x0 + IFLA_CAN_BITTIMING = 0x1 + IFLA_CAN_BITTIMING_CONST = 0x2 + IFLA_CAN_CLOCK = 0x3 + IFLA_CAN_STATE = 0x4 + IFLA_CAN_CTRLMODE = 0x5 + IFLA_CAN_RESTART_MS = 0x6 + IFLA_CAN_RESTART = 0x7 + IFLA_CAN_BERR_COUNTER = 0x8 + IFLA_CAN_DATA_BITTIMING = 0x9 + IFLA_CAN_DATA_BITTIMING_CONST = 0xa + IFLA_CAN_TERMINATION = 0xb + IFLA_CAN_TERMINATION_CONST = 0xc + IFLA_CAN_BITRATE_CONST = 0xd + IFLA_CAN_DATA_BITRATE_CONST = 0xe + IFLA_CAN_BITRATE_MAX = 0xf +) + +type KCMAttach struct { + Fd int32 + Bpf_fd int32 +} + +type KCMUnattach struct { + Fd int32 +} + +type KCMClone struct { + Fd int32 +} + +const ( + NL80211_AC_BE = 0x2 + NL80211_AC_BK = 0x3 + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED = 0x0 + NL80211_ACL_POLICY_DENY_UNLESS_LISTED = 0x1 + NL80211_AC_VI = 0x1 + NL80211_AC_VO = 0x0 + NL80211_ATTR_4ADDR = 0x53 + NL80211_ATTR_ACK = 0x5c + NL80211_ATTR_ACK_SIGNAL = 0x107 + NL80211_ATTR_ACL_POLICY = 0xa5 + NL80211_ATTR_ADMITTED_TIME = 0xd4 + NL80211_ATTR_AIRTIME_WEIGHT = 0x112 + NL80211_ATTR_AKM_SUITES = 0x4c + NL80211_ATTR_AP_ISOLATE = 0x60 + NL80211_ATTR_AUTH_DATA = 0x9c + NL80211_ATTR_AUTH_TYPE = 0x35 + NL80211_ATTR_BANDS = 0xef + NL80211_ATTR_BEACON_HEAD = 0xe + NL80211_ATTR_BEACON_INTERVAL = 0xc + NL80211_ATTR_BEACON_TAIL = 0xf + NL80211_ATTR_BG_SCAN_PERIOD = 0x98 + NL80211_ATTR_BSS_BASIC_RATES = 0x24 + NL80211_ATTR_BSS = 0x2f + NL80211_ATTR_BSS_CTS_PROT = 0x1c + NL80211_ATTR_BSS_HT_OPMODE = 0x6d + NL80211_ATTR_BSSID = 0xf5 + NL80211_ATTR_BSS_SELECT = 0xe3 + NL80211_ATTR_BSS_SHORT_PREAMBLE = 0x1d + NL80211_ATTR_BSS_SHORT_SLOT_TIME = 0x1e + NL80211_ATTR_CENTER_FREQ1 = 0xa0 + NL80211_ATTR_CENTER_FREQ1_OFFSET = 0x123 + NL80211_ATTR_CENTER_FREQ2 = 0xa1 + NL80211_ATTR_CHANNEL_WIDTH = 0x9f + NL80211_ATTR_CH_SWITCH_BLOCK_TX = 0xb8 + NL80211_ATTR_CH_SWITCH_COUNT = 0xb7 + NL80211_ATTR_CIPHER_SUITE_GROUP = 0x4a + NL80211_ATTR_CIPHER_SUITES = 0x39 + NL80211_ATTR_CIPHER_SUITES_PAIRWISE = 0x49 + NL80211_ATTR_CNTDWN_OFFS_BEACON = 0xba + NL80211_ATTR_CNTDWN_OFFS_PRESP = 0xbb + NL80211_ATTR_COALESCE_RULE = 0xb6 + NL80211_ATTR_COALESCE_RULE_CONDITION = 0x2 + NL80211_ATTR_COALESCE_RULE_DELAY = 0x1 + NL80211_ATTR_COALESCE_RULE_MAX = 0x3 + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN = 0x3 + NL80211_ATTR_CONN_FAILED_REASON = 0x9b + NL80211_ATTR_CONTROL_PORT = 0x44 + NL80211_ATTR_CONTROL_PORT_ETHERTYPE = 0x66 + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT = 0x67 + NL80211_ATTR_CONTROL_PORT_NO_PREAUTH = 0x11e + NL80211_ATTR_CONTROL_PORT_OVER_NL80211 = 0x108 + NL80211_ATTR_COOKIE = 0x58 + NL80211_ATTR_CQM_BEACON_LOSS_EVENT = 0x8 + NL80211_ATTR_CQM = 0x5e + NL80211_ATTR_CQM_MAX = 0x9 + NL80211_ATTR_CQM_PKT_LOSS_EVENT = 0x4 + NL80211_ATTR_CQM_RSSI_HYST = 0x2 + NL80211_ATTR_CQM_RSSI_LEVEL = 0x9 + NL80211_ATTR_CQM_RSSI_THOLD = 0x1 + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT = 0x3 + NL80211_ATTR_CQM_TXE_INTVL = 0x7 + NL80211_ATTR_CQM_TXE_PKTS = 0x6 + NL80211_ATTR_CQM_TXE_RATE = 0x5 + NL80211_ATTR_CRIT_PROT_ID = 0xb3 + NL80211_ATTR_CSA_C_OFF_BEACON = 0xba + NL80211_ATTR_CSA_C_OFF_PRESP = 0xbb + NL80211_ATTR_CSA_C_OFFSETS_TX = 0xcd + NL80211_ATTR_CSA_IES = 0xb9 + NL80211_ATTR_DEVICE_AP_SME = 0x8d + NL80211_ATTR_DFS_CAC_TIME = 0x7 + NL80211_ATTR_DFS_REGION = 0x92 + NL80211_ATTR_DISABLE_HE = 0x12d + NL80211_ATTR_DISABLE_HT = 0x93 + NL80211_ATTR_DISABLE_VHT = 0xaf + NL80211_ATTR_DISCONNECTED_BY_AP = 0x47 + NL80211_ATTR_DONT_WAIT_FOR_ACK = 0x8e + NL80211_ATTR_DTIM_PERIOD = 0xd + NL80211_ATTR_DURATION = 0x57 + NL80211_ATTR_EXT_CAPA = 0xa9 + NL80211_ATTR_EXT_CAPA_MASK = 0xaa + NL80211_ATTR_EXTERNAL_AUTH_ACTION = 0x104 + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT = 0x105 + NL80211_ATTR_EXT_FEATURES = 0xd9 + NL80211_ATTR_FEATURE_FLAGS = 0x8f + NL80211_ATTR_FILS_CACHE_ID = 0xfd + NL80211_ATTR_FILS_DISCOVERY = 0x126 + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM = 0xfb + NL80211_ATTR_FILS_ERP_REALM = 0xfa + NL80211_ATTR_FILS_ERP_RRK = 0xfc + NL80211_ATTR_FILS_ERP_USERNAME = 0xf9 + NL80211_ATTR_FILS_KEK = 0xf2 + NL80211_ATTR_FILS_NONCES = 0xf3 + NL80211_ATTR_FRAME = 0x33 + NL80211_ATTR_FRAME_MATCH = 0x5b + NL80211_ATTR_FRAME_TYPE = 0x65 + NL80211_ATTR_FREQ_AFTER = 0x3b + NL80211_ATTR_FREQ_BEFORE = 0x3a + NL80211_ATTR_FREQ_FIXED = 0x3c + NL80211_ATTR_FREQ_RANGE_END = 0x3 + NL80211_ATTR_FREQ_RANGE_MAX_BW = 0x4 + NL80211_ATTR_FREQ_RANGE_START = 0x2 + NL80211_ATTR_FTM_RESPONDER = 0x10e + NL80211_ATTR_FTM_RESPONDER_STATS = 0x10f + NL80211_ATTR_GENERATION = 0x2e + NL80211_ATTR_HANDLE_DFS = 0xbf + NL80211_ATTR_HE_6GHZ_CAPABILITY = 0x125 + NL80211_ATTR_HE_BSS_COLOR = 0x11b + NL80211_ATTR_HE_CAPABILITY = 0x10d + NL80211_ATTR_HE_OBSS_PD = 0x117 + NL80211_ATTR_HIDDEN_SSID = 0x7e + NL80211_ATTR_HT_CAPABILITY = 0x1f + NL80211_ATTR_HT_CAPABILITY_MASK = 0x94 + NL80211_ATTR_IE_ASSOC_RESP = 0x80 + NL80211_ATTR_IE = 0x2a + NL80211_ATTR_IE_PROBE_RESP = 0x7f + NL80211_ATTR_IE_RIC = 0xb2 + NL80211_ATTR_IFACE_SOCKET_OWNER = 0xcc + NL80211_ATTR_IFINDEX = 0x3 + NL80211_ATTR_IFNAME = 0x4 + NL80211_ATTR_IFTYPE_AKM_SUITES = 0x11c + NL80211_ATTR_IFTYPE = 0x5 + NL80211_ATTR_IFTYPE_EXT_CAPA = 0xe6 + NL80211_ATTR_INACTIVITY_TIMEOUT = 0x96 + NL80211_ATTR_INTERFACE_COMBINATIONS = 0x78 + NL80211_ATTR_KEY_CIPHER = 0x9 + NL80211_ATTR_KEY = 0x50 + NL80211_ATTR_KEY_DATA = 0x7 + NL80211_ATTR_KEY_DEFAULT = 0xb + NL80211_ATTR_KEY_DEFAULT_MGMT = 0x28 + NL80211_ATTR_KEY_DEFAULT_TYPES = 0x6e + NL80211_ATTR_KEY_IDX = 0x8 + NL80211_ATTR_KEYS = 0x51 + NL80211_ATTR_KEY_SEQ = 0xa + NL80211_ATTR_KEY_TYPE = 0x37 + NL80211_ATTR_LOCAL_MESH_POWER_MODE = 0xa4 + NL80211_ATTR_LOCAL_STATE_CHANGE = 0x5f + NL80211_ATTR_MAC_ACL_MAX = 0xa7 + NL80211_ATTR_MAC_ADDRS = 0xa6 + NL80211_ATTR_MAC = 0x6 + NL80211_ATTR_MAC_HINT = 0xc8 + NL80211_ATTR_MAC_MASK = 0xd7 + NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca + NL80211_ATTR_MAX = 0x135 + NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 + NL80211_ATTR_MAX_CSA_COUNTERS = 0xce + NL80211_ATTR_MAX_MATCH_SETS = 0x85 + NL80211_ATTR_MAX_NUM_PMKIDS = 0x56 + NL80211_ATTR_MAX_NUM_SCAN_SSIDS = 0x2b + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS = 0xde + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS = 0x7b + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION = 0x6f + NL80211_ATTR_MAX_SCAN_IE_LEN = 0x38 + NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL = 0xdf + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS = 0xe0 + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN = 0x7c + NL80211_ATTR_MCAST_RATE = 0x6b + NL80211_ATTR_MDID = 0xb1 + NL80211_ATTR_MEASUREMENT_DURATION = 0xeb + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY = 0xec + NL80211_ATTR_MESH_CONFIG = 0x23 + NL80211_ATTR_MESH_ID = 0x18 + NL80211_ATTR_MESH_PEER_AID = 0xed + NL80211_ATTR_MESH_SETUP = 0x70 + NL80211_ATTR_MGMT_SUBTYPE = 0x29 + NL80211_ATTR_MNTR_FLAGS = 0x17 + NL80211_ATTR_MPATH_INFO = 0x1b + NL80211_ATTR_MPATH_NEXT_HOP = 0x1a + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED = 0xf4 + NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR = 0xe8 + NL80211_ATTR_MU_MIMO_GROUP_DATA = 0xe7 + NL80211_ATTR_NAN_FUNC = 0xf0 + NL80211_ATTR_NAN_MASTER_PREF = 0xee + NL80211_ATTR_NAN_MATCH = 0xf1 + NL80211_ATTR_NETNS_FD = 0xdb + NL80211_ATTR_NOACK_MAP = 0x95 + NL80211_ATTR_NSS = 0x106 + NL80211_ATTR_OFFCHANNEL_TX_OK = 0x6c + NL80211_ATTR_OPER_CLASS = 0xd6 + NL80211_ATTR_OPMODE_NOTIF = 0xc2 + NL80211_ATTR_P2P_CTWINDOW = 0xa2 + NL80211_ATTR_P2P_OPPPS = 0xa3 + NL80211_ATTR_PAD = 0xe5 + NL80211_ATTR_PBSS = 0xe2 + NL80211_ATTR_PEER_AID = 0xb5 + NL80211_ATTR_PEER_MEASUREMENTS = 0x111 + NL80211_ATTR_PID = 0x52 + NL80211_ATTR_PMK = 0xfe + NL80211_ATTR_PMKID = 0x55 + NL80211_ATTR_PMK_LIFETIME = 0x11f + NL80211_ATTR_PMKR0_NAME = 0x102 + NL80211_ATTR_PMK_REAUTH_THRESHOLD = 0x120 + NL80211_ATTR_PMKSA_CANDIDATE = 0x86 + NL80211_ATTR_PORT_AUTHORIZED = 0x103 + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN = 0x5 + NL80211_ATTR_POWER_RULE_MAX_EIRP = 0x6 + NL80211_ATTR_PREV_BSSID = 0x4f + NL80211_ATTR_PRIVACY = 0x46 + NL80211_ATTR_PROBE_RESP = 0x91 + NL80211_ATTR_PROBE_RESP_OFFLOAD = 0x90 + NL80211_ATTR_PROTOCOL_FEATURES = 0xad + NL80211_ATTR_PS_STATE = 0x5d + NL80211_ATTR_QOS_MAP = 0xc7 + NL80211_ATTR_RADAR_EVENT = 0xa8 + NL80211_ATTR_REASON_CODE = 0x36 + NL80211_ATTR_RECEIVE_MULTICAST = 0x121 + NL80211_ATTR_RECONNECT_REQUESTED = 0x12b + NL80211_ATTR_REG_ALPHA2 = 0x21 + NL80211_ATTR_REG_INDOOR = 0xdd + NL80211_ATTR_REG_INITIATOR = 0x30 + NL80211_ATTR_REG_RULE_FLAGS = 0x1 + NL80211_ATTR_REG_RULES = 0x22 + NL80211_ATTR_REG_TYPE = 0x31 + NL80211_ATTR_REKEY_DATA = 0x7a + NL80211_ATTR_REQ_IE = 0x4d + NL80211_ATTR_RESP_IE = 0x4e + NL80211_ATTR_ROAM_SUPPORT = 0x83 + NL80211_ATTR_RX_FRAME_TYPES = 0x64 + NL80211_ATTR_RXMGMT_FLAGS = 0xbc + NL80211_ATTR_RX_SIGNAL_DBM = 0x97 + NL80211_ATTR_S1G_CAPABILITY = 0x128 + NL80211_ATTR_S1G_CAPABILITY_MASK = 0x129 + NL80211_ATTR_SAE_DATA = 0x9c + NL80211_ATTR_SAE_PASSWORD = 0x115 + NL80211_ATTR_SAE_PWE = 0x12a + NL80211_ATTR_SAR_SPEC = 0x12c + NL80211_ATTR_SCAN_FLAGS = 0x9e + NL80211_ATTR_SCAN_FREQ_KHZ = 0x124 + NL80211_ATTR_SCAN_FREQUENCIES = 0x2c + NL80211_ATTR_SCAN_GENERATION = 0x2e + NL80211_ATTR_SCAN_SSIDS = 0x2d + NL80211_ATTR_SCAN_START_TIME_TSF_BSSID = 0xea + NL80211_ATTR_SCAN_START_TIME_TSF = 0xe9 + NL80211_ATTR_SCAN_SUPP_RATES = 0x7d + NL80211_ATTR_SCHED_SCAN_DELAY = 0xdc + NL80211_ATTR_SCHED_SCAN_INTERVAL = 0x77 + NL80211_ATTR_SCHED_SCAN_MATCH = 0x84 + NL80211_ATTR_SCHED_SCAN_MATCH_SSID = 0x1 + NL80211_ATTR_SCHED_SCAN_MAX_REQS = 0x100 + NL80211_ATTR_SCHED_SCAN_MULTI = 0xff + NL80211_ATTR_SCHED_SCAN_PLANS = 0xe1 + NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI = 0xf6 + NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST = 0xf7 + NL80211_ATTR_SMPS_MODE = 0xd5 + NL80211_ATTR_SOCKET_OWNER = 0xcc + NL80211_ATTR_SOFTWARE_IFTYPES = 0x79 + NL80211_ATTR_SPLIT_WIPHY_DUMP = 0xae + NL80211_ATTR_SSID = 0x34 + NL80211_ATTR_STA_AID = 0x10 + NL80211_ATTR_STA_CAPABILITY = 0xab + NL80211_ATTR_STA_EXT_CAPABILITY = 0xac + NL80211_ATTR_STA_FLAGS2 = 0x43 + NL80211_ATTR_STA_FLAGS = 0x11 + NL80211_ATTR_STA_INFO = 0x15 + NL80211_ATTR_STA_LISTEN_INTERVAL = 0x12 + NL80211_ATTR_STA_PLINK_ACTION = 0x19 + NL80211_ATTR_STA_PLINK_STATE = 0x74 + NL80211_ATTR_STA_SUPPORTED_CHANNELS = 0xbd + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES = 0xbe + NL80211_ATTR_STA_SUPPORTED_RATES = 0x13 + NL80211_ATTR_STA_SUPPORT_P2P_PS = 0xe4 + NL80211_ATTR_STATUS_CODE = 0x48 + NL80211_ATTR_STA_TX_POWER = 0x114 + NL80211_ATTR_STA_TX_POWER_SETTING = 0x113 + NL80211_ATTR_STA_VLAN = 0x14 + NL80211_ATTR_STA_WME = 0x81 + NL80211_ATTR_SUPPORT_10_MHZ = 0xc1 + NL80211_ATTR_SUPPORT_5_MHZ = 0xc0 + NL80211_ATTR_SUPPORT_AP_UAPSD = 0x82 + NL80211_ATTR_SUPPORTED_COMMANDS = 0x32 + NL80211_ATTR_SUPPORTED_IFTYPES = 0x20 + NL80211_ATTR_SUPPORT_IBSS_RSN = 0x68 + NL80211_ATTR_SUPPORT_MESH_AUTH = 0x73 + NL80211_ATTR_SURVEY_INFO = 0x54 + NL80211_ATTR_SURVEY_RADIO_STATS = 0xda + NL80211_ATTR_TDLS_ACTION = 0x88 + NL80211_ATTR_TDLS_DIALOG_TOKEN = 0x89 + NL80211_ATTR_TDLS_EXTERNAL_SETUP = 0x8c + NL80211_ATTR_TDLS_INITIATOR = 0xcf + NL80211_ATTR_TDLS_OPERATION = 0x8a + NL80211_ATTR_TDLS_PEER_CAPABILITY = 0xcb + NL80211_ATTR_TDLS_SUPPORT = 0x8b + NL80211_ATTR_TESTDATA = 0x45 + NL80211_ATTR_TID_CONFIG = 0x11d + NL80211_ATTR_TIMED_OUT = 0x41 + NL80211_ATTR_TIMEOUT = 0x110 + NL80211_ATTR_TIMEOUT_REASON = 0xf8 + NL80211_ATTR_TSID = 0xd2 + NL80211_ATTR_TWT_RESPONDER = 0x116 + NL80211_ATTR_TX_FRAME_TYPES = 0x63 + NL80211_ATTR_TX_NO_CCK_RATE = 0x87 + NL80211_ATTR_TXQ_LIMIT = 0x10a + NL80211_ATTR_TXQ_MEMORY_LIMIT = 0x10b + NL80211_ATTR_TXQ_QUANTUM = 0x10c + NL80211_ATTR_TXQ_STATS = 0x109 + NL80211_ATTR_TX_RATES = 0x5a + NL80211_ATTR_UNSOL_BCAST_PROBE_RESP = 0x127 + NL80211_ATTR_UNSPEC = 0x0 + NL80211_ATTR_USE_MFP = 0x42 + NL80211_ATTR_USER_PRIO = 0xd3 + NL80211_ATTR_USER_REG_HINT_TYPE = 0x9a + NL80211_ATTR_USE_RRM = 0xd0 + NL80211_ATTR_VENDOR_DATA = 0xc5 + NL80211_ATTR_VENDOR_EVENTS = 0xc6 + NL80211_ATTR_VENDOR_ID = 0xc3 + NL80211_ATTR_VENDOR_SUBCMD = 0xc4 + NL80211_ATTR_VHT_CAPABILITY = 0x9d + NL80211_ATTR_VHT_CAPABILITY_MASK = 0xb0 + NL80211_ATTR_VLAN_ID = 0x11a + NL80211_ATTR_WANT_1X_4WAY_HS = 0x101 + NL80211_ATTR_WDEV = 0x99 + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX = 0x72 + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX = 0x71 + NL80211_ATTR_WIPHY_ANTENNA_RX = 0x6a + NL80211_ATTR_WIPHY_ANTENNA_TX = 0x69 + NL80211_ATTR_WIPHY_BANDS = 0x16 + NL80211_ATTR_WIPHY_CHANNEL_TYPE = 0x27 + NL80211_ATTR_WIPHY = 0x1 + NL80211_ATTR_WIPHY_COVERAGE_CLASS = 0x59 + NL80211_ATTR_WIPHY_DYN_ACK = 0xd1 + NL80211_ATTR_WIPHY_EDMG_BW_CONFIG = 0x119 + NL80211_ATTR_WIPHY_EDMG_CHANNELS = 0x118 + NL80211_ATTR_WIPHY_FRAG_THRESHOLD = 0x3f + NL80211_ATTR_WIPHY_FREQ = 0x26 + NL80211_ATTR_WIPHY_FREQ_HINT = 0xc9 + NL80211_ATTR_WIPHY_FREQ_OFFSET = 0x122 + NL80211_ATTR_WIPHY_NAME = 0x2 + NL80211_ATTR_WIPHY_RETRY_LONG = 0x3e + NL80211_ATTR_WIPHY_RETRY_SHORT = 0x3d + NL80211_ATTR_WIPHY_RTS_THRESHOLD = 0x40 + NL80211_ATTR_WIPHY_SELF_MANAGED_REG = 0xd8 + NL80211_ATTR_WIPHY_TX_POWER_LEVEL = 0x62 + NL80211_ATTR_WIPHY_TX_POWER_SETTING = 0x61 + NL80211_ATTR_WIPHY_TXQ_PARAMS = 0x25 + NL80211_ATTR_WOWLAN_TRIGGERS = 0x75 + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED = 0x76 + NL80211_ATTR_WPA_VERSIONS = 0x4b + NL80211_AUTHTYPE_AUTOMATIC = 0x8 + NL80211_AUTHTYPE_FILS_PK = 0x7 + NL80211_AUTHTYPE_FILS_SK = 0x5 + NL80211_AUTHTYPE_FILS_SK_PFS = 0x6 + NL80211_AUTHTYPE_FT = 0x2 + NL80211_AUTHTYPE_MAX = 0x7 + NL80211_AUTHTYPE_NETWORK_EAP = 0x3 + NL80211_AUTHTYPE_OPEN_SYSTEM = 0x0 + NL80211_AUTHTYPE_SAE = 0x4 + NL80211_AUTHTYPE_SHARED_KEY = 0x1 + NL80211_BAND_2GHZ = 0x0 + NL80211_BAND_5GHZ = 0x1 + NL80211_BAND_60GHZ = 0x2 + NL80211_BAND_6GHZ = 0x3 + NL80211_BAND_ATTR_EDMG_BW_CONFIG = 0xb + NL80211_BAND_ATTR_EDMG_CHANNELS = 0xa + NL80211_BAND_ATTR_FREQS = 0x1 + NL80211_BAND_ATTR_HT_AMPDU_DENSITY = 0x6 + NL80211_BAND_ATTR_HT_AMPDU_FACTOR = 0x5 + NL80211_BAND_ATTR_HT_CAPA = 0x4 + NL80211_BAND_ATTR_HT_MCS_SET = 0x3 + NL80211_BAND_ATTR_IFTYPE_DATA = 0x9 + NL80211_BAND_ATTR_MAX = 0xb + NL80211_BAND_ATTR_RATES = 0x2 + NL80211_BAND_ATTR_VHT_CAPA = 0x8 + NL80211_BAND_ATTR_VHT_MCS_SET = 0x7 + NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA = 0x6 + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC = 0x2 + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET = 0x4 + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY = 0x3 + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE = 0x5 + NL80211_BAND_IFTYPE_ATTR_IFTYPES = 0x1 + NL80211_BAND_IFTYPE_ATTR_MAX = 0x7 + NL80211_BAND_S1GHZ = 0x4 + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE = 0x2 + NL80211_BITRATE_ATTR_MAX = 0x2 + NL80211_BITRATE_ATTR_RATE = 0x1 + NL80211_BSS_BEACON_IES = 0xb + NL80211_BSS_BEACON_INTERVAL = 0x4 + NL80211_BSS_BEACON_TSF = 0xd + NL80211_BSS_BSSID = 0x1 + NL80211_BSS_CAPABILITY = 0x5 + NL80211_BSS_CHAIN_SIGNAL = 0x13 + NL80211_BSS_CHAN_WIDTH_10 = 0x1 + NL80211_BSS_CHAN_WIDTH_1 = 0x3 + NL80211_BSS_CHAN_WIDTH_20 = 0x0 + NL80211_BSS_CHAN_WIDTH_2 = 0x4 + NL80211_BSS_CHAN_WIDTH_5 = 0x2 + NL80211_BSS_CHAN_WIDTH = 0xc + NL80211_BSS_FREQUENCY = 0x2 + NL80211_BSS_FREQUENCY_OFFSET = 0x14 + NL80211_BSS_INFORMATION_ELEMENTS = 0x6 + NL80211_BSS_LAST_SEEN_BOOTTIME = 0xf + NL80211_BSS_MAX = 0x14 + NL80211_BSS_PAD = 0x10 + NL80211_BSS_PARENT_BSSID = 0x12 + NL80211_BSS_PARENT_TSF = 0x11 + NL80211_BSS_PRESP_DATA = 0xe + NL80211_BSS_SEEN_MS_AGO = 0xa + NL80211_BSS_SELECT_ATTR_BAND_PREF = 0x2 + NL80211_BSS_SELECT_ATTR_MAX = 0x3 + NL80211_BSS_SELECT_ATTR_RSSI_ADJUST = 0x3 + NL80211_BSS_SELECT_ATTR_RSSI = 0x1 + NL80211_BSS_SIGNAL_MBM = 0x7 + NL80211_BSS_SIGNAL_UNSPEC = 0x8 + NL80211_BSS_STATUS_ASSOCIATED = 0x1 + NL80211_BSS_STATUS_AUTHENTICATED = 0x0 + NL80211_BSS_STATUS = 0x9 + NL80211_BSS_STATUS_IBSS_JOINED = 0x2 + NL80211_BSS_TSF = 0x3 + NL80211_CHAN_HT20 = 0x1 + NL80211_CHAN_HT40MINUS = 0x2 + NL80211_CHAN_HT40PLUS = 0x3 + NL80211_CHAN_NO_HT = 0x0 + NL80211_CHAN_WIDTH_10 = 0x7 + NL80211_CHAN_WIDTH_160 = 0x5 + NL80211_CHAN_WIDTH_16 = 0xc + NL80211_CHAN_WIDTH_1 = 0x8 + NL80211_CHAN_WIDTH_20 = 0x1 + NL80211_CHAN_WIDTH_20_NOHT = 0x0 + NL80211_CHAN_WIDTH_2 = 0x9 + NL80211_CHAN_WIDTH_40 = 0x2 + NL80211_CHAN_WIDTH_4 = 0xa + NL80211_CHAN_WIDTH_5 = 0x6 + NL80211_CHAN_WIDTH_80 = 0x3 + NL80211_CHAN_WIDTH_80P80 = 0x4 + NL80211_CHAN_WIDTH_8 = 0xb + NL80211_CMD_ABORT_SCAN = 0x72 + NL80211_CMD_ACTION = 0x3b + NL80211_CMD_ACTION_TX_STATUS = 0x3c + NL80211_CMD_ADD_NAN_FUNCTION = 0x75 + NL80211_CMD_ADD_TX_TS = 0x69 + NL80211_CMD_ASSOCIATE = 0x26 + NL80211_CMD_AUTHENTICATE = 0x25 + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL = 0x38 + NL80211_CMD_CHANGE_NAN_CONFIG = 0x77 + NL80211_CMD_CHANNEL_SWITCH = 0x66 + NL80211_CMD_CH_SWITCH_NOTIFY = 0x58 + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY = 0x6e + NL80211_CMD_CONNECT = 0x2e + NL80211_CMD_CONN_FAILED = 0x5b + NL80211_CMD_CONTROL_PORT_FRAME = 0x81 + NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS = 0x8b + NL80211_CMD_CRIT_PROTOCOL_START = 0x62 + NL80211_CMD_CRIT_PROTOCOL_STOP = 0x63 + NL80211_CMD_DEAUTHENTICATE = 0x27 + NL80211_CMD_DEL_BEACON = 0x10 + NL80211_CMD_DEL_INTERFACE = 0x8 + NL80211_CMD_DEL_KEY = 0xc + NL80211_CMD_DEL_MPATH = 0x18 + NL80211_CMD_DEL_NAN_FUNCTION = 0x76 + NL80211_CMD_DEL_PMK = 0x7c + NL80211_CMD_DEL_PMKSA = 0x35 + NL80211_CMD_DEL_STATION = 0x14 + NL80211_CMD_DEL_TX_TS = 0x6a + NL80211_CMD_DEL_WIPHY = 0x4 + NL80211_CMD_DISASSOCIATE = 0x28 + NL80211_CMD_DISCONNECT = 0x30 + NL80211_CMD_EXTERNAL_AUTH = 0x7f + NL80211_CMD_FLUSH_PMKSA = 0x36 + NL80211_CMD_FRAME = 0x3b + NL80211_CMD_FRAME_TX_STATUS = 0x3c + NL80211_CMD_FRAME_WAIT_CANCEL = 0x43 + NL80211_CMD_FT_EVENT = 0x61 + NL80211_CMD_GET_BEACON = 0xd + NL80211_CMD_GET_COALESCE = 0x64 + NL80211_CMD_GET_FTM_RESPONDER_STATS = 0x82 + NL80211_CMD_GET_INTERFACE = 0x5 + NL80211_CMD_GET_KEY = 0x9 + NL80211_CMD_GET_MESH_CONFIG = 0x1c + NL80211_CMD_GET_MESH_PARAMS = 0x1c + NL80211_CMD_GET_MPATH = 0x15 + NL80211_CMD_GET_MPP = 0x6b + NL80211_CMD_GET_POWER_SAVE = 0x3e + NL80211_CMD_GET_PROTOCOL_FEATURES = 0x5f + NL80211_CMD_GET_REG = 0x1f + NL80211_CMD_GET_SCAN = 0x20 + NL80211_CMD_GET_STATION = 0x11 + NL80211_CMD_GET_SURVEY = 0x32 + NL80211_CMD_GET_WIPHY = 0x1 + NL80211_CMD_GET_WOWLAN = 0x49 + NL80211_CMD_JOIN_IBSS = 0x2b + NL80211_CMD_JOIN_MESH = 0x44 + NL80211_CMD_JOIN_OCB = 0x6c + NL80211_CMD_LEAVE_IBSS = 0x2c + NL80211_CMD_LEAVE_MESH = 0x45 + NL80211_CMD_LEAVE_OCB = 0x6d + NL80211_CMD_MAX = 0x93 + NL80211_CMD_MICHAEL_MIC_FAILURE = 0x29 + NL80211_CMD_NAN_MATCH = 0x78 + NL80211_CMD_NEW_BEACON = 0xf + NL80211_CMD_NEW_INTERFACE = 0x7 + NL80211_CMD_NEW_KEY = 0xb + NL80211_CMD_NEW_MPATH = 0x17 + NL80211_CMD_NEW_PEER_CANDIDATE = 0x48 + NL80211_CMD_NEW_SCAN_RESULTS = 0x22 + NL80211_CMD_NEW_STATION = 0x13 + NL80211_CMD_NEW_SURVEY_RESULTS = 0x33 + NL80211_CMD_NEW_WIPHY = 0x3 + NL80211_CMD_NOTIFY_CQM = 0x40 + NL80211_CMD_NOTIFY_RADAR = 0x86 + NL80211_CMD_PEER_MEASUREMENT_COMPLETE = 0x85 + NL80211_CMD_PEER_MEASUREMENT_RESULT = 0x84 + NL80211_CMD_PEER_MEASUREMENT_START = 0x83 + NL80211_CMD_PMKSA_CANDIDATE = 0x50 + NL80211_CMD_PORT_AUTHORIZED = 0x7d + NL80211_CMD_PROBE_CLIENT = 0x54 + NL80211_CMD_PROBE_MESH_LINK = 0x88 + NL80211_CMD_RADAR_DETECT = 0x5e + NL80211_CMD_REG_BEACON_HINT = 0x2a + NL80211_CMD_REG_CHANGE = 0x24 + NL80211_CMD_REGISTER_ACTION = 0x3a + NL80211_CMD_REGISTER_BEACONS = 0x55 + NL80211_CMD_REGISTER_FRAME = 0x3a + NL80211_CMD_RELOAD_REGDB = 0x7e + NL80211_CMD_REMAIN_ON_CHANNEL = 0x37 + NL80211_CMD_REQ_SET_REG = 0x1b + NL80211_CMD_ROAM = 0x2f + NL80211_CMD_SCAN_ABORTED = 0x23 + NL80211_CMD_SCHED_SCAN_RESULTS = 0x4d + NL80211_CMD_SCHED_SCAN_STOPPED = 0x4e + NL80211_CMD_SET_BEACON = 0xe + NL80211_CMD_SET_BSS = 0x19 + NL80211_CMD_SET_CHANNEL = 0x41 + NL80211_CMD_SET_COALESCE = 0x65 + NL80211_CMD_SET_CQM = 0x3f + NL80211_CMD_SET_INTERFACE = 0x6 + NL80211_CMD_SET_KEY = 0xa + NL80211_CMD_SET_MAC_ACL = 0x5d + NL80211_CMD_SET_MCAST_RATE = 0x5c + NL80211_CMD_SET_MESH_CONFIG = 0x1d + NL80211_CMD_SET_MESH_PARAMS = 0x1d + NL80211_CMD_SET_MGMT_EXTRA_IE = 0x1e + NL80211_CMD_SET_MPATH = 0x16 + NL80211_CMD_SET_MULTICAST_TO_UNICAST = 0x79 + NL80211_CMD_SET_NOACK_MAP = 0x57 + NL80211_CMD_SET_PMK = 0x7b + NL80211_CMD_SET_PMKSA = 0x34 + NL80211_CMD_SET_POWER_SAVE = 0x3d + NL80211_CMD_SET_QOS_MAP = 0x68 + NL80211_CMD_SET_REG = 0x1a + NL80211_CMD_SET_REKEY_OFFLOAD = 0x4f + NL80211_CMD_SET_SAR_SPECS = 0x8c + NL80211_CMD_SET_STATION = 0x12 + NL80211_CMD_SET_TID_CONFIG = 0x89 + NL80211_CMD_SET_TX_BITRATE_MASK = 0x39 + NL80211_CMD_SET_WDS_PEER = 0x42 + NL80211_CMD_SET_WIPHY = 0x2 + NL80211_CMD_SET_WIPHY_NETNS = 0x31 + NL80211_CMD_SET_WOWLAN = 0x4a + NL80211_CMD_STA_OPMODE_CHANGED = 0x80 + NL80211_CMD_START_AP = 0xf + NL80211_CMD_START_NAN = 0x73 + NL80211_CMD_START_P2P_DEVICE = 0x59 + NL80211_CMD_START_SCHED_SCAN = 0x4b + NL80211_CMD_STOP_AP = 0x10 + NL80211_CMD_STOP_NAN = 0x74 + NL80211_CMD_STOP_P2P_DEVICE = 0x5a + NL80211_CMD_STOP_SCHED_SCAN = 0x4c + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH = 0x70 + NL80211_CMD_TDLS_CHANNEL_SWITCH = 0x6f + NL80211_CMD_TDLS_MGMT = 0x52 + NL80211_CMD_TDLS_OPER = 0x51 + NL80211_CMD_TESTMODE = 0x2d + NL80211_CMD_TRIGGER_SCAN = 0x21 + NL80211_CMD_UNEXPECTED_4ADDR_FRAME = 0x56 + NL80211_CMD_UNEXPECTED_FRAME = 0x53 + NL80211_CMD_UNPROT_BEACON = 0x8a + NL80211_CMD_UNPROT_DEAUTHENTICATE = 0x46 + NL80211_CMD_UNPROT_DISASSOCIATE = 0x47 + NL80211_CMD_UNSPEC = 0x0 + NL80211_CMD_UPDATE_CONNECT_PARAMS = 0x7a + NL80211_CMD_UPDATE_FT_IES = 0x60 + NL80211_CMD_UPDATE_OWE_INFO = 0x87 + NL80211_CMD_VENDOR = 0x67 + NL80211_CMD_WIPHY_REG_CHANGE = 0x71 + NL80211_COALESCE_CONDITION_MATCH = 0x0 + NL80211_COALESCE_CONDITION_NO_MATCH = 0x1 + NL80211_CONN_FAIL_BLOCKED_CLIENT = 0x1 + NL80211_CONN_FAIL_MAX_CLIENTS = 0x0 + NL80211_CQM_RSSI_BEACON_LOSS_EVENT = 0x2 + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH = 0x1 + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW = 0x0 + NL80211_CQM_TXE_MAX_INTVL = 0x708 + NL80211_CRIT_PROTO_APIPA = 0x3 + NL80211_CRIT_PROTO_DHCP = 0x1 + NL80211_CRIT_PROTO_EAPOL = 0x2 + NL80211_CRIT_PROTO_MAX_DURATION = 0x1388 + NL80211_CRIT_PROTO_UNSPEC = 0x0 + NL80211_DFS_AVAILABLE = 0x2 + NL80211_DFS_ETSI = 0x2 + NL80211_DFS_FCC = 0x1 + NL80211_DFS_JP = 0x3 + NL80211_DFS_UNAVAILABLE = 0x1 + NL80211_DFS_UNSET = 0x0 + NL80211_DFS_USABLE = 0x0 + NL80211_EDMG_BW_CONFIG_MAX = 0xf + NL80211_EDMG_BW_CONFIG_MIN = 0x4 + NL80211_EDMG_CHANNELS_MAX = 0x3c + NL80211_EDMG_CHANNELS_MIN = 0x1 + NL80211_EXTERNAL_AUTH_ABORT = 0x1 + NL80211_EXTERNAL_AUTH_START = 0x0 + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK = 0x32 + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X = 0x10 + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK = 0xf + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP = 0x12 + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT = 0x1b + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS = 0x21 + NL80211_EXT_FEATURE_AP_PMKSA_CACHING = 0x22 + NL80211_EXT_FEATURE_AQL = 0x28 + NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT = 0x2e + NL80211_EXT_FEATURE_BEACON_PROTECTION = 0x29 + NL80211_EXT_FEATURE_BEACON_RATE_HE = 0x36 + NL80211_EXT_FEATURE_BEACON_RATE_HT = 0x7 + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY = 0x6 + NL80211_EXT_FEATURE_BEACON_RATE_VHT = 0x8 + NL80211_EXT_FEATURE_BSS_PARENT_TSF = 0x4 + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0 = 0x1f + NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH = 0x2a + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211 = 0x1a + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS = 0x30 + NL80211_EXT_FEATURE_CQM_RSSI_LIST = 0xd + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = 0x1b + NL80211_EXT_FEATURE_DEL_IBSS_STA = 0x2c + NL80211_EXT_FEATURE_DFS_OFFLOAD = 0x19 + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER = 0x20 + NL80211_EXT_FEATURE_EXT_KEY_ID = 0x24 + NL80211_EXT_FEATURE_FILS_DISCOVERY = 0x34 + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME = 0x11 + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD = 0xe + NL80211_EXT_FEATURE_FILS_STA = 0x9 + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN = 0x18 + NL80211_EXT_FEATURE_LOW_POWER_SCAN = 0x17 + NL80211_EXT_FEATURE_LOW_SPAN_SCAN = 0x16 + NL80211_EXT_FEATURE_MFP_OPTIONAL = 0x15 + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA = 0xa + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED = 0xb + NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS = 0x2d + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER = 0x2 + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 0x14 + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE = 0x13 + NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION = 0x31 + NL80211_EXT_FEATURE_PROTECTED_TWT = 0x2b + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE = 0x39 + NL80211_EXT_FEATURE_RRM = 0x1 + NL80211_EXT_FEATURE_SAE_OFFLOAD_AP = 0x33 + NL80211_EXT_FEATURE_SAE_OFFLOAD = 0x26 + NL80211_EXT_FEATURE_SCAN_FREQ_KHZ = 0x2f + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT = 0x1e + NL80211_EXT_FEATURE_SCAN_RANDOM_SN = 0x1d + NL80211_EXT_FEATURE_SCAN_START_TIME = 0x3 + NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD = 0x23 + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI = 0xc + NL80211_EXT_FEATURE_SECURE_LTF = 0x37 + NL80211_EXT_FEATURE_SECURE_RTT = 0x38 + NL80211_EXT_FEATURE_SET_SCAN_DWELL = 0x5 + NL80211_EXT_FEATURE_STA_TX_PWR = 0x25 + NL80211_EXT_FEATURE_TXQS = 0x1c + NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP = 0x35 + NL80211_EXT_FEATURE_VHT_IBSS = 0x0 + NL80211_EXT_FEATURE_VLAN_OFFLOAD = 0x27 + NL80211_FEATURE_ACKTO_ESTIMATION = 0x800000 + NL80211_FEATURE_ACTIVE_MONITOR = 0x20000 + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 0x4000 + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 0x40000 + NL80211_FEATURE_AP_SCAN = 0x100 + NL80211_FEATURE_CELL_BASE_REG_HINTS = 0x8 + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 0x80000 + NL80211_FEATURE_DYNAMIC_SMPS = 0x2000000 + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 0x8000 + NL80211_FEATURE_HT_IBSS = 0x2 + NL80211_FEATURE_INACTIVITY_TIMER = 0x4 + NL80211_FEATURE_LOW_PRIORITY_SCAN = 0x40 + NL80211_FEATURE_MAC_ON_CREATE = 0x8000000 + NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 0x80000000 + NL80211_FEATURE_NEED_OBSS_SCAN = 0x400 + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 0x10 + NL80211_FEATURE_P2P_GO_CTWIN = 0x800 + NL80211_FEATURE_P2P_GO_OPPPS = 0x1000 + NL80211_FEATURE_QUIET = 0x200000 + NL80211_FEATURE_SAE = 0x20 + NL80211_FEATURE_SCAN_FLUSH = 0x80 + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 0x20000000 + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 0x40000000 + NL80211_FEATURE_SK_TX_STATUS = 0x1 + NL80211_FEATURE_STATIC_SMPS = 0x1000000 + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 0x4000000 + NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 0x10000000 + NL80211_FEATURE_TX_POWER_INSERTION = 0x400000 + NL80211_FEATURE_USERSPACE_MPM = 0x10000 + NL80211_FEATURE_VIF_TXPOWER = 0x200 + NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 0x100000 + NL80211_FILS_DISCOVERY_ATTR_INT_MAX = 0x2 + NL80211_FILS_DISCOVERY_ATTR_INT_MIN = 0x1 + NL80211_FILS_DISCOVERY_ATTR_MAX = 0x3 + NL80211_FILS_DISCOVERY_ATTR_TMPL = 0x3 + NL80211_FILS_DISCOVERY_TMPL_MIN_LEN = 0x2a + NL80211_FREQUENCY_ATTR_16MHZ = 0x19 + NL80211_FREQUENCY_ATTR_1MHZ = 0x15 + NL80211_FREQUENCY_ATTR_2MHZ = 0x16 + NL80211_FREQUENCY_ATTR_4MHZ = 0x17 + NL80211_FREQUENCY_ATTR_8MHZ = 0x18 + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME = 0xd + NL80211_FREQUENCY_ATTR_DFS_STATE = 0x7 + NL80211_FREQUENCY_ATTR_DFS_TIME = 0x8 + NL80211_FREQUENCY_ATTR_DISABLED = 0x2 + NL80211_FREQUENCY_ATTR_FREQ = 0x1 + NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf + NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe + NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf + NL80211_FREQUENCY_ATTR_MAX = 0x19 + NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 + NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 + NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc + NL80211_FREQUENCY_ATTR_NO_20MHZ = 0x10 + NL80211_FREQUENCY_ATTR_NO_80MHZ = 0xb + NL80211_FREQUENCY_ATTR_NO_HE = 0x13 + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS = 0x9 + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS = 0xa + NL80211_FREQUENCY_ATTR_NO_IBSS = 0x3 + NL80211_FREQUENCY_ATTR_NO_IR = 0x3 + NL80211_FREQUENCY_ATTR_OFFSET = 0x14 + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN = 0x3 + NL80211_FREQUENCY_ATTR_RADAR = 0x5 + NL80211_FREQUENCY_ATTR_WMM = 0x12 + NL80211_FTM_RESP_ATTR_CIVICLOC = 0x3 + NL80211_FTM_RESP_ATTR_ENABLED = 0x1 + NL80211_FTM_RESP_ATTR_LCI = 0x2 + NL80211_FTM_RESP_ATTR_MAX = 0x3 + NL80211_FTM_STATS_ASAP_NUM = 0x4 + NL80211_FTM_STATS_FAILED_NUM = 0x3 + NL80211_FTM_STATS_MAX = 0xa + NL80211_FTM_STATS_NON_ASAP_NUM = 0x5 + NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM = 0x9 + NL80211_FTM_STATS_PAD = 0xa + NL80211_FTM_STATS_PARTIAL_NUM = 0x2 + NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM = 0x8 + NL80211_FTM_STATS_SUCCESS_NUM = 0x1 + NL80211_FTM_STATS_TOTAL_DURATION_MSEC = 0x6 + NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM = 0x7 + NL80211_GENL_NAME = "nl80211" + NL80211_HE_BSS_COLOR_ATTR_COLOR = 0x1 + NL80211_HE_BSS_COLOR_ATTR_DISABLED = 0x2 + NL80211_HE_BSS_COLOR_ATTR_MAX = 0x3 + NL80211_HE_BSS_COLOR_ATTR_PARTIAL = 0x3 + NL80211_HE_MAX_CAPABILITY_LEN = 0x36 + NL80211_HE_MIN_CAPABILITY_LEN = 0x10 + NL80211_HE_NSS_MAX = 0x8 + NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP = 0x4 + NL80211_HE_OBSS_PD_ATTR_MAX = 0x6 + NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET = 0x2 + NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET = 0x1 + NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET = 0x3 + NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP = 0x5 + NL80211_HE_OBSS_PD_ATTR_SR_CTRL = 0x6 + NL80211_HIDDEN_SSID_NOT_IN_USE = 0x0 + NL80211_HIDDEN_SSID_ZERO_CONTENTS = 0x2 + NL80211_HIDDEN_SSID_ZERO_LEN = 0x1 + NL80211_HT_CAPABILITY_LEN = 0x1a + NL80211_IFACE_COMB_BI_MIN_GCD = 0x7 + NL80211_IFACE_COMB_LIMITS = 0x1 + NL80211_IFACE_COMB_MAXNUM = 0x2 + NL80211_IFACE_COMB_NUM_CHANNELS = 0x4 + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS = 0x6 + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS = 0x5 + NL80211_IFACE_COMB_STA_AP_BI_MATCH = 0x3 + NL80211_IFACE_COMB_UNSPEC = 0x0 + NL80211_IFACE_LIMIT_MAX = 0x1 + NL80211_IFACE_LIMIT_TYPES = 0x2 + NL80211_IFACE_LIMIT_UNSPEC = 0x0 + NL80211_IFTYPE_ADHOC = 0x1 + NL80211_IFTYPE_AKM_ATTR_IFTYPES = 0x1 + NL80211_IFTYPE_AKM_ATTR_MAX = 0x2 + NL80211_IFTYPE_AKM_ATTR_SUITES = 0x2 + NL80211_IFTYPE_AP = 0x3 + NL80211_IFTYPE_AP_VLAN = 0x4 + NL80211_IFTYPE_MAX = 0xc + NL80211_IFTYPE_MESH_POINT = 0x7 + NL80211_IFTYPE_MONITOR = 0x6 + NL80211_IFTYPE_NAN = 0xc + NL80211_IFTYPE_OCB = 0xb + NL80211_IFTYPE_P2P_CLIENT = 0x8 + NL80211_IFTYPE_P2P_DEVICE = 0xa + NL80211_IFTYPE_P2P_GO = 0x9 + NL80211_IFTYPE_STATION = 0x2 + NL80211_IFTYPE_UNSPECIFIED = 0x0 + NL80211_IFTYPE_WDS = 0x5 + NL80211_KCK_EXT_LEN = 0x18 + NL80211_KCK_LEN = 0x10 + NL80211_KEK_EXT_LEN = 0x20 + NL80211_KEK_LEN = 0x10 + NL80211_KEY_CIPHER = 0x3 + NL80211_KEY_DATA = 0x1 + NL80211_KEY_DEFAULT_BEACON = 0xa + NL80211_KEY_DEFAULT = 0x5 + NL80211_KEY_DEFAULT_MGMT = 0x6 + NL80211_KEY_DEFAULT_TYPE_MULTICAST = 0x2 + NL80211_KEY_DEFAULT_TYPES = 0x8 + NL80211_KEY_DEFAULT_TYPE_UNICAST = 0x1 + NL80211_KEY_IDX = 0x2 + NL80211_KEY_MAX = 0xa + NL80211_KEY_MODE = 0x9 + NL80211_KEY_NO_TX = 0x1 + NL80211_KEY_RX_TX = 0x0 + NL80211_KEY_SEQ = 0x4 + NL80211_KEY_SET_TX = 0x2 + NL80211_KEY_TYPE = 0x7 + NL80211_KEYTYPE_GROUP = 0x0 + NL80211_KEYTYPE_PAIRWISE = 0x1 + NL80211_KEYTYPE_PEERKEY = 0x2 + NL80211_MAX_NR_AKM_SUITES = 0x2 + NL80211_MAX_NR_CIPHER_SUITES = 0x5 + NL80211_MAX_SUPP_HT_RATES = 0x4d + NL80211_MAX_SUPP_RATES = 0x20 + NL80211_MAX_SUPP_REG_RULES = 0x80 + NL80211_MESHCONF_ATTR_MAX = 0x1f + NL80211_MESHCONF_AUTO_OPEN_PLINKS = 0x7 + NL80211_MESHCONF_AWAKE_WINDOW = 0x1b + NL80211_MESHCONF_CONFIRM_TIMEOUT = 0x2 + NL80211_MESHCONF_CONNECTED_TO_AS = 0x1f + NL80211_MESHCONF_CONNECTED_TO_GATE = 0x1d + NL80211_MESHCONF_ELEMENT_TTL = 0xf + NL80211_MESHCONF_FORWARDING = 0x13 + NL80211_MESHCONF_GATE_ANNOUNCEMENTS = 0x11 + NL80211_MESHCONF_HOLDING_TIMEOUT = 0x3 + NL80211_MESHCONF_HT_OPMODE = 0x16 + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT = 0xb + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL = 0x19 + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES = 0x8 + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME = 0xd + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT = 0x17 + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL = 0x12 + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL = 0xc + NL80211_MESHCONF_HWMP_RANN_INTERVAL = 0x10 + NL80211_MESHCONF_HWMP_ROOT_INTERVAL = 0x18 + NL80211_MESHCONF_HWMP_ROOTMODE = 0xe + NL80211_MESHCONF_MAX_PEER_LINKS = 0x4 + NL80211_MESHCONF_MAX_RETRIES = 0x5 + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT = 0xa + NL80211_MESHCONF_NOLEARN = 0x1e + NL80211_MESHCONF_PATH_REFRESH_TIME = 0x9 + NL80211_MESHCONF_PLINK_TIMEOUT = 0x1c + NL80211_MESHCONF_POWER_MODE = 0x1a + NL80211_MESHCONF_RETRY_TIMEOUT = 0x1 + NL80211_MESHCONF_RSSI_THRESHOLD = 0x14 + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR = 0x15 + NL80211_MESHCONF_TTL = 0x6 + NL80211_MESH_POWER_ACTIVE = 0x1 + NL80211_MESH_POWER_DEEP_SLEEP = 0x3 + NL80211_MESH_POWER_LIGHT_SLEEP = 0x2 + NL80211_MESH_POWER_MAX = 0x3 + NL80211_MESH_POWER_UNKNOWN = 0x0 + NL80211_MESH_SETUP_ATTR_MAX = 0x8 + NL80211_MESH_SETUP_AUTH_PROTOCOL = 0x8 + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC = 0x2 + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL = 0x1 + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC = 0x6 + NL80211_MESH_SETUP_IE = 0x3 + NL80211_MESH_SETUP_USERSPACE_AMPE = 0x5 + NL80211_MESH_SETUP_USERSPACE_AUTH = 0x4 + NL80211_MESH_SETUP_USERSPACE_MPM = 0x7 + NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE = 0x3 + NL80211_MFP_NO = 0x0 + NL80211_MFP_OPTIONAL = 0x2 + NL80211_MFP_REQUIRED = 0x1 + NL80211_MIN_REMAIN_ON_CHANNEL_TIME = 0xa + NL80211_MNTR_FLAG_ACTIVE = 0x6 + NL80211_MNTR_FLAG_CONTROL = 0x3 + NL80211_MNTR_FLAG_COOK_FRAMES = 0x5 + NL80211_MNTR_FLAG_FCSFAIL = 0x1 + NL80211_MNTR_FLAG_MAX = 0x6 + NL80211_MNTR_FLAG_OTHER_BSS = 0x4 + NL80211_MNTR_FLAG_PLCPFAIL = 0x2 + NL80211_MPATH_FLAG_ACTIVE = 0x1 + NL80211_MPATH_FLAG_FIXED = 0x8 + NL80211_MPATH_FLAG_RESOLVED = 0x10 + NL80211_MPATH_FLAG_RESOLVING = 0x2 + NL80211_MPATH_FLAG_SN_VALID = 0x4 + NL80211_MPATH_INFO_DISCOVERY_RETRIES = 0x7 + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT = 0x6 + NL80211_MPATH_INFO_EXPTIME = 0x4 + NL80211_MPATH_INFO_FLAGS = 0x5 + NL80211_MPATH_INFO_FRAME_QLEN = 0x1 + NL80211_MPATH_INFO_HOP_COUNT = 0x8 + NL80211_MPATH_INFO_MAX = 0x9 + NL80211_MPATH_INFO_METRIC = 0x3 + NL80211_MPATH_INFO_PATH_CHANGE = 0x9 + NL80211_MPATH_INFO_SN = 0x2 + NL80211_MULTICAST_GROUP_CONFIG = "config" + NL80211_MULTICAST_GROUP_MLME = "mlme" + NL80211_MULTICAST_GROUP_NAN = "nan" + NL80211_MULTICAST_GROUP_REG = "regulatory" + NL80211_MULTICAST_GROUP_SCAN = "scan" + NL80211_MULTICAST_GROUP_TESTMODE = "testmode" + NL80211_MULTICAST_GROUP_VENDOR = "vendor" + NL80211_NAN_FUNC_ATTR_MAX = 0x10 + NL80211_NAN_FUNC_CLOSE_RANGE = 0x9 + NL80211_NAN_FUNC_FOLLOW_UP = 0x2 + NL80211_NAN_FUNC_FOLLOW_UP_DEST = 0x8 + NL80211_NAN_FUNC_FOLLOW_UP_ID = 0x6 + NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID = 0x7 + NL80211_NAN_FUNC_INSTANCE_ID = 0xf + NL80211_NAN_FUNC_MAX_TYPE = 0x2 + NL80211_NAN_FUNC_PUBLISH_BCAST = 0x4 + NL80211_NAN_FUNC_PUBLISH = 0x0 + NL80211_NAN_FUNC_PUBLISH_TYPE = 0x3 + NL80211_NAN_FUNC_RX_MATCH_FILTER = 0xd + NL80211_NAN_FUNC_SERVICE_ID = 0x2 + NL80211_NAN_FUNC_SERVICE_ID_LEN = 0x6 + NL80211_NAN_FUNC_SERVICE_INFO = 0xb + NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN = 0xff + NL80211_NAN_FUNC_SRF = 0xc + NL80211_NAN_FUNC_SRF_MAX_LEN = 0xff + NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE = 0x5 + NL80211_NAN_FUNC_SUBSCRIBE = 0x1 + NL80211_NAN_FUNC_TERM_REASON = 0x10 + NL80211_NAN_FUNC_TERM_REASON_ERROR = 0x2 + NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED = 0x1 + NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST = 0x0 + NL80211_NAN_FUNC_TTL = 0xa + NL80211_NAN_FUNC_TX_MATCH_FILTER = 0xe + NL80211_NAN_FUNC_TYPE = 0x1 + NL80211_NAN_MATCH_ATTR_MAX = 0x2 + NL80211_NAN_MATCH_FUNC_LOCAL = 0x1 + NL80211_NAN_MATCH_FUNC_PEER = 0x2 + NL80211_NAN_SOLICITED_PUBLISH = 0x1 + NL80211_NAN_SRF_ATTR_MAX = 0x4 + NL80211_NAN_SRF_BF = 0x2 + NL80211_NAN_SRF_BF_IDX = 0x3 + NL80211_NAN_SRF_INCLUDE = 0x1 + NL80211_NAN_SRF_MAC_ADDRS = 0x4 + NL80211_NAN_UNSOLICITED_PUBLISH = 0x2 + NL80211_NUM_ACS = 0x4 + NL80211_P2P_PS_SUPPORTED = 0x1 + NL80211_P2P_PS_UNSUPPORTED = 0x0 + NL80211_PKTPAT_MASK = 0x1 + NL80211_PKTPAT_OFFSET = 0x3 + NL80211_PKTPAT_PATTERN = 0x2 + NL80211_PLINK_ACTION_BLOCK = 0x2 + NL80211_PLINK_ACTION_NO_ACTION = 0x0 + NL80211_PLINK_ACTION_OPEN = 0x1 + NL80211_PLINK_BLOCKED = 0x6 + NL80211_PLINK_CNF_RCVD = 0x3 + NL80211_PLINK_ESTAB = 0x4 + NL80211_PLINK_HOLDING = 0x5 + NL80211_PLINK_LISTEN = 0x0 + NL80211_PLINK_OPN_RCVD = 0x2 + NL80211_PLINK_OPN_SNT = 0x1 + NL80211_PMKSA_CANDIDATE_BSSID = 0x2 + NL80211_PMKSA_CANDIDATE_INDEX = 0x1 + NL80211_PMKSA_CANDIDATE_PREAUTH = 0x3 + NL80211_PMSR_ATTR_MAX = 0x5 + NL80211_PMSR_ATTR_MAX_PEERS = 0x1 + NL80211_PMSR_ATTR_PEERS = 0x5 + NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR = 0x3 + NL80211_PMSR_ATTR_REPORT_AP_TSF = 0x2 + NL80211_PMSR_ATTR_TYPE_CAPA = 0x4 + NL80211_PMSR_FTM_CAPA_ATTR_ASAP = 0x1 + NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS = 0x6 + NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT = 0x7 + NL80211_PMSR_FTM_CAPA_ATTR_MAX = 0xa + NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST = 0x8 + NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP = 0x2 + NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED = 0xa + NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES = 0x5 + NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC = 0x4 + NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI = 0x3 + NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED = 0x9 + NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS = 0x7 + NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP = 0x5 + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE = 0x1 + NL80211_PMSR_FTM_FAILURE_PEER_BUSY = 0x6 + NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE = 0x4 + NL80211_PMSR_FTM_FAILURE_REJECTED = 0x2 + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED = 0x0 + NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL = 0x3 + NL80211_PMSR_FTM_REQ_ATTR_ASAP = 0x1 + NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION = 0x5 + NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD = 0x4 + NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST = 0x6 + NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK = 0xc + NL80211_PMSR_FTM_REQ_ATTR_MAX = 0xd + NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED = 0xb + NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP = 0x3 + NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES = 0x7 + NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE = 0x2 + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC = 0x9 + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI = 0x8 + NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED = 0xa + NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION = 0x7 + NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX = 0x2 + NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME = 0x5 + NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC = 0x14 + NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG = 0x10 + NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD = 0x12 + NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE = 0x11 + NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON = 0x1 + NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST = 0x8 + NL80211_PMSR_FTM_RESP_ATTR_LCI = 0x13 + NL80211_PMSR_FTM_RESP_ATTR_MAX = 0x15 + NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP = 0x6 + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS = 0x3 + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES = 0x4 + NL80211_PMSR_FTM_RESP_ATTR_PAD = 0x15 + NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG = 0x9 + NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD = 0xa + NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG = 0xd + NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD = 0xf + NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE = 0xe + NL80211_PMSR_FTM_RESP_ATTR_RX_RATE = 0xc + NL80211_PMSR_FTM_RESP_ATTR_TX_RATE = 0xb + NL80211_PMSR_PEER_ATTR_ADDR = 0x1 + NL80211_PMSR_PEER_ATTR_CHAN = 0x2 + NL80211_PMSR_PEER_ATTR_MAX = 0x4 + NL80211_PMSR_PEER_ATTR_REQ = 0x3 + NL80211_PMSR_PEER_ATTR_RESP = 0x4 + NL80211_PMSR_REQ_ATTR_DATA = 0x1 + NL80211_PMSR_REQ_ATTR_GET_AP_TSF = 0x2 + NL80211_PMSR_REQ_ATTR_MAX = 0x2 + NL80211_PMSR_RESP_ATTR_AP_TSF = 0x4 + NL80211_PMSR_RESP_ATTR_DATA = 0x1 + NL80211_PMSR_RESP_ATTR_FINAL = 0x5 + NL80211_PMSR_RESP_ATTR_HOST_TIME = 0x3 + NL80211_PMSR_RESP_ATTR_MAX = 0x6 + NL80211_PMSR_RESP_ATTR_PAD = 0x6 + NL80211_PMSR_RESP_ATTR_STATUS = 0x2 + NL80211_PMSR_STATUS_FAILURE = 0x3 + NL80211_PMSR_STATUS_REFUSED = 0x1 + NL80211_PMSR_STATUS_SUCCESS = 0x0 + NL80211_PMSR_STATUS_TIMEOUT = 0x2 + NL80211_PMSR_TYPE_FTM = 0x1 + NL80211_PMSR_TYPE_INVALID = 0x0 + NL80211_PMSR_TYPE_MAX = 0x1 + NL80211_PREAMBLE_DMG = 0x3 + NL80211_PREAMBLE_HE = 0x4 + NL80211_PREAMBLE_HT = 0x1 + NL80211_PREAMBLE_LEGACY = 0x0 + NL80211_PREAMBLE_VHT = 0x2 + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 0x8 + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 0x4 + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 0x2 + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 0x1 + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 0x1 + NL80211_PS_DISABLED = 0x0 + NL80211_PS_ENABLED = 0x1 + NL80211_RADAR_CAC_ABORTED = 0x2 + NL80211_RADAR_CAC_FINISHED = 0x1 + NL80211_RADAR_CAC_STARTED = 0x5 + NL80211_RADAR_DETECTED = 0x0 + NL80211_RADAR_NOP_FINISHED = 0x3 + NL80211_RADAR_PRE_CAC_EXPIRED = 0x4 + NL80211_RATE_INFO_10_MHZ_WIDTH = 0xb + NL80211_RATE_INFO_160_MHZ_WIDTH = 0xa + NL80211_RATE_INFO_40_MHZ_WIDTH = 0x3 + NL80211_RATE_INFO_5_MHZ_WIDTH = 0xc + NL80211_RATE_INFO_80_MHZ_WIDTH = 0x8 + NL80211_RATE_INFO_80P80_MHZ_WIDTH = 0x9 + NL80211_RATE_INFO_BITRATE32 = 0x5 + NL80211_RATE_INFO_BITRATE = 0x1 + NL80211_RATE_INFO_HE_1XLTF = 0x0 + NL80211_RATE_INFO_HE_2XLTF = 0x1 + NL80211_RATE_INFO_HE_4XLTF = 0x2 + NL80211_RATE_INFO_HE_DCM = 0x10 + NL80211_RATE_INFO_HE_GI_0_8 = 0x0 + NL80211_RATE_INFO_HE_GI_1_6 = 0x1 + NL80211_RATE_INFO_HE_GI_3_2 = 0x2 + NL80211_RATE_INFO_HE_GI = 0xf + NL80211_RATE_INFO_HE_MCS = 0xd + NL80211_RATE_INFO_HE_NSS = 0xe + NL80211_RATE_INFO_HE_RU_ALLOC_106 = 0x2 + NL80211_RATE_INFO_HE_RU_ALLOC_242 = 0x3 + NL80211_RATE_INFO_HE_RU_ALLOC_26 = 0x0 + NL80211_RATE_INFO_HE_RU_ALLOC_2x996 = 0x6 + NL80211_RATE_INFO_HE_RU_ALLOC_484 = 0x4 + NL80211_RATE_INFO_HE_RU_ALLOC_52 = 0x1 + NL80211_RATE_INFO_HE_RU_ALLOC_996 = 0x5 + NL80211_RATE_INFO_HE_RU_ALLOC = 0x11 + NL80211_RATE_INFO_MAX = 0x11 + NL80211_RATE_INFO_MCS = 0x2 + NL80211_RATE_INFO_SHORT_GI = 0x4 + NL80211_RATE_INFO_VHT_MCS = 0x6 + NL80211_RATE_INFO_VHT_NSS = 0x7 + NL80211_REGDOM_SET_BY_CORE = 0x0 + NL80211_REGDOM_SET_BY_COUNTRY_IE = 0x3 + NL80211_REGDOM_SET_BY_DRIVER = 0x2 + NL80211_REGDOM_SET_BY_USER = 0x1 + NL80211_REGDOM_TYPE_COUNTRY = 0x0 + NL80211_REGDOM_TYPE_CUSTOM_WORLD = 0x2 + NL80211_REGDOM_TYPE_INTERSECTION = 0x3 + NL80211_REGDOM_TYPE_WORLD = 0x1 + NL80211_REG_RULE_ATTR_MAX = 0x7 + NL80211_REKEY_DATA_AKM = 0x4 + NL80211_REKEY_DATA_KCK = 0x2 + NL80211_REKEY_DATA_KEK = 0x1 + NL80211_REKEY_DATA_REPLAY_CTR = 0x3 + NL80211_REPLAY_CTR_LEN = 0x8 + NL80211_RRF_AUTO_BW = 0x800 + NL80211_RRF_DFS = 0x10 + NL80211_RRF_GO_CONCURRENT = 0x1000 + NL80211_RRF_IR_CONCURRENT = 0x1000 + NL80211_RRF_NO_160MHZ = 0x10000 + NL80211_RRF_NO_80MHZ = 0x8000 + NL80211_RRF_NO_CCK = 0x2 + NL80211_RRF_NO_HE = 0x20000 + NL80211_RRF_NO_HT40 = 0x6000 + NL80211_RRF_NO_HT40MINUS = 0x2000 + NL80211_RRF_NO_HT40PLUS = 0x4000 + NL80211_RRF_NO_IBSS = 0x80 + NL80211_RRF_NO_INDOOR = 0x4 + NL80211_RRF_NO_IR_ALL = 0x180 + NL80211_RRF_NO_IR = 0x80 + NL80211_RRF_NO_OFDM = 0x1 + NL80211_RRF_NO_OUTDOOR = 0x8 + NL80211_RRF_PASSIVE_SCAN = 0x80 + NL80211_RRF_PTMP_ONLY = 0x40 + NL80211_RRF_PTP_ONLY = 0x20 + NL80211_RXMGMT_FLAG_ANSWERED = 0x1 + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 0x2 + NL80211_SAE_PWE_BOTH = 0x3 + NL80211_SAE_PWE_HASH_TO_ELEMENT = 0x2 + NL80211_SAE_PWE_HUNT_AND_PECK = 0x1 + NL80211_SAE_PWE_UNSPECIFIED = 0x0 + NL80211_SAR_ATTR_MAX = 0x2 + NL80211_SAR_ATTR_SPECS = 0x2 + NL80211_SAR_ATTR_SPECS_END_FREQ = 0x4 + NL80211_SAR_ATTR_SPECS_MAX = 0x4 + NL80211_SAR_ATTR_SPECS_POWER = 0x1 + NL80211_SAR_ATTR_SPECS_RANGE_INDEX = 0x2 + NL80211_SAR_ATTR_SPECS_START_FREQ = 0x3 + NL80211_SAR_ATTR_TYPE = 0x1 + NL80211_SAR_TYPE_POWER = 0x0 + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 0x20 + NL80211_SCAN_FLAG_AP = 0x4 + NL80211_SCAN_FLAG_COLOCATED_6GHZ = 0x4000 + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 0x10 + NL80211_SCAN_FLAG_FLUSH = 0x2 + NL80211_SCAN_FLAG_FREQ_KHZ = 0x2000 + NL80211_SCAN_FLAG_HIGH_ACCURACY = 0x400 + NL80211_SCAN_FLAG_LOW_POWER = 0x200 + NL80211_SCAN_FLAG_LOW_PRIORITY = 0x1 + NL80211_SCAN_FLAG_LOW_SPAN = 0x100 + NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 0x1000 + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 0x80 + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 0x40 + NL80211_SCAN_FLAG_RANDOM_ADDR = 0x8 + NL80211_SCAN_FLAG_RANDOM_SN = 0x800 + NL80211_SCAN_RSSI_THOLD_OFF = -0x12c + NL80211_SCHED_SCAN_MATCH_ATTR_BSSID = 0x5 + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = 0x6 + NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI = 0x3 + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST = 0x4 + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI = 0x2 + NL80211_SCHED_SCAN_MATCH_ATTR_SSID = 0x1 + NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI = 0x6 + NL80211_SCHED_SCAN_PLAN_INTERVAL = 0x1 + NL80211_SCHED_SCAN_PLAN_ITERATIONS = 0x2 + NL80211_SCHED_SCAN_PLAN_MAX = 0x2 + NL80211_SMPS_DYNAMIC = 0x2 + NL80211_SMPS_MAX = 0x2 + NL80211_SMPS_OFF = 0x0 + NL80211_SMPS_STATIC = 0x1 + NL80211_STA_BSS_PARAM_BEACON_INTERVAL = 0x5 + NL80211_STA_BSS_PARAM_CTS_PROT = 0x1 + NL80211_STA_BSS_PARAM_DTIM_PERIOD = 0x4 + NL80211_STA_BSS_PARAM_MAX = 0x5 + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE = 0x2 + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME = 0x3 + NL80211_STA_FLAG_ASSOCIATED = 0x7 + NL80211_STA_FLAG_AUTHENTICATED = 0x5 + NL80211_STA_FLAG_AUTHORIZED = 0x1 + NL80211_STA_FLAG_MAX = 0x7 + NL80211_STA_FLAG_MAX_OLD_API = 0x6 + NL80211_STA_FLAG_MFP = 0x4 + NL80211_STA_FLAG_SHORT_PREAMBLE = 0x2 + NL80211_STA_FLAG_TDLS_PEER = 0x6 + NL80211_STA_FLAG_WME = 0x3 + NL80211_STA_INFO_ACK_SIGNAL_AVG = 0x23 + NL80211_STA_INFO_ACK_SIGNAL = 0x22 + NL80211_STA_INFO_AIRTIME_LINK_METRIC = 0x29 + NL80211_STA_INFO_AIRTIME_WEIGHT = 0x28 + NL80211_STA_INFO_ASSOC_AT_BOOTTIME = 0x2a + NL80211_STA_INFO_BEACON_LOSS = 0x12 + NL80211_STA_INFO_BEACON_RX = 0x1d + NL80211_STA_INFO_BEACON_SIGNAL_AVG = 0x1e + NL80211_STA_INFO_BSS_PARAM = 0xf + NL80211_STA_INFO_CHAIN_SIGNAL_AVG = 0x1a + NL80211_STA_INFO_CHAIN_SIGNAL = 0x19 + NL80211_STA_INFO_CONNECTED_TIME = 0x10 + NL80211_STA_INFO_CONNECTED_TO_AS = 0x2b + NL80211_STA_INFO_CONNECTED_TO_GATE = 0x26 + NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG = 0x23 + NL80211_STA_INFO_EXPECTED_THROUGHPUT = 0x1b + NL80211_STA_INFO_FCS_ERROR_COUNT = 0x25 + NL80211_STA_INFO_INACTIVE_TIME = 0x1 + NL80211_STA_INFO_LLID = 0x4 + NL80211_STA_INFO_LOCAL_PM = 0x14 + NL80211_STA_INFO_MAX = 0x2b + NL80211_STA_INFO_NONPEER_PM = 0x16 + NL80211_STA_INFO_PAD = 0x21 + NL80211_STA_INFO_PEER_PM = 0x15 + NL80211_STA_INFO_PLID = 0x5 + NL80211_STA_INFO_PLINK_STATE = 0x6 + NL80211_STA_INFO_RX_BITRATE = 0xe + NL80211_STA_INFO_RX_BYTES64 = 0x17 + NL80211_STA_INFO_RX_BYTES = 0x2 + NL80211_STA_INFO_RX_DROP_MISC = 0x1c + NL80211_STA_INFO_RX_DURATION = 0x20 + NL80211_STA_INFO_RX_MPDUS = 0x24 + NL80211_STA_INFO_RX_PACKETS = 0x9 + NL80211_STA_INFO_SIGNAL_AVG = 0xd + NL80211_STA_INFO_SIGNAL = 0x7 + NL80211_STA_INFO_STA_FLAGS = 0x11 + NL80211_STA_INFO_TID_STATS = 0x1f + NL80211_STA_INFO_T_OFFSET = 0x13 + NL80211_STA_INFO_TX_BITRATE = 0x8 + NL80211_STA_INFO_TX_BYTES64 = 0x18 + NL80211_STA_INFO_TX_BYTES = 0x3 + NL80211_STA_INFO_TX_DURATION = 0x27 + NL80211_STA_INFO_TX_FAILED = 0xc + NL80211_STA_INFO_TX_PACKETS = 0xa + NL80211_STA_INFO_TX_RETRIES = 0xb + NL80211_STA_WME_MAX = 0x2 + NL80211_STA_WME_MAX_SP = 0x2 + NL80211_STA_WME_UAPSD_QUEUES = 0x1 + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY = 0x5 + NL80211_SURVEY_INFO_CHANNEL_TIME = 0x4 + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 0x6 + NL80211_SURVEY_INFO_CHANNEL_TIME_RX = 0x7 + NL80211_SURVEY_INFO_CHANNEL_TIME_TX = 0x8 + NL80211_SURVEY_INFO_FREQUENCY = 0x1 + NL80211_SURVEY_INFO_FREQUENCY_OFFSET = 0xc + NL80211_SURVEY_INFO_IN_USE = 0x3 + NL80211_SURVEY_INFO_MAX = 0xc + NL80211_SURVEY_INFO_NOISE = 0x2 + NL80211_SURVEY_INFO_PAD = 0xa + NL80211_SURVEY_INFO_TIME_BSS_RX = 0xb + NL80211_SURVEY_INFO_TIME_BUSY = 0x5 + NL80211_SURVEY_INFO_TIME = 0x4 + NL80211_SURVEY_INFO_TIME_EXT_BUSY = 0x6 + NL80211_SURVEY_INFO_TIME_RX = 0x7 + NL80211_SURVEY_INFO_TIME_SCAN = 0x9 + NL80211_SURVEY_INFO_TIME_TX = 0x8 + NL80211_TDLS_DISABLE_LINK = 0x4 + NL80211_TDLS_DISCOVERY_REQ = 0x0 + NL80211_TDLS_ENABLE_LINK = 0x3 + NL80211_TDLS_PEER_HE = 0x8 + NL80211_TDLS_PEER_HT = 0x1 + NL80211_TDLS_PEER_VHT = 0x2 + NL80211_TDLS_PEER_WMM = 0x4 + NL80211_TDLS_SETUP = 0x1 + NL80211_TDLS_TEARDOWN = 0x2 + NL80211_TID_CONFIG_ATTR_AMPDU_CTRL = 0x9 + NL80211_TID_CONFIG_ATTR_AMSDU_CTRL = 0xb + NL80211_TID_CONFIG_ATTR_MAX = 0xd + NL80211_TID_CONFIG_ATTR_NOACK = 0x6 + NL80211_TID_CONFIG_ATTR_OVERRIDE = 0x4 + NL80211_TID_CONFIG_ATTR_PAD = 0x1 + NL80211_TID_CONFIG_ATTR_PEER_SUPP = 0x3 + NL80211_TID_CONFIG_ATTR_RETRY_LONG = 0x8 + NL80211_TID_CONFIG_ATTR_RETRY_SHORT = 0x7 + NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL = 0xa + NL80211_TID_CONFIG_ATTR_TIDS = 0x5 + NL80211_TID_CONFIG_ATTR_TX_RATE = 0xd + NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE = 0xc + NL80211_TID_CONFIG_ATTR_VIF_SUPP = 0x2 + NL80211_TID_CONFIG_DISABLE = 0x1 + NL80211_TID_CONFIG_ENABLE = 0x0 + NL80211_TID_STATS_MAX = 0x6 + NL80211_TID_STATS_PAD = 0x5 + NL80211_TID_STATS_RX_MSDU = 0x1 + NL80211_TID_STATS_TX_MSDU = 0x2 + NL80211_TID_STATS_TX_MSDU_FAILED = 0x4 + NL80211_TID_STATS_TX_MSDU_RETRIES = 0x3 + NL80211_TID_STATS_TXQ_STATS = 0x6 + NL80211_TIMEOUT_ASSOC = 0x3 + NL80211_TIMEOUT_AUTH = 0x2 + NL80211_TIMEOUT_SCAN = 0x1 + NL80211_TIMEOUT_UNSPECIFIED = 0x0 + NL80211_TKIP_DATA_OFFSET_ENCR_KEY = 0x0 + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY = 0x18 + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY = 0x10 + NL80211_TX_POWER_AUTOMATIC = 0x0 + NL80211_TX_POWER_FIXED = 0x2 + NL80211_TX_POWER_LIMITED = 0x1 + NL80211_TXQ_ATTR_AC = 0x1 + NL80211_TXQ_ATTR_AIFS = 0x5 + NL80211_TXQ_ATTR_CWMAX = 0x4 + NL80211_TXQ_ATTR_CWMIN = 0x3 + NL80211_TXQ_ATTR_MAX = 0x5 + NL80211_TXQ_ATTR_QUEUE = 0x1 + NL80211_TXQ_ATTR_TXOP = 0x2 + NL80211_TXQ_Q_BE = 0x2 + NL80211_TXQ_Q_BK = 0x3 + NL80211_TXQ_Q_VI = 0x1 + NL80211_TXQ_Q_VO = 0x0 + NL80211_TXQ_STATS_BACKLOG_BYTES = 0x1 + NL80211_TXQ_STATS_BACKLOG_PACKETS = 0x2 + NL80211_TXQ_STATS_COLLISIONS = 0x8 + NL80211_TXQ_STATS_DROPS = 0x4 + NL80211_TXQ_STATS_ECN_MARKS = 0x5 + NL80211_TXQ_STATS_FLOWS = 0x3 + NL80211_TXQ_STATS_MAX = 0xb + NL80211_TXQ_STATS_MAX_FLOWS = 0xb + NL80211_TXQ_STATS_OVERLIMIT = 0x6 + NL80211_TXQ_STATS_OVERMEMORY = 0x7 + NL80211_TXQ_STATS_TX_BYTES = 0x9 + NL80211_TXQ_STATS_TX_PACKETS = 0xa + NL80211_TX_RATE_AUTOMATIC = 0x0 + NL80211_TXRATE_DEFAULT_GI = 0x0 + NL80211_TX_RATE_FIXED = 0x2 + NL80211_TXRATE_FORCE_LGI = 0x2 + NL80211_TXRATE_FORCE_SGI = 0x1 + NL80211_TXRATE_GI = 0x4 + NL80211_TXRATE_HE = 0x5 + NL80211_TXRATE_HE_GI = 0x6 + NL80211_TXRATE_HE_LTF = 0x7 + NL80211_TXRATE_HT = 0x2 + NL80211_TXRATE_LEGACY = 0x1 + NL80211_TX_RATE_LIMITED = 0x1 + NL80211_TXRATE_MAX = 0x7 + NL80211_TXRATE_MCS = 0x2 + NL80211_TXRATE_VHT = 0x3 + NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT = 0x1 + NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX = 0x2 + NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL = 0x2 + NL80211_USER_REG_HINT_CELL_BASE = 0x1 + NL80211_USER_REG_HINT_INDOOR = 0x2 + NL80211_USER_REG_HINT_USER = 0x0 + NL80211_VENDOR_ID_IS_LINUX = 0x80000000 + NL80211_VHT_CAPABILITY_LEN = 0xc + NL80211_VHT_NSS_MAX = 0x8 + NL80211_WIPHY_NAME_MAXLEN = 0x40 + NL80211_WMMR_AIFSN = 0x3 + NL80211_WMMR_CW_MAX = 0x2 + NL80211_WMMR_CW_MIN = 0x1 + NL80211_WMMR_MAX = 0x4 + NL80211_WMMR_TXOP = 0x4 + NL80211_WOWLAN_PKTPAT_MASK = 0x1 + NL80211_WOWLAN_PKTPAT_OFFSET = 0x3 + NL80211_WOWLAN_PKTPAT_PATTERN = 0x2 + NL80211_WOWLAN_TCP_DATA_INTERVAL = 0x9 + NL80211_WOWLAN_TCP_DATA_PAYLOAD = 0x6 + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ = 0x7 + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN = 0x8 + NL80211_WOWLAN_TCP_DST_IPV4 = 0x2 + NL80211_WOWLAN_TCP_DST_MAC = 0x3 + NL80211_WOWLAN_TCP_DST_PORT = 0x5 + NL80211_WOWLAN_TCP_SRC_IPV4 = 0x1 + NL80211_WOWLAN_TCP_SRC_PORT = 0x4 + NL80211_WOWLAN_TCP_WAKE_MASK = 0xb + NL80211_WOWLAN_TCP_WAKE_PAYLOAD = 0xa + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE = 0x8 + NL80211_WOWLAN_TRIG_ANY = 0x1 + NL80211_WOWLAN_TRIG_DISCONNECT = 0x2 + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST = 0x7 + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE = 0x6 + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED = 0x5 + NL80211_WOWLAN_TRIG_MAGIC_PKT = 0x3 + NL80211_WOWLAN_TRIG_NET_DETECT = 0x12 + NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS = 0x13 + NL80211_WOWLAN_TRIG_PKT_PATTERN = 0x4 + NL80211_WOWLAN_TRIG_RFKILL_RELEASE = 0x9 + NL80211_WOWLAN_TRIG_TCP_CONNECTION = 0xe + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 = 0xa + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN = 0xb + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 = 0xc + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN = 0xd + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST = 0x10 + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH = 0xf + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS = 0x11 + NL80211_WPA_VERSION_1 = 0x1 + NL80211_WPA_VERSION_2 = 0x2 + NL80211_WPA_VERSION_3 = 0x4 +) + +const ( + FRA_UNSPEC = 0x0 + FRA_DST = 0x1 + FRA_SRC = 0x2 + FRA_IIFNAME = 0x3 + FRA_GOTO = 0x4 + FRA_UNUSED2 = 0x5 + FRA_PRIORITY = 0x6 + FRA_UNUSED3 = 0x7 + FRA_UNUSED4 = 0x8 + FRA_UNUSED5 = 0x9 + FRA_FWMARK = 0xa + FRA_FLOW = 0xb + FRA_TUN_ID = 0xc + FRA_SUPPRESS_IFGROUP = 0xd + FRA_SUPPRESS_PREFIXLEN = 0xe + FRA_TABLE = 0xf + FRA_FWMASK = 0x10 + FRA_OIFNAME = 0x11 + FRA_PAD = 0x12 + FRA_L3MDEV = 0x13 + FRA_UID_RANGE = 0x14 + FRA_PROTOCOL = 0x15 + FRA_IP_PROTO = 0x16 + FRA_SPORT_RANGE = 0x17 + FRA_DPORT_RANGE = 0x18 + FR_ACT_UNSPEC = 0x0 + FR_ACT_TO_TBL = 0x1 + FR_ACT_GOTO = 0x2 + FR_ACT_NOP = 0x3 + FR_ACT_RES3 = 0x4 + FR_ACT_RES4 = 0x5 + FR_ACT_BLACKHOLE = 0x6 + FR_ACT_UNREACHABLE = 0x7 + FR_ACT_PROHIBIT = 0x8 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index bea25494..4948362f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux @@ -240,6 +240,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -250,6 +254,13 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ [116]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -311,6 +322,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index b8c8f289..f64345e0 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux @@ -255,6 +255,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -265,6 +269,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -324,6 +336,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 4db44301..72469c79 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux @@ -231,6 +231,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -241,6 +245,13 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ [116]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -302,6 +313,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 3ebcad8a..68f07228 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux @@ -234,6 +234,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -244,6 +248,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -303,6 +315,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go new file mode 100644 index 00000000..090ae46c --- /dev/null +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -0,0 +1,679 @@ +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build loong64 && linux +// +build loong64,linux + +package unix + +const ( + SizeofPtr = 0x8 + SizeofLong = 0x8 +) + +type ( + _C_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Timex struct { + Modes uint32 + Offset int64 + Freq int64 + Maxerror int64 + Esterror int64 + Status int32 + Constant int64 + Precision int64 + Tolerance int64 + Time Timeval + Tick int64 + Ppsfreq int64 + Jitter int64 + Shift int32 + Stabil int64 + Jitcnt int64 + Calcnt int64 + Errcnt int64 + Stbcnt int64 + Tai int32 + _ [44]byte +} + +type Time_t int64 + +type Tms struct { + Utime int64 + Stime int64 + Cutime int64 + Cstime int64 +} + +type Utimbuf struct { + Actime int64 + Modtime int64 +} + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Stat_t struct { + Dev uint64 + Ino uint64 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint64 + _ uint64 + Size int64 + Blksize int32 + _ int32 + Blocks int64 + Atim Timespec + Mtim Timespec + Ctim Timespec + _ [2]int32 +} + +type Dirent struct { + Ino uint64 + Off int64 + Reclen uint16 + Type uint8 + Name [256]int8 + _ [5]byte +} + +type Flock_t struct { + Type int16 + Whence int16 + Start int64 + Len int64 + Pid int32 + _ [4]byte +} + +type DmNameList struct { + Dev uint64 + Next uint32 + Name [0]byte + _ [4]byte +} + +const ( + FADV_DONTNEED = 0x4 + FADV_NOREUSE = 0x5 +) + +type RawSockaddrNFCLLCP struct { + Sa_family uint16 + Dev_idx uint32 + Target_idx uint32 + Nfc_protocol uint32 + Dsap uint8 + Ssap uint8 + Service_name [63]uint8 + Service_name_len uint64 +} + +type RawSockaddr struct { + Family uint16 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [96]int8 +} + +type Iovec struct { + Base *byte + Len uint64 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + _ [4]byte +} + +type Cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type ifreq struct { + Ifrn [16]byte + Ifru [24]byte +} + +const ( + SizeofSockaddrNFCLLCP = 0x60 + SizeofIovec = 0x10 + SizeofMsghdr = 0x38 + SizeofCmsghdr = 0x10 +) + +const ( + SizeofSockFprog = 0x10 +) + +type PtraceRegs struct { + Regs [32]uint64 + Orig_a0 uint64 + Era uint64 + Badv uint64 + Reserved [10]uint64 +} + +type FdSet struct { + Bits [16]int64 +} + +type Sysinfo_t struct { + Uptime int64 + Loads [3]uint64 + Totalram uint64 + Freeram uint64 + Sharedram uint64 + Bufferram uint64 + Totalswap uint64 + Freeswap uint64 + Procs uint16 + Pad uint16 + Totalhigh uint64 + Freehigh uint64 + Unit uint32 + _ [0]int8 + _ [4]byte +} + +type Ustat_t struct { + Tfree int32 + Tinode uint64 + Fname [6]int8 + Fpack [6]int8 + _ [4]byte +} + +type EpollEvent struct { + Events uint32 + _ int32 + Fd int32 + Pad int32 +} + +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + +const ( + POLLRDHUP = 0x2000 +) + +type Sigset_t struct { + Val [16]uint64 +} + +const _C__NSIG = 0x41 + +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Line uint8 + Cc [19]uint8 + Ispeed uint32 + Ospeed uint32 +} + +type Taskstats struct { + Version uint16 + Ac_exitcode uint32 + Ac_flag uint8 + Ac_nice uint8 + Cpu_count uint64 + Cpu_delay_total uint64 + Blkio_count uint64 + Blkio_delay_total uint64 + Swapin_count uint64 + Swapin_delay_total uint64 + Cpu_run_real_total uint64 + Cpu_run_virtual_total uint64 + Ac_comm [32]int8 + Ac_sched uint8 + Ac_pad [3]uint8 + _ [4]byte + Ac_uid uint32 + Ac_gid uint32 + Ac_pid uint32 + Ac_ppid uint32 + Ac_btime uint32 + Ac_etime uint64 + Ac_utime uint64 + Ac_stime uint64 + Ac_minflt uint64 + Ac_majflt uint64 + Coremem uint64 + Virtmem uint64 + Hiwater_rss uint64 + Hiwater_vm uint64 + Read_char uint64 + Write_char uint64 + Read_syscalls uint64 + Write_syscalls uint64 + Read_bytes uint64 + Write_bytes uint64 + Cancelled_write_bytes uint64 + Nvcsw uint64 + Nivcsw uint64 + Ac_utimescaled uint64 + Ac_stimescaled uint64 + Cpu_scaled_run_real_total uint64 + Freepages_count uint64 + Freepages_delay_total uint64 + Thrashing_count uint64 + Thrashing_delay_total uint64 + Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 +} + +type cpuMask uint64 + +const ( + _NCPUBITS = 0x40 +) + +const ( + CBitFieldMaskBit0 = 0x1 + CBitFieldMaskBit1 = 0x2 + CBitFieldMaskBit2 = 0x4 + CBitFieldMaskBit3 = 0x8 + CBitFieldMaskBit4 = 0x10 + CBitFieldMaskBit5 = 0x20 + CBitFieldMaskBit6 = 0x40 + CBitFieldMaskBit7 = 0x80 + CBitFieldMaskBit8 = 0x100 + CBitFieldMaskBit9 = 0x200 + CBitFieldMaskBit10 = 0x400 + CBitFieldMaskBit11 = 0x800 + CBitFieldMaskBit12 = 0x1000 + CBitFieldMaskBit13 = 0x2000 + CBitFieldMaskBit14 = 0x4000 + CBitFieldMaskBit15 = 0x8000 + CBitFieldMaskBit16 = 0x10000 + CBitFieldMaskBit17 = 0x20000 + CBitFieldMaskBit18 = 0x40000 + CBitFieldMaskBit19 = 0x80000 + CBitFieldMaskBit20 = 0x100000 + CBitFieldMaskBit21 = 0x200000 + CBitFieldMaskBit22 = 0x400000 + CBitFieldMaskBit23 = 0x800000 + CBitFieldMaskBit24 = 0x1000000 + CBitFieldMaskBit25 = 0x2000000 + CBitFieldMaskBit26 = 0x4000000 + CBitFieldMaskBit27 = 0x8000000 + CBitFieldMaskBit28 = 0x10000000 + CBitFieldMaskBit29 = 0x20000000 + CBitFieldMaskBit30 = 0x40000000 + CBitFieldMaskBit31 = 0x80000000 + CBitFieldMaskBit32 = 0x100000000 + CBitFieldMaskBit33 = 0x200000000 + CBitFieldMaskBit34 = 0x400000000 + CBitFieldMaskBit35 = 0x800000000 + CBitFieldMaskBit36 = 0x1000000000 + CBitFieldMaskBit37 = 0x2000000000 + CBitFieldMaskBit38 = 0x4000000000 + CBitFieldMaskBit39 = 0x8000000000 + CBitFieldMaskBit40 = 0x10000000000 + CBitFieldMaskBit41 = 0x20000000000 + CBitFieldMaskBit42 = 0x40000000000 + CBitFieldMaskBit43 = 0x80000000000 + CBitFieldMaskBit44 = 0x100000000000 + CBitFieldMaskBit45 = 0x200000000000 + CBitFieldMaskBit46 = 0x400000000000 + CBitFieldMaskBit47 = 0x800000000000 + CBitFieldMaskBit48 = 0x1000000000000 + CBitFieldMaskBit49 = 0x2000000000000 + CBitFieldMaskBit50 = 0x4000000000000 + CBitFieldMaskBit51 = 0x8000000000000 + CBitFieldMaskBit52 = 0x10000000000000 + CBitFieldMaskBit53 = 0x20000000000000 + CBitFieldMaskBit54 = 0x40000000000000 + CBitFieldMaskBit55 = 0x80000000000000 + CBitFieldMaskBit56 = 0x100000000000000 + CBitFieldMaskBit57 = 0x200000000000000 + CBitFieldMaskBit58 = 0x400000000000000 + CBitFieldMaskBit59 = 0x800000000000000 + CBitFieldMaskBit60 = 0x1000000000000000 + CBitFieldMaskBit61 = 0x2000000000000000 + CBitFieldMaskBit62 = 0x4000000000000000 + CBitFieldMaskBit63 = 0x8000000000000000 +) + +type SockaddrStorage struct { + Family uint16 + _ [118]int8 + _ uint64 +} + +type HDGeometry struct { + Heads uint8 + Sectors uint8 + Cylinders uint16 + Start uint64 +} + +type Statfs_t struct { + Type int64 + Bsize int64 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid Fsid + Namelen int64 + Frsize int64 + Flags int64 + Spare [4]int64 +} + +type TpacketHdr struct { + Status uint64 + Len uint32 + Snaplen uint32 + Mac uint16 + Net uint16 + Sec uint32 + Usec uint32 + _ [4]byte +} + +const ( + SizeofTpacketHdr = 0x20 +) + +type RTCPLLInfo struct { + Ctrl int32 + Value int32 + Max int32 + Min int32 + Posmult int32 + Negmult int32 + Clock int64 +} + +type BlkpgPartition struct { + Start int64 + Length int64 + Pno int32 + Devname [64]uint8 + Volname [64]uint8 + _ [4]byte +} + +const ( + BLKPG = 0x1269 +) + +type XDPUmemReg struct { + Addr uint64 + Len uint64 + Size uint32 + Headroom uint32 + Flags uint32 + _ [4]byte +} + +type CryptoUserAlg struct { + Name [64]int8 + Driver_name [64]int8 + Module_name [64]int8 + Type uint32 + Mask uint32 + Refcnt uint32 + Flags uint32 +} + +type CryptoStatAEAD struct { + Type [64]int8 + Encrypt_cnt uint64 + Encrypt_tlen uint64 + Decrypt_cnt uint64 + Decrypt_tlen uint64 + Err_cnt uint64 +} + +type CryptoStatAKCipher struct { + Type [64]int8 + Encrypt_cnt uint64 + Encrypt_tlen uint64 + Decrypt_cnt uint64 + Decrypt_tlen uint64 + Verify_cnt uint64 + Sign_cnt uint64 + Err_cnt uint64 +} + +type CryptoStatCipher struct { + Type [64]int8 + Encrypt_cnt uint64 + Encrypt_tlen uint64 + Decrypt_cnt uint64 + Decrypt_tlen uint64 + Err_cnt uint64 +} + +type CryptoStatCompress struct { + Type [64]int8 + Compress_cnt uint64 + Compress_tlen uint64 + Decompress_cnt uint64 + Decompress_tlen uint64 + Err_cnt uint64 +} + +type CryptoStatHash struct { + Type [64]int8 + Hash_cnt uint64 + Hash_tlen uint64 + Err_cnt uint64 +} + +type CryptoStatKPP struct { + Type [64]int8 + Setsecret_cnt uint64 + Generate_public_key_cnt uint64 + Compute_shared_secret_cnt uint64 + Err_cnt uint64 +} + +type CryptoStatRNG struct { + Type [64]int8 + Generate_cnt uint64 + Generate_tlen uint64 + Seed_cnt uint64 + Err_cnt uint64 +} + +type CryptoStatLarval struct { + Type [64]int8 +} + +type CryptoReportLarval struct { + Type [64]int8 +} + +type CryptoReportHash struct { + Type [64]int8 + Blocksize uint32 + Digestsize uint32 +} + +type CryptoReportCipher struct { + Type [64]int8 + Blocksize uint32 + Min_keysize uint32 + Max_keysize uint32 +} + +type CryptoReportBlkCipher struct { + Type [64]int8 + Geniv [64]int8 + Blocksize uint32 + Min_keysize uint32 + Max_keysize uint32 + Ivsize uint32 +} + +type CryptoReportAEAD struct { + Type [64]int8 + Geniv [64]int8 + Blocksize uint32 + Maxauthsize uint32 + Ivsize uint32 +} + +type CryptoReportComp struct { + Type [64]int8 +} + +type CryptoReportRNG struct { + Type [64]int8 + Seedsize uint32 +} + +type CryptoReportAKCipher struct { + Type [64]int8 +} + +type CryptoReportKPP struct { + Type [64]int8 +} + +type CryptoReportAcomp struct { + Type [64]int8 +} + +type LoopInfo struct { + Number int32 + Device uint32 + Inode uint64 + Rdevice uint32 + Offset int32 + Encrypt_type int32 + Encrypt_key_size int32 + Flags int32 + Name [64]int8 + Encrypt_key [32]uint8 + Init [2]uint64 + Reserved [4]int8 + _ [4]byte +} + +type TIPCSubscr struct { + Seq TIPCServiceRange + Timeout uint32 + Filter uint32 + Handle [8]int8 +} + +type TIPCSIOCLNReq struct { + Peer uint32 + Id uint32 + Linkname [68]int8 +} + +type TIPCSIOCNodeIDReq struct { + Peer uint32 + Id [16]int8 +} + +type PPSKInfo struct { + Assert_sequence uint32 + Clear_sequence uint32 + Assert_tu PPSKTime + Clear_tu PPSKTime + Current_mode int32 + _ [4]byte +} + +const ( + PPS_GETPARAMS = 0x800870a1 + PPS_SETPARAMS = 0x400870a2 + PPS_GETCAP = 0x800870a3 + PPS_FETCH = 0xc00870a4 +) + +const ( + PIDFD_NONBLOCK = 0x800 +) + +type SysvIpcPerm struct { + Key int32 + Uid uint32 + Gid uint32 + Cuid uint32 + Cgid uint32 + Mode uint32 + _ [0]uint8 + Seq uint16 + _ uint16 + _ uint64 + _ uint64 +} +type SysvShmDesc struct { + Perm SysvIpcPerm + Segsz uint64 + Atime int64 + Dtime int64 + Ctime int64 + Cpid int32 + Lpid int32 + Nattch uint64 + _ uint64 + _ uint64 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 3eb33e48..03604cca 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux @@ -236,6 +236,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -246,6 +250,13 @@ type Sigset_t struct { const _C__NSIG = 0x80 +type Siginfo struct { + Signo int32 + Code int32 + Errno int32 + _ [116]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -307,6 +318,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index 79a94467..fe57a7b2 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux @@ -237,6 +237,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -247,6 +251,14 @@ type Sigset_t struct { const _C__NSIG = 0x80 +type Siginfo struct { + Signo int32 + Code int32 + Errno int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -306,6 +318,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 8f4b107c..3f0db4da 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux @@ -237,6 +237,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -247,6 +251,14 @@ type Sigset_t struct { const _C__NSIG = 0x80 +type Siginfo struct { + Signo int32 + Code int32 + Errno int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -306,6 +318,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index e4eb2179..70ecd3b2 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux @@ -236,6 +236,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -246,6 +250,13 @@ type Sigset_t struct { const _C__NSIG = 0x80 +type Siginfo struct { + Signo int32 + Code int32 + Errno int32 + _ [116]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -307,6 +318,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index d5b21f0f..4e700120 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux @@ -243,6 +243,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -253,6 +257,13 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ [116]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -314,6 +325,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 5188d142..34a57c69 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux @@ -244,6 +244,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -254,6 +258,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -313,6 +325,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index de4dd4c7..6b84a472 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux @@ -244,6 +244,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -254,6 +258,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -313,6 +325,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index dccbf9b0..c4a305fe 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux @@ -262,6 +262,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -272,6 +276,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -331,6 +343,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index 63588061..a1f1e4c9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux @@ -210,8 +210,8 @@ type PtraceFpregs struct { } type PtracePer struct { - _ [0]uint64 - _ [32]byte + Control_regs [3]uint64 + _ [8]byte Starting_addr uint64 Ending_addr uint64 Perc_atmid uint16 @@ -257,6 +257,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x80000 +) + const ( POLLRDHUP = 0x2000 ) @@ -267,6 +271,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -326,6 +338,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 765edc13..df95ebf3 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include /build/unix/linux/types.go | go run mkpost.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux @@ -239,6 +239,10 @@ type EpollEvent struct { Pad int32 } +const ( + OPEN_TREE_CLOEXEC = 0x400000 +) + const ( POLLRDHUP = 0x800 ) @@ -249,6 +253,14 @@ type Sigset_t struct { const _C__NSIG = 0x41 +type Siginfo struct { + Signo int32 + Errno int32 + Code int32 + _ int32 + _ [112]byte +} + type Termios struct { Iflag uint32 Oflag uint32 @@ -308,6 +320,8 @@ type Taskstats struct { Thrashing_count uint64 Thrashing_delay_total uint64 Ac_btime64 uint64 + Compact_count uint64 + Compact_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go index baf5fe65..2ed718ca 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go @@ -94,10 +94,10 @@ type Statfs_t struct { F_namemax uint32 F_owner uint32 F_ctime uint64 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - F_mntfromspec [90]int8 + F_fstypename [16]byte + F_mntonname [90]byte + F_mntfromname [90]byte + F_mntfromspec [90]byte Pad_cgo_0 [2]byte Mount_info [160]byte } diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go index e21ae8ec..b4fb97eb 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go @@ -96,10 +96,10 @@ type Statfs_t struct { F_namemax uint32 F_owner uint32 F_ctime uint64 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - F_mntfromspec [90]int8 + F_fstypename [16]byte + F_mntonname [90]byte + F_mntfromname [90]byte + F_mntfromspec [90]byte _ [2]byte Mount_info [160]byte } diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go index f190651c..2c467504 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go @@ -98,10 +98,10 @@ type Statfs_t struct { F_namemax uint32 F_owner uint32 F_ctime uint64 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - F_mntfromspec [90]int8 + F_fstypename [16]byte + F_mntonname [90]byte + F_mntfromname [90]byte + F_mntfromspec [90]byte _ [2]byte Mount_info [160]byte } diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go index 84747c58..ddee0451 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go @@ -94,10 +94,10 @@ type Statfs_t struct { F_namemax uint32 F_owner uint32 F_ctime uint64 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - F_mntfromspec [90]int8 + F_fstypename [16]byte + F_mntonname [90]byte + F_mntfromname [90]byte + F_mntfromspec [90]byte _ [2]byte Mount_info [160]byte } diff --git a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go index ac5c8b63..eb13d4e8 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go @@ -94,10 +94,10 @@ type Statfs_t struct { F_namemax uint32 F_owner uint32 F_ctime uint64 - F_fstypename [16]int8 - F_mntonname [90]int8 - F_mntfromname [90]int8 - F_mntfromspec [90]int8 + F_fstypename [16]byte + F_mntonname [90]byte + F_mntfromname [90]byte + F_mntfromspec [90]byte _ [2]byte Mount_info [160]byte } diff --git a/vendor/golang.org/x/sys/windows/exec_windows.go b/vendor/golang.org/x/sys/windows/exec_windows.go index 855698bb..75980fd4 100644 --- a/vendor/golang.org/x/sys/windows/exec_windows.go +++ b/vendor/golang.org/x/sys/windows/exec_windows.go @@ -15,11 +15,11 @@ import ( // in http://msdn.microsoft.com/en-us/library/ms880421. // This function returns "" (2 double quotes) if s is empty. // Alternatively, these transformations are done: -// - every back slash (\) is doubled, but only if immediately -// followed by double quote ("); -// - every double quote (") is escaped by back slash (\); -// - finally, s is wrapped with double quotes (arg -> "arg"), -// but only if there is space or tab inside s. +// - every back slash (\) is doubled, but only if immediately +// followed by double quote ("); +// - every double quote (") is escaped by back slash (\); +// - finally, s is wrapped with double quotes (arg -> "arg"), +// but only if there is space or tab inside s. func EscapeArg(s string) string { if len(s) == 0 { return "\"\"" diff --git a/vendor/golang.org/x/sys/windows/registry/key.go b/vendor/golang.org/x/sys/windows/registry/key.go index 906325e0..6c8d97b6 100644 --- a/vendor/golang.org/x/sys/windows/registry/key.go +++ b/vendor/golang.org/x/sys/windows/registry/key.go @@ -20,7 +20,6 @@ // log.Fatal(err) // } // fmt.Printf("Windows system root is %q\n", s) -// package registry import ( diff --git a/vendor/golang.org/x/sys/windows/svc/eventlog/log.go b/vendor/golang.org/x/sys/windows/svc/eventlog/log.go index a1796fbb..f37b4b51 100644 --- a/vendor/golang.org/x/sys/windows/svc/eventlog/log.go +++ b/vendor/golang.org/x/sys/windows/svc/eventlog/log.go @@ -6,7 +6,6 @@ // +build windows // Package eventlog implements access to Windows event log. -// package eventlog import ( diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go index de75f4a4..c2dc8701 100644 --- a/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go +++ b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go @@ -9,7 +9,6 @@ // It can be used to install and remove them. It can also start, // stop and pause them. The package can query / change current // service state and config parameters. -// package mgr import ( diff --git a/vendor/golang.org/x/sys/windows/svc/service.go b/vendor/golang.org/x/sys/windows/svc/service.go index 5b05c3e3..806baa05 100644 --- a/vendor/golang.org/x/sys/windows/svc/service.go +++ b/vendor/golang.org/x/sys/windows/svc/service.go @@ -6,7 +6,6 @@ // +build windows // Package svc provides everything required to build Windows service. -// package svc import ( diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index cf44e693..636e5de6 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -10,6 +10,7 @@ import ( errorspkg "errors" "fmt" "runtime" + "strings" "sync" "syscall" "time" @@ -86,10 +87,8 @@ func StringToUTF16(s string) []uint16 { // s, with a terminating NUL added. If s contains a NUL byte at any // location, it returns (nil, syscall.EINVAL). func UTF16FromString(s string) ([]uint16, error) { - for i := 0; i < len(s); i++ { - if s[i] == 0 { - return nil, syscall.EINVAL - } + if strings.IndexByte(s, 0) != -1 { + return nil, syscall.EINVAL } return utf16.Encode([]rune(s + "\x00")), nil } @@ -186,8 +185,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) //sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState -//sys ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) -//sys WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) +//sys readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = ReadFile +//sys writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = WriteFile //sys GetOverlappedResult(handle Handle, overlapped *Overlapped, done *uint32, wait bool) (err error) //sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) [failretval==0xffffffff] //sys CloseHandle(handle Handle) (err error) @@ -549,12 +548,6 @@ func Read(fd Handle, p []byte) (n int, err error) { } return 0, e } - if raceenabled { - if done > 0 { - raceWriteRange(unsafe.Pointer(&p[0]), int(done)) - } - raceAcquire(unsafe.Pointer(&ioSync)) - } return int(done), nil } @@ -567,12 +560,31 @@ func Write(fd Handle, p []byte) (n int, err error) { if e != nil { return 0, e } - if raceenabled && done > 0 { - raceReadRange(unsafe.Pointer(&p[0]), int(done)) - } return int(done), nil } +func ReadFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error { + err := readFile(fd, p, done, overlapped) + if raceenabled { + if *done > 0 { + raceWriteRange(unsafe.Pointer(&p[0]), int(*done)) + } + raceAcquire(unsafe.Pointer(&ioSync)) + } + return err +} + +func WriteFile(fd Handle, p []byte, done *uint32, overlapped *Overlapped) error { + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + err := writeFile(fd, p, done, overlapped) + if raceenabled && *done > 0 { + raceReadRange(unsafe.Pointer(&p[0]), int(*done)) + } + return err +} + var ioSync int64 func Seek(fd Handle, offset int64, whence int) (newoffset int64, err error) { @@ -611,7 +623,6 @@ var ( func getStdHandle(stdhandle uint32) (fd Handle) { r, _ := GetStdHandle(stdhandle) - CloseOnExec(r) return r } diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 9ea1a44f..68f52c1e 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -2761,7 +2761,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree return } -func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { +func readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] @@ -3203,7 +3203,7 @@ func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, return } -func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { +func writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] diff --git a/vendor/golang.org/x/term/codereview.cfg b/vendor/golang.org/x/term/codereview.cfg new file mode 100644 index 00000000..3f8b14b6 --- /dev/null +++ b/vendor/golang.org/x/term/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/golang.org/x/term/term.go b/vendor/golang.org/x/term/term.go index 1f6a38fa..d5927088 100644 --- a/vendor/golang.org/x/term/term.go +++ b/vendor/golang.org/x/term/term.go @@ -12,6 +12,8 @@ // panic(err) // } // defer term.Restore(int(os.Stdin.Fd()), oldState) +// +// Note that on non-Unix systems os.Stdin.Fd() may not be 0. package term // State contains the state of a terminal. diff --git a/vendor/modules.txt b/vendor/modules.txt index cb1a2db6..3b698e75 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -146,7 +146,7 @@ github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc github.com/json-iterator/go # github.com/kylelemons/godebug v1.1.0 ## explicit; go 1.11 -# github.com/lucas-clemente/quic-go v0.27.1 => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da +# github.com/lucas-clemente/quic-go v0.28.1 => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e ## explicit; go 1.16 github.com/lucas-clemente/quic-go github.com/lucas-clemente/quic-go/internal/ackhandler @@ -164,12 +164,15 @@ github.com/lucas-clemente/quic-go/quicvarint # github.com/marten-seemann/qtls-go1-16 v0.1.5 ## explicit; go 1.16 github.com/marten-seemann/qtls-go1-16 -# github.com/marten-seemann/qtls-go1-17 v0.1.1 +# github.com/marten-seemann/qtls-go1-17 v0.1.2 ## explicit; go 1.17 github.com/marten-seemann/qtls-go1-17 -# github.com/marten-seemann/qtls-go1-18 v0.1.1 +# github.com/marten-seemann/qtls-go1-18 v0.1.2 ## explicit; go 1.18 github.com/marten-seemann/qtls-go1-18 +# github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 +## explicit; go 1.19 +github.com/marten-seemann/qtls-go1-19 # github.com/mattn/go-colorable v0.1.8 ## explicit; go 1.13 github.com/mattn/go-colorable @@ -338,7 +341,7 @@ golang.org/x/crypto/ssh/terminal ## explicit; go 1.12 golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d +# golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e ## explicit; go 1.17 golang.org/x/net/bpf golang.org/x/net/context @@ -363,7 +366,7 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ## explicit golang.org/x/sync/errgroup -# golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 +# golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -375,7 +378,7 @@ golang.org/x/sys/windows/registry golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/eventlog golang.org/x/sys/windows/svc/mgr -# golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b +# golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 ## explicit; go 1.17 golang.org/x/term # golang.org/x/text v0.3.7 @@ -543,6 +546,6 @@ zombiezen.com/go/capnproto2/schemas zombiezen.com/go/capnproto2/server zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220607112311-13144fbde8da +# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 # gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 From d4d9a43dd7b673627532afcd08fdfe1aae916a60 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 8 Aug 2022 15:17:01 +0100 Subject: [PATCH 148/238] TUN-6639: Validate cyclic ingress configuration It is currently possible to set cloudflared to proxy to the hostname that traffic is ingressing from as an origin service. This change checks for this configuration error and prompts a change. --- ingress/ingress.go | 4 ++++ ingress/ingress_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/ingress/ingress.go b/ingress/ingress.go index 05a90a8b..cc0fca55 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -232,6 +232,10 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq } else { service = newTCPOverWSService(u) } + + if u.Hostname() == r.Hostname { + return Ingress{}, fmt.Errorf("Cyclic Ingress configuration: Hostname:%s points to service:%s.", r.Hostname, r.Service) + } } if err := validateHostname(r, i, len(ingress)); err != nil { diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 1ff5e11c..5bf3f912 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -404,6 +404,16 @@ ingress: service: https://localhost:8000 - hostname: "*" service: https://localhost:8001 +`}, + wantErr: true, + }, + { + name: "Cyclic hostname definition", + args: args{rawYAML: ` +ingress: + - hostname: "test.example.com" + service: https://test.example.com + - service: http_status_404 `}, wantErr: true, }, From 4016334efcceefaf3e2c99990b01f3a9803c8b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Tue, 9 Aug 2022 18:10:51 +0100 Subject: [PATCH 149/238] TUN-6642: Fix unexpected close of quic stream triggered by upstream origin close This commit guarantees that stream is only closed once the are finished handling the stream. Without it, we were seeing closes being triggered by the code that proxies to the origin, which was resulting in failures to actually send downstream the status code of the proxy request to the eyeball. This was then subsequently triggering unexpected retries to cloudflared in situations such as cloudflared being unable to reach the origin. --- connection/quic.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/connection/quic.go b/connection/quic.go index 12e1d2b6..84bbb1e8 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -147,7 +147,11 @@ func (q *QUICConnection) runStream(quicStream quic.Stream) { stream := quicpogs.NewSafeStreamCloser(quicStream) defer stream.Close() - if err := q.handleStream(ctx, stream); err != nil { + // we are going to fuse readers/writers from stream <- cloudflared -> origin, and we want to guarantee that + // code executed in the code path of handleStream don't trigger an earlier close to the downstream stream. + // So, we wrap the stream with a no-op closer and only this method can actually close the stream. + noCloseStream := &nopCloserReadWriter{stream} + if err := q.handleStream(ctx, noCloseStream); err != nil { q.logger.Err(err).Msg("Failed to handle QUIC stream") } } @@ -395,3 +399,11 @@ func isTransferEncodingChunked(req *http.Request) bool { // separated value as well. return strings.Contains(strings.ToLower(transferEncodingVal), "chunked") } + +type nopCloserReadWriter struct { + io.ReadWriteCloser +} + +func (n *nopCloserReadWriter) Close() error { + return nil +} From 065d8355c5bdc9dd1329eb28422114ba4ed6aad0 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 10 Aug 2022 14:19:03 +0100 Subject: [PATCH 150/238] TUN-6637: Upgrade quic-go --- connection/quic_test.go | 122 +++++++++--------- go.mod | 2 +- go.sum | 4 +- .../lucas-clemente/quic-go/client.go | 7 +- .../lucas-clemente/quic-go/config.go | 23 ++-- .../quic-go/conn_id_generator.go | 9 +- .../lucas-clemente/quic-go/connection.go | 2 + .../lucas-clemente/quic-go/interface.go | 17 +++ .../internal/protocol/connection_id.go | 12 ++ .../lucas-clemente/quic-go/server.go | 12 +- .../quic-go/sys_conn_df_linux.go | 5 +- vendor/modules.txt | 4 +- 12 files changed, 130 insertions(+), 89 deletions(-) diff --git a/connection/quic_test.go b/connection/quic_test.go index 2557e67b..c35c7d51 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -31,8 +31,9 @@ import ( var ( testTLSServerConfig = quicpogs.GenerateTLSConfig() testQUICConfig = &quic.Config{ - KeepAlivePeriod: 5 * time.Second, - EnableDatagrams: true, + ConnectionIDLength: 16, + KeepAlivePeriod: 5 * time.Second, + EnableDatagrams: true, } ) @@ -80,63 +81,63 @@ func TestQUICServer(t *testing.T) { }, expectedResponse: []byte("OK"), }, - { - desc: "test http body request streaming", - dest: "/slow_echo_body", - connectionType: quicpogs.ConnectionTypeHTTP, - metadata: []quicpogs.Metadata{ - { - Key: "HttpHeader:Cf-Ray", - Val: "123123123", - }, - { - Key: "HttpHost", - Val: "cf.host", - }, - { - Key: "HttpMethod", - Val: "POST", - }, - { - Key: "HttpHeader:Content-Length", - Val: "24", - }, - }, - message: []byte("This is the message body"), - expectedResponse: []byte("This is the message body"), - }, - { - desc: "test ws proxy", - dest: "/ws/echo", - connectionType: quicpogs.ConnectionTypeWebsocket, - metadata: []quicpogs.Metadata{ - { - Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", - Val: "Websocket", - }, - { - Key: "HttpHeader:Another-Header", - Val: "Misc", - }, - { - Key: "HttpHost", - Val: "cf.host", - }, - { - Key: "HttpMethod", - Val: "get", - }, - }, - message: wsBuf.Bytes(), - expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, - }, - { - desc: "test tcp proxy", - connectionType: quicpogs.ConnectionTypeTCP, - metadata: []quicpogs.Metadata{}, - message: []byte("Here is some tcp data"), - expectedResponse: []byte("Here is some tcp data"), - }, + //{ + // desc: "test http body request streaming", + // dest: "/slow_echo_body", + // connectionType: quicpogs.ConnectionTypeHTTP, + // metadata: []quicpogs.Metadata{ + // { + // Key: "HttpHeader:Cf-Ray", + // Val: "123123123", + // }, + // { + // Key: "HttpHost", + // Val: "cf.host", + // }, + // { + // Key: "HttpMethod", + // Val: "POST", + // }, + // { + // Key: "HttpHeader:Content-Length", + // Val: "24", + // }, + // }, + // message: []byte("This is the message body"), + // expectedResponse: []byte("This is the message body"), + //}, + //{ + // desc: "test ws proxy", + // dest: "/ws/echo", + // connectionType: quicpogs.ConnectionTypeWebsocket, + // metadata: []quicpogs.Metadata{ + // { + // Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", + // Val: "Websocket", + // }, + // { + // Key: "HttpHeader:Another-Header", + // Val: "Misc", + // }, + // { + // Key: "HttpHost", + // Val: "cf.host", + // }, + // { + // Key: "HttpMethod", + // Val: "get", + // }, + // }, + // message: wsBuf.Bytes(), + // expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, + //}, + //{ + // desc: "test tcp proxy", + // connectionType: quicpogs.ConnectionTypeTCP, + // metadata: []quicpogs.Metadata{}, + // message: []byte("Here is some tcp data"), + // expectedResponse: []byte("Here is some tcp data"), + //}, } for _, test := range tests { @@ -503,6 +504,7 @@ func TestServeUDPSession(t *testing.T) { defer udpListener.Close() ctx, cancel := context.WithCancel(context.Background()) + val := udpListener.LocalAddr() // Establish QUIC connection with edge edgeQUICSessionChan := make(chan quic.Connection) @@ -515,7 +517,7 @@ func TestServeUDPSession(t *testing.T) { edgeQUICSessionChan <- edgeQUICSession }() - qc := testQUICConnection(udpListener.LocalAddr(), t) + qc := testQUICConnection(val, t) go qc.Serve(ctx) edgeQUICSession := <-edgeQUICSessionChan diff --git a/go.mod b/go.mod index ecda62d2..ab4a143d 100644 --- a/go.mod +++ b/go.mod @@ -100,7 +100,7 @@ require ( replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e +replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f // Avoid 'CVE-2022-21698' replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 diff --git a/go.sum b/go.sum index 9732bc39..d260fb39 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e h1:HanU8Gx2eTN9X0miD0HNdl/doTs08ZLQzlQMIrGVHgk= -github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= +github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f h1:UWC3XjwZzocdNCzzXxq9j/1SdHMZXhcTOsh/+gNRBUQ= +github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/vendor/github.com/lucas-clemente/quic-go/client.go b/vendor/github.com/lucas-clemente/quic-go/client.go index be8390e6..76b59061 100644 --- a/vendor/github.com/lucas-clemente/quic-go/client.go +++ b/vendor/github.com/lucas-clemente/quic-go/client.go @@ -43,8 +43,7 @@ type client struct { } var ( - // make it possible to mock connection ID generation in the tests - generateConnectionID = protocol.GenerateConnectionID + // make it possible to mock connection ID for initial generation in the tests generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial ) @@ -193,7 +192,7 @@ func dialContext( return nil, err } config = populateClientConfig(config, createdPacketConn) - packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) + packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer) if err != nil { return nil, err } @@ -256,7 +255,7 @@ func newClient( } } - srcConnID, err := generateConnectionID(config.ConnectionIDLength) + srcConnID, err := config.ConnectionIDGenerator.GenerateConnectionID() if err != nil { return nil, err } diff --git a/vendor/github.com/lucas-clemente/quic-go/config.go b/vendor/github.com/lucas-clemente/quic-go/config.go index ef7a2622..884cff00 100644 --- a/vendor/github.com/lucas-clemente/quic-go/config.go +++ b/vendor/github.com/lucas-clemente/quic-go/config.go @@ -35,10 +35,7 @@ func validateConfig(config *Config) error { // populateServerConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateServerConfig(config *Config) *Config { - config = populateConfig(config) - if config.ConnectionIDLength == 0 { - config.ConnectionIDLength = protocol.DefaultConnectionIDLength - } + config = populateConfig(config, protocol.DefaultConnectionIDLength) if config.AcceptToken == nil { config.AcceptToken = defaultAcceptToken } @@ -48,14 +45,16 @@ func populateServerConfig(config *Config) *Config { // populateClientConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateClientConfig(config *Config, createdPacketConn bool) *Config { - config = populateConfig(config) - if config.ConnectionIDLength == 0 && !createdPacketConn { - config.ConnectionIDLength = protocol.DefaultConnectionIDLength + var defaultConnIdLen = protocol.DefaultConnectionIDLength + if createdPacketConn { + defaultConnIdLen = 0 } + + config = populateConfig(config, defaultConnIdLen) return config } -func populateConfig(config *Config) *Config { +func populateConfig(config *Config, defaultConnIDLen int) *Config { if config == nil { config = &Config{} } @@ -63,6 +62,9 @@ func populateConfig(config *Config) *Config { if len(versions) == 0 { versions = protocol.SupportedVersions } + if config.ConnectionIDLength == 0 { + config.ConnectionIDLength = defaultConnIDLen + } handshakeIdleTimeout := protocol.DefaultHandshakeIdleTimeout if config.HandshakeIdleTimeout != 0 { handshakeIdleTimeout = config.HandshakeIdleTimeout @@ -103,6 +105,10 @@ func populateConfig(config *Config) *Config { if maxDatagrameFrameSize == 0 { maxDatagrameFrameSize = int64(protocol.DefaultMaxDatagramFrameSize) } + connIDGenerator := config.ConnectionIDGenerator + if connIDGenerator == nil { + connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: config.ConnectionIDLength} + } return &Config{ Versions: versions, @@ -118,6 +124,7 @@ func populateConfig(config *Config) *Config { MaxIncomingStreams: maxIncomingStreams, MaxIncomingUniStreams: maxIncomingUniStreams, ConnectionIDLength: config.ConnectionIDLength, + ConnectionIDGenerator: connIDGenerator, StatelessResetKey: config.StatelessResetKey, TokenStore: config.TokenStore, EnableDatagrams: config.EnableDatagrams, diff --git a/vendor/github.com/lucas-clemente/quic-go/conn_id_generator.go b/vendor/github.com/lucas-clemente/quic-go/conn_id_generator.go index 90c2b7a6..15a95bab 100644 --- a/vendor/github.com/lucas-clemente/quic-go/conn_id_generator.go +++ b/vendor/github.com/lucas-clemente/quic-go/conn_id_generator.go @@ -10,7 +10,7 @@ import ( ) type connIDGenerator struct { - connIDLen int + generator ConnectionIDGenerator highestSeq uint64 activeSrcConnIDs map[uint64]protocol.ConnectionID @@ -35,10 +35,11 @@ func newConnIDGenerator( retireConnectionID func(protocol.ConnectionID), replaceWithClosed func(protocol.ConnectionID, packetHandler), queueControlFrame func(wire.Frame), + generator ConnectionIDGenerator, version protocol.VersionNumber, ) *connIDGenerator { m := &connIDGenerator{ - connIDLen: initialConnectionID.Len(), + generator: generator, activeSrcConnIDs: make(map[uint64]protocol.ConnectionID), addConnectionID: addConnectionID, getStatelessResetToken: getStatelessResetToken, @@ -54,7 +55,7 @@ func newConnIDGenerator( } func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error { - if m.connIDLen == 0 { + if m.generator.ConnectionIDLen() == 0 { return nil } // The active_connection_id_limit transport parameter is the number of @@ -99,7 +100,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect } func (m *connIDGenerator) issueNewConnID() error { - connID, err := protocol.GenerateConnectionID(m.connIDLen) + connID, err := m.generator.GenerateConnectionID() if err != nil { return err } diff --git a/vendor/github.com/lucas-clemente/quic-go/connection.go b/vendor/github.com/lucas-clemente/quic-go/connection.go index 6495eddf..fa9bd154 100644 --- a/vendor/github.com/lucas-clemente/quic-go/connection.go +++ b/vendor/github.com/lucas-clemente/quic-go/connection.go @@ -280,6 +280,7 @@ var newConnection = func( runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, + s.config.ConnectionIDGenerator, s.version, ) s.preSetup() @@ -410,6 +411,7 @@ var newClientConnection = func( runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, + s.config.ConnectionIDGenerator, s.version, ) s.preSetup() diff --git a/vendor/github.com/lucas-clemente/quic-go/interface.go b/vendor/github.com/lucas-clemente/quic-go/interface.go index e0c9f439..4ca98609 100644 --- a/vendor/github.com/lucas-clemente/quic-go/interface.go +++ b/vendor/github.com/lucas-clemente/quic-go/interface.go @@ -211,6 +211,18 @@ type EarlyConnection interface { NextConnection() Connection } +// A ConnectionIDGenerator is a interface that allows clients to implement their own format +// for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets. +type ConnectionIDGenerator interface { + + // GenerateConnectionID generates a new ConnectionID. + GenerateConnectionID() ([]byte, error) + + // ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of + // this interface. + ConnectionIDLen() int +} + // Config contains all configuration data needed for a QUIC server or client. type Config struct { // The QUIC versions that can be negotiated. @@ -223,6 +235,11 @@ type Config struct { // If used for a server, or dialing on a packet conn, a 4 byte connection ID will be used. // When dialing on a packet conn, the ConnectionIDLength value must be the same for every Dial call. ConnectionIDLength int + // An optional ConnectionIDGenerator to be used for ConnectionIDs generated during the lifecycle of a QUIC connection. + // The goal is to give some control on how connection IDs, which can be useful in some scenarios, in particular for servers. + // By default, if not provided, random connection IDs with the length given by ConnectionIDLength is used. + // Otherwise, if one is provided, then ConnectionIDLength is ignored. + ConnectionIDGenerator ConnectionIDGenerator // HandshakeIdleTimeout is the idle timeout before completion of the handshake. // Specifically, if we don't receive any packet from the peer within this time, the connection attempt is aborted. // If this value is zero, the timeout is set to 5 seconds. diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go index 3aec2cd3..7ae7d9dc 100644 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go +++ b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go @@ -67,3 +67,15 @@ func (c ConnectionID) String() string { } return fmt.Sprintf("%x", c.Bytes()) } + +type DefaultConnectionIDGenerator struct { + ConnLen int +} + +func (d *DefaultConnectionIDGenerator) GenerateConnectionID() ([]byte, error) { + return GenerateConnectionID(d.ConnLen) +} + +func (d *DefaultConnectionIDGenerator) ConnectionIDLen() int { + return d.ConnLen +} diff --git a/vendor/github.com/lucas-clemente/quic-go/server.go b/vendor/github.com/lucas-clemente/quic-go/server.go index 0e642970..7e686881 100644 --- a/vendor/github.com/lucas-clemente/quic-go/server.go +++ b/vendor/github.com/lucas-clemente/quic-go/server.go @@ -190,7 +190,7 @@ func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarl } } - connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) + connHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDGenerator.ConnectionIDLen(), config.StatelessResetKey, config.Tracer) if err != nil { return nil, err } @@ -341,7 +341,7 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s } // If we're creating a new connection, the packet will be passed to the connection. // The header will then be parsed again. - hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength) + hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDGenerator.ConnectionIDLen()) if err != nil && err != wire.ErrUnsupportedVersion { if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) @@ -450,11 +450,11 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro return nil } - connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) + connID, err := s.config.ConnectionIDGenerator.GenerateConnectionID() if err != nil { return err } - s.logger.Debugf("Changing connection ID to %s.", connID) + s.logger.Debugf("Changing connection ID to %s.", protocol.ConnectionID(connID)) var conn quicConn tracingID := nextConnTracingID() if added := s.connHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { @@ -535,7 +535,7 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *pack // Log the Initial packet now. // If no Retry is sent, the packet will be logged by the connection. (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) - srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) + srcConnID, err := s.config.ConnectionIDGenerator.GenerateConnectionID() if err != nil { return err } @@ -551,7 +551,7 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *pack replyHdr.DestConnectionID = hdr.SrcConnectionID replyHdr.Token = token if s.logger.Debug() { - s.logger.Debugf("Changing connection ID to %s.", srcConnID) + s.logger.Debugf("Changing connection ID to %s.", protocol.ConnectionID(srcConnID)) s.logger.Debugf("-> Sending Retry") replyHdr.Log(s.logger) } diff --git a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go index 17ac67f1..69f8fc93 100644 --- a/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go +++ b/vendor/github.com/lucas-clemente/quic-go/sys_conn_df_linux.go @@ -7,8 +7,9 @@ import ( "errors" "syscall" - "github.com/lucas-clemente/quic-go/internal/utils" "golang.org/x/sys/unix" + + "github.com/lucas-clemente/quic-go/internal/utils" ) func setDF(rawConn syscall.RawConn) error { @@ -29,7 +30,7 @@ func setDF(rawConn syscall.RawConn) error { case errDFIPv4 != nil && errDFIPv6 == nil: utils.DefaultLogger.Debugf("Setting DF for IPv6.") case errDFIPv4 != nil && errDFIPv6 != nil: - return errors.New("setting DF failed for both IPv4 and IPv6") + utils.DefaultLogger.Errorf("setting DF failed for both IPv4 and IPv6") } return nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 3b698e75..674ea445 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -146,7 +146,7 @@ github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc github.com/json-iterator/go # github.com/kylelemons/godebug v1.1.0 ## explicit; go 1.11 -# github.com/lucas-clemente/quic-go v0.28.1 => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e +# github.com/lucas-clemente/quic-go v0.28.1 => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f ## explicit; go 1.16 github.com/lucas-clemente/quic-go github.com/lucas-clemente/quic-go/internal/ackhandler @@ -546,6 +546,6 @@ zombiezen.com/go/capnproto2/schemas zombiezen.com/go/capnproto2/server zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d -# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220808144024-f036dcbe387e +# github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 # gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 From 91eba53035a19599a434a295ee8dd4615c585859 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 9 Aug 2022 11:14:12 +0100 Subject: [PATCH 151/238] TUN-6639: Validate cyclic ingress configuration This reverts commit d4d9a43dd7b673627532afcd08fdfe1aae916a60. We revert this change because the value this configuration addition brings is small (it only stops an explicit cyclic configuration versus not accounting for local hosts and ip based cycles amongst other things) whilst the potential inconvenience it may cause is high (for example, someone had a cyclic configuration as an ingress rule that they weren't even using). --- ingress/ingress.go | 4 ---- ingress/ingress_test.go | 10 ---------- 2 files changed, 14 deletions(-) diff --git a/ingress/ingress.go b/ingress/ingress.go index cc0fca55..05a90a8b 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -232,10 +232,6 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq } else { service = newTCPOverWSService(u) } - - if u.Hostname() == r.Hostname { - return Ingress{}, fmt.Errorf("Cyclic Ingress configuration: Hostname:%s points to service:%s.", r.Hostname, r.Service) - } } if err := validateHostname(r, i, len(ingress)); err != nil { diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 5bf3f912..1ff5e11c 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -404,16 +404,6 @@ ingress: service: https://localhost:8000 - hostname: "*" service: https://localhost:8001 -`}, - wantErr: true, - }, - { - name: "Cyclic hostname definition", - args: args{rawYAML: ` -ingress: - - hostname: "test.example.com" - service: https://test.example.com - - service: http_status_404 `}, wantErr: true, }, From 9de4e88ca652f5727b22201b79e705853b60ee33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 10 Aug 2022 14:21:55 +0100 Subject: [PATCH 152/238] TUN-6646: Add support to SafeStreamCloser to close only write side of stream --- quic/safe_stream.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/quic/safe_stream.go b/quic/safe_stream.go index 12ba76f4..c151ff2e 100644 --- a/quic/safe_stream.go +++ b/quic/safe_stream.go @@ -41,3 +41,14 @@ func (s *SafeStreamCloser) Close() error { s.stream.CancelRead(0) return s.stream.Close() } + +func (s *SafeStreamCloser) CloseWrite() error { + s.lock.Lock() + defer s.lock.Unlock() + + // As documented by the quic-go library, this doesn't actually close the entire stream. + // It prevents further writes, which in turn will result in an EOF signal being sent the other side of stream when + // reading. + // We can still read from this stream. + return s.stream.Close() +} From a768132d378f97cc4a3cabef67296f7aaa8738c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 10 Aug 2022 22:53:08 +0100 Subject: [PATCH 153/238] Release 2022.8.0 --- RELEASE_NOTES | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index fd144c9a..568bb514 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,19 @@ +2022.8.0 +- 2022-08-10 TUN-6637: Upgrade quic-go +- 2022-08-10 TUN-6646: Add support to SafeStreamCloser to close only write side of stream +- 2022-08-09 TUN-6642: Fix unexpected close of quic stream triggered by upstream origin close +- 2022-08-09 TUN-6639: Validate cyclic ingress configuration +- 2022-08-08 TUN-6637: Upgrade go version and quic-go +- 2022-08-08 TUN-6639: Validate cyclic ingress configuration +- 2022-08-04 EDGEPLAT-3918: build cloudflared for Bookworm +- 2022-08-02 Revert "TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span" +- 2022-07-27 TUN-6601: Update gopkg.in/yaml.v3 references in modules +- 2022-07-26 TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span +- 2022-07-26 TUN-6576: Consume cf-trace-id from incoming TCP requests to create root span +- 2022-07-25 TUN-6598: Remove auto assignees on github issues +- 2022-07-20 TUN-6583: Remove legacy --ui flag +- 2022-07-20 cURL supports stdin and uses os pipes directly without copying +- 2022-07-07 TUN-6517: Use QUIC stream context while proxying HTTP requests and TCP connections 2022.7.1 - 2022-07-06 TUN-6503: Fix transport fallback from QUIC in face of dial error "no network activity" From 679a89c7df5f0046acb91f874c07fa7458247d6c Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 11 Aug 2022 13:31:35 +0100 Subject: [PATCH 154/238] TUN-6617: Dont fallback to http2 if QUIC conn was successful. cloudflared falls back aggressively to HTTP/2 protocol if a connection attempt with QUIC failed. This was done to ensure that machines with UDP egress disabled did not stop clients from connecting to the cloudlfare edge. This PR improves on that experience by having cloudflared remember if a QUIC connection was successful which implies UDP egress works. In this case, cloudflared does not fallback to HTTP/2 and keeps trying to connect to the edge with QUIC. --- connection/control.go | 7 ++ connection/event.go | 1 + connection/http2_test.go | 4 ++ connection/observer.go | 4 +- connection/quic_test.go | 114 ++++++++++++++++---------------- connection/rpc.go | 4 -- metrics/readiness_test.go | 22 +++--- supervisor/conn_aware_logger.go | 4 +- supervisor/supervisor.go | 6 +- supervisor/tunnel.go | 9 +++ tunnelstate/conntracker.go | 45 ++++++++++--- 11 files changed, 133 insertions(+), 87 deletions(-) diff --git a/connection/control.go b/connection/control.go index a7d49772..a7fe1ac9 100644 --- a/connection/control.go +++ b/connection/control.go @@ -2,6 +2,7 @@ package connection import ( "context" + "fmt" "io" "net" "time" @@ -21,6 +22,7 @@ type controlStream struct { namedTunnelProperties *NamedTunnelProperties connIndex uint8 edgeAddress net.IP + protocol Protocol newRPCClientFunc RPCClientFunc @@ -51,6 +53,7 @@ func NewControlStream( newRPCClientFunc RPCClientFunc, gracefulShutdownC <-chan struct{}, gracePeriod time.Duration, + protocol Protocol, ) ControlStreamHandler { if newRPCClientFunc == nil { newRPCClientFunc = newRegistrationRPCClient @@ -64,6 +67,7 @@ func NewControlStream( edgeAddress: edgeAddress, gracefulShutdownC: gracefulShutdownC, gracePeriod: gracePeriod, + protocol: protocol, } } @@ -80,6 +84,9 @@ func (c *controlStream) ServeControlStream( rpcClient.Close() return err } + + c.observer.logServerInfo(c.connIndex, registrationDetails.Location, c.edgeAddress, fmt.Sprintf("Connection %s registered", registrationDetails.UUID)) + c.observer.sendConnectedEvent(c.connIndex, c.protocol, registrationDetails.Location) c.connectedFuse.Connected() // if conn index is 0 and tunnel is not remotely managed, then send local ingress rules configuration diff --git a/connection/event.go b/connection/event.go index ab6d0d33..d10b92fc 100644 --- a/connection/event.go +++ b/connection/event.go @@ -5,6 +5,7 @@ type Event struct { Index uint8 EventType Status Location string + Protocol Protocol URL string } diff --git a/connection/http2_test.go b/connection/http2_test.go index e962353f..b7f1c49c 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -43,6 +43,7 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { nil, nil, 1*time.Second, + HTTP2, ) return NewHTTP2Connection( cfdConn, @@ -366,6 +367,7 @@ func TestServeControlStream(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -417,6 +419,7 @@ func TestFailRegistration(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -464,6 +467,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { rpcClientFactory.newMockRPCClient, shutdownC, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream diff --git a/connection/observer.go b/connection/observer.go index 3a855878..7429b32c 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -55,8 +55,8 @@ func (o *Observer) sendRegisteringEvent(connIndex uint8) { o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel}) } -func (o *Observer) sendConnectedEvent(connIndex uint8, location string) { - o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) +func (o *Observer) sendConnectedEvent(connIndex uint8, protocol Protocol, location string) { + o.sendEvent(Event{Index: connIndex, EventType: Connected, Protocol: protocol, Location: location}) } func (o *Observer) SendURL(url string) { diff --git a/connection/quic_test.go b/connection/quic_test.go index c35c7d51..d82947c2 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -81,63 +81,63 @@ func TestQUICServer(t *testing.T) { }, expectedResponse: []byte("OK"), }, - //{ - // desc: "test http body request streaming", - // dest: "/slow_echo_body", - // connectionType: quicpogs.ConnectionTypeHTTP, - // metadata: []quicpogs.Metadata{ - // { - // Key: "HttpHeader:Cf-Ray", - // Val: "123123123", - // }, - // { - // Key: "HttpHost", - // Val: "cf.host", - // }, - // { - // Key: "HttpMethod", - // Val: "POST", - // }, - // { - // Key: "HttpHeader:Content-Length", - // Val: "24", - // }, - // }, - // message: []byte("This is the message body"), - // expectedResponse: []byte("This is the message body"), - //}, - //{ - // desc: "test ws proxy", - // dest: "/ws/echo", - // connectionType: quicpogs.ConnectionTypeWebsocket, - // metadata: []quicpogs.Metadata{ - // { - // Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", - // Val: "Websocket", - // }, - // { - // Key: "HttpHeader:Another-Header", - // Val: "Misc", - // }, - // { - // Key: "HttpHost", - // Val: "cf.host", - // }, - // { - // Key: "HttpMethod", - // Val: "get", - // }, - // }, - // message: wsBuf.Bytes(), - // expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, - //}, - //{ - // desc: "test tcp proxy", - // connectionType: quicpogs.ConnectionTypeTCP, - // metadata: []quicpogs.Metadata{}, - // message: []byte("Here is some tcp data"), - // expectedResponse: []byte("Here is some tcp data"), - //}, + { + desc: "test http body request streaming", + dest: "/slow_echo_body", + connectionType: quicpogs.ConnectionTypeHTTP, + metadata: []quicpogs.Metadata{ + { + Key: "HttpHeader:Cf-Ray", + Val: "123123123", + }, + { + Key: "HttpHost", + Val: "cf.host", + }, + { + Key: "HttpMethod", + Val: "POST", + }, + { + Key: "HttpHeader:Content-Length", + Val: "24", + }, + }, + message: []byte("This is the message body"), + expectedResponse: []byte("This is the message body"), + }, + { + desc: "test ws proxy", + dest: "/ws/echo", + connectionType: quicpogs.ConnectionTypeWebsocket, + metadata: []quicpogs.Metadata{ + { + Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", + Val: "Websocket", + }, + { + Key: "HttpHeader:Another-Header", + Val: "Misc", + }, + { + Key: "HttpHost", + Val: "cf.host", + }, + { + Key: "HttpMethod", + Val: "get", + }, + }, + message: wsBuf.Bytes(), + expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, + }, + { + desc: "test tcp proxy", + connectionType: quicpogs.ConnectionTypeTCP, + metadata: []quicpogs.Metadata{}, + message: []byte("Here is some tcp data"), + expectedResponse: []byte("Here is some tcp data"), + }, } for _, test := range tests { diff --git a/connection/rpc.go b/connection/rpc.go index b288a0f8..f602290b 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -2,7 +2,6 @@ package connection import ( "context" - "fmt" "io" "net" "time" @@ -117,9 +116,6 @@ func (rsc *registrationServerClient) RegisterConnection( observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() - observer.logServerInfo(connIndex, conn.Location, edgeAddress, fmt.Sprintf("Connection %s registered", conn.UUID)) - observer.sendConnectedEvent(connIndex, conn.Location) - return conn, nil } diff --git a/metrics/readiness_test.go b/metrics/readiness_test.go index 62e42f63..8e035f85 100644 --- a/metrics/readiness_test.go +++ b/metrics/readiness_test.go @@ -14,7 +14,7 @@ import ( func TestReadyServer_makeResponse(t *testing.T) { type fields struct { - isConnected map[int]bool + isConnected map[uint8]tunnelstate.ConnectionInfo } tests := []struct { name string @@ -25,11 +25,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "One connection online => HTTP 200", fields: fields{ - isConnected: map[int]bool{ - 0: false, - 1: false, - 2: true, - 3: false, + isConnected: map[uint8]tunnelstate.ConnectionInfo{ + 0: {IsConnected: false}, + 1: {IsConnected: false}, + 2: {IsConnected: true}, + 3: {IsConnected: false}, }, }, wantOK: true, @@ -38,11 +38,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "No connections online => no HTTP 200", fields: fields{ - isConnected: map[int]bool{ - 0: false, - 1: false, - 2: false, - 3: false, + isConnected: map[uint8]tunnelstate.ConnectionInfo{ + 0: {IsConnected: false}, + 1: {IsConnected: false}, + 2: {IsConnected: false}, + 3: {IsConnected: false}, }, }, wantReadyConnections: 0, diff --git a/supervisor/conn_aware_logger.go b/supervisor/conn_aware_logger.go index 6e717588..1311f20b 100644 --- a/supervisor/conn_aware_logger.go +++ b/supervisor/conn_aware_logger.go @@ -12,9 +12,9 @@ type ConnAwareLogger struct { logger *zerolog.Logger } -func NewConnAwareLogger(logger *zerolog.Logger, observer *connection.Observer) *ConnAwareLogger { +func NewConnAwareLogger(logger *zerolog.Logger, tracker *tunnelstate.ConnTracker, observer *connection.Observer) *ConnAwareLogger { connAwareLogger := &ConnAwareLogger{ - tracker: tunnelstate.NewConnTracker(logger), + tracker: tracker, logger: logger, } diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index c3f08844..7a100a59 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -19,6 +19,7 @@ import ( "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" + "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -88,7 +89,9 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato } reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) - log := NewConnAwareLogger(config.Log, config.Observer) + + tracker := tunnelstate.NewConnTracker(config.Log) + log := NewConnAwareLogger(config.Log, tracker, config.Observer) var edgeAddrHandler EdgeAddrHandler if isStaticEdge { // static edge addresses @@ -106,6 +109,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato credentialManager: reconnectCredentialManager, edgeAddrs: edgeIPs, edgeAddrHandler: edgeAddrHandler, + tracker: tracker, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, connAwareLogger: log, diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 2abf09f5..8ff22ca1 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -26,6 +26,7 @@ import ( "github.com/cloudflare/cloudflared/signal" "github.com/cloudflare/cloudflared/tunnelrpc" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" + "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -190,6 +191,7 @@ type EdgeTunnelServer struct { edgeAddrs *edgediscovery.Edge reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} + tracker *tunnelstate.ConnTracker connAwareLogger *ConnAwareLogger } @@ -272,6 +274,12 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFa return err } + // If a single connection has connected with the current protocol, we know we know we don't have to fallback + // to a different protocol. + if e.tracker.HasConnectedWith(e.config.ProtocolSelector.Current()) { + return err + } + if !selectNextProtocol( connLog.Logger(), protocolFallback, @@ -461,6 +469,7 @@ func serveTunnel( nil, gracefulShutdownC, config.GracePeriod, + protocol, ) switch protocol { diff --git a/tunnelstate/conntracker.go b/tunnelstate/conntracker.go index 06fb176f..426ba483 100644 --- a/tunnelstate/conntracker.go +++ b/tunnelstate/conntracker.go @@ -10,20 +10,26 @@ import ( type ConnTracker struct { sync.RWMutex - isConnected map[int]bool - log *zerolog.Logger + // int is the connection Index + connectionInfo map[uint8]ConnectionInfo + log *zerolog.Logger +} + +type ConnectionInfo struct { + IsConnected bool + Protocol connection.Protocol } func NewConnTracker(log *zerolog.Logger) *ConnTracker { return &ConnTracker{ - isConnected: make(map[int]bool, 0), - log: log, + connectionInfo: make(map[uint8]ConnectionInfo, 0), + log: log, } } -func MockedConnTracker(mocked map[int]bool) *ConnTracker { +func MockedConnTracker(mocked map[uint8]ConnectionInfo) *ConnTracker { return &ConnTracker{ - isConnected: mocked, + connectionInfo: mocked, } } @@ -31,11 +37,17 @@ func (ct *ConnTracker) OnTunnelEvent(c connection.Event) { switch c.EventType { case connection.Connected: ct.Lock() - ct.isConnected[int(c.Index)] = true + ci := ConnectionInfo{ + IsConnected: true, + Protocol: c.Protocol, + } + ct.connectionInfo[c.Index] = ci ct.Unlock() case connection.Disconnected, connection.Reconnecting, connection.RegisteringTunnel, connection.Unregistering: ct.Lock() - ct.isConnected[int(c.Index)] = false + ci := ct.connectionInfo[c.Index] + ci.IsConnected = false + ct.connectionInfo[c.Index] = ci ct.Unlock() default: ct.log.Error().Msgf("Unknown connection event case %v", c) @@ -46,10 +58,23 @@ func (ct *ConnTracker) CountActiveConns() uint { ct.RLock() defer ct.RUnlock() active := uint(0) - for _, connected := range ct.isConnected { - if connected { + for _, ci := range ct.connectionInfo { + if ci.IsConnected { active++ } } return active } + +// HasConnectedWith checks if we've ever had a successful connection to the edge +// with said protocol. +func (ct *ConnTracker) HasConnectedWith(protocol connection.Protocol) bool { + ct.RLock() + defer ct.RUnlock() + for _, ci := range ct.connectionInfo { + if ci.Protocol == protocol { + return true + } + } + return false +} From 68d370af19024662fa90671e4c98eaf956294182 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 11 Aug 2022 18:53:27 +0100 Subject: [PATCH 155/238] TUN-6617: Dont fallback to http2 if QUIC conn was successful. cloudflared falls back aggressively to HTTP/2 protocol if a connection attempt with QUIC failed. This was done to ensure that machines with UDP egress disabled did not stop clients from connecting to the cloudlfare edge. This PR improves on that experience by having cloudflared remember if a QUIC connection was successful which implies UDP egress works. In this case, cloudflared does not fallback to HTTP/2 and keeps trying to connect to the edge with QUIC. --- component-tests/util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/component-tests/util.py b/component-tests/util.py index aae9b85f..6e60d08e 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -61,13 +61,15 @@ def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root): def run_cloudflared_background(cmd, allow_input, capture_output): output = subprocess.PIPE if capture_output else subprocess.DEVNULL stdin = subprocess.PIPE if allow_input else None + cfd = None try: cfd = subprocess.Popen(cmd, stdin=stdin, stdout=output, stderr=output) yield cfd finally: - cfd.terminate() - if capture_output: - LOGGER.info(f"cloudflared log: {cfd.stderr.read()}") + if cfd: + cfd.terminate() + if capture_output: + LOGGER.info(f"cloudflared log: {cfd.stderr.read()}") def wait_tunnel_ready(tunnel_url=None, require_min_connections=1, cfd_logs=None): From d3fd581b7bdca838be08a24080659c01e46f6802 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 11 Aug 2022 20:27:22 +0100 Subject: [PATCH 156/238] Revert "TUN-6617: Dont fallback to http2 if QUIC conn was successful." This reverts commit 679a89c7df5f0046acb91f874c07fa7458247d6c. --- connection/control.go | 7 -- connection/event.go | 1 - connection/http2_test.go | 4 -- connection/observer.go | 4 +- connection/quic_test.go | 114 ++++++++++++++++---------------- connection/rpc.go | 4 ++ metrics/readiness_test.go | 22 +++--- supervisor/conn_aware_logger.go | 4 +- supervisor/supervisor.go | 6 +- supervisor/tunnel.go | 9 --- tunnelstate/conntracker.go | 45 +++---------- 11 files changed, 87 insertions(+), 133 deletions(-) diff --git a/connection/control.go b/connection/control.go index a7fe1ac9..a7d49772 100644 --- a/connection/control.go +++ b/connection/control.go @@ -2,7 +2,6 @@ package connection import ( "context" - "fmt" "io" "net" "time" @@ -22,7 +21,6 @@ type controlStream struct { namedTunnelProperties *NamedTunnelProperties connIndex uint8 edgeAddress net.IP - protocol Protocol newRPCClientFunc RPCClientFunc @@ -53,7 +51,6 @@ func NewControlStream( newRPCClientFunc RPCClientFunc, gracefulShutdownC <-chan struct{}, gracePeriod time.Duration, - protocol Protocol, ) ControlStreamHandler { if newRPCClientFunc == nil { newRPCClientFunc = newRegistrationRPCClient @@ -67,7 +64,6 @@ func NewControlStream( edgeAddress: edgeAddress, gracefulShutdownC: gracefulShutdownC, gracePeriod: gracePeriod, - protocol: protocol, } } @@ -84,9 +80,6 @@ func (c *controlStream) ServeControlStream( rpcClient.Close() return err } - - c.observer.logServerInfo(c.connIndex, registrationDetails.Location, c.edgeAddress, fmt.Sprintf("Connection %s registered", registrationDetails.UUID)) - c.observer.sendConnectedEvent(c.connIndex, c.protocol, registrationDetails.Location) c.connectedFuse.Connected() // if conn index is 0 and tunnel is not remotely managed, then send local ingress rules configuration diff --git a/connection/event.go b/connection/event.go index d10b92fc..ab6d0d33 100644 --- a/connection/event.go +++ b/connection/event.go @@ -5,7 +5,6 @@ type Event struct { Index uint8 EventType Status Location string - Protocol Protocol URL string } diff --git a/connection/http2_test.go b/connection/http2_test.go index b7f1c49c..e962353f 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -43,7 +43,6 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { nil, nil, 1*time.Second, - HTTP2, ) return NewHTTP2Connection( cfdConn, @@ -367,7 +366,6 @@ func TestServeControlStream(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, - HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -419,7 +417,6 @@ func TestFailRegistration(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, - HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -467,7 +464,6 @@ func TestGracefulShutdownHTTP2(t *testing.T) { rpcClientFactory.newMockRPCClient, shutdownC, 1*time.Second, - HTTP2, ) http2Conn.controlStreamHandler = controlStream diff --git a/connection/observer.go b/connection/observer.go index 7429b32c..3a855878 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -55,8 +55,8 @@ func (o *Observer) sendRegisteringEvent(connIndex uint8) { o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel}) } -func (o *Observer) sendConnectedEvent(connIndex uint8, protocol Protocol, location string) { - o.sendEvent(Event{Index: connIndex, EventType: Connected, Protocol: protocol, Location: location}) +func (o *Observer) sendConnectedEvent(connIndex uint8, location string) { + o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) } func (o *Observer) SendURL(url string) { diff --git a/connection/quic_test.go b/connection/quic_test.go index d82947c2..c35c7d51 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -81,63 +81,63 @@ func TestQUICServer(t *testing.T) { }, expectedResponse: []byte("OK"), }, - { - desc: "test http body request streaming", - dest: "/slow_echo_body", - connectionType: quicpogs.ConnectionTypeHTTP, - metadata: []quicpogs.Metadata{ - { - Key: "HttpHeader:Cf-Ray", - Val: "123123123", - }, - { - Key: "HttpHost", - Val: "cf.host", - }, - { - Key: "HttpMethod", - Val: "POST", - }, - { - Key: "HttpHeader:Content-Length", - Val: "24", - }, - }, - message: []byte("This is the message body"), - expectedResponse: []byte("This is the message body"), - }, - { - desc: "test ws proxy", - dest: "/ws/echo", - connectionType: quicpogs.ConnectionTypeWebsocket, - metadata: []quicpogs.Metadata{ - { - Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", - Val: "Websocket", - }, - { - Key: "HttpHeader:Another-Header", - Val: "Misc", - }, - { - Key: "HttpHost", - Val: "cf.host", - }, - { - Key: "HttpMethod", - Val: "get", - }, - }, - message: wsBuf.Bytes(), - expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, - }, - { - desc: "test tcp proxy", - connectionType: quicpogs.ConnectionTypeTCP, - metadata: []quicpogs.Metadata{}, - message: []byte("Here is some tcp data"), - expectedResponse: []byte("Here is some tcp data"), - }, + //{ + // desc: "test http body request streaming", + // dest: "/slow_echo_body", + // connectionType: quicpogs.ConnectionTypeHTTP, + // metadata: []quicpogs.Metadata{ + // { + // Key: "HttpHeader:Cf-Ray", + // Val: "123123123", + // }, + // { + // Key: "HttpHost", + // Val: "cf.host", + // }, + // { + // Key: "HttpMethod", + // Val: "POST", + // }, + // { + // Key: "HttpHeader:Content-Length", + // Val: "24", + // }, + // }, + // message: []byte("This is the message body"), + // expectedResponse: []byte("This is the message body"), + //}, + //{ + // desc: "test ws proxy", + // dest: "/ws/echo", + // connectionType: quicpogs.ConnectionTypeWebsocket, + // metadata: []quicpogs.Metadata{ + // { + // Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", + // Val: "Websocket", + // }, + // { + // Key: "HttpHeader:Another-Header", + // Val: "Misc", + // }, + // { + // Key: "HttpHost", + // Val: "cf.host", + // }, + // { + // Key: "HttpMethod", + // Val: "get", + // }, + // }, + // message: wsBuf.Bytes(), + // expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, + //}, + //{ + // desc: "test tcp proxy", + // connectionType: quicpogs.ConnectionTypeTCP, + // metadata: []quicpogs.Metadata{}, + // message: []byte("Here is some tcp data"), + // expectedResponse: []byte("Here is some tcp data"), + //}, } for _, test := range tests { diff --git a/connection/rpc.go b/connection/rpc.go index f602290b..b288a0f8 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -2,6 +2,7 @@ package connection import ( "context" + "fmt" "io" "net" "time" @@ -116,6 +117,9 @@ func (rsc *registrationServerClient) RegisterConnection( observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() + observer.logServerInfo(connIndex, conn.Location, edgeAddress, fmt.Sprintf("Connection %s registered", conn.UUID)) + observer.sendConnectedEvent(connIndex, conn.Location) + return conn, nil } diff --git a/metrics/readiness_test.go b/metrics/readiness_test.go index 8e035f85..62e42f63 100644 --- a/metrics/readiness_test.go +++ b/metrics/readiness_test.go @@ -14,7 +14,7 @@ import ( func TestReadyServer_makeResponse(t *testing.T) { type fields struct { - isConnected map[uint8]tunnelstate.ConnectionInfo + isConnected map[int]bool } tests := []struct { name string @@ -25,11 +25,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "One connection online => HTTP 200", fields: fields{ - isConnected: map[uint8]tunnelstate.ConnectionInfo{ - 0: {IsConnected: false}, - 1: {IsConnected: false}, - 2: {IsConnected: true}, - 3: {IsConnected: false}, + isConnected: map[int]bool{ + 0: false, + 1: false, + 2: true, + 3: false, }, }, wantOK: true, @@ -38,11 +38,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "No connections online => no HTTP 200", fields: fields{ - isConnected: map[uint8]tunnelstate.ConnectionInfo{ - 0: {IsConnected: false}, - 1: {IsConnected: false}, - 2: {IsConnected: false}, - 3: {IsConnected: false}, + isConnected: map[int]bool{ + 0: false, + 1: false, + 2: false, + 3: false, }, }, wantReadyConnections: 0, diff --git a/supervisor/conn_aware_logger.go b/supervisor/conn_aware_logger.go index 1311f20b..6e717588 100644 --- a/supervisor/conn_aware_logger.go +++ b/supervisor/conn_aware_logger.go @@ -12,9 +12,9 @@ type ConnAwareLogger struct { logger *zerolog.Logger } -func NewConnAwareLogger(logger *zerolog.Logger, tracker *tunnelstate.ConnTracker, observer *connection.Observer) *ConnAwareLogger { +func NewConnAwareLogger(logger *zerolog.Logger, observer *connection.Observer) *ConnAwareLogger { connAwareLogger := &ConnAwareLogger{ - tracker: tracker, + tracker: tunnelstate.NewConnTracker(logger), logger: logger, } diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 7a100a59..c3f08844 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -19,7 +19,6 @@ import ( "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -89,9 +88,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato } reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) - - tracker := tunnelstate.NewConnTracker(config.Log) - log := NewConnAwareLogger(config.Log, tracker, config.Observer) + log := NewConnAwareLogger(config.Log, config.Observer) var edgeAddrHandler EdgeAddrHandler if isStaticEdge { // static edge addresses @@ -109,7 +106,6 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato credentialManager: reconnectCredentialManager, edgeAddrs: edgeIPs, edgeAddrHandler: edgeAddrHandler, - tracker: tracker, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, connAwareLogger: log, diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 8ff22ca1..2abf09f5 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -26,7 +26,6 @@ import ( "github.com/cloudflare/cloudflared/signal" "github.com/cloudflare/cloudflared/tunnelrpc" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" - "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -191,7 +190,6 @@ type EdgeTunnelServer struct { edgeAddrs *edgediscovery.Edge reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} - tracker *tunnelstate.ConnTracker connAwareLogger *ConnAwareLogger } @@ -274,12 +272,6 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFa return err } - // If a single connection has connected with the current protocol, we know we know we don't have to fallback - // to a different protocol. - if e.tracker.HasConnectedWith(e.config.ProtocolSelector.Current()) { - return err - } - if !selectNextProtocol( connLog.Logger(), protocolFallback, @@ -469,7 +461,6 @@ func serveTunnel( nil, gracefulShutdownC, config.GracePeriod, - protocol, ) switch protocol { diff --git a/tunnelstate/conntracker.go b/tunnelstate/conntracker.go index 426ba483..06fb176f 100644 --- a/tunnelstate/conntracker.go +++ b/tunnelstate/conntracker.go @@ -10,26 +10,20 @@ import ( type ConnTracker struct { sync.RWMutex - // int is the connection Index - connectionInfo map[uint8]ConnectionInfo - log *zerolog.Logger -} - -type ConnectionInfo struct { - IsConnected bool - Protocol connection.Protocol + isConnected map[int]bool + log *zerolog.Logger } func NewConnTracker(log *zerolog.Logger) *ConnTracker { return &ConnTracker{ - connectionInfo: make(map[uint8]ConnectionInfo, 0), - log: log, + isConnected: make(map[int]bool, 0), + log: log, } } -func MockedConnTracker(mocked map[uint8]ConnectionInfo) *ConnTracker { +func MockedConnTracker(mocked map[int]bool) *ConnTracker { return &ConnTracker{ - connectionInfo: mocked, + isConnected: mocked, } } @@ -37,17 +31,11 @@ func (ct *ConnTracker) OnTunnelEvent(c connection.Event) { switch c.EventType { case connection.Connected: ct.Lock() - ci := ConnectionInfo{ - IsConnected: true, - Protocol: c.Protocol, - } - ct.connectionInfo[c.Index] = ci + ct.isConnected[int(c.Index)] = true ct.Unlock() case connection.Disconnected, connection.Reconnecting, connection.RegisteringTunnel, connection.Unregistering: ct.Lock() - ci := ct.connectionInfo[c.Index] - ci.IsConnected = false - ct.connectionInfo[c.Index] = ci + ct.isConnected[int(c.Index)] = false ct.Unlock() default: ct.log.Error().Msgf("Unknown connection event case %v", c) @@ -58,23 +46,10 @@ func (ct *ConnTracker) CountActiveConns() uint { ct.RLock() defer ct.RUnlock() active := uint(0) - for _, ci := range ct.connectionInfo { - if ci.IsConnected { + for _, connected := range ct.isConnected { + if connected { active++ } } return active } - -// HasConnectedWith checks if we've ever had a successful connection to the edge -// with said protocol. -func (ct *ConnTracker) HasConnectedWith(protocol connection.Protocol) bool { - ct.RLock() - defer ct.RUnlock() - for _, ci := range ct.connectionInfo { - if ci.Protocol == protocol { - return true - } - } - return false -} From 278df5478a7cf7a411a44f8686cc3cd835938afd Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 1 Aug 2022 13:48:33 +0100 Subject: [PATCH 157/238] TUN-6584: Define QUIC datagram v2 format to support proxying IP packets --- connection/quic.go | 28 ++-- datagramsession/event.go | 6 - datagramsession/manager.go | 101 +++++-------- datagramsession/manager_test.go | 244 ++++++++++++++---------------- datagramsession/session.go | 60 ++++---- datagramsession/session_test.go | 57 +++++-- datagramsession/transport.go | 13 -- datagramsession/transport_test.go | 36 ----- quic/datagram.go | 92 +++++++---- quic/datagram_test.go | 126 ++++++++++++--- quic/datagramv2.go | 136 +++++++++++++++++ supervisor/tunnel.go | 1 + 12 files changed, 548 insertions(+), 352 deletions(-) delete mode 100644 datagramsession/transport.go delete mode 100644 datagramsession/transport_test.go create mode 100644 quic/datagramv2.go diff --git a/connection/quic.go b/connection/quic.go index 84bbb1e8..a7f15e69 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -33,14 +33,19 @@ const ( HTTPHostKey = "HttpHost" QUICMetadataFlowID = "FlowID" + // emperically this capacity has been working well + demuxChanCapacity = 16 ) // QUICConnection represents the type that facilitates Proxying via QUIC streams. type QUICConnection struct { - session quic.Connection - logger *zerolog.Logger - orchestrator Orchestrator - sessionManager datagramsession.Manager + session quic.Connection + logger *zerolog.Logger + orchestrator Orchestrator + // sessionManager tracks active sessions. It receives datagrams from quic connection via datagramMuxer + sessionManager datagramsession.Manager + // datagramMuxer mux/demux datagrams from quic connection + datagramMuxer *quicpogs.DatagramMuxer controlStreamHandler ControlStreamHandler connOptions *tunnelpogs.ConnectionOptions } @@ -60,18 +65,16 @@ func NewQUICConnection( return nil, &EdgeQuicDialError{Cause: err} } - datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger) - if err != nil { - return nil, err - } - - sessionManager := datagramsession.NewManager(datagramMuxer, logger) + demuxChan := make(chan *quicpogs.SessionDatagram, demuxChanCapacity) + datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan) + sessionManager := datagramsession.NewManager(logger, datagramMuxer.MuxSession, demuxChan) return &QUICConnection{ session: session, orchestrator: orchestrator, logger: logger, sessionManager: sessionManager, + datagramMuxer: datagramMuxer, controlStreamHandler: controlStreamHandler, connOptions: connOptions, }, nil @@ -108,6 +111,11 @@ func (q *QUICConnection) Serve(ctx context.Context) error { return q.sessionManager.Serve(ctx) }) + errGroup.Go(func() error { + defer cancel() + return q.datagramMuxer.ServeReceive(ctx) + }) + return errGroup.Wait() } diff --git a/datagramsession/event.go b/datagramsession/event.go index d79c6b31..2e91964e 100644 --- a/datagramsession/event.go +++ b/datagramsession/event.go @@ -42,9 +42,3 @@ func (sc *errClosedSession) Error() string { return fmt.Sprintf("session closed by local due to %s", sc.message) } } - -// newDatagram is an event when transport receives new datagram -type newDatagram struct { - sessionID uuid.UUID - payload []byte -} diff --git a/datagramsession/manager.go b/datagramsession/manager.go index 54c5c24d..3cf198bd 100644 --- a/datagramsession/manager.go +++ b/datagramsession/manager.go @@ -7,9 +7,9 @@ import ( "time" "github.com/google/uuid" - "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" - "golang.org/x/sync/errgroup" + + quicpogs "github.com/cloudflare/cloudflared/quic" ) const ( @@ -36,26 +36,25 @@ type Manager interface { type manager struct { registrationChan chan *registerSessionEvent unregistrationChan chan *unregisterSessionEvent - datagramChan chan *newDatagram - closedChan chan struct{} - transport transport + sendFunc transportSender + receiveChan <-chan *quicpogs.SessionDatagram + closedChan <-chan struct{} sessions map[uuid.UUID]*Session log *zerolog.Logger // timeout waiting for an API to finish. This can be overriden in test timeout time.Duration } -func NewManager(transport transport, log *zerolog.Logger) *manager { +func NewManager(log *zerolog.Logger, sendF transportSender, receiveChan <-chan *quicpogs.SessionDatagram) *manager { return &manager{ registrationChan: make(chan *registerSessionEvent), unregistrationChan: make(chan *unregisterSessionEvent), - // datagramChan is buffered, so it can read more datagrams from transport while the event loop is processing other events - datagramChan: make(chan *newDatagram, requestChanCapacity), - closedChan: make(chan struct{}), - transport: transport, - sessions: make(map[uuid.UUID]*Session), - log: log, - timeout: defaultReqTimeout, + sendFunc: sendF, + receiveChan: receiveChan, + closedChan: make(chan struct{}), + sessions: make(map[uuid.UUID]*Session), + log: log, + timeout: defaultReqTimeout, } } @@ -65,49 +64,21 @@ func (m *manager) UpdateLogger(log *zerolog.Logger) { } func (m *manager) Serve(ctx context.Context) error { - errGroup, ctx := errgroup.WithContext(ctx) - errGroup.Go(func() error { - for { - sessionID, payload, err := m.transport.ReceiveFrom() - if err != nil { - if aerr, ok := err.(*quic.ApplicationError); ok && uint64(aerr.ErrorCode) == uint64(quic.NoError) { - return nil - } else { - return err - } - } - datagram := &newDatagram{ - sessionID: sessionID, - payload: payload, - } - select { - case <-ctx.Done(): - return ctx.Err() - // Only the event loop routine can update/lookup the sessions map to avoid concurrent access - // Send the datagram to the event loop. It will find the session to send to - case m.datagramChan <- datagram: - } + for { + select { + case <-ctx.Done(): + m.shutdownSessions(ctx.Err()) + return ctx.Err() + // receiveChan is buffered, so the transport can read more datagrams from transport while the event loop is + // processing other events + case datagram := <-m.receiveChan: + m.sendToSession(datagram) + case registration := <-m.registrationChan: + m.registerSession(ctx, registration) + case unregistration := <-m.unregistrationChan: + m.unregisterSession(unregistration) } - }) - errGroup.Go(func() error { - for { - select { - case <-ctx.Done(): - return nil - case datagram := <-m.datagramChan: - m.sendToSession(datagram) - case registration := <-m.registrationChan: - m.registerSession(ctx, registration) - // TODO: TUN-5422: Unregister inactive session upon timeout - case unregistration := <-m.unregistrationChan: - m.unregisterSession(unregistration) - } - } - }) - err := errGroup.Wait() - close(m.closedChan) - m.shutdownSessions(err) - return err + } } func (m *manager) shutdownSessions(err error) { @@ -149,16 +120,17 @@ func (m *manager) registerSession(ctx context.Context, registration *registerSes } func (m *manager) newSession(id uuid.UUID, dstConn io.ReadWriteCloser) *Session { + logger := m.log.With().Str("sessionID", id.String()).Logger() return &Session{ - ID: id, - transport: m.transport, - dstConn: dstConn, + ID: id, + sendFunc: m.sendFunc, + dstConn: dstConn, // activeAtChan has low capacity. It can be full when there are many concurrent read/write. markActive() will // drop instead of blocking because last active time only needs to be an approximation activeAtChan: make(chan time.Time, 2), // capacity is 2 because close() and dstToTransport routine in Serve() can write to this channel closeChan: make(chan error, 2), - log: m.log, + log: &logger, } } @@ -191,16 +163,13 @@ func (m *manager) unregisterSession(unregistration *unregisterSessionEvent) { } } -func (m *manager) sendToSession(datagram *newDatagram) { - session, ok := m.sessions[datagram.sessionID] +func (m *manager) sendToSession(datagram *quicpogs.SessionDatagram) { + session, ok := m.sessions[datagram.ID] if !ok { - m.log.Error().Str("sessionID", datagram.sessionID.String()).Msg("session not found") + m.log.Error().Str("sessionID", datagram.ID.String()).Msg("session not found") return } // session writes to destination over a connected UDP socket, which should not be blocking, so this call doesn't // need to run in another go routine - _, err := session.transportToDst(datagram.payload) - if err != nil { - m.log.Err(err).Str("sessionID", datagram.sessionID.String()).Msg("Failed to write payload to session") - } + session.transportToDst(datagram.Payload) } diff --git a/datagramsession/manager_test.go b/datagramsession/manager_test.go index 7b17bf7d..1d73b33f 100644 --- a/datagramsession/manager_test.go +++ b/datagramsession/manager_test.go @@ -14,22 +14,30 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + quicpogs "github.com/cloudflare/cloudflared/quic" +) + +var ( + nopLogger = zerolog.Nop() ) func TestManagerServe(t *testing.T) { const ( - sessions = 20 - msgs = 50 + sessions = 2 + msgs = 5 remoteUnregisterMsg = "eyeball closed connection" ) - mg, transport := newTestManager(1) - - eyeballTracker := make(map[uuid.UUID]*datagramChannel) - for i := 0; i < sessions; i++ { - sessionID := uuid.New() - eyeballTracker[sessionID] = newDatagramChannel(1) + requestChan := make(chan *quicpogs.SessionDatagram) + transport := mockQUICTransport{ + sessions: make(map[uuid.UUID]chan []byte), } + for i := 0; i < sessions; i++ { + transport.sessions[uuid.New()] = make(chan []byte) + } + + mg := NewManager(&nopLogger, transport.MuxSession, requestChan) ctx, cancel := context.WithCancel(context.Background()) serveDone := make(chan struct{}) @@ -38,53 +46,42 @@ func TestManagerServe(t *testing.T) { close(serveDone) }(ctx) - go func(ctx context.Context) { - for { - sessionID, payload, err := transport.respChan.Receive(ctx) - if err != nil { - require.Equal(t, context.Canceled, err) - return - } - respChan := eyeballTracker[sessionID] - require.NoError(t, respChan.Send(ctx, sessionID, payload)) - } - }(ctx) - errGroup, ctx := errgroup.WithContext(ctx) - for sID, receiver := range eyeballTracker { + for sessionID, eyeballRespChan := range transport.sessions { + // Assign loop variables to local variables + sID := sessionID + payload := testPayload(sID) + expectResp := testResponse(payload) + + cfdConn, originConn := net.Pipe() + + origin := mockOrigin{ + expectMsgCount: msgs, + expectedMsg: payload, + expectedResp: expectResp, + conn: originConn, + } + + eyeball := mockEyeballSession{ + id: sID, + expectedMsgCount: msgs, + expectedMsg: payload, + expectedResponse: expectResp, + respReceiver: eyeballRespChan, + } + // Assign loop variables to local variables - sessionID := sID - eyeballRespReceiver := receiver errGroup.Go(func() error { - payload := testPayload(sessionID) - expectResp := testResponse(payload) - - cfdConn, originConn := net.Pipe() - - origin := mockOrigin{ - expectMsgCount: msgs, - expectedMsg: payload, - expectedResp: expectResp, - conn: originConn, - } - eyeball := mockEyeball{ - expectMsgCount: msgs, - expectedMsg: expectResp, - expectSessionID: sessionID, - respReceiver: eyeballRespReceiver, - } - + session, err := mg.RegisterSession(ctx, sID, cfdConn) + require.NoError(t, err) reqErrGroup, reqCtx := errgroup.WithContext(ctx) reqErrGroup.Go(func() error { return origin.serve() }) reqErrGroup.Go(func() error { - return eyeball.serve(reqCtx) + return eyeball.serve(reqCtx, requestChan) }) - session, err := mg.RegisterSession(ctx, sessionID, cfdConn) - require.NoError(t, err) - sessionDone := make(chan struct{}) go func() { closedByRemote, err := session.Serve(ctx, time.Minute*2) @@ -97,23 +94,17 @@ func TestManagerServe(t *testing.T) { close(sessionDone) }() - for i := 0; i < msgs; i++ { - require.NoError(t, transport.newRequest(ctx, sessionID, testPayload(sessionID))) - } - // Make sure eyeball and origin have received all messages before unregistering the session require.NoError(t, reqErrGroup.Wait()) - require.NoError(t, mg.UnregisterSession(ctx, sessionID, remoteUnregisterMsg, true)) + require.NoError(t, mg.UnregisterSession(ctx, sID, remoteUnregisterMsg, true)) <-sessionDone - return nil }) } require.NoError(t, errGroup.Wait()) cancel() - transport.close() <-serveDone } @@ -122,7 +113,7 @@ func TestTimeout(t *testing.T) { testTimeout = time.Millisecond * 50 ) - mg, _ := newTestManager(1) + mg := NewManager(&nopLogger, nil, nil) mg.timeout = testTimeout ctx := context.Background() sessionID := uuid.New() @@ -135,9 +126,51 @@ func TestTimeout(t *testing.T) { require.ErrorIs(t, err, context.DeadlineExceeded) } -func TestCloseTransportCloseSessions(t *testing.T) { - mg, transport := newTestManager(1) - ctx := context.Background() +func TestUnregisterSessionCloseSession(t *testing.T) { + sessionID := uuid.New() + payload := []byte(t.Name()) + sender := newMockTransportSender(sessionID, payload) + mg := NewManager(&nopLogger, sender.muxSession, nil) + ctx, cancel := context.WithCancel(context.Background()) + + managerDone := make(chan struct{}) + go func() { + err := mg.Serve(ctx) + require.Error(t, err) + close(managerDone) + }() + + cfdConn, originConn := net.Pipe() + session, err := mg.RegisterSession(ctx, sessionID, cfdConn) + require.NoError(t, err) + require.NotNil(t, session) + + unregisteredChan := make(chan struct{}) + go func() { + _, err := originConn.Write(payload) + require.NoError(t, err) + + err = mg.UnregisterSession(ctx, sessionID, "eyeball closed session", true) + require.NoError(t, err) + + close(unregisteredChan) + }() + + closedByRemote, err := session.Serve(ctx, time.Minute) + require.True(t, closedByRemote) + require.Error(t, err) + + <-unregisteredChan + cancel() + <-managerDone +} + +func TestManagerCtxDoneCloseSessions(t *testing.T) { + sessionID := uuid.New() + payload := []byte(t.Name()) + sender := newMockTransportSender(sessionID, payload) + mg := NewManager(&nopLogger, sender.muxSession, nil) + ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup wg.Add(1) @@ -147,35 +180,26 @@ func TestCloseTransportCloseSessions(t *testing.T) { require.Error(t, err) }() - cfdConn, eyeballConn := net.Pipe() - session, err := mg.RegisterSession(ctx, uuid.New(), cfdConn) + cfdConn, originConn := net.Pipe() + session, err := mg.RegisterSession(ctx, sessionID, cfdConn) require.NoError(t, err) require.NotNil(t, session) wg.Add(1) go func() { defer wg.Done() - _, err := eyeballConn.Write([]byte(t.Name())) + _, err := originConn.Write(payload) require.NoError(t, err) - transport.close() + cancel() }() closedByRemote, err := session.Serve(ctx, time.Minute) - require.True(t, closedByRemote) + require.False(t, closedByRemote) require.Error(t, err) wg.Wait() } -func newTestManager(capacity uint) (*manager, *mockQUICTransport) { - log := zerolog.Nop() - transport := &mockQUICTransport{ - reqChan: newDatagramChannel(capacity), - respChan: newDatagramChannel(capacity), - } - return NewManager(transport, &log), transport -} - type mockOrigin struct { expectMsgCount int expectedMsg []byte @@ -197,7 +221,6 @@ func (mo *mockOrigin) serve() error { if !bytes.Equal(readBuffer[:n], mo.expectedMsg) { return fmt.Errorf("Expect %v, read %v", mo.expectedMsg, readBuffer[:n]) } - _, err = mo.conn.Write(mo.expectedResp) if err != nil { return err @@ -214,72 +237,35 @@ func testResponse(msg []byte) []byte { return []byte(fmt.Sprintf("Response to %v", msg)) } -type mockEyeball struct { - expectMsgCount int - expectedMsg []byte - expectSessionID uuid.UUID - respReceiver *datagramChannel +type mockQUICTransport struct { + sessions map[uuid.UUID]chan []byte } -func (me *mockEyeball) serve(ctx context.Context) error { - for i := 0; i < me.expectMsgCount; i++ { - sessionID, msg, err := me.respReceiver.Receive(ctx) - if err != nil { - return err - } - if sessionID != me.expectSessionID { - return fmt.Errorf("Expect session %s, got %s", me.expectSessionID, sessionID) - } - if !bytes.Equal(msg, me.expectedMsg) { - return fmt.Errorf("Expect %v, read %v", me.expectedMsg, msg) - } - } +func (me *mockQUICTransport) MuxSession(id uuid.UUID, payload []byte) error { + session := me.sessions[id] + session <- payload return nil } -// datagramChannel is a channel for Datagram with wrapper to send/receive with context -type datagramChannel struct { - datagramChan chan *newDatagram - closedChan chan struct{} +type mockEyeballSession struct { + id uuid.UUID + expectedMsgCount int + expectedMsg []byte + expectedResponse []byte + respReceiver <-chan []byte } -func newDatagramChannel(capacity uint) *datagramChannel { - return &datagramChannel{ - datagramChan: make(chan *newDatagram, capacity), - closedChan: make(chan struct{}), - } -} - -func (rc *datagramChannel) Send(ctx context.Context, sessionID uuid.UUID, payload []byte) error { - select { - case <-ctx.Done(): - return ctx.Err() - case <-rc.closedChan: - return &errClosedSession{ - message: fmt.Errorf("datagram channel closed").Error(), - byRemote: true, +func (me *mockEyeballSession) serve(ctx context.Context, requestChan chan *quicpogs.SessionDatagram) error { + for i := 0; i < me.expectedMsgCount; i++ { + requestChan <- &quicpogs.SessionDatagram{ + ID: me.id, + Payload: me.expectedMsg, } - case rc.datagramChan <- &newDatagram{sessionID: sessionID, payload: payload}: - return nil - } -} - -func (rc *datagramChannel) Receive(ctx context.Context) (uuid.UUID, []byte, error) { - select { - case <-ctx.Done(): - return uuid.Nil, nil, ctx.Err() - case <-rc.closedChan: - err := &errClosedSession{ - message: fmt.Errorf("datagram channel closed").Error(), - byRemote: true, + resp := <-me.respReceiver + if !bytes.Equal(resp, me.expectedResponse) { + return fmt.Errorf("Expect %v, read %v", me.expectedResponse, resp) } - return uuid.Nil, nil, err - case msg := <-rc.datagramChan: - return msg.sessionID, msg.payload, nil + fmt.Println("Resp", resp) } -} - -func (rc *datagramChannel) Close() { - // No need to close msgChan, it will be garbage collect once there is no reference to it - close(rc.closedChan) + return nil } diff --git a/datagramsession/session.go b/datagramsession/session.go index 351ae298..8851b01e 100644 --- a/datagramsession/session.go +++ b/datagramsession/session.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "net" "time" "github.com/google/uuid" @@ -18,21 +19,20 @@ func SessionIdleErr(timeout time.Duration) error { return fmt.Errorf("session idle for %v", timeout) } +type transportSender func(sessionID uuid.UUID, payload []byte) error + // Session is a bidirectional pipe of datagrams between transport and dstConn -// Currently the only implementation of transport is quic DatagramMuxer // Destination can be a connection with origin or with eyeball // When the destination is origin: -// - Datagrams from edge are read by Manager from the transport. Manager finds the corresponding Session and calls the -// write method of the Session to send to origin -// - Datagrams from origin are read from conn and SentTo transport. Transport will return them to eyeball +// - Manager receives datagrams from receiveChan and calls the transportToDst method of the Session to send to origin +// - Datagrams from origin are read from conn and Send to transport using the transportSender callback. Transport will return them to eyeball // When the destination is eyeball: -// - Datagrams from eyeball are read from conn and SentTo transport. Transport will send them to cloudflared -// - Datagrams from cloudflared are read by Manager from the transport. Manager finds the corresponding Session and calls the -// write method of the Session to send to eyeball +// - Datagrams from eyeball are read from conn and Send to transport. Transport will send them to cloudflared using the transportSender callback. +// - Manager receives datagrams from receiveChan and calls the transportToDst method of the Session to send to the eyeball type Session struct { - ID uuid.UUID - transport transport - dstConn io.ReadWriteCloser + ID uuid.UUID + sendFunc transportSender + dstConn io.ReadWriteCloser // activeAtChan is used to communicate the last read/write time activeAtChan chan time.Time closeChan chan error @@ -46,9 +46,16 @@ func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (clos const maxPacketSize = 1500 readBuffer := make([]byte, maxPacketSize) for { - if err := s.dstToTransport(readBuffer); err != nil { - s.closeChan <- err - return + if closeSession, err := s.dstToTransport(readBuffer); err != nil { + if err != net.ErrClosed { + s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport") + } else { + s.log.Debug().Msg("Session cannot read from destination because the connection is closed") + } + if closeSession { + s.closeChan <- err + return + } } } }() @@ -89,32 +96,25 @@ func (s *Session) waitForCloseCondition(ctx context.Context, closeAfterIdle time } } -func (s *Session) dstToTransport(buffer []byte) error { +func (s *Session) dstToTransport(buffer []byte) (closeSession bool, err error) { n, err := s.dstConn.Read(buffer) s.markActive() // https://pkg.go.dev/io#Reader suggests caller should always process n > 0 bytes - if n > 0 { - if n <= int(s.transport.MTU()) { - err = s.transport.SendTo(s.ID, buffer[:n]) - } else { - // drop packet for now, eventually reply with ICMP for PMTUD - s.log.Debug(). - Str("session", s.ID.String()). - Int("len", n). - Int("mtu", s.transport.MTU()). - Msg("dropped packet exceeding MTU") + if n > 0 || err == nil { + if sendErr := s.sendFunc(s.ID, buffer[:n]); sendErr != nil { + return false, sendErr } } - // Some UDP application might send 0-size payload. - if err == nil && n == 0 { - err = s.transport.SendTo(s.ID, []byte{}) - } - return err + return err != nil, err } func (s *Session) transportToDst(payload []byte) (int, error) { s.markActive() - return s.dstConn.Write(payload) + n, err := s.dstConn.Write(payload) + if err != nil { + s.log.Err(err).Msg("Failed to write payload to session") + } + return n, err } // Sends the last active time to the idle checker loop without blocking. activeAtChan will only be full when there diff --git a/datagramsession/session_test.go b/datagramsession/session_test.go index db4201f6..b0b7a66e 100644 --- a/datagramsession/session_test.go +++ b/datagramsession/session_test.go @@ -11,8 +11,11 @@ import ( "time" "github.com/google/uuid" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + quicpogs "github.com/cloudflare/cloudflared/quic" ) // TestCloseSession makes sure a session will stop after context is done @@ -41,7 +44,8 @@ func testSessionReturns(t *testing.T, closeBy closeMethod, closeAfterIdle time.D cfdConn, originConn := net.Pipe() payload := testPayload(sessionID) - mg, _ := newTestManager(1) + log := zerolog.Nop() + mg := NewManager(&log, nil, nil) session := mg.newSession(sessionID, cfdConn) ctx, cancel := context.WithCancel(context.Background()) @@ -114,7 +118,9 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool) cfdConn, originConn := net.Pipe() payload := testPayload(sessionID) - mg, _ := newTestManager(100) + respChan := make(chan *quicpogs.SessionDatagram) + sender := newMockTransportSender(sessionID, payload) + mg := NewManager(&nopLogger, sender.muxSession, respChan) session := mg.newSession(sessionID, cfdConn) startTime := time.Now() @@ -177,7 +183,7 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool) func TestMarkActiveNotBlocking(t *testing.T) { const concurrentCalls = 50 - mg, _ := newTestManager(1) + mg := NewManager(&nopLogger, nil, nil) session := mg.newSession(uuid.New(), nil) var wg sync.WaitGroup wg.Add(concurrentCalls) @@ -190,11 +196,16 @@ func TestMarkActiveNotBlocking(t *testing.T) { wg.Wait() } +// Some UDP application might send 0-size payload. func TestZeroBytePayload(t *testing.T) { sessionID := uuid.New() cfdConn, originConn := net.Pipe() - mg, transport := newTestManager(1) + sender := sendOnceTransportSender{ + baseSender: newMockTransportSender(sessionID, make([]byte, 0)), + sentChan: make(chan struct{}), + } + mg := NewManager(&nopLogger, sender.muxSession, nil) session := mg.newSession(sessionID, cfdConn) ctx, cancel := context.WithCancel(context.Background()) @@ -215,11 +226,39 @@ func TestZeroBytePayload(t *testing.T) { return nil }) - receivedSessionID, payload, err := transport.respChan.Receive(ctx) - require.NoError(t, err) - require.Len(t, payload, 0) - require.Equal(t, sessionID, receivedSessionID) - + <-sender.sentChan cancel() require.NoError(t, errGroup.Wait()) } + +type mockTransportSender struct { + expectedSessionID uuid.UUID + expectedPayload []byte +} + +func newMockTransportSender(expectedSessionID uuid.UUID, expectedPayload []byte) *mockTransportSender { + return &mockTransportSender{ + expectedSessionID: expectedSessionID, + expectedPayload: expectedPayload, + } +} + +func (mts *mockTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error { + if sessionID != mts.expectedSessionID { + return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, sessionID) + } + if !bytes.Equal(payload, mts.expectedPayload) { + return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, payload) + } + return nil +} + +type sendOnceTransportSender struct { + baseSender *mockTransportSender + sentChan chan struct{} +} + +func (sots *sendOnceTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error { + defer close(sots.sentChan) + return sots.baseSender.muxSession(sessionID, payload) +} diff --git a/datagramsession/transport.go b/datagramsession/transport.go deleted file mode 100644 index f41e1e83..00000000 --- a/datagramsession/transport.go +++ /dev/null @@ -1,13 +0,0 @@ -package datagramsession - -import "github.com/google/uuid" - -// Transport is a connection between cloudflared and edge that can multiplex datagrams from multiple sessions -type transport interface { - // SendTo writes payload for a session to the transport - SendTo(sessionID uuid.UUID, payload []byte) error - // ReceiveFrom reads the next datagram from the transport - ReceiveFrom() (uuid.UUID, []byte, error) - // Max transmission unit to receive from the transport - MTU() int -} diff --git a/datagramsession/transport_test.go b/datagramsession/transport_test.go deleted file mode 100644 index 6b187722..00000000 --- a/datagramsession/transport_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package datagramsession - -import ( - "context" - - "github.com/google/uuid" -) - -type mockQUICTransport struct { - reqChan *datagramChannel - respChan *datagramChannel -} - -func (mt *mockQUICTransport) SendTo(sessionID uuid.UUID, payload []byte) error { - buf := make([]byte, len(payload)) - // The QUIC implementation copies data to another buffer before returning https://github.com/lucas-clemente/quic-go/blob/v0.24.0/session.go#L1967-L1975 - copy(buf, payload) - return mt.respChan.Send(context.Background(), sessionID, buf) -} - -func (mt *mockQUICTransport) ReceiveFrom() (uuid.UUID, []byte, error) { - return mt.reqChan.Receive(context.Background()) -} - -func (mt *mockQUICTransport) MTU() int { - return 1280 -} - -func (mt *mockQUICTransport) newRequest(ctx context.Context, sessionID uuid.UUID, payload []byte) error { - return mt.reqChan.Send(ctx, sessionID, payload) -} - -func (mt *mockQUICTransport) close() { - mt.reqChan.Close() - mt.respChan.Close() -} diff --git a/quic/datagram.go b/quic/datagram.go index 350b601f..e8e4fad7 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -1,6 +1,7 @@ package quic import ( + "context" "fmt" "github.com/google/uuid" @@ -13,53 +14,88 @@ const ( sessionIDLen = len(uuid.UUID{}) ) +type SessionDatagram struct { + ID uuid.UUID + Payload []byte +} + +type BaseDatagramMuxer interface { + // MuxSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the + // payload from multiple datagram sessions + MuxSession(sessionID uuid.UUID, payload []byte) error + // ServeReceive starts a loop to receive datagrams from the QUIC connection + ServeReceive(ctx context.Context) error +} + type DatagramMuxer struct { - session quic.Connection - logger *zerolog.Logger + session quic.Connection + logger *zerolog.Logger + demuxChan chan<- *SessionDatagram } -func NewDatagramMuxer(quicSession quic.Connection, logger *zerolog.Logger) (*DatagramMuxer, error) { +func NewDatagramMuxer(quicSession quic.Connection, log *zerolog.Logger, demuxChan chan<- *SessionDatagram) *DatagramMuxer { + logger := log.With().Uint8("datagramVersion", 1).Logger() return &DatagramMuxer{ - session: quicSession, - logger: logger, - }, nil + session: quicSession, + logger: &logger, + demuxChan: demuxChan, + } } -// SendTo suffix the session ID to the payload so the other end of the QUIC session can demultiplex -// the payload from multiple datagram sessions -func (dm *DatagramMuxer) SendTo(sessionID uuid.UUID, payload []byte) error { - if len(payload) > maxDatagramPayloadSize { +// Maximum application payload to send to / receive from QUIC datagram frame +func (dm *DatagramMuxer) mtu() int { + return maxDatagramPayloadSize +} + +func (dm *DatagramMuxer) MuxSession(sessionID uuid.UUID, payload []byte) error { + if len(payload) > dm.mtu() { // TODO: TUN-5302 return ICMP packet too big message - return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.MTU()) + // drop packet for now, eventually reply with ICMP for PMTUD + return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu()) } - msgWithID, err := suffixSessionID(sessionID, payload) + payloadWithMetadata, err := suffixSessionID(sessionID, payload) if err != nil { return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") } - if err := dm.session.SendMessage(msgWithID); err != nil { + if err := dm.session.SendMessage(payloadWithMetadata); err != nil { return errors.Wrap(err, "Failed to send datagram back to edge") } return nil } -// ReceiveFrom extracts datagram session ID, then sends the session ID and payload to session manager -// which determines how to proxy to the origin. It assumes the datagram session has already been -// registered with session manager through other side channel -func (dm *DatagramMuxer) ReceiveFrom() (uuid.UUID, []byte, error) { - msg, err := dm.session.ReceiveMessage() - if err != nil { - return uuid.Nil, nil, err +func (dm *DatagramMuxer) ServeReceive(ctx context.Context) error { + for { + // Extracts datagram session ID, then sends the session ID and payload to receiver + // which determines how to proxy to the origin. It assumes the datagram session has already been + // registered with receiver through other side channel + msg, err := dm.session.ReceiveMessage() + if err != nil { + return err + } + if err := dm.demux(ctx, msg); err != nil { + dm.logger.Error().Err(err).Msg("Failed to demux datagram") + if err == context.Canceled { + return err + } + } } - sessionID, payload, err := extractSessionID(msg) - if err != nil { - return uuid.Nil, nil, err - } - return sessionID, payload, nil } -// Maximum application payload to send to / receive from QUIC datagram frame -func (dm *DatagramMuxer) MTU() int { - return maxDatagramPayloadSize +func (dm *DatagramMuxer) demux(ctx context.Context, msg []byte) error { + sessionID, payload, err := extractSessionID(msg) + if err != nil { + return err + } + sessionDatagram := SessionDatagram{ + ID: sessionID, + Payload: payload, + } + select { + case dm.demuxChan <- &sessionDatagram: + return nil + case <-ctx.Done(): + return ctx.Err() + } } // Each QUIC datagram should be suffixed with session ID. diff --git a/quic/datagram_test.go b/quic/datagram_test.go index ac32f410..00673835 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -8,6 +8,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "fmt" "math/big" "testing" "time" @@ -52,9 +53,29 @@ func TestSuffixSessionIDError(t *testing.T) { require.Error(t, err) } -func TestMaxDatagramPayload(t *testing.T) { - payload := make([]byte, maxDatagramPayloadSize) +func TestDatagram(t *testing.T) { + maxPayload := make([]byte, maxDatagramPayloadSize) + noPayloadSession := uuid.New() + maxPayloadSession := uuid.New() + sessionToPayload := []*SessionDatagram{ + { + ID: noPayloadSession, + Payload: make([]byte, 0), + }, + { + ID: maxPayloadSession, + Payload: maxPayload, + }, + } + flowPayloads := [][]byte{ + maxPayload, + } + testDatagram(t, 1, sessionToPayload, nil) + testDatagram(t, 2, sessionToPayload, flowPayloads) +} + +func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatagram, packetPayloads [][]byte) { quicConfig := &quic.Config{ KeepAlivePeriod: 5 * time.Millisecond, EnableDatagrams: true, @@ -63,6 +84,8 @@ func TestMaxDatagramPayload(t *testing.T) { quicListener := newQUICListener(t, quicConfig) defer quicListener.Close() + logger := zerolog.Nop() + errGroup, ctx := errgroup.WithContext(context.Background()) // Run edge side of datagram muxer errGroup.Go(func() error { @@ -72,22 +95,32 @@ func TestMaxDatagramPayload(t *testing.T) { return err } - logger := zerolog.Nop() - muxer, err := NewDatagramMuxer(quicSession, &logger) - if err != nil { - return err + sessionDemuxChan := make(chan *SessionDatagram, 16) + + switch version { + case 1: + muxer := NewDatagramMuxer(quicSession, &logger, sessionDemuxChan) + muxer.ServeReceive(ctx) + case 2: + packetDemuxChan := make(chan []byte, len(packetPayloads)) + muxer := NewDatagramMuxerV2(quicSession, &logger, sessionDemuxChan, packetDemuxChan) + muxer.ServeReceive(ctx) + + for _, expectedPayload := range packetPayloads { + require.Equal(t, expectedPayload, <-packetDemuxChan) + } + default: + return fmt.Errorf("unknown datagram version %d", version) } - sessionID, receivedPayload, err := muxer.ReceiveFrom() - if err != nil { - return err + for _, expectedPayload := range sessionToPayloads { + actualPayload := <-sessionDemuxChan + require.Equal(t, expectedPayload, actualPayload) } - require.Equal(t, testSessionID, sessionID) - require.True(t, bytes.Equal(payload, receivedPayload)) - return nil }) + largePayload := make([]byte, MaxDatagramFrameSize) // Run cloudflared side of datagram muxer errGroup.Go(func() error { tlsClientConfig := &tls.Config{ @@ -97,24 +130,35 @@ func TestMaxDatagramPayload(t *testing.T) { // Establish quic connection quicSession, err := quic.DialAddrEarly(quicListener.Addr().String(), tlsClientConfig, quicConfig) require.NoError(t, err) - - logger := zerolog.Nop() - muxer, err := NewDatagramMuxer(quicSession, &logger) - if err != nil { - return err - } + defer quicSession.CloseWithError(0, "") // Wait a few milliseconds for MTU discovery to take place time.Sleep(time.Millisecond * 100) - err = muxer.SendTo(testSessionID, payload) - if err != nil { - return err + + var muxer BaseDatagramMuxer + switch version { + case 1: + muxer = NewDatagramMuxer(quicSession, &logger, nil) + case 2: + muxerV2 := NewDatagramMuxerV2(quicSession, &logger, nil, nil) + for _, payload := range packetPayloads { + require.NoError(t, muxerV2.MuxPacket(payload)) + } + // Payload larger than transport MTU, should not be sent + require.Error(t, muxerV2.MuxPacket(largePayload)) + muxer = muxerV2 + default: + return fmt.Errorf("unknown datagram version %d", version) } - // Payload larger than transport MTU, should return an error - largePayload := make([]byte, MaxDatagramFrameSize) - err = muxer.SendTo(testSessionID, largePayload) - require.Error(t, err) + for _, sessionDatagram := range sessionToPayloads { + require.NoError(t, muxer.MuxSession(sessionDatagram.ID, sessionDatagram.Payload)) + } + // Payload larger than transport MTU, should not be sent + require.Error(t, muxer.MuxSession(testSessionID, largePayload)) + + // Wait for edge to finish receiving the messages + time.Sleep(time.Millisecond * 100) return nil }) @@ -154,3 +198,35 @@ func generateTLSConfig() *tls.Config { NextProtos: []string{"argotunnel"}, } } + +type sessionMuxer interface { + SendToSession(sessionID uuid.UUID, payload []byte) error +} + +type mockSessionReceiver struct { + expectedSessionToPayload map[uuid.UUID][]byte + receivedCount int +} + +func (msr *mockSessionReceiver) ReceiveDatagram(sessionID uuid.UUID, payload []byte) error { + expectedPayload := msr.expectedSessionToPayload[sessionID] + if !bytes.Equal(expectedPayload, payload) { + return fmt.Errorf("expect %v to have payload %s, got %s", sessionID, string(expectedPayload), string(payload)) + } + msr.receivedCount++ + return nil +} + +type mockFlowReceiver struct { + expectedPayloads [][]byte + receivedCount int +} + +func (mfr *mockFlowReceiver) ReceiveFlow(payload []byte) error { + expectedPayload := mfr.expectedPayloads[mfr.receivedCount] + if !bytes.Equal(expectedPayload, payload) { + return fmt.Errorf("expect flow %d to have payload %s, got %s", mfr.receivedCount, string(expectedPayload), string(payload)) + } + mfr.receivedCount++ + return nil +} diff --git a/quic/datagramv2.go b/quic/datagramv2.go new file mode 100644 index 00000000..73e55f7d --- /dev/null +++ b/quic/datagramv2.go @@ -0,0 +1,136 @@ +package quic + +import ( + "context" + "fmt" + + "github.com/google/uuid" + "github.com/lucas-clemente/quic-go" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +type datagramV2Type byte + +const ( + udp datagramV2Type = iota + ip +) + +func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) { + if len(b)+1 > MaxDatagramFrameSize { + return nil, fmt.Errorf("datagram size %d exceeds max frame size %d", len(b), MaxDatagramFrameSize) + } + b = append(b, byte(datagramType)) + return b, nil +} + +// Maximum application payload to send to / receive from QUIC datagram frame +func (dm *DatagramMuxerV2) mtu() int { + return maxDatagramPayloadSize +} + +type DatagramMuxerV2 struct { + session quic.Connection + logger *zerolog.Logger + sessionDemuxChan chan<- *SessionDatagram + packetDemuxChan chan<- []byte +} + +func NewDatagramMuxerV2( + quicSession quic.Connection, + log *zerolog.Logger, + sessionDemuxChan chan<- *SessionDatagram, + packetDemuxChan chan<- []byte) *DatagramMuxerV2 { + logger := log.With().Uint8("datagramVersion", 2).Logger() + return &DatagramMuxerV2{ + session: quicSession, + logger: &logger, + sessionDemuxChan: sessionDemuxChan, + packetDemuxChan: packetDemuxChan, + } +} + +// MuxSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can +// demultiplex the payload from multiple datagram sessions +func (dm *DatagramMuxerV2) MuxSession(sessionID uuid.UUID, payload []byte) error { + if len(payload) > dm.mtu() { + // TODO: TUN-5302 return ICMP packet too big message + return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu()) + } + msgWithID, err := suffixSessionID(sessionID, payload) + if err != nil { + return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") + } + msgWithIDAndType, err := suffixType(msgWithID, udp) + if err != nil { + return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped") + } + if err := dm.session.SendMessage(msgWithIDAndType); err != nil { + return errors.Wrap(err, "Failed to send datagram back to edge") + } + return nil +} + +// MuxPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing +// the payload as IP and look at the source and destination. +func (dm *DatagramMuxerV2) MuxPacket(packet []byte) error { + payloadWithVersion, err := suffixType(packet, ip) + if err != nil { + return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped") + } + if err := dm.session.SendMessage(payloadWithVersion); err != nil { + return errors.Wrap(err, "Failed to send datagram back to edge") + } + return nil +} + +// Demux reads datagrams from the QUIC connection and demuxes depending on whether it's a session or packet +func (dm *DatagramMuxerV2) ServeReceive(ctx context.Context) error { + for { + msg, err := dm.session.ReceiveMessage() + if err != nil { + return err + } + if err := dm.demux(ctx, msg); err != nil { + dm.logger.Error().Err(err).Msg("Failed to demux datagram") + if err == context.Canceled { + return err + } + } + } +} + +func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error { + if len(msgWithType) < 1 { + return fmt.Errorf("QUIC datagram should have at least 1 byte") + } + msgType := datagramV2Type(msgWithType[len(msgWithType)-1]) + msg := msgWithType[0 : len(msgWithType)-1] + switch msgType { + case udp: + sessionID, payload, err := extractSessionID(msg) + if err != nil { + return err + } + sessionDatagram := SessionDatagram{ + ID: sessionID, + Payload: payload, + } + select { + case dm.sessionDemuxChan <- &sessionDatagram: + return nil + case <-ctx.Done(): + return ctx.Err() + } + case ip: + select { + case dm.packetDemuxChan <- msg: + return nil + case <-ctx.Done(): + return ctx.Err() + } + default: + return fmt.Errorf("Unexpected datagram type %d", msgType) + } +} diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 2abf09f5..7517a02b 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -33,6 +33,7 @@ const ( FeatureSerializedHeaders = "serialized_headers" FeatureQuickReconnects = "quick_reconnects" FeatureAllowRemoteConfig = "allow_remote_config" + FeatureDatagramV2 = "support_datagram_v2" ) type TunnelConfig struct { From 99f39225f1fa85c3a340b57470326c2373b57792 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 11 Aug 2022 21:31:36 +0100 Subject: [PATCH 158/238] TUN-6617: Dont fallback to http2 if QUIC conn was successful. cloudflared falls back aggressively to HTTP/2 protocol if a connection attempt with QUIC failed. This was done to ensure that machines with UDP egress disabled did not stop clients from connecting to the cloudlfare edge. This PR improves on that experience by having cloudflared remember if a QUIC connection was successful which implies UDP egress works. In this case, cloudflared does not fallback to HTTP/2 and keeps trying to connect to the edge with QUIC. --- connection/control.go | 7 ++ connection/event.go | 1 + connection/http2_test.go | 4 ++ connection/observer.go | 4 +- connection/quic_test.go | 114 ++++++++++++++++---------------- connection/rpc.go | 9 +-- metrics/readiness_test.go | 22 +++--- supervisor/conn_aware_logger.go | 4 +- supervisor/supervisor.go | 6 +- supervisor/tunnel.go | 9 +++ tunnelstate/conntracker.go | 45 ++++++++++--- 11 files changed, 138 insertions(+), 87 deletions(-) diff --git a/connection/control.go b/connection/control.go index a7d49772..a7fe1ac9 100644 --- a/connection/control.go +++ b/connection/control.go @@ -2,6 +2,7 @@ package connection import ( "context" + "fmt" "io" "net" "time" @@ -21,6 +22,7 @@ type controlStream struct { namedTunnelProperties *NamedTunnelProperties connIndex uint8 edgeAddress net.IP + protocol Protocol newRPCClientFunc RPCClientFunc @@ -51,6 +53,7 @@ func NewControlStream( newRPCClientFunc RPCClientFunc, gracefulShutdownC <-chan struct{}, gracePeriod time.Duration, + protocol Protocol, ) ControlStreamHandler { if newRPCClientFunc == nil { newRPCClientFunc = newRegistrationRPCClient @@ -64,6 +67,7 @@ func NewControlStream( edgeAddress: edgeAddress, gracefulShutdownC: gracefulShutdownC, gracePeriod: gracePeriod, + protocol: protocol, } } @@ -80,6 +84,9 @@ func (c *controlStream) ServeControlStream( rpcClient.Close() return err } + + c.observer.logServerInfo(c.connIndex, registrationDetails.Location, c.edgeAddress, fmt.Sprintf("Connection %s registered", registrationDetails.UUID)) + c.observer.sendConnectedEvent(c.connIndex, c.protocol, registrationDetails.Location) c.connectedFuse.Connected() // if conn index is 0 and tunnel is not remotely managed, then send local ingress rules configuration diff --git a/connection/event.go b/connection/event.go index ab6d0d33..d10b92fc 100644 --- a/connection/event.go +++ b/connection/event.go @@ -5,6 +5,7 @@ type Event struct { Index uint8 EventType Status Location string + Protocol Protocol URL string } diff --git a/connection/http2_test.go b/connection/http2_test.go index e962353f..b7f1c49c 100644 --- a/connection/http2_test.go +++ b/connection/http2_test.go @@ -43,6 +43,7 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) { nil, nil, 1*time.Second, + HTTP2, ) return NewHTTP2Connection( cfdConn, @@ -366,6 +367,7 @@ func TestServeControlStream(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -417,6 +419,7 @@ func TestFailRegistration(t *testing.T) { rpcClientFactory.newMockRPCClient, nil, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream @@ -464,6 +467,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) { rpcClientFactory.newMockRPCClient, shutdownC, 1*time.Second, + HTTP2, ) http2Conn.controlStreamHandler = controlStream diff --git a/connection/observer.go b/connection/observer.go index 3a855878..7429b32c 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -55,8 +55,8 @@ func (o *Observer) sendRegisteringEvent(connIndex uint8) { o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel}) } -func (o *Observer) sendConnectedEvent(connIndex uint8, location string) { - o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) +func (o *Observer) sendConnectedEvent(connIndex uint8, protocol Protocol, location string) { + o.sendEvent(Event{Index: connIndex, EventType: Connected, Protocol: protocol, Location: location}) } func (o *Observer) SendURL(url string) { diff --git a/connection/quic_test.go b/connection/quic_test.go index c35c7d51..d82947c2 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -81,63 +81,63 @@ func TestQUICServer(t *testing.T) { }, expectedResponse: []byte("OK"), }, - //{ - // desc: "test http body request streaming", - // dest: "/slow_echo_body", - // connectionType: quicpogs.ConnectionTypeHTTP, - // metadata: []quicpogs.Metadata{ - // { - // Key: "HttpHeader:Cf-Ray", - // Val: "123123123", - // }, - // { - // Key: "HttpHost", - // Val: "cf.host", - // }, - // { - // Key: "HttpMethod", - // Val: "POST", - // }, - // { - // Key: "HttpHeader:Content-Length", - // Val: "24", - // }, - // }, - // message: []byte("This is the message body"), - // expectedResponse: []byte("This is the message body"), - //}, - //{ - // desc: "test ws proxy", - // dest: "/ws/echo", - // connectionType: quicpogs.ConnectionTypeWebsocket, - // metadata: []quicpogs.Metadata{ - // { - // Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", - // Val: "Websocket", - // }, - // { - // Key: "HttpHeader:Another-Header", - // Val: "Misc", - // }, - // { - // Key: "HttpHost", - // Val: "cf.host", - // }, - // { - // Key: "HttpMethod", - // Val: "get", - // }, - // }, - // message: wsBuf.Bytes(), - // expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, - //}, - //{ - // desc: "test tcp proxy", - // connectionType: quicpogs.ConnectionTypeTCP, - // metadata: []quicpogs.Metadata{}, - // message: []byte("Here is some tcp data"), - // expectedResponse: []byte("Here is some tcp data"), - //}, + { + desc: "test http body request streaming", + dest: "/slow_echo_body", + connectionType: quicpogs.ConnectionTypeHTTP, + metadata: []quicpogs.Metadata{ + { + Key: "HttpHeader:Cf-Ray", + Val: "123123123", + }, + { + Key: "HttpHost", + Val: "cf.host", + }, + { + Key: "HttpMethod", + Val: "POST", + }, + { + Key: "HttpHeader:Content-Length", + Val: "24", + }, + }, + message: []byte("This is the message body"), + expectedResponse: []byte("This is the message body"), + }, + { + desc: "test ws proxy", + dest: "/ws/echo", + connectionType: quicpogs.ConnectionTypeWebsocket, + metadata: []quicpogs.Metadata{ + { + Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", + Val: "Websocket", + }, + { + Key: "HttpHeader:Another-Header", + Val: "Misc", + }, + { + Key: "HttpHost", + Val: "cf.host", + }, + { + Key: "HttpMethod", + Val: "get", + }, + }, + message: wsBuf.Bytes(), + expectedResponse: []byte{0x82, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f}, + }, + { + desc: "test tcp proxy", + connectionType: quicpogs.ConnectionTypeTCP, + metadata: []quicpogs.Metadata{}, + message: []byte("Here is some tcp data"), + expectedResponse: []byte("Here is some tcp data"), + }, } for _, test := range tests { diff --git a/connection/rpc.go b/connection/rpc.go index b288a0f8..384be0dc 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -117,9 +117,6 @@ func (rsc *registrationServerClient) RegisterConnection( observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc() - observer.logServerInfo(connIndex, conn.Location, edgeAddress, fmt.Sprintf("Connection %s registered", conn.UUID)) - observer.sendConnectedEvent(connIndex, conn.Location) - return conn, nil } @@ -293,9 +290,13 @@ func (h *h2muxConnection) registerNamedTunnel( rpcClient := h.newRPCClientFunc(ctx, stream, h.observer.log) defer rpcClient.Close() - if _, err = rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, nil, h.observer); err != nil { + registrationDetails, err := rpcClient.RegisterConnection(ctx, namedTunnel, connOptions, h.connIndex, nil, h.observer) + if err != nil { return err } + h.observer.logServerInfo(h.connIndex, registrationDetails.Location, nil, fmt.Sprintf("Connection %s registered", registrationDetails.UUID)) + h.observer.sendConnectedEvent(h.connIndex, H2mux, registrationDetails.Location) + return nil } diff --git a/metrics/readiness_test.go b/metrics/readiness_test.go index 62e42f63..8e035f85 100644 --- a/metrics/readiness_test.go +++ b/metrics/readiness_test.go @@ -14,7 +14,7 @@ import ( func TestReadyServer_makeResponse(t *testing.T) { type fields struct { - isConnected map[int]bool + isConnected map[uint8]tunnelstate.ConnectionInfo } tests := []struct { name string @@ -25,11 +25,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "One connection online => HTTP 200", fields: fields{ - isConnected: map[int]bool{ - 0: false, - 1: false, - 2: true, - 3: false, + isConnected: map[uint8]tunnelstate.ConnectionInfo{ + 0: {IsConnected: false}, + 1: {IsConnected: false}, + 2: {IsConnected: true}, + 3: {IsConnected: false}, }, }, wantOK: true, @@ -38,11 +38,11 @@ func TestReadyServer_makeResponse(t *testing.T) { { name: "No connections online => no HTTP 200", fields: fields{ - isConnected: map[int]bool{ - 0: false, - 1: false, - 2: false, - 3: false, + isConnected: map[uint8]tunnelstate.ConnectionInfo{ + 0: {IsConnected: false}, + 1: {IsConnected: false}, + 2: {IsConnected: false}, + 3: {IsConnected: false}, }, }, wantReadyConnections: 0, diff --git a/supervisor/conn_aware_logger.go b/supervisor/conn_aware_logger.go index 6e717588..1311f20b 100644 --- a/supervisor/conn_aware_logger.go +++ b/supervisor/conn_aware_logger.go @@ -12,9 +12,9 @@ type ConnAwareLogger struct { logger *zerolog.Logger } -func NewConnAwareLogger(logger *zerolog.Logger, observer *connection.Observer) *ConnAwareLogger { +func NewConnAwareLogger(logger *zerolog.Logger, tracker *tunnelstate.ConnTracker, observer *connection.Observer) *ConnAwareLogger { connAwareLogger := &ConnAwareLogger{ - tracker: tunnelstate.NewConnTracker(logger), + tracker: tracker, logger: logger, } diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index c3f08844..7a100a59 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -19,6 +19,7 @@ import ( "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" + "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -88,7 +89,9 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato } reconnectCredentialManager := newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections) - log := NewConnAwareLogger(config.Log, config.Observer) + + tracker := tunnelstate.NewConnTracker(config.Log) + log := NewConnAwareLogger(config.Log, tracker, config.Observer) var edgeAddrHandler EdgeAddrHandler if isStaticEdge { // static edge addresses @@ -106,6 +109,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato credentialManager: reconnectCredentialManager, edgeAddrs: edgeIPs, edgeAddrHandler: edgeAddrHandler, + tracker: tracker, reconnectCh: reconnectCh, gracefulShutdownC: gracefulShutdownC, connAwareLogger: log, diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 7517a02b..0b9998d8 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -26,6 +26,7 @@ import ( "github.com/cloudflare/cloudflared/signal" "github.com/cloudflare/cloudflared/tunnelrpc" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" + "github.com/cloudflare/cloudflared/tunnelstate" ) const ( @@ -191,6 +192,7 @@ type EdgeTunnelServer struct { edgeAddrs *edgediscovery.Edge reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} + tracker *tunnelstate.ConnTracker connAwareLogger *ConnAwareLogger } @@ -273,6 +275,12 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFa return err } + // If a single connection has connected with the current protocol, we know we know we don't have to fallback + // to a different protocol. + if e.tracker.HasConnectedWith(e.config.ProtocolSelector.Current()) { + return err + } + if !selectNextProtocol( connLog.Logger(), protocolFallback, @@ -462,6 +470,7 @@ func serveTunnel( nil, gracefulShutdownC, config.GracePeriod, + protocol, ) switch protocol { diff --git a/tunnelstate/conntracker.go b/tunnelstate/conntracker.go index 06fb176f..426ba483 100644 --- a/tunnelstate/conntracker.go +++ b/tunnelstate/conntracker.go @@ -10,20 +10,26 @@ import ( type ConnTracker struct { sync.RWMutex - isConnected map[int]bool - log *zerolog.Logger + // int is the connection Index + connectionInfo map[uint8]ConnectionInfo + log *zerolog.Logger +} + +type ConnectionInfo struct { + IsConnected bool + Protocol connection.Protocol } func NewConnTracker(log *zerolog.Logger) *ConnTracker { return &ConnTracker{ - isConnected: make(map[int]bool, 0), - log: log, + connectionInfo: make(map[uint8]ConnectionInfo, 0), + log: log, } } -func MockedConnTracker(mocked map[int]bool) *ConnTracker { +func MockedConnTracker(mocked map[uint8]ConnectionInfo) *ConnTracker { return &ConnTracker{ - isConnected: mocked, + connectionInfo: mocked, } } @@ -31,11 +37,17 @@ func (ct *ConnTracker) OnTunnelEvent(c connection.Event) { switch c.EventType { case connection.Connected: ct.Lock() - ct.isConnected[int(c.Index)] = true + ci := ConnectionInfo{ + IsConnected: true, + Protocol: c.Protocol, + } + ct.connectionInfo[c.Index] = ci ct.Unlock() case connection.Disconnected, connection.Reconnecting, connection.RegisteringTunnel, connection.Unregistering: ct.Lock() - ct.isConnected[int(c.Index)] = false + ci := ct.connectionInfo[c.Index] + ci.IsConnected = false + ct.connectionInfo[c.Index] = ci ct.Unlock() default: ct.log.Error().Msgf("Unknown connection event case %v", c) @@ -46,10 +58,23 @@ func (ct *ConnTracker) CountActiveConns() uint { ct.RLock() defer ct.RUnlock() active := uint(0) - for _, connected := range ct.isConnected { - if connected { + for _, ci := range ct.connectionInfo { + if ci.IsConnected { active++ } } return active } + +// HasConnectedWith checks if we've ever had a successful connection to the edge +// with said protocol. +func (ct *ConnTracker) HasConnectedWith(protocol connection.Protocol) bool { + ct.RLock() + defer ct.RUnlock() + for _, ci := range ct.connectionInfo { + if ci.Protocol == protocol { + return true + } + } + return false +} From 88235356d5a7c0a5012153b610c6dc9f5ff57a88 Mon Sep 17 00:00:00 2001 From: Opeyemi Onikute Date: Fri, 12 Aug 2022 09:50:51 +0100 Subject: [PATCH 159/238] EDGEPLAT-3918: bump go and go-boring to 1.18.5 --- cfsetup.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 96ce9c2a..71fadc1b 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -1,5 +1,5 @@ -pinned_go: &pinned_go go=1.18-1 -pinned_go_fips: &pinned_go_fips go-boring=1.18-1 +pinned_go: &pinned_go go=1.18.5-1 +pinned_go_fips: &pinned_go_fips go-boring=1.18.5-2 build_dir: &build_dir /cfsetup_build default-flavor: bullseye @@ -255,8 +255,8 @@ centos-7: pre-cache: - yum install -y fakeroot - yum upgrade -y binutils-2.27-44.base.el7.x86_64 - - wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz -P /tmp/ - - tar -C /usr/local -xzf /tmp/go1.18.1.linux-amd64.tar.gz + - wget https://go.dev/dl/go1.18.5.linux-amd64.tar.gz -P /tmp/ + - tar -C /usr/local -xzf /tmp/go1.18.5.linux-amd64.tar.gz post-cache: - export PATH=$PATH:/usr/local/go/bin - export GOOS=linux From 0538953a3977594e766a5e8dcf60896bafdce7ef Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 12 Aug 2022 16:01:32 +0100 Subject: [PATCH 160/238] TUN-6652: Publish dockerfile for both amd64 and arm64 This change seeks to push an arm64 built image to dockerhub for arm users to run. This should spin cloudflared on arm machines without the warning WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested --- .docker-images | 5 ++++- Dockerfile | 4 ++-- Dockerfile.amd64 | 27 +++++++++++++++++++++++++++ Dockerfile.arm64 | 27 +++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 Dockerfile.amd64 create mode 100644 Dockerfile.arm64 diff --git a/.docker-images b/.docker-images index 28bf15aa..984a3183 100644 --- a/.docker-images +++ b/.docker-images @@ -1,6 +1,6 @@ images: - name: cloudflared - dockerfile: Dockerfile + dockerfile: Dockerfile.$ARCH context: . versions: - latest @@ -8,3 +8,6 @@ images: - name: docker.io/cloudflare user: env:DOCKER_USER password: env:DOCKER_PASSWORD + architectures: + - amd64 + - arm64 diff --git a/Dockerfile b/Dockerfile index f10076d9..8ca108e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # use a builder image for building cloudflare ARG TARGET_GOOS ARG TARGET_GOARCH -FROM golang:1.18.1 as builder +FROM golang:1.19 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ TARGET_GOOS=${TARGET_GOOS} \ @@ -18,7 +18,7 @@ COPY . . RUN make cloudflared # use a distroless base image with glibc -FROM gcr.io/distroless/base-debian10:nonroot +FROM gcr.io/distroless/base-debian11:nonroot # copy our compiled binary COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 new file mode 100644 index 00000000..cadfebfc --- /dev/null +++ b/Dockerfile.amd64 @@ -0,0 +1,27 @@ +# use a builder image for building cloudflare +FROM golang:1.19 as builder +ENV GO111MODULE=on \ + CGO_ENABLED=0 + +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" + +WORKDIR /go/src/github.com/cloudflare/cloudflared/ + +# copy our sources into the builder image +COPY . . + +# compile cloudflared +RUN GOOS=linux GOARCH=amd64 make cloudflared + +# use a distroless base image with glibc +FROM gcr.io/distroless/base-debian11:nonroot + +# copy our compiled binary +COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ + +# run as non-privileged user +USER nonroot + +# command / entrypoint of container +ENTRYPOINT ["cloudflared", "--no-autoupdate"] +CMD ["version"] diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 00000000..2bc6d982 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,27 @@ +# use a builder image for building cloudflare +FROM golang:1.19 as builder +ENV GO111MODULE=on \ + CGO_ENABLED=0 + +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" + +WORKDIR /go/src/github.com/cloudflare/cloudflared/ + +# copy our sources into the builder image +COPY . . + +# compile cloudflared +RUN GOOS=linux GOARCH=arm64 make cloudflared + +# use a distroless base image with glibc +FROM gcr.io/distroless/base-debian11:nonroot + +# copy our compiled binary +COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ + +# run as non-privileged user +USER nonroot + +# command / entrypoint of container +ENTRYPOINT ["cloudflared", "--no-autoupdate"] +CMD ["version"] From bd88093de0f407313898490b3803d5bae863e001 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Mon, 15 Aug 2022 17:41:06 +0100 Subject: [PATCH 161/238] TUN-6617: Updated CHANGES.md for protocol stickiness --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 511cb600..3dbd8749 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +## 2022.8.1 +### New Features +- cloudflared now remembers if it connected to a certain protocol successfully. If it did, it does not fall back to a lower + protocol on connection failures. + ## 2022.7.1 ### New Features - It is now possible to connect cloudflared tunnel to Cloudflare Global Network with IPv6. See `cloudflared tunnel --help` and look for `edge-ip-version` for more information. For now, the default behavior is to still connect with IPv4 only. From e09c62a7961c73bdab1f2f71cdd6e8f5c56a0bc4 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 16 Aug 2022 09:21:02 +0100 Subject: [PATCH 162/238] Release 2022.8.1 --- RELEASE_NOTES | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 568bb514..daa0832d 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,13 @@ +2022.8.1 +- 2022-08-15 TUN-6617: Updated CHANGES.md for protocol stickiness +- 2022-08-12 EDGEPLAT-3918: bump go and go-boring to 1.18.5 +- 2022-08-12 TUN-6652: Publish dockerfile for both amd64 and arm64 +- 2022-08-11 TUN-6617: Dont fallback to http2 if QUIC conn was successful. +- 2022-08-11 TUN-6617: Dont fallback to http2 if QUIC conn was successful. +- 2022-08-11 Revert "TUN-6617: Dont fallback to http2 if QUIC conn was successful." +- 2022-08-11 TUN-6617: Dont fallback to http2 if QUIC conn was successful. +- 2022-08-01 TUN-6584: Define QUIC datagram v2 format to support proxying IP packets + 2022.8.0 - 2022-08-10 TUN-6637: Upgrade quic-go - 2022-08-10 TUN-6646: Add support to SafeStreamCloser to close only write side of stream From 906eb2d8403353cf0e9c641ce9319e929a425fd0 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 16 Aug 2022 14:26:50 +0100 Subject: [PATCH 163/238] TUN-6656: Docker for arm64 should not be deployed in an amd64 container --- Dockerfile.arm64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 2bc6d982..3ca14ce8 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -14,7 +14,7 @@ COPY . . RUN GOOS=linux GOARCH=arm64 make cloudflared # use a distroless base image with glibc -FROM gcr.io/distroless/base-debian11:nonroot +FROM gcr.io/distroless/base-debian11:nonroot-arm64 # copy our compiled binary COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ From e123bbe1c5fa2377c7e838cc9787449da317687d Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 16 Aug 2022 15:05:14 +0100 Subject: [PATCH 164/238] Release 2022.8.2 --- RELEASE_NOTES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index daa0832d..417d4cc5 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,6 @@ +2022.8.2 +- 2022-08-16 TUN-6656: Docker for arm64 should not be deployed in an amd64 container + 2022.8.1 - 2022-08-15 TUN-6617: Updated CHANGES.md for protocol stickiness - 2022-08-12 EDGEPLAT-3918: bump go and go-boring to 1.18.5 From a97673e8b93a9642498031791bfdaa435c074fa1 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 11 Aug 2022 14:54:12 -0700 Subject: [PATCH 165/238] TUN-6575: Consume cf-trace-id from incoming http2 TCP requests --- connection/connection.go | 1 + connection/http2.go | 7 ++++--- proxy/proxy.go | 2 +- proxy/proxy_test.go | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index 5200f4bb..be72b5d2 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -174,6 +174,7 @@ func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) erro Status: switchingProtocolText, StatusCode: http.StatusSwitchingProtocols, ContentLength: -1, + Header: http.Header{}, } if secWebsocketKey := h.req.Header.Get("Sec-WebSocket-Key"); secWebsocketKey != "" { diff --git a/connection/http2.go b/connection/http2.go index 1b78d748..8b488010 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -149,9 +149,10 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) { rws := NewHTTPResponseReadWriterAcker(respWriter, r) if err := originProxy.ProxyTCP(r.Context(), rws, &TCPRequest{ - Dest: host, - CFRay: FindCfRayHeader(r), - LBProbe: IsLBProbeRequest(r), + Dest: host, + CFRay: FindCfRayHeader(r), + LBProbe: IsLBProbeRequest(r), + CfTraceID: r.Header.Get(tracing.TracerContextName), }); err != nil { respWriter.WriteErrorResponse() } diff --git a/proxy/proxy.go b/proxy/proxy.go index 0c2c21c0..9bc37615 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -256,7 +256,7 @@ func (p *Proxy) proxyStream( connectionProxy ingress.StreamBasedOriginProxy, ) error { ctx := tr.Context - _, connectSpan := tr.Tracer().Start(ctx, "stream_connect") + _, connectSpan := tr.Tracer().Start(ctx, "stream-connect") originConn, err := connectionProxy.EstablishConnection(ctx, dest) if err != nil { tracing.EndWithErrorStatus(connectSpan, err) diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index f85281b3..da90288e 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -525,6 +525,7 @@ func TestConnections(t *testing.T) { }, want: want{ message: []byte("echo-test2"), + headers: http.Header{}, }, }, { @@ -544,6 +545,7 @@ func TestConnections(t *testing.T) { message: []byte("echo-test3"), // We expect no headers here because they are sent back via // the stream. + headers: http.Header{}, }, }, { From 8e9e1d973e63653e53428727fa0918082f9ef53e Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 16 Aug 2022 15:48:33 +0100 Subject: [PATCH 166/238] TUN-6657: Ask for Tunnel ID and Configuration on Bug Report --- .github/ISSUE_TEMPLATE/---bug-report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 8d26bfe7..9a3a4f6d 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -15,6 +15,10 @@ Steps to reproduce the behavior: 2. Run '....' 3. See error +If it's an issue with Cloudflare Tunnel: +4. Tunnel ID : +5. cloudflared config: + **Expected behavior** A clear and concise description of what you expected to happen. From 20ed7557f9257b3d6a1d4dc6b9681a303e866a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 22 Aug 2022 23:48:45 +0100 Subject: [PATCH 167/238] TUN-6679: Allow client side of quic request to close body In a previous commit, we fixed a bug where the client roundtrip code could close the request body, which in fact would be the quic.Stream, thus closing the write-side. The way that was fixed, prevented the client roundtrip code from closing also read-side (the body). This fixes that, by allowing close to only close the read side, which will guarantee that any subsquent will fail with an error or EOF it occurred before the close. --- connection/quic.go | 39 +++++++++++++++++++++++++++---- connection/quic_test.go | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index a7f15e69..5de16dc2 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -9,6 +9,7 @@ import ( "net/http" "strconv" "strings" + "sync/atomic" "time" "github.com/google/uuid" @@ -156,9 +157,10 @@ func (q *QUICConnection) runStream(quicStream quic.Stream) { defer stream.Close() // we are going to fuse readers/writers from stream <- cloudflared -> origin, and we want to guarantee that - // code executed in the code path of handleStream don't trigger an earlier close to the downstream stream. - // So, we wrap the stream with a no-op closer and only this method can actually close the stream. - noCloseStream := &nopCloserReadWriter{stream} + // code executed in the code path of handleStream don't trigger an earlier close to the downstream write stream. + // So, we wrap the stream with a no-op write closer and only this method can actually close write side of the stream. + // A call to close will simulate a close to the read-side, which will fail subsequent reads. + noCloseStream := &nopCloserReadWriter{ReadWriteCloser: stream} if err := q.handleStream(ctx, noCloseStream); err != nil { q.logger.Err(err).Msg("Failed to handle QUIC stream") } @@ -408,10 +410,39 @@ func isTransferEncodingChunked(req *http.Request) bool { return strings.Contains(strings.ToLower(transferEncodingVal), "chunked") } +// A helper struct that guarantees a call to close only affects read side, but not write side. type nopCloserReadWriter struct { io.ReadWriteCloser + + // for use by Read only + // we don't need a memory barrier here because there is an implicit assumption that + // Read calls can't happen concurrently by different go-routines. + sawEOF bool + // should be updated and read using atomic primitives. + // value is read in Read method and written in Close method, which could be done by different + // go-routines. + closed uint32 } -func (n *nopCloserReadWriter) Close() error { +func (np *nopCloserReadWriter) Read(p []byte) (n int, err error) { + if np.sawEOF { + return 0, io.EOF + } + + if atomic.LoadUint32(&np.closed) > 0 { + return 0, fmt.Errorf("closed by handler") + } + + n, err = np.ReadWriteCloser.Read(p) + if err == io.EOF { + np.sawEOF = true + } + + return +} + +func (np *nopCloserReadWriter) Close() error { + atomic.StoreUint32(&np.closed, 1) + return nil } diff --git a/connection/quic_test.go b/connection/quic_test.go index d82947c2..0afb3953 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/url" "os" + "strings" "sync" "testing" "time" @@ -527,6 +528,44 @@ func TestServeUDPSession(t *testing.T) { cancel() } +func TestNopCloserReadWriterCloseBeforeEOF(t *testing.T) { + readerWriter := nopCloserReadWriter{ReadWriteCloser: &mockReaderNoopWriter{Reader: strings.NewReader("123456789")}} + buffer := make([]byte, 5) + + n, err := readerWriter.Read(buffer) + require.NoError(t, err) + require.Equal(t, n, 5) + + // close + require.NoError(t, readerWriter.Close()) + + // read should get error + n, err = readerWriter.Read(buffer) + require.Equal(t, n, 0) + require.Equal(t, err, fmt.Errorf("closed by handler")) +} + +func TestNopCloserReadWriterCloseAfterEOF(t *testing.T) { + readerWriter := nopCloserReadWriter{ReadWriteCloser: &mockReaderNoopWriter{Reader: strings.NewReader("123456789")}} + buffer := make([]byte, 20) + + n, err := readerWriter.Read(buffer) + require.NoError(t, err) + require.Equal(t, n, 9) + + // force another read to read eof + n, err = readerWriter.Read(buffer) + require.Equal(t, err, io.EOF) + + // close + require.NoError(t, readerWriter.Close()) + + // read should get EOF still + n, err = readerWriter.Read(buffer) + require.Equal(t, n, 0) + require.Equal(t, err, io.EOF) +} + func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Connection, closeType closeReason, expectedReason string, t *testing.T) { var ( payload = []byte(t.Name()) @@ -647,3 +686,15 @@ func testQUICConnection(udpListenerAddr net.Addr, t *testing.T) *QUICConnection require.NoError(t, err) return qc } + +type mockReaderNoopWriter struct { + io.Reader +} + +func (m *mockReaderNoopWriter) Write(p []byte) (n int, err error) { + return len(p), nil +} + +func (m *mockReaderNoopWriter) Close() error { + return nil +} From bad2e8e812d898c32b0aacc66a6f947109eaf24a Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 17 Aug 2022 16:46:49 +0100 Subject: [PATCH 168/238] TUN-6666: Define packet package This package defines IP and ICMP packet, decoders, encoder and flow --- connection/quic.go | 5 +- datagramsession/manager.go | 8 +- datagramsession/manager_test.go | 14 +- datagramsession/session.go | 10 +- datagramsession/session_test.go | 18 +- go.mod | 5 +- go.sum | 6 + packet/decoder.go | 184 + packet/decoder_test.go | 252 + packet/encoder.go | 39 + packet/flow.go | 97 + packet/packet.go | 115 + packet/session.go | 8 + quic/datagram.go | 27 +- quic/datagram_test.go | 50 +- quic/datagramv2.go | 17 +- vendor/github.com/google/gopacket/.gitignore | 38 + .../google/gopacket/.travis.gofmt.sh | 7 + .../google/gopacket/.travis.golint.sh | 28 + .../google/gopacket/.travis.govet.sh | 10 + .../google/gopacket/.travis.install.sh | 9 + .../google/gopacket/.travis.script.sh | 10 + vendor/github.com/google/gopacket/.travis.yml | 57 + vendor/github.com/google/gopacket/AUTHORS | 54 + .../google/gopacket/CONTRIBUTING.md | 215 + vendor/github.com/google/gopacket/LICENSE | 28 + vendor/github.com/google/gopacket/README.md | 12 + vendor/github.com/google/gopacket/base.go | 178 + vendor/github.com/google/gopacket/decode.go | 157 + vendor/github.com/google/gopacket/doc.go | 432 + vendor/github.com/google/gopacket/flows.go | 236 + vendor/github.com/google/gopacket/gc | 288 + .../github.com/google/gopacket/layerclass.go | 107 + .../google/gopacket/layers/.lint_blacklist | 39 + .../github.com/google/gopacket/layers/arp.go | 118 + .../github.com/google/gopacket/layers/asf.go | 166 + .../gopacket/layers/asf_presencepong.go | 194 + .../github.com/google/gopacket/layers/base.go | 52 + .../github.com/google/gopacket/layers/bfd.go | 481 + .../github.com/google/gopacket/layers/cdp.go | 659 + .../github.com/google/gopacket/layers/ctp.go | 109 + .../google/gopacket/layers/dhcpv4.go | 592 + .../google/gopacket/layers/dhcpv6.go | 360 + .../google/gopacket/layers/dhcpv6_options.go | 621 + .../github.com/google/gopacket/layers/dns.go | 1098 ++ .../github.com/google/gopacket/layers/doc.go | 61 + .../google/gopacket/layers/dot11.go | 2118 +++ .../google/gopacket/layers/dot1q.go | 75 + .../github.com/google/gopacket/layers/eap.go | 114 + .../google/gopacket/layers/eapol.go | 302 + .../google/gopacket/layers/endpoints.go | 97 + .../google/gopacket/layers/enums.go | 443 + .../google/gopacket/layers/enums_generated.go | 434 + .../google/gopacket/layers/erspan2.go | 86 + .../google/gopacket/layers/etherip.go | 45 + .../google/gopacket/layers/ethernet.go | 123 + .../github.com/google/gopacket/layers/fddi.go | 41 + .../google/gopacket/layers/fuzz_layer.go | 39 + .../google/gopacket/layers/gen_linted.sh | 3 + .../google/gopacket/layers/geneve.go | 121 + .../github.com/google/gopacket/layers/gre.go | 200 + .../github.com/google/gopacket/layers/gtp.go | 184 + .../google/gopacket/layers/iana_ports.go | 11351 ++++++++++++++++ .../google/gopacket/layers/icmp4.go | 267 + .../google/gopacket/layers/icmp6.go | 266 + .../google/gopacket/layers/icmp6msg.go | 578 + .../github.com/google/gopacket/layers/igmp.go | 355 + .../github.com/google/gopacket/layers/ip4.go | 325 + .../github.com/google/gopacket/layers/ip6.go | 722 + .../google/gopacket/layers/ipsec.go | 77 + .../google/gopacket/layers/layertypes.go | 223 + .../github.com/google/gopacket/layers/lcm.go | 218 + .../google/gopacket/layers/linux_sll.go | 98 + .../github.com/google/gopacket/layers/llc.go | 193 + .../github.com/google/gopacket/layers/lldp.go | 1603 +++ .../google/gopacket/layers/loopback.go | 80 + .../google/gopacket/layers/mldv1.go | 182 + .../google/gopacket/layers/mldv2.go | 619 + .../google/gopacket/layers/modbustcp.go | 150 + .../github.com/google/gopacket/layers/mpls.go | 87 + .../github.com/google/gopacket/layers/ndp.go | 611 + .../github.com/google/gopacket/layers/ntp.go | 416 + .../github.com/google/gopacket/layers/ospf.go | 715 + .../google/gopacket/layers/pflog.go | 84 + .../google/gopacket/layers/ports.go | 156 + .../github.com/google/gopacket/layers/ppp.go | 88 + .../google/gopacket/layers/pppoe.go | 60 + .../google/gopacket/layers/prism.go | 146 + .../google/gopacket/layers/radiotap.go | 1076 ++ .../google/gopacket/layers/radius.go | 560 + .../github.com/google/gopacket/layers/rmcp.go | 170 + .../github.com/google/gopacket/layers/rudp.go | 93 + .../github.com/google/gopacket/layers/sctp.go | 746 + .../google/gopacket/layers/sflow.go | 2567 ++++ .../github.com/google/gopacket/layers/sip.go | 542 + .../github.com/google/gopacket/layers/stp.go | 27 + .../github.com/google/gopacket/layers/tcp.go | 341 + .../google/gopacket/layers/tcpip.go | 104 + .../google/gopacket/layers/test_creator.py | 103 + .../github.com/google/gopacket/layers/tls.go | 283 + .../google/gopacket/layers/tls_alert.go | 165 + .../google/gopacket/layers/tls_appdata.go | 34 + .../google/gopacket/layers/tls_cipherspec.go | 64 + .../google/gopacket/layers/tls_handshake.go | 28 + .../github.com/google/gopacket/layers/udp.go | 133 + .../google/gopacket/layers/udplite.go | 44 + .../github.com/google/gopacket/layers/usb.go | 292 + .../github.com/google/gopacket/layers/vrrp.go | 156 + .../google/gopacket/layers/vxlan.go | 123 + .../google/gopacket/layers_decoder.go | 101 + .../github.com/google/gopacket/layertype.go | 111 + vendor/github.com/google/gopacket/packet.go | 864 ++ vendor/github.com/google/gopacket/parser.go | 350 + vendor/github.com/google/gopacket/time.go | 72 + vendor/github.com/google/gopacket/writer.go | 232 + vendor/golang.org/x/net/AUTHORS | 3 - vendor/golang.org/x/net/CONTRIBUTORS | 3 - vendor/golang.org/x/net/http2/frame.go | 22 +- vendor/golang.org/x/net/http2/hpack/encode.go | 2 +- vendor/golang.org/x/net/http2/hpack/hpack.go | 16 +- vendor/golang.org/x/net/http2/http2.go | 8 +- vendor/golang.org/x/net/http2/server.go | 9 + vendor/golang.org/x/net/http2/transport.go | 51 +- vendor/golang.org/x/net/icmp/dstunreach.go | 59 + vendor/golang.org/x/net/icmp/echo.go | 173 + vendor/golang.org/x/net/icmp/endpoint.go | 113 + vendor/golang.org/x/net/icmp/extension.go | 170 + vendor/golang.org/x/net/icmp/helper_posix.go | 76 + vendor/golang.org/x/net/icmp/interface.go | 322 + vendor/golang.org/x/net/icmp/ipv4.go | 69 + vendor/golang.org/x/net/icmp/ipv6.go | 23 + vendor/golang.org/x/net/icmp/listen_posix.go | 106 + vendor/golang.org/x/net/icmp/listen_stub.go | 36 + vendor/golang.org/x/net/icmp/message.go | 162 + vendor/golang.org/x/net/icmp/messagebody.go | 52 + vendor/golang.org/x/net/icmp/mpls.go | 77 + vendor/golang.org/x/net/icmp/multipart.go | 129 + vendor/golang.org/x/net/icmp/packettoobig.go | 43 + vendor/golang.org/x/net/icmp/paramprob.go | 72 + vendor/golang.org/x/net/icmp/sys_freebsd.go | 11 + vendor/golang.org/x/net/icmp/timeexceeded.go | 57 + .../x/net/internal/socket/rawconn_msg.go | 48 +- .../x/net/internal/socket/sys_stub.go | 6 +- .../x/net/internal/socket/sys_unix.go | 101 +- .../x/net/internal/socket/sys_windows.go | 7 +- .../x/net/internal/socket/sys_zos_s390x.go | 37 +- .../x/net/internal/socket/zsys_darwin_arm.go | 30 - ..._darwin_386.go => zsys_freebsd_riscv64.go} | 8 +- .../x/net/ipv4/zsys_freebsd_riscv64.go | 52 + .../x/net/ipv6/zsys_freebsd_riscv64.go | 64 + vendor/golang.org/x/sys/AUTHORS | 3 - vendor/golang.org/x/sys/CONTRIBUTORS | 3 - vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c | 29 +- .../golang.org/x/sys/cpu/cpu_other_riscv64.go | 12 + .../golang.org/x/sys/unix/asm_bsd_riscv64.s | 29 + .../golang.org/x/sys/unix/asm_linux_loong64.s | 4 +- .../x/sys/unix/errors_freebsd_386.go | 233 - .../x/sys/unix/errors_freebsd_amd64.go | 233 - .../x/sys/unix/errors_freebsd_arm.go | 226 - .../x/sys/unix/errors_freebsd_arm64.go | 17 - vendor/golang.org/x/sys/unix/mkall.sh | 13 +- vendor/golang.org/x/sys/unix/mkerrors.sh | 10 +- vendor/golang.org/x/sys/unix/syscall_aix.go | 61 +- vendor/golang.org/x/sys/unix/syscall_bsd.go | 46 +- .../golang.org/x/sys/unix/syscall_darwin.go | 7 + .../golang.org/x/sys/unix/syscall_freebsd.go | 325 +- .../x/sys/unix/syscall_freebsd_386.go | 4 +- .../x/sys/unix/syscall_freebsd_amd64.go | 4 +- .../x/sys/unix/syscall_freebsd_arm.go | 2 +- .../x/sys/unix/syscall_freebsd_arm64.go | 2 +- .../x/sys/unix/syscall_freebsd_riscv64.go | 63 + .../golang.org/x/sys/unix/syscall_illumos.go | 5 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 45 +- .../x/sys/unix/syscall_linux_loong64.go | 39 +- .../x/sys/unix/syscall_linux_riscv64.go | 1 + .../x/sys/unix/syscall_openbsd_mips64.go | 4 + .../golang.org/x/sys/unix/syscall_solaris.go | 51 +- vendor/golang.org/x/sys/unix/syscall_unix.go | 74 +- .../x/sys/unix/zerrors_freebsd_386.go | 109 +- .../x/sys/unix/zerrors_freebsd_amd64.go | 107 +- .../x/sys/unix/zerrors_freebsd_arm.go | 220 +- .../x/sys/unix/zerrors_freebsd_arm64.go | 100 +- .../x/sys/unix/zerrors_freebsd_riscv64.go | 2148 +++ vendor/golang.org/x/sys/unix/zerrors_linux.go | 389 +- .../x/sys/unix/zerrors_linux_386.go | 2 + .../x/sys/unix/zerrors_linux_amd64.go | 2 + .../x/sys/unix/zerrors_linux_arm.go | 2 + .../x/sys/unix/zerrors_linux_arm64.go | 3 + .../x/sys/unix/zerrors_linux_loong64.go | 4 +- .../x/sys/unix/zerrors_linux_mips.go | 2 + .../x/sys/unix/zerrors_linux_mips64.go | 2 + .../x/sys/unix/zerrors_linux_mips64le.go | 2 + .../x/sys/unix/zerrors_linux_mipsle.go | 2 + .../x/sys/unix/zerrors_linux_ppc.go | 2 + .../x/sys/unix/zerrors_linux_ppc64.go | 2 + .../x/sys/unix/zerrors_linux_ppc64le.go | 2 + .../x/sys/unix/zerrors_linux_riscv64.go | 2 + .../x/sys/unix/zerrors_linux_s390x.go | 2 + .../x/sys/unix/zerrors_linux_sparc64.go | 2 + .../x/sys/unix/zsyscall_freebsd_386.go | 141 +- .../x/sys/unix/zsyscall_freebsd_amd64.go | 139 +- .../x/sys/unix/zsyscall_freebsd_arm.go | 173 +- .../x/sys/unix/zsyscall_freebsd_arm64.go | 139 +- .../x/sys/unix/zsyscall_freebsd_riscv64.go | 1889 +++ .../x/sys/unix/zsyscall_linux_loong64.go | 25 - .../x/sys/unix/zsyscall_linux_riscv64.go | 11 + .../x/sys/unix/zsyscall_solaris_amd64.go | 14 + .../x/sys/unix/zsysnum_freebsd_386.go | 107 +- .../x/sys/unix/zsysnum_freebsd_amd64.go | 107 +- .../x/sys/unix/zsysnum_freebsd_arm.go | 107 +- .../x/sys/unix/zsysnum_freebsd_arm64.go | 107 +- .../x/sys/unix/zsysnum_freebsd_riscv64.go | 394 + .../x/sys/unix/zsysnum_linux_loong64.go | 2 - .../x/sys/unix/zsysnum_linux_riscv64.go | 1 + .../x/sys/unix/ztypes_darwin_amd64.go | 73 +- .../x/sys/unix/ztypes_darwin_arm64.go | 73 +- .../x/sys/unix/ztypes_freebsd_386.go | 97 +- .../x/sys/unix/ztypes_freebsd_amd64.go | 94 +- .../x/sys/unix/ztypes_freebsd_arm.go | 145 +- .../x/sys/unix/ztypes_freebsd_arm64.go | 92 +- .../x/sys/unix/ztypes_freebsd_riscv64.go | 626 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 27 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 7 + .../x/sys/unix/ztypes_linux_amd64.go | 6 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 7 + .../x/sys/unix/ztypes_linux_arm64.go | 6 + .../x/sys/unix/ztypes_linux_loong64.go | 6 + .../x/sys/unix/ztypes_linux_mips.go | 7 + .../x/sys/unix/ztypes_linux_mips64.go | 6 + .../x/sys/unix/ztypes_linux_mips64le.go | 6 + .../x/sys/unix/ztypes_linux_mipsle.go | 7 + .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 7 + .../x/sys/unix/ztypes_linux_ppc64.go | 6 + .../x/sys/unix/ztypes_linux_ppc64le.go | 6 + .../x/sys/unix/ztypes_linux_riscv64.go | 6 + .../x/sys/unix/ztypes_linux_s390x.go | 6 + .../x/sys/unix/ztypes_linux_sparc64.go | 6 + .../x/sys/unix/ztypes_solaris_amd64.go | 2 +- .../x/sys/windows/syscall_windows.go | 9 + .../golang.org/x/sys/windows/types_windows.go | 81 +- .../x/sys/windows/zsyscall_windows.go | 9 + vendor/modules.txt | 9 +- 242 files changed, 49761 insertions(+), 2642 deletions(-) create mode 100644 packet/decoder.go create mode 100644 packet/decoder_test.go create mode 100644 packet/encoder.go create mode 100644 packet/flow.go create mode 100644 packet/packet.go create mode 100644 packet/session.go create mode 100644 vendor/github.com/google/gopacket/.gitignore create mode 100644 vendor/github.com/google/gopacket/.travis.gofmt.sh create mode 100644 vendor/github.com/google/gopacket/.travis.golint.sh create mode 100644 vendor/github.com/google/gopacket/.travis.govet.sh create mode 100644 vendor/github.com/google/gopacket/.travis.install.sh create mode 100644 vendor/github.com/google/gopacket/.travis.script.sh create mode 100644 vendor/github.com/google/gopacket/.travis.yml create mode 100644 vendor/github.com/google/gopacket/AUTHORS create mode 100644 vendor/github.com/google/gopacket/CONTRIBUTING.md create mode 100644 vendor/github.com/google/gopacket/LICENSE create mode 100644 vendor/github.com/google/gopacket/README.md create mode 100644 vendor/github.com/google/gopacket/base.go create mode 100644 vendor/github.com/google/gopacket/decode.go create mode 100644 vendor/github.com/google/gopacket/doc.go create mode 100644 vendor/github.com/google/gopacket/flows.go create mode 100644 vendor/github.com/google/gopacket/gc create mode 100644 vendor/github.com/google/gopacket/layerclass.go create mode 100644 vendor/github.com/google/gopacket/layers/.lint_blacklist create mode 100644 vendor/github.com/google/gopacket/layers/arp.go create mode 100644 vendor/github.com/google/gopacket/layers/asf.go create mode 100644 vendor/github.com/google/gopacket/layers/asf_presencepong.go create mode 100644 vendor/github.com/google/gopacket/layers/base.go create mode 100644 vendor/github.com/google/gopacket/layers/bfd.go create mode 100644 vendor/github.com/google/gopacket/layers/cdp.go create mode 100644 vendor/github.com/google/gopacket/layers/ctp.go create mode 100644 vendor/github.com/google/gopacket/layers/dhcpv4.go create mode 100644 vendor/github.com/google/gopacket/layers/dhcpv6.go create mode 100644 vendor/github.com/google/gopacket/layers/dhcpv6_options.go create mode 100644 vendor/github.com/google/gopacket/layers/dns.go create mode 100644 vendor/github.com/google/gopacket/layers/doc.go create mode 100644 vendor/github.com/google/gopacket/layers/dot11.go create mode 100644 vendor/github.com/google/gopacket/layers/dot1q.go create mode 100644 vendor/github.com/google/gopacket/layers/eap.go create mode 100644 vendor/github.com/google/gopacket/layers/eapol.go create mode 100644 vendor/github.com/google/gopacket/layers/endpoints.go create mode 100644 vendor/github.com/google/gopacket/layers/enums.go create mode 100644 vendor/github.com/google/gopacket/layers/enums_generated.go create mode 100644 vendor/github.com/google/gopacket/layers/erspan2.go create mode 100644 vendor/github.com/google/gopacket/layers/etherip.go create mode 100644 vendor/github.com/google/gopacket/layers/ethernet.go create mode 100644 vendor/github.com/google/gopacket/layers/fddi.go create mode 100644 vendor/github.com/google/gopacket/layers/fuzz_layer.go create mode 100644 vendor/github.com/google/gopacket/layers/gen_linted.sh create mode 100644 vendor/github.com/google/gopacket/layers/geneve.go create mode 100644 vendor/github.com/google/gopacket/layers/gre.go create mode 100644 vendor/github.com/google/gopacket/layers/gtp.go create mode 100644 vendor/github.com/google/gopacket/layers/iana_ports.go create mode 100644 vendor/github.com/google/gopacket/layers/icmp4.go create mode 100644 vendor/github.com/google/gopacket/layers/icmp6.go create mode 100644 vendor/github.com/google/gopacket/layers/icmp6msg.go create mode 100644 vendor/github.com/google/gopacket/layers/igmp.go create mode 100644 vendor/github.com/google/gopacket/layers/ip4.go create mode 100644 vendor/github.com/google/gopacket/layers/ip6.go create mode 100644 vendor/github.com/google/gopacket/layers/ipsec.go create mode 100644 vendor/github.com/google/gopacket/layers/layertypes.go create mode 100644 vendor/github.com/google/gopacket/layers/lcm.go create mode 100644 vendor/github.com/google/gopacket/layers/linux_sll.go create mode 100644 vendor/github.com/google/gopacket/layers/llc.go create mode 100644 vendor/github.com/google/gopacket/layers/lldp.go create mode 100644 vendor/github.com/google/gopacket/layers/loopback.go create mode 100644 vendor/github.com/google/gopacket/layers/mldv1.go create mode 100644 vendor/github.com/google/gopacket/layers/mldv2.go create mode 100644 vendor/github.com/google/gopacket/layers/modbustcp.go create mode 100644 vendor/github.com/google/gopacket/layers/mpls.go create mode 100644 vendor/github.com/google/gopacket/layers/ndp.go create mode 100644 vendor/github.com/google/gopacket/layers/ntp.go create mode 100644 vendor/github.com/google/gopacket/layers/ospf.go create mode 100644 vendor/github.com/google/gopacket/layers/pflog.go create mode 100644 vendor/github.com/google/gopacket/layers/ports.go create mode 100644 vendor/github.com/google/gopacket/layers/ppp.go create mode 100644 vendor/github.com/google/gopacket/layers/pppoe.go create mode 100644 vendor/github.com/google/gopacket/layers/prism.go create mode 100644 vendor/github.com/google/gopacket/layers/radiotap.go create mode 100644 vendor/github.com/google/gopacket/layers/radius.go create mode 100644 vendor/github.com/google/gopacket/layers/rmcp.go create mode 100644 vendor/github.com/google/gopacket/layers/rudp.go create mode 100644 vendor/github.com/google/gopacket/layers/sctp.go create mode 100644 vendor/github.com/google/gopacket/layers/sflow.go create mode 100644 vendor/github.com/google/gopacket/layers/sip.go create mode 100644 vendor/github.com/google/gopacket/layers/stp.go create mode 100644 vendor/github.com/google/gopacket/layers/tcp.go create mode 100644 vendor/github.com/google/gopacket/layers/tcpip.go create mode 100644 vendor/github.com/google/gopacket/layers/test_creator.py create mode 100644 vendor/github.com/google/gopacket/layers/tls.go create mode 100644 vendor/github.com/google/gopacket/layers/tls_alert.go create mode 100644 vendor/github.com/google/gopacket/layers/tls_appdata.go create mode 100644 vendor/github.com/google/gopacket/layers/tls_cipherspec.go create mode 100644 vendor/github.com/google/gopacket/layers/tls_handshake.go create mode 100644 vendor/github.com/google/gopacket/layers/udp.go create mode 100644 vendor/github.com/google/gopacket/layers/udplite.go create mode 100644 vendor/github.com/google/gopacket/layers/usb.go create mode 100644 vendor/github.com/google/gopacket/layers/vrrp.go create mode 100644 vendor/github.com/google/gopacket/layers/vxlan.go create mode 100644 vendor/github.com/google/gopacket/layers_decoder.go create mode 100644 vendor/github.com/google/gopacket/layertype.go create mode 100644 vendor/github.com/google/gopacket/packet.go create mode 100644 vendor/github.com/google/gopacket/parser.go create mode 100644 vendor/github.com/google/gopacket/time.go create mode 100644 vendor/github.com/google/gopacket/writer.go delete mode 100644 vendor/golang.org/x/net/AUTHORS delete mode 100644 vendor/golang.org/x/net/CONTRIBUTORS create mode 100644 vendor/golang.org/x/net/icmp/dstunreach.go create mode 100644 vendor/golang.org/x/net/icmp/echo.go create mode 100644 vendor/golang.org/x/net/icmp/endpoint.go create mode 100644 vendor/golang.org/x/net/icmp/extension.go create mode 100644 vendor/golang.org/x/net/icmp/helper_posix.go create mode 100644 vendor/golang.org/x/net/icmp/interface.go create mode 100644 vendor/golang.org/x/net/icmp/ipv4.go create mode 100644 vendor/golang.org/x/net/icmp/ipv6.go create mode 100644 vendor/golang.org/x/net/icmp/listen_posix.go create mode 100644 vendor/golang.org/x/net/icmp/listen_stub.go create mode 100644 vendor/golang.org/x/net/icmp/message.go create mode 100644 vendor/golang.org/x/net/icmp/messagebody.go create mode 100644 vendor/golang.org/x/net/icmp/mpls.go create mode 100644 vendor/golang.org/x/net/icmp/multipart.go create mode 100644 vendor/golang.org/x/net/icmp/packettoobig.go create mode 100644 vendor/golang.org/x/net/icmp/paramprob.go create mode 100644 vendor/golang.org/x/net/icmp/sys_freebsd.go create mode 100644 vendor/golang.org/x/net/icmp/timeexceeded.go delete mode 100644 vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go rename vendor/golang.org/x/net/internal/socket/{zsys_darwin_386.go => zsys_freebsd_riscv64.go} (79%) create mode 100644 vendor/golang.org/x/net/ipv4/zsys_freebsd_riscv64.go create mode 100644 vendor/golang.org/x/net/ipv6/zsys_freebsd_riscv64.go delete mode 100644 vendor/golang.org/x/sys/AUTHORS delete mode 100644 vendor/golang.org/x/sys/CONTRIBUTORS create mode 100644 vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_386.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_arm.go delete mode 100644 vendor/golang.org/x/sys/unix/errors_freebsd_arm64.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go create mode 100644 vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go diff --git a/connection/quic.go b/connection/quic.go index 5de16dc2..33a2fc7a 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -20,6 +20,7 @@ import ( "github.com/cloudflare/cloudflared/datagramsession" "github.com/cloudflare/cloudflared/ingress" + "github.com/cloudflare/cloudflared/packet" quicpogs "github.com/cloudflare/cloudflared/quic" "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" @@ -66,9 +67,9 @@ func NewQUICConnection( return nil, &EdgeQuicDialError{Cause: err} } - demuxChan := make(chan *quicpogs.SessionDatagram, demuxChanCapacity) + demuxChan := make(chan *packet.Session, demuxChanCapacity) datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan) - sessionManager := datagramsession.NewManager(logger, datagramMuxer.MuxSession, demuxChan) + sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, demuxChan) return &QUICConnection{ session: session, diff --git a/datagramsession/manager.go b/datagramsession/manager.go index 3cf198bd..a461df4c 100644 --- a/datagramsession/manager.go +++ b/datagramsession/manager.go @@ -9,7 +9,7 @@ import ( "github.com/google/uuid" "github.com/rs/zerolog" - quicpogs "github.com/cloudflare/cloudflared/quic" + "github.com/cloudflare/cloudflared/packet" ) const ( @@ -37,7 +37,7 @@ type manager struct { registrationChan chan *registerSessionEvent unregistrationChan chan *unregisterSessionEvent sendFunc transportSender - receiveChan <-chan *quicpogs.SessionDatagram + receiveChan <-chan *packet.Session closedChan <-chan struct{} sessions map[uuid.UUID]*Session log *zerolog.Logger @@ -45,7 +45,7 @@ type manager struct { timeout time.Duration } -func NewManager(log *zerolog.Logger, sendF transportSender, receiveChan <-chan *quicpogs.SessionDatagram) *manager { +func NewManager(log *zerolog.Logger, sendF transportSender, receiveChan <-chan *packet.Session) *manager { return &manager{ registrationChan: make(chan *registerSessionEvent), unregistrationChan: make(chan *unregisterSessionEvent), @@ -163,7 +163,7 @@ func (m *manager) unregisterSession(unregistration *unregisterSessionEvent) { } } -func (m *manager) sendToSession(datagram *quicpogs.SessionDatagram) { +func (m *manager) sendToSession(datagram *packet.Session) { session, ok := m.sessions[datagram.ID] if !ok { m.log.Error().Str("sessionID", datagram.ID.String()).Msg("session not found") diff --git a/datagramsession/manager_test.go b/datagramsession/manager_test.go index 1d73b33f..496ee16d 100644 --- a/datagramsession/manager_test.go +++ b/datagramsession/manager_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" - quicpogs "github.com/cloudflare/cloudflared/quic" + "github.com/cloudflare/cloudflared/packet" ) var ( @@ -29,7 +29,7 @@ func TestManagerServe(t *testing.T) { remoteUnregisterMsg = "eyeball closed connection" ) - requestChan := make(chan *quicpogs.SessionDatagram) + requestChan := make(chan *packet.Session) transport := mockQUICTransport{ sessions: make(map[uuid.UUID]chan []byte), } @@ -241,9 +241,9 @@ type mockQUICTransport struct { sessions map[uuid.UUID]chan []byte } -func (me *mockQUICTransport) MuxSession(id uuid.UUID, payload []byte) error { - session := me.sessions[id] - session <- payload +func (me *mockQUICTransport) MuxSession(session *packet.Session) error { + s := me.sessions[session.ID] + s <- session.Payload return nil } @@ -255,9 +255,9 @@ type mockEyeballSession struct { respReceiver <-chan []byte } -func (me *mockEyeballSession) serve(ctx context.Context, requestChan chan *quicpogs.SessionDatagram) error { +func (me *mockEyeballSession) serve(ctx context.Context, requestChan chan *packet.Session) error { for i := 0; i < me.expectedMsgCount; i++ { - requestChan <- &quicpogs.SessionDatagram{ + requestChan <- &packet.Session{ ID: me.id, Payload: me.expectedMsg, } diff --git a/datagramsession/session.go b/datagramsession/session.go index 8851b01e..c28c27fb 100644 --- a/datagramsession/session.go +++ b/datagramsession/session.go @@ -9,6 +9,8 @@ import ( "github.com/google/uuid" "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/packet" ) const ( @@ -19,7 +21,7 @@ func SessionIdleErr(timeout time.Duration) error { return fmt.Errorf("session idle for %v", timeout) } -type transportSender func(sessionID uuid.UUID, payload []byte) error +type transportSender func(session *packet.Session) error // Session is a bidirectional pipe of datagrams between transport and dstConn // Destination can be a connection with origin or with eyeball @@ -101,7 +103,11 @@ func (s *Session) dstToTransport(buffer []byte) (closeSession bool, err error) { s.markActive() // https://pkg.go.dev/io#Reader suggests caller should always process n > 0 bytes if n > 0 || err == nil { - if sendErr := s.sendFunc(s.ID, buffer[:n]); sendErr != nil { + session := packet.Session{ + ID: s.ID, + Payload: buffer[:n], + } + if sendErr := s.sendFunc(&session); sendErr != nil { return false, sendErr } } diff --git a/datagramsession/session_test.go b/datagramsession/session_test.go index b0b7a66e..997624b7 100644 --- a/datagramsession/session_test.go +++ b/datagramsession/session_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" - quicpogs "github.com/cloudflare/cloudflared/quic" + "github.com/cloudflare/cloudflared/packet" ) // TestCloseSession makes sure a session will stop after context is done @@ -118,7 +118,7 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool) cfdConn, originConn := net.Pipe() payload := testPayload(sessionID) - respChan := make(chan *quicpogs.SessionDatagram) + respChan := make(chan *packet.Session) sender := newMockTransportSender(sessionID, payload) mg := NewManager(&nopLogger, sender.muxSession, respChan) session := mg.newSession(sessionID, cfdConn) @@ -243,12 +243,12 @@ func newMockTransportSender(expectedSessionID uuid.UUID, expectedPayload []byte) } } -func (mts *mockTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error { - if sessionID != mts.expectedSessionID { - return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, sessionID) +func (mts *mockTransportSender) muxSession(session *packet.Session) error { + if session.ID != mts.expectedSessionID { + return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, session.ID) } - if !bytes.Equal(payload, mts.expectedPayload) { - return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, payload) + if !bytes.Equal(session.Payload, mts.expectedPayload) { + return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, session.Payload) } return nil } @@ -258,7 +258,7 @@ type sendOnceTransportSender struct { sentChan chan struct{} } -func (sots *sendOnceTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error { +func (sots *sendOnceTransportSender) muxSession(session *packet.Session) error { defer close(sots.sentChan) - return sots.baseSender.muxSession(sessionID, payload) + return sots.baseSender.muxSession(session) } diff --git a/go.mod b/go.mod index ab4a143d..3cec487f 100644 --- a/go.mod +++ b/go.mod @@ -34,9 +34,9 @@ require ( go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f - golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e + golang.org/x/net v0.0.0-20220812174116-3211cb980234 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/protobuf v1.28.0 gopkg.in/coreos/go-oidc.v2 v2.2.1 @@ -67,6 +67,7 @@ require ( github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gopacket v1.1.19 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect diff --git a/go.sum b/go.sum index d260fb39..2c4d11fb 100644 --- a/go.sum +++ b/go.sum @@ -292,6 +292,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -710,6 +712,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -816,6 +820,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/packet/decoder.go b/packet/decoder.go new file mode 100644 index 00000000..3af4d025 --- /dev/null +++ b/packet/decoder.go @@ -0,0 +1,184 @@ +package packet + +import ( + "fmt" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/pkg/errors" + "golang.org/x/net/icmp" +) + +func FindProtocol(p []byte) (layers.IPProtocol, error) { + version, err := FindIPVersion(p) + if err != nil { + return 0, err + } + switch version { + case 4: + if len(p) < ipv4HeaderLen { + return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4HeaderLen, len(p)) + } + // Protocol is in the 10th byte of IPv4 header + return layers.IPProtocol(p[9]), nil + case 6: + if len(p) < ipv6HeaderLen { + return 0, fmt.Errorf("IPv6 packet should have at least %d bytes, got %d bytes", ipv6HeaderLen, len(p)) + } + // Next header is in the 7th byte of IPv6 header + return layers.IPProtocol(p[6]), nil + default: + return 0, fmt.Errorf("unknow ip version %d", version) + } +} + +func FindIPVersion(p []byte) (uint8, error) { + if len(p) == 0 { + return 0, fmt.Errorf("packet length is 0") + } + return p[0] >> 4, nil +} + +// IPDecoder decodes raw packets into IP. It can process packets sequentially without allocating +// memory for the layers, so it cannot be called concurrently. +type IPDecoder struct { + ipv4 *layers.IPv4 + ipv6 *layers.IPv6 + layers uint8 + + v4parser *gopacket.DecodingLayerParser + v6parser *gopacket.DecodingLayerParser +} + +func NewIPDecoder() *IPDecoder { + var ( + ipv4 layers.IPv4 + ipv6 layers.IPv6 + ) + dlpv4 := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4) + dlpv4.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil)) + dlpv4.AddDecodingLayer(&ipv4) + // Stop parsing when it encounter a layer that it doesn't have a parser + dlpv4.IgnoreUnsupported = true + + dlpv6 := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv6) + dlpv6.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil)) + dlpv6.AddDecodingLayer(&ipv6) + dlpv6.IgnoreUnsupported = true + + return &IPDecoder{ + ipv4: &ipv4, + ipv6: &ipv6, + layers: 1, + v4parser: dlpv4, + v6parser: dlpv6, + } +} + +func (pd *IPDecoder) Decode(packet []byte) (*IP, error) { + // Should decode to IP layer + decoded, err := pd.decodeByVersion(packet) + if err != nil { + return nil, err + } + + for _, layerType := range decoded { + switch layerType { + case layers.LayerTypeIPv4: + return newIPv4(pd.ipv4) + case layers.LayerTypeIPv6: + return newIPv6(pd.ipv6) + } + } + return nil, fmt.Errorf("no ip layer is decoded") +} + +func (pd *IPDecoder) decodeByVersion(packet []byte) ([]gopacket.LayerType, error) { + version, err := FindIPVersion(packet) + if err != nil { + return nil, err + } + decoded := make([]gopacket.LayerType, 0, pd.layers) + switch version { + case 4: + err = pd.v4parser.DecodeLayers(packet, &decoded) + case 6: + err = pd.v6parser.DecodeLayers(packet, &decoded) + default: + err = fmt.Errorf("unknow ip version %d", version) + } + if err != nil { + return nil, err + } + return decoded, nil +} + +// ICMPDecoder decodes raw packets into IP and ICMP. It can process packets sequentially without allocating +// memory for the layers, so it cannot be called concurrently. +type ICMPDecoder struct { + *IPDecoder + icmpv4 *layers.ICMPv4 + icmpv6 *layers.ICMPv6 +} + +func NewICMPDecoder() *ICMPDecoder { + ipDecoder := NewIPDecoder() + + var ( + icmpv4 layers.ICMPv4 + icmpv6 layers.ICMPv6 + ) + ipDecoder.layers++ + ipDecoder.v4parser.AddDecodingLayer(&icmpv4) + ipDecoder.v6parser.AddDecodingLayer(&icmpv6) + + return &ICMPDecoder{ + IPDecoder: ipDecoder, + icmpv4: &icmpv4, + icmpv6: &icmpv6, + } +} + +func (pd *ICMPDecoder) Decode(packet []byte) (*ICMP, error) { + // Should decode to IP and optionally ICMP layer + decoded, err := pd.decodeByVersion(packet) + if err != nil { + return nil, err + } + + for _, layerType := range decoded { + switch layerType { + case layers.LayerTypeICMPv4: + ipv4, err := newIPv4(pd.ipv4) + if err != nil { + return nil, err + } + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), append(pd.icmpv4.Contents, pd.icmpv4.Payload...)) + if err != nil { + return nil, errors.Wrap(err, "failed to parse ICMPv4 message") + } + return &ICMP{ + IP: ipv4, + Message: msg, + }, nil + case layers.LayerTypeICMPv6: + ipv6, err := newIPv6(pd.ipv6) + if err != nil { + return nil, err + } + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv6), append(pd.icmpv6.Contents, pd.icmpv6.Payload...)) + if err != nil { + return nil, errors.Wrap(err, "failed to parse ICMPv6") + } + return &ICMP{ + IP: ipv6, + Message: msg, + }, nil + } + } + layers := make([]string, len(decoded)) + for i, l := range decoded { + layers[i] = l.String() + } + return nil, fmt.Errorf("Expect to decode IP and ICMP layers, got %s", layers) +} diff --git a/packet/decoder_test.go b/packet/decoder_test.go new file mode 100644 index 00000000..e315bf1f --- /dev/null +++ b/packet/decoder_test.go @@ -0,0 +1,252 @@ +package packet + +import ( + "net" + "net/netip" + "testing" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +func TestDecodeIP(t *testing.T) { + ipDecoder := NewIPDecoder() + icmpDecoder := NewICMPDecoder() + udps := []UDP{ + { + IP: IP{ + Src: netip.MustParseAddr("172.16.0.1"), + Dst: netip.MustParseAddr("10.0.0.1"), + Protocol: layers.IPProtocolUDP, + }, + SrcPort: 31678, + DstPort: 53, + }, + { + + IP: IP{ + Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), + Protocol: layers.IPProtocolUDP, + }, + SrcPort: 52139, + DstPort: 1053, + }, + } + + encoder := NewEncoder() + for _, udp := range udps { + p, err := encoder.Encode(&udp) + require.NoError(t, err) + + ipPacket, err := ipDecoder.Decode(p.Data) + require.NoError(t, err) + assertIPLayer(t, &udp.IP, ipPacket) + + icmpPacket, err := icmpDecoder.Decode(p.Data) + require.Error(t, err) + require.Nil(t, icmpPacket) + } +} + +func TestDecodeICMP(t *testing.T) { + ipDecoder := NewIPDecoder() + icmpDecoder := NewICMPDecoder() + var ( + ipv4Packet = IP{ + Src: netip.MustParseAddr("172.16.0.1"), + Dst: netip.MustParseAddr("10.0.0.1"), + Protocol: layers.IPProtocolICMPv4, + } + ipv6Packet = IP{ + Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), + Protocol: layers.IPProtocolICMPv6, + } + icmpID = 100 + icmpSeq = 52819 + ) + tests := []struct { + testCase string + packet *ICMP + }{ + { + testCase: "icmpv4 time exceed", + packet: &ICMP{ + IP: &ipv4Packet, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeTimeExceeded, + Code: 0, + Body: &icmp.TimeExceeded{ + Data: []byte("original packet"), + }, + }, + }, + }, + { + testCase: "icmpv4 echo", + packet: &ICMP{ + IP: &ipv4Packet, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: icmpID, + Seq: icmpSeq, + Data: []byte("icmpv4 echo"), + }, + }, + }, + }, + { + testCase: "icmpv6 destination unreachable", + packet: &ICMP{ + IP: &ipv6Packet, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeDestinationUnreachable, + Code: 4, + Body: &icmp.DstUnreach{ + Data: []byte("original packet"), + }, + }, + }, + }, + { + testCase: "icmpv6 echo", + packet: &ICMP{ + IP: &ipv6Packet, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, + Code: 0, + Body: &icmp.Echo{ + ID: icmpID, + Seq: icmpSeq, + Data: []byte("icmpv6 echo"), + }, + }, + }, + }, + } + + encoder := NewEncoder() + for _, test := range tests { + p, err := encoder.Encode(test.packet) + require.NoError(t, err) + + ipPacket, err := ipDecoder.Decode(p.Data) + require.NoError(t, err) + if ipPacket.Src.Is4() { + assertIPLayer(t, &ipv4Packet, ipPacket) + } else { + assertIPLayer(t, &ipv6Packet, ipPacket) + } + icmpPacket, err := icmpDecoder.Decode(p.Data) + require.NoError(t, err) + require.Equal(t, ipPacket, icmpPacket.IP) + + require.Equal(t, test.packet.Type, icmpPacket.Type) + require.Equal(t, test.packet.Code, icmpPacket.Code) + require.Equal(t, test.packet.Body, icmpPacket.Body) + expectedBody, err := test.packet.Body.Marshal(test.packet.Type.Protocol()) + require.NoError(t, err) + decodedBody, err := icmpPacket.Body.Marshal(test.packet.Type.Protocol()) + require.NoError(t, err) + require.Equal(t, expectedBody, decodedBody) + } +} + +// TestDecodeBadPackets makes sure decoders don't decode invalid packets +func TestDecodeBadPackets(t *testing.T) { + var ( + srcIPv4 = net.ParseIP("172.16.0.1") + dstIPv4 = net.ParseIP("10.0.0.1") + ) + + ipLayer := layers.IPv4{ + Version: 10, + SrcIP: srcIPv4, + DstIP: dstIPv4, + Protocol: layers.IPProtocolICMPv4, + TTL: defaultTTL, + } + icmpLayer := layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(uint8(ipv4.ICMPTypeEcho), 0), + Id: 100, + Seq: 52819, + } + wrongIPVersion, err := createPacket(&ipLayer, &icmpLayer, nil, nil) + require.NoError(t, err) + + tests := []struct { + testCase string + packet []byte + }{ + { + testCase: "unknown IP version", + packet: wrongIPVersion, + }, + { + testCase: "invalid packet", + packet: []byte("not a packet"), + }, + { + testCase: "zero length packet", + packet: []byte{}, + }, + } + + ipDecoder := NewIPDecoder() + icmpDecoder := NewICMPDecoder() + for _, test := range tests { + ipPacket, err := ipDecoder.Decode(test.packet) + require.Error(t, err) + require.Nil(t, ipPacket) + + icmpPacket, err := icmpDecoder.Decode(test.packet) + require.Error(t, err) + require.Nil(t, icmpPacket) + } +} + +func createPacket(ipLayer, secondLayer, thirdLayer gopacket.SerializableLayer, body []byte) ([]byte, error) { + payload := gopacket.Payload(body) + packet := gopacket.NewSerializeBuffer() + var err error + if thirdLayer != nil { + err = gopacket.SerializeLayers(packet, serializeOpts, ipLayer, secondLayer, thirdLayer, payload) + } else { + err = gopacket.SerializeLayers(packet, serializeOpts, ipLayer, secondLayer, payload) + } + if err != nil { + return nil, err + } + return packet.Bytes(), nil +} + +func assertIPLayer(t *testing.T, expected, actual *IP) { + require.Equal(t, expected.Src, actual.Src) + require.Equal(t, expected.Dst, actual.Dst) + require.Equal(t, expected.Protocol, actual.Protocol) +} + +type UDP struct { + IP + SrcPort, DstPort layers.UDPPort +} + +func (u *UDP) EncodeLayers() ([]gopacket.SerializableLayer, error) { + ipLayers, err := u.IP.EncodeLayers() + if err != nil { + return nil, err + } + udpLayer := layers.UDP{ + SrcPort: u.SrcPort, + DstPort: u.DstPort, + } + udpLayer.SetNetworkLayerForChecksum(ipLayers[0].(gopacket.NetworkLayer)) + return append(ipLayers, &udpLayer), nil +} diff --git a/packet/encoder.go b/packet/encoder.go new file mode 100644 index 00000000..906d2b6d --- /dev/null +++ b/packet/encoder.go @@ -0,0 +1,39 @@ +package packet + +import "github.com/google/gopacket" + +var ( + serializeOpts = gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } +) + +// RawPacket represents a raw packet or one encoded by Encoder +type RawPacket struct { + Data []byte +} + +type Encoder struct { + // buf is reusable because SerializeLayers calls the Clear method before each encoding + buf gopacket.SerializeBuffer +} + +func NewEncoder() *Encoder { + return &Encoder{ + buf: gopacket.NewSerializeBuffer(), + } +} + +func (e Encoder) Encode(packet Packet) (RawPacket, error) { + encodedLayers, err := packet.EncodeLayers() + if err != nil { + return RawPacket{}, err + } + if err := gopacket.SerializeLayers(e.buf, serializeOpts, encodedLayers...); err != nil { + return RawPacket{}, err + } + return RawPacket{ + Data: e.buf.Bytes(), + }, nil +} diff --git a/packet/flow.go b/packet/flow.go new file mode 100644 index 00000000..f4196fa1 --- /dev/null +++ b/packet/flow.go @@ -0,0 +1,97 @@ +package packet + +import ( + "errors" + "net" + "net/netip" + "sync" +) + +type flowID string + +var ( + ErrFlowNotFound = errors.New("flow not found") +) + +func newFlowID(ip net.IP) flowID { + return flowID(ip.String()) +} + +type Flow struct { + Src netip.Addr + Dst netip.Addr + Responder FlowResponder +} + +func isSameFlow(f1, f2 *Flow) bool { + if f1 == nil || f2 == nil { + return false + } + return *f1 == *f2 +} + +// FlowResponder sends response packets to the flow +type FlowResponder interface { + // SendPacket returns a packet to the flow. It must not modify the packet, + // and after return it must not read the packet + SendPacket(pk RawPacket) error +} + +// SrcFlowTracker tracks flow from the perspective of eyeball to origin +// flowID is the source IP +type SrcFlowTracker struct { + lock sync.RWMutex + flows map[flowID]*Flow +} + +func NewSrcFlowTracker() *SrcFlowTracker { + return &SrcFlowTracker{ + flows: make(map[flowID]*Flow), + } +} + +func (sft *SrcFlowTracker) Get(srcIP net.IP) (*Flow, bool) { + sft.lock.RLock() + defer sft.lock.RUnlock() + id := newFlowID(srcIP) + flow, ok := sft.flows[id] + return flow, ok +} + +// Registers a flow. If shouldReplace = true, replace the current flow +func (sft *SrcFlowTracker) Register(flow *Flow, shouldReplace bool) (replaced bool) { + sft.lock.Lock() + defer sft.lock.Unlock() + id := flowID(flow.Src.String()) + currentFlow, ok := sft.flows[id] + if !ok { + sft.flows[id] = flow + return false + } + + if shouldReplace && isSameFlow(currentFlow, flow) { + sft.flows[id] = flow + return true + } + return false +} + +// Unregisters a flow. If force = true, delete it even if it maps to a different flow +func (sft *SrcFlowTracker) Unregister(flow *Flow, force bool) (forceDeleted bool) { + sft.lock.Lock() + defer sft.lock.Unlock() + id := flowID(flow.Src.String()) + currentFlow, ok := sft.flows[id] + if !ok { + return false + } + if isSameFlow(currentFlow, flow) { + delete(sft.flows, id) + return false + } + if force { + delete(sft.flows, id) + return true + } + return false +} diff --git a/packet/packet.go b/packet/packet.go new file mode 100644 index 00000000..62b32ed8 --- /dev/null +++ b/packet/packet.go @@ -0,0 +1,115 @@ +package packet + +import ( + "fmt" + "net/netip" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "golang.org/x/net/icmp" +) + +const ( + defaultTTL uint8 = 64 + ipv4HeaderLen = 20 + ipv6HeaderLen = 40 +) + +// Packet represents an IP packet or a packet that is encapsulated by IP +type Packet interface { + // IPLayer returns the IP of the packet + IPLayer() *IP + // EncodeLayers returns the layers that make up this packet. They can be passed to an Encoder to serialize into RawPacket + EncodeLayers() ([]gopacket.SerializableLayer, error) +} + +// IP represents a generic IP packet. It can be embedded in more specific IP protocols +type IP struct { + Src netip.Addr + Dst netip.Addr + Protocol layers.IPProtocol +} + +func newIPv4(ipLayer *layers.IPv4) (*IP, error) { + src, ok := netip.AddrFromSlice(ipLayer.SrcIP) + if !ok { + return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP) + } + dst, ok := netip.AddrFromSlice(ipLayer.DstIP) + if !ok { + return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP) + } + return &IP{ + Src: src, + Dst: dst, + Protocol: ipLayer.Protocol, + }, nil +} + +func newIPv6(ipLayer *layers.IPv6) (*IP, error) { + src, ok := netip.AddrFromSlice(ipLayer.SrcIP) + if !ok { + return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP) + } + dst, ok := netip.AddrFromSlice(ipLayer.DstIP) + if !ok { + return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP) + } + return &IP{ + Src: src, + Dst: dst, + Protocol: ipLayer.NextHeader, + }, nil +} + +func (ip *IP) IPLayer() *IP { + return ip +} + +func (ip *IP) isIPv4() bool { + return ip.Src.Is4() +} + +func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) { + if ip.isIPv4() { + return []gopacket.SerializableLayer{ + &layers.IPv4{ + Version: 4, + SrcIP: ip.Src.AsSlice(), + DstIP: ip.Dst.AsSlice(), + Protocol: layers.IPProtocol(ip.Protocol), + TTL: defaultTTL, + }, + }, nil + } else { + return []gopacket.SerializableLayer{ + &layers.IPv6{ + Version: 6, + SrcIP: ip.Src.AsSlice(), + DstIP: ip.Dst.AsSlice(), + NextHeader: layers.IPProtocol(ip.Protocol), + HopLimit: defaultTTL, + }, + }, nil + } +} + +// ICMP represents is an IP packet + ICMP message +type ICMP struct { + *IP + *icmp.Message +} + +func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) { + ipLayers, err := i.IP.EncodeLayers() + if err != nil { + return nil, err + } + + msg, err := i.Marshal(nil) + if err != nil { + return nil, err + } + icmpLayer := gopacket.Payload(msg) + return append(ipLayers, icmpLayer), nil +} diff --git a/packet/session.go b/packet/session.go new file mode 100644 index 00000000..421f8158 --- /dev/null +++ b/packet/session.go @@ -0,0 +1,8 @@ +package packet + +import "github.com/google/uuid" + +type Session struct { + ID uuid.UUID + Payload []byte +} diff --git a/quic/datagram.go b/quic/datagram.go index e8e4fad7..8527d948 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -8,21 +8,18 @@ import ( "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/packet" ) const ( sessionIDLen = len(uuid.UUID{}) ) -type SessionDatagram struct { - ID uuid.UUID - Payload []byte -} - type BaseDatagramMuxer interface { - // MuxSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the - // payload from multiple datagram sessions - MuxSession(sessionID uuid.UUID, payload []byte) error + // SendToSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the + // payload from multiple datagram sessions. + SendToSession(session *packet.Session) error // ServeReceive starts a loop to receive datagrams from the QUIC connection ServeReceive(ctx context.Context) error } @@ -30,10 +27,10 @@ type BaseDatagramMuxer interface { type DatagramMuxer struct { session quic.Connection logger *zerolog.Logger - demuxChan chan<- *SessionDatagram + demuxChan chan<- *packet.Session } -func NewDatagramMuxer(quicSession quic.Connection, log *zerolog.Logger, demuxChan chan<- *SessionDatagram) *DatagramMuxer { +func NewDatagramMuxer(quicSession quic.Connection, log *zerolog.Logger, demuxChan chan<- *packet.Session) *DatagramMuxer { logger := log.With().Uint8("datagramVersion", 1).Logger() return &DatagramMuxer{ session: quicSession, @@ -47,13 +44,13 @@ func (dm *DatagramMuxer) mtu() int { return maxDatagramPayloadSize } -func (dm *DatagramMuxer) MuxSession(sessionID uuid.UUID, payload []byte) error { - if len(payload) > dm.mtu() { +func (dm *DatagramMuxer) SendToSession(session *packet.Session) error { + if len(session.Payload) > dm.mtu() { // TODO: TUN-5302 return ICMP packet too big message // drop packet for now, eventually reply with ICMP for PMTUD - return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu()) + return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } - payloadWithMetadata, err := suffixSessionID(sessionID, payload) + payloadWithMetadata, err := suffixSessionID(session.ID, session.Payload) if err != nil { return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") } @@ -86,7 +83,7 @@ func (dm *DatagramMuxer) demux(ctx context.Context, msg []byte) error { if err != nil { return err } - sessionDatagram := SessionDatagram{ + sessionDatagram := packet.Session{ ID: sessionID, Payload: payload, } diff --git a/quic/datagram_test.go b/quic/datagram_test.go index 00673835..32ce4399 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -1,7 +1,6 @@ package quic import ( - "bytes" "context" "crypto/rand" "crypto/rsa" @@ -18,6 +17,8 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + "github.com/cloudflare/cloudflared/packet" ) var ( @@ -57,7 +58,7 @@ func TestDatagram(t *testing.T) { maxPayload := make([]byte, maxDatagramPayloadSize) noPayloadSession := uuid.New() maxPayloadSession := uuid.New() - sessionToPayload := []*SessionDatagram{ + sessionToPayload := []*packet.Session{ { ID: noPayloadSession, Payload: make([]byte, 0), @@ -75,7 +76,7 @@ func TestDatagram(t *testing.T) { testDatagram(t, 2, sessionToPayload, flowPayloads) } -func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatagram, packetPayloads [][]byte) { +func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Session, packetPayloads [][]byte) { quicConfig := &quic.Config{ KeepAlivePeriod: 5 * time.Millisecond, EnableDatagrams: true, @@ -95,7 +96,7 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag return err } - sessionDemuxChan := make(chan *SessionDatagram, 16) + sessionDemuxChan := make(chan *packet.Session, 16) switch version { case 1: @@ -151,11 +152,14 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag return fmt.Errorf("unknown datagram version %d", version) } - for _, sessionDatagram := range sessionToPayloads { - require.NoError(t, muxer.MuxSession(sessionDatagram.ID, sessionDatagram.Payload)) + for _, session := range sessionToPayloads { + require.NoError(t, muxer.SendToSession(session)) } // Payload larger than transport MTU, should not be sent - require.Error(t, muxer.MuxSession(testSessionID, largePayload)) + require.Error(t, muxer.SendToSession(&packet.Session{ + ID: testSessionID, + Payload: largePayload, + })) // Wait for edge to finish receiving the messages time.Sleep(time.Millisecond * 100) @@ -198,35 +202,3 @@ func generateTLSConfig() *tls.Config { NextProtos: []string{"argotunnel"}, } } - -type sessionMuxer interface { - SendToSession(sessionID uuid.UUID, payload []byte) error -} - -type mockSessionReceiver struct { - expectedSessionToPayload map[uuid.UUID][]byte - receivedCount int -} - -func (msr *mockSessionReceiver) ReceiveDatagram(sessionID uuid.UUID, payload []byte) error { - expectedPayload := msr.expectedSessionToPayload[sessionID] - if !bytes.Equal(expectedPayload, payload) { - return fmt.Errorf("expect %v to have payload %s, got %s", sessionID, string(expectedPayload), string(payload)) - } - msr.receivedCount++ - return nil -} - -type mockFlowReceiver struct { - expectedPayloads [][]byte - receivedCount int -} - -func (mfr *mockFlowReceiver) ReceiveFlow(payload []byte) error { - expectedPayload := mfr.expectedPayloads[mfr.receivedCount] - if !bytes.Equal(expectedPayload, payload) { - return fmt.Errorf("expect flow %d to have payload %s, got %s", mfr.receivedCount, string(expectedPayload), string(payload)) - } - mfr.receivedCount++ - return nil -} diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 73e55f7d..d11bcfaf 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -4,10 +4,11 @@ import ( "context" "fmt" - "github.com/google/uuid" "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/packet" ) type datagramV2Type byte @@ -33,14 +34,14 @@ func (dm *DatagramMuxerV2) mtu() int { type DatagramMuxerV2 struct { session quic.Connection logger *zerolog.Logger - sessionDemuxChan chan<- *SessionDatagram + sessionDemuxChan chan<- *packet.Session packetDemuxChan chan<- []byte } func NewDatagramMuxerV2( quicSession quic.Connection, log *zerolog.Logger, - sessionDemuxChan chan<- *SessionDatagram, + sessionDemuxChan chan<- *packet.Session, packetDemuxChan chan<- []byte) *DatagramMuxerV2 { logger := log.With().Uint8("datagramVersion", 2).Logger() return &DatagramMuxerV2{ @@ -53,12 +54,12 @@ func NewDatagramMuxerV2( // MuxSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can // demultiplex the payload from multiple datagram sessions -func (dm *DatagramMuxerV2) MuxSession(sessionID uuid.UUID, payload []byte) error { - if len(payload) > dm.mtu() { +func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { + if len(session.Payload) > dm.mtu() { // TODO: TUN-5302 return ICMP packet too big message - return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu()) + return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } - msgWithID, err := suffixSessionID(sessionID, payload) + msgWithID, err := suffixSessionID(session.ID, session.Payload) if err != nil { return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") } @@ -113,7 +114,7 @@ func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error if err != nil { return err } - sessionDatagram := SessionDatagram{ + sessionDatagram := packet.Session{ ID: sessionID, Payload: payload, } diff --git a/vendor/github.com/google/gopacket/.gitignore b/vendor/github.com/google/gopacket/.gitignore new file mode 100644 index 00000000..149266fd --- /dev/null +++ b/vendor/github.com/google/gopacket/.gitignore @@ -0,0 +1,38 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +#* +*~ + +# examples binaries +examples/synscan/synscan +examples/pfdump/pfdump +examples/pcapdump/pcapdump +examples/httpassembly/httpassembly +examples/statsassembly/statsassembly +examples/arpscan/arpscan +examples/bidirectional/bidirectional +examples/bytediff/bytediff +examples/reassemblydump/reassemblydump +layers/gen +macs/gen +pcap/pcap_tester diff --git a/vendor/github.com/google/gopacket/.travis.gofmt.sh b/vendor/github.com/google/gopacket/.travis.gofmt.sh new file mode 100644 index 00000000..e341a1cb --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.gofmt.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd "$(dirname $0)" +if [ -n "$(go fmt ./...)" ]; then + echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2 + exit 1 +fi diff --git a/vendor/github.com/google/gopacket/.travis.golint.sh b/vendor/github.com/google/gopacket/.travis.golint.sh new file mode 100644 index 00000000..0e267f52 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.golint.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +cd "$(dirname $0)" + +go get golang.org/x/lint/golint +DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag" +# Add subdirectories here as we clean up golint on each. +for subdir in $DIRS; do + pushd $subdir + if golint | + grep -v CannotSetRFMon | # pcap exported error name + grep -v DataLost | # tcpassembly/tcpreader exported error name + grep .; then + exit 1 + fi + popd +done + +pushd layers +for file in *.go; do + if cat .lint_blacklist | grep -q $file; then + echo "Skipping lint of $file due to .lint_blacklist" + elif golint $file | grep .; then + echo "Lint error in file $file" + exit 1 + fi +done +popd diff --git a/vendor/github.com/google/gopacket/.travis.govet.sh b/vendor/github.com/google/gopacket/.travis.govet.sh new file mode 100644 index 00000000..a5c13544 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.govet.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cd "$(dirname $0)" +DIRS=". layers pcap pcapgo tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag" +set -e +for subdir in $DIRS; do + pushd $subdir + go vet + popd +done diff --git a/vendor/github.com/google/gopacket/.travis.install.sh b/vendor/github.com/google/gopacket/.travis.install.sh new file mode 100644 index 00000000..648c9016 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.install.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ev + +go get github.com/google/gopacket +go get github.com/google/gopacket/layers +go get github.com/google/gopacket/tcpassembly +go get github.com/google/gopacket/reassembly +go get github.com/google/gopacket/pcapgo diff --git a/vendor/github.com/google/gopacket/.travis.script.sh b/vendor/github.com/google/gopacket/.travis.script.sh new file mode 100644 index 00000000..a483f4f7 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.script.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -ev + +go test github.com/google/gopacket +go test github.com/google/gopacket/layers +go test github.com/google/gopacket/tcpassembly +go test github.com/google/gopacket/reassembly +go test github.com/google/gopacket/pcapgo +go test github.com/google/gopacket/pcap diff --git a/vendor/github.com/google/gopacket/.travis.yml b/vendor/github.com/google/gopacket/.travis.yml new file mode 100644 index 00000000..84f1f494 --- /dev/null +++ b/vendor/github.com/google/gopacket/.travis.yml @@ -0,0 +1,57 @@ +language: go +go: + - 1.11.x + - 1.12.x + - 1.13.x + - master + +addons: + apt: + packages: + libpcap-dev + +# use modules except for older versions (see below) +install: true + +env: + - GO111MODULE=on + +script: ./.travis.script.sh + +matrix: + fast_finish: true + allow_failures: + - go: master + +jobs: + include: + - go: 1.5.x + install: ./.travis.install.sh + - go: 1.6.x + install: ./.travis.install.sh + - go: 1.7.x + install: ./.travis.install.sh + - go: 1.8.x + install: ./.travis.install.sh + - go: 1.9.x + install: ./.travis.install.sh + - go: 1.10.x + install: ./.travis.install.sh + - os: osx + go: 1.x +# windows doesn't work on travis (package installation just hangs and then errors out) +# - os: windows +# go: 1.x +# # We don't need nmap - but that's the only way to get npcap: +# before_install: choco install npcap --version 0.86 -y + - stage: style + name: "fmt/vet/lint" + go: 1.x + script: + - ./.travis.gofmt.sh + - ./.travis.govet.sh + - ./.travis.golint.sh + +stages: + - style + - test diff --git a/vendor/github.com/google/gopacket/AUTHORS b/vendor/github.com/google/gopacket/AUTHORS new file mode 100644 index 00000000..24e834e4 --- /dev/null +++ b/vendor/github.com/google/gopacket/AUTHORS @@ -0,0 +1,54 @@ +AUTHORS AND MAINTAINERS: + +MAIN DEVELOPERS: +Graeme Connell + +AUTHORS: +Nigel Tao +Cole Mickens +Ben Daglish +Luis Martinez +Remco Verhoef +Hiroaki Kawai +Lukas Lueg +Laurent Hausermann +Bill Green +Christian Mäder +Gernot Vormayr +Vitor Garcia Graveto +Elias Chavarria Reyes +Daniel Rittweiler + +CONTRIBUTORS: +Attila Oláh +Vittus Mikiassen +Matthias Radestock +Matthew Sackman +Loic Prylli +Alexandre Fiori +Adrian Tam +Satoshi Matsumoto +David Stainton +Jesse Ward +Kane Mathers +Jose Selvi +Yerden Zhumabekov +Jensen Hwa + +----------------------------------------------- +FORKED FROM github.com/akrennmair/gopcap +ALL THE FOLLOWING ARE FOR THAT PROJECT + +MAIN DEVELOPERS: +Andreas Krennmair + +CONTRIBUTORS: +Andrea Nall +Daniel Arndt +Dustin Sallings +Graeme Connell +Guillaume Savary +Mark Smith +Miek Gieben +Mike Bell +Trevor Strohman diff --git a/vendor/github.com/google/gopacket/CONTRIBUTING.md b/vendor/github.com/google/gopacket/CONTRIBUTING.md new file mode 100644 index 00000000..99ab7a2e --- /dev/null +++ b/vendor/github.com/google/gopacket/CONTRIBUTING.md @@ -0,0 +1,215 @@ +Contributing To gopacket +======================== + +So you've got some code and you'd like it to be part of gopacket... wonderful! +We're happy to accept contributions, whether they're fixes to old protocols, new +protocols entirely, or anything else you think would improve the gopacket +library. This document is designed to help you to do just that. + +The first section deals with the plumbing: how to actually get a change +submitted. + +The second section deals with coding style... Go is great in that it +has a uniform style implemented by 'go fmt', but there's still some decisions +we've made that go above and beyond, and if you follow them, they won't come up +in your code review. + +The third section deals with some of the implementation decisions we've made, +which may help you to understand the current code and which we may ask you to +conform to (or provide compelling reasons for ignoring). + +Overall, we hope this document will help you to understand our system and write +great code which fits in, and help us to turn around on your code review quickly +so the code can make it into the master branch as quickly as possible. + + +How To Submit Code +------------------ + +We use github.com's Pull Request feature to receive code contributions from +external contributors. See +https://help.github.com/articles/creating-a-pull-request/ for details on +how to create a request. + +Also, there's a local script `gc` in the base directory of GoPacket that +runs a local set of checks, which should give you relatively high confidence +that your pull won't fail github pull checks. + +```sh +go get github.com/google/gopacket +cd $GOROOT/src/pkg/github.com/google/gopacket +git checkout -b # create a new branch to work from +... code code code ... +./gc # Run this to do local commits, it performs a number of checks +``` + +To sum up: + +* DO + + Pull down the latest version. + + Make a feature-specific branch. + + Code using the style and methods discussed in the rest of this document. + + Use the ./gc command to do local commits or check correctness. + + Push your new feature branch up to github.com, as a pull request. + + Handle comments and requests from reviewers, pushing new commits up to + your feature branch as problems are addressed. + + Put interesting comments and discussions into commit comments. +* DON'T + + Push to someone else's branch without their permission. + + +Coding Style +------------ + +* Go code must be run through `go fmt`, `go vet`, and `golint` +* Follow http://golang.org/doc/effective_go.html as much as possible. + + In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums + should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs. + TcpSourcePort or TCP_SOURCE_PORT). +* Bonus points for giving enum types a String() field. +* Any exported types or functions should have commentary + (http://golang.org/doc/effective_go.html#commentary) + + +Coding Methods And Implementation Notes +--------------------------------------- + +### Error Handling + +Many times, you'll be decoding a protocol and run across something bad, a packet +corruption or the like. How do you handle this? First off, ALWAYS report the +error. You can do this either by returning the error from the decode() function +(most common), or if you're up for it you can implement and add an ErrorLayer +through the packet builder (the first method is a simple shortcut that does +exactly this, then stops any future decoding). + +Often, you'll already have decode some part of your protocol by the time you hit +your error. Use your own discretion to determine whether the stuff you've +already decoded should be returned to the caller or not: + +```go +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + prot := &MyProtocol{} + if len(data) < 10 { + // This error occurred before we did ANYTHING, so there's nothing in my + // protocol that the caller could possibly want. Just return the error. + return fmt.Errorf("Length %d less than 10", len(data)) + } + prot.ImportantField1 = data[:5] + prot.ImportantField2 = data[5:10] + // At this point, we've already got enough information in 'prot' to + // warrant returning it to the caller, so we'll add it now. + p.AddLayer(prot) + if len(data) < 15 { + // We encountered an error later in the packet, but the caller already + // has the important info we've gleaned so far. + return fmt.Errorf("Length %d less than 15", len(data)) + } + prot.ImportantField3 = data[10:15] + return nil // We've already added the layer, we can just return success. +} +``` + +In general, our code follows the approach of returning the first error it +encounters. In general, we don't trust any bytes after the first error we see. + +### What Is A Layer? + +The definition of a layer is up to the discretion of the coder. It should be +something important enough that it's actually useful to the caller (IE: every +TLV value should probably NOT be a layer). However, it can be more granular +than a single protocol... IPv6 and SCTP both implement many layers to handle the +various parts of the protocol. Use your best judgement, and prepare to defend +your decisions during code review. ;) + +### Performance + +We strive to make gopacket as fast as possible while still providing lots of +features. In general, this means: + +* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize + others on an as-needed basis (tons of MPLS on your network? Time to optimize + MPLS!) +* Use fast operations. See the toplevel benchmark_test for benchmarks of some + of Go's underlying features and types. +* Test your performance changes! You should use the ./gc script's --benchmark + flag to submit any performance-related changes. Use pcap/gopacket_benchmark + to test your change against a PCAP file based on your traffic patterns. +* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes + a huge performance hit, due to the way that Go currently handles its segmented + stack... don't be afraid to clean it up anyway. We'll trust the Go compiler + to get good enough over time to handle this. Also, this type of + compiler-specific optimization is very fragile; someone adding a field to an + entirely different struct elsewhere in the codebase could reverse any gains + you might achieve by aligning your allocations. +* Try to minimize memory allocations. If possible, use []byte to reference + pieces of the input, instead of using string, which requires copying the bytes + into a new memory allocation. +* Think hard about what should be evaluated lazily vs. not. In general, a + layer's struct should almost exactly mirror the layer's frame. Anything + that's more interesting should be a function. This may not always be + possible, but it's a good rule of thumb. +* Don't fear micro-optimizations. With the above in mind, we welcome + micro-optimizations that we think will have positive/neutral impacts on the + majority of workloads. A prime example of this is pre-allocating certain + structs within a larger one: + +```go +type MyProtocol struct { + // Most packets have 1-4 of VeryCommon, so we preallocate it here. + initialAllocation [4]uint32 + VeryCommon []uint32 +} + +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + prot := &MyProtocol{} + prot.VeryCommon = proto.initialAllocation[:0] + for len(data) > 4 { + field := binary.BigEndian.Uint32(data[:4]) + data = data[4:] + // Since we're using the underlying initialAllocation, we won't need to + // allocate new memory for the following append unless we more than 16 + // bytes of data, which should be the uncommon case. + prot.VeryCommon = append(prot.VeryCommon, field) + } + p.AddLayer(prot) + if len(data) > 0 { + return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data)) + } + return nil +} +``` + +### Slices And Data + +If you're pulling a slice from the data you're decoding, don't copy it. Just +use the slice itself. + +```go +type MyProtocol struct { + A, B net.IP +} +func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error { + p.AddLayer(&MyProtocol{ + A: data[:4], + B: data[4:8], + }) + return nil +} +``` + +The caller has already agreed, by using this library, that they won't modify the +set of bytes they pass in to the decoder, or the library has already copied the +set of bytes to a read-only location. See DecodeOptions.NoCopy for more +information. + +### Enums/Types + +If a protocol has an integer field (uint8, uint16, etc) with a couple of known +values that mean something special, make it a type. This allows us to do really +nice things like adding a String() function to them, so we can more easily +display those to users. Check out layers/enums.go for one example, as well as +layers/icmp.go for layer-specific enums. + +When naming things, try for descriptiveness over suscinctness. For example, +choose DNSResponseRecord over DNSRR. diff --git a/vendor/github.com/google/gopacket/LICENSE b/vendor/github.com/google/gopacket/LICENSE new file mode 100644 index 00000000..2100d524 --- /dev/null +++ b/vendor/github.com/google/gopacket/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2012 Google, Inc. All rights reserved. +Copyright (c) 2009-2011 Andreas Krennmair. 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 Andreas Krennmair, Google, 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/google/gopacket/README.md b/vendor/github.com/google/gopacket/README.md new file mode 100644 index 00000000..efe462ee --- /dev/null +++ b/vendor/github.com/google/gopacket/README.md @@ -0,0 +1,12 @@ +# GoPacket + +This library provides packet decoding capabilities for Go. +See [godoc](https://godoc.org/github.com/google/gopacket) for more details. + +[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket) +[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket) + +Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.9 due to x/sys/unix dependencies. + +Originally forked from the gopcap project written by Andreas +Krennmair (http://github.com/akrennmair/gopcap). diff --git a/vendor/github.com/google/gopacket/base.go b/vendor/github.com/google/gopacket/base.go new file mode 100644 index 00000000..91e150c2 --- /dev/null +++ b/vendor/github.com/google/gopacket/base.go @@ -0,0 +1,178 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// Layer represents a single decoded packet layer (using either the +// OSI or TCP/IP definition of a layer). When decoding, a packet's data is +// broken up into a number of layers. The caller may call LayerType() to +// figure out which type of layer they've received from the packet. Optionally, +// they may then use a type assertion to get the actual layer type for deep +// inspection of the data. +type Layer interface { + // LayerType is the gopacket type for this layer. + LayerType() LayerType + // LayerContents returns the set of bytes that make up this layer. + LayerContents() []byte + // LayerPayload returns the set of bytes contained within this layer, not + // including the layer itself. + LayerPayload() []byte +} + +// Payload is a Layer containing the payload of a packet. The definition of +// what constitutes the payload of a packet depends on previous layers; for +// TCP and UDP, we stop decoding above layer 4 and return the remaining +// bytes as a Payload. Payload is an ApplicationLayer. +type Payload []byte + +// LayerType returns LayerTypePayload +func (p Payload) LayerType() LayerType { return LayerTypePayload } + +// LayerContents returns the bytes making up this layer. +func (p Payload) LayerContents() []byte { return []byte(p) } + +// LayerPayload returns the payload within this layer. +func (p Payload) LayerPayload() []byte { return nil } + +// Payload returns this layer as bytes. +func (p Payload) Payload() []byte { return []byte(p) } + +// String implements fmt.Stringer. +func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) } + +// GoString implements fmt.GoStringer. +func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) } + +// CanDecode implements DecodingLayer. +func (p Payload) CanDecode() LayerClass { return LayerTypePayload } + +// NextLayerType implements DecodingLayer. +func (p Payload) NextLayerType() LayerType { return LayerTypeZero } + +// DecodeFromBytes implements DecodingLayer. +func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error { + *p = Payload(data) + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error { + bytes, err := b.PrependBytes(len(p)) + if err != nil { + return err + } + copy(bytes, p) + return nil +} + +// decodePayload decodes data by returning it all in a Payload layer. +func decodePayload(data []byte, p PacketBuilder) error { + payload := &Payload{} + if err := payload.DecodeFromBytes(data, p); err != nil { + return err + } + p.AddLayer(payload) + p.SetApplicationLayer(payload) + return nil +} + +// Fragment is a Layer containing a fragment of a larger frame, used by layers +// like IPv4 and IPv6 that allow for fragmentation of their payloads. +type Fragment []byte + +// LayerType returns LayerTypeFragment +func (p *Fragment) LayerType() LayerType { return LayerTypeFragment } + +// LayerContents implements Layer. +func (p *Fragment) LayerContents() []byte { return []byte(*p) } + +// LayerPayload implements Layer. +func (p *Fragment) LayerPayload() []byte { return nil } + +// Payload returns this layer as a byte slice. +func (p *Fragment) Payload() []byte { return []byte(*p) } + +// String implements fmt.Stringer. +func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) } + +// CanDecode implements DecodingLayer. +func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment } + +// NextLayerType implements DecodingLayer. +func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero } + +// DecodeFromBytes implements DecodingLayer. +func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error { + *p = Fragment(data) + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error { + bytes, err := b.PrependBytes(len(*p)) + if err != nil { + return err + } + copy(bytes, *p) + return nil +} + +// decodeFragment decodes data by returning it all in a Fragment layer. +func decodeFragment(data []byte, p PacketBuilder) error { + payload := &Fragment{} + if err := payload.DecodeFromBytes(data, p); err != nil { + return err + } + p.AddLayer(payload) + p.SetApplicationLayer(payload) + return nil +} + +// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their +// corresponding OSI layers, as best as possible. + +// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2) +type LinkLayer interface { + Layer + LinkFlow() Flow +} + +// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI +// layer 3) +type NetworkLayer interface { + Layer + NetworkFlow() Flow +} + +// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI +// layer 4) +type TransportLayer interface { + Layer + TransportFlow() Flow +} + +// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI +// layer 7), also known as the packet payload. +type ApplicationLayer interface { + Layer + Payload() []byte +} + +// ErrorLayer is a packet layer created when decoding of the packet has failed. +// Its payload is all the bytes that we were unable to decode, and the returned +// error details why the decoding failed. +type ErrorLayer interface { + Layer + Error() error +} diff --git a/vendor/github.com/google/gopacket/decode.go b/vendor/github.com/google/gopacket/decode.go new file mode 100644 index 00000000..2633f848 --- /dev/null +++ b/vendor/github.com/google/gopacket/decode.go @@ -0,0 +1,157 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "errors" +) + +// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata. +type DecodeFeedback interface { + // SetTruncated should be called if during decoding you notice that a packet + // is shorter than internal layer variables (HeaderLength, or the like) say it + // should be. It sets packet.Metadata().Truncated. + SetTruncated() +} + +type nilDecodeFeedback struct{} + +func (nilDecodeFeedback) SetTruncated() {} + +// NilDecodeFeedback implements DecodeFeedback by doing nothing. +var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{} + +// PacketBuilder is used by layer decoders to store the layers they've decoded, +// and to defer future decoding via NextDecoder. +// Typically, the pattern for use is: +// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error { +// if myLayer, err := myDecodingLogic(data); err != nil { +// return err +// } else { +// p.AddLayer(myLayer) +// } +// // maybe do this, if myLayer is a LinkLayer +// p.SetLinkLayer(myLayer) +// return p.NextDecoder(nextDecoder) +// } +type PacketBuilder interface { + DecodeFeedback + // AddLayer should be called by a decoder immediately upon successful + // decoding of a layer. + AddLayer(l Layer) + // The following functions set the various specific layers in the final + // packet. Note that if many layers call SetX, the first call is kept and all + // other calls are ignored. + SetLinkLayer(LinkLayer) + SetNetworkLayer(NetworkLayer) + SetTransportLayer(TransportLayer) + SetApplicationLayer(ApplicationLayer) + SetErrorLayer(ErrorLayer) + // NextDecoder should be called by a decoder when they're done decoding a + // packet layer but not done with decoding the entire packet. The next + // decoder will be called to decode the last AddLayer's LayerPayload. + // Because of this, NextDecoder must only be called once all other + // PacketBuilder calls have been made. Set*Layer and AddLayer calls after + // NextDecoder calls will behave incorrectly. + NextDecoder(next Decoder) error + // DumpPacketData is used solely for decoding. If you come across an error + // you need to diagnose while processing a packet, call this and your packet's + // data will be dumped to stderr so you can create a test. This should never + // be called from a production decoder. + DumpPacketData() + // DecodeOptions returns the decode options + DecodeOptions() *DecodeOptions +} + +// Decoder is an interface for logic to decode a packet layer. Users may +// implement a Decoder to handle their own strange packet types, or may use one +// of the many decoders available in the 'layers' subpackage to decode things +// for them. +type Decoder interface { + // Decode decodes the bytes of a packet, sending decoded values and other + // information to PacketBuilder, and returning an error if unsuccessful. See + // the PacketBuilder documentation for more details. + Decode([]byte, PacketBuilder) error +} + +// DecodeFunc wraps a function to make it a Decoder. +type DecodeFunc func([]byte, PacketBuilder) error + +// Decode implements Decoder by calling itself. +func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error { + // function, call thyself. + return d(data, p) +} + +// DecodePayload is a Decoder that returns a Payload layer containing all +// remaining bytes. +var DecodePayload Decoder = DecodeFunc(decodePayload) + +// DecodeUnknown is a Decoder that returns an Unknown layer containing all +// remaining bytes, useful if you run up against a layer that you're unable to +// decode yet. This layer is considered an ErrorLayer. +var DecodeUnknown Decoder = DecodeFunc(decodeUnknown) + +// DecodeFragment is a Decoder that returns a Fragment layer containing all +// remaining bytes. +var DecodeFragment Decoder = DecodeFunc(decodeFragment) + +// LayerTypeZero is an invalid layer type, but can be used to determine whether +// layer type has actually been set correctly. +var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown}) + +// LayerTypeDecodeFailure is the layer type for the default error layer. +var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown}) + +// LayerTypePayload is the layer type for a payload that we don't try to decode +// but treat as a success, IE: an application-level payload. +var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload}) + +// LayerTypeFragment is the layer type for a fragment of a layer transported +// by an underlying layer that supports fragmentation. +var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment}) + +// DecodeFailure is a packet layer created if decoding of the packet data failed +// for some reason. It implements ErrorLayer. LayerContents will be the entire +// set of bytes that failed to parse, and Error will return the reason parsing +// failed. +type DecodeFailure struct { + data []byte + err error + stack []byte +} + +// Error returns the error encountered during decoding. +func (d *DecodeFailure) Error() error { return d.err } + +// LayerContents implements Layer. +func (d *DecodeFailure) LayerContents() []byte { return d.data } + +// LayerPayload implements Layer. +func (d *DecodeFailure) LayerPayload() []byte { return nil } + +// String implements fmt.Stringer. +func (d *DecodeFailure) String() string { + return "Packet decoding error: " + d.Error().Error() +} + +// Dump implements Dumper. +func (d *DecodeFailure) Dump() (s string) { + if d.stack != nil { + s = string(d.stack) + } + return +} + +// LayerType returns LayerTypeDecodeFailure +func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure } + +// decodeUnknown "decodes" unsupported data types by returning an error. +// This decoder will thus always return a DecodeFailure layer. +func decodeUnknown(data []byte, p PacketBuilder) error { + return errors.New("Layer type not currently supported") +} diff --git a/vendor/github.com/google/gopacket/doc.go b/vendor/github.com/google/gopacket/doc.go new file mode 100644 index 00000000..b46e43df --- /dev/null +++ b/vendor/github.com/google/gopacket/doc.go @@ -0,0 +1,432 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +Package gopacket provides packet decoding for the Go language. + +gopacket contains many sub-packages with additional functionality you may find +useful, including: + + * layers: You'll probably use this every time. This contains of the logic + built into gopacket for decoding packet protocols. Note that all example + code below assumes that you have imported both gopacket and + gopacket/layers. + * pcap: C bindings to use libpcap to read packets off the wire. + * pfring: C bindings to use PF_RING to read packets off the wire. + * afpacket: C bindings for Linux's AF_PACKET to read packets off the wire. + * tcpassembly: TCP stream reassembly + +Also, if you're looking to dive right into code, see the examples subdirectory +for numerous simple binaries built using gopacket libraries. + +Minimum go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, +and bsdbpf which need at least 1.7 due to x/sys/unix dependencies. + +Basic Usage + +gopacket takes in packet data as a []byte and decodes it into a packet with +a non-zero number of "layers". Each layer corresponds to a protocol +within the bytes. Once a packet has been decoded, the layers of the packet +can be requested from the packet. + + // Decode a packet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) + // Get the TCP layer from this packet + if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + fmt.Println("This is a TCP packet!") + // Get actual TCP data from this layer + tcp, _ := tcpLayer.(*layers.TCP) + fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort) + } + // Iterate over all layers, printing out each layer type + for _, layer := range packet.Layers() { + fmt.Println("PACKET LAYER:", layer.LayerType()) + } + +Packets can be decoded from a number of starting points. Many of our base +types implement Decoder, which allow us to decode packets for which +we don't have full data. + + // Decode an ethernet packet + ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default) + // Decode an IPv6 header and everything it contains + ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default) + // Decode a TCP header and its payload + tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default) + + +Reading Packets From A Source + +Most of the time, you won't just have a []byte of packet data lying around. +Instead, you'll want to read packets in from somewhere (file, interface, etc) +and process them. To do that, you'll want to build a PacketSource. + +First, you'll need to construct an object that implements the PacketDataSource +interface. There are implementations of this interface bundled with gopacket +in the gopacket/pcap and gopacket/pfring subpackages... see their documentation +for more information on their usage. Once you have a PacketDataSource, you can +pass it into NewPacketSource, along with a Decoder of your choice, to create +a PacketSource. + +Once you have a PacketSource, you can read packets from it in multiple ways. +See the docs for PacketSource for more details. The easiest method is the +Packets function, which returns a channel, then asynchronously writes new +packets into that channel, closing the channel if the packetSource hits an +end-of-file. + + packetSource := ... // construct using pcap or pfring + for packet := range packetSource.Packets() { + handlePacket(packet) // do something with each packet + } + +You can change the decoding options of the packetSource by setting fields in +packetSource.DecodeOptions... see the following sections for more details. + + +Lazy Decoding + +gopacket optionally decodes packet data lazily, meaning it +only decodes a packet layer when it needs to handle a function call. + + // Create a packet, but don't actually decode anything yet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + // Now, decode the packet up to the first IPv4 layer found but no further. + // If no IPv4 layer was found, the whole packet will be decoded looking for + // it. + ip4 := packet.Layer(layers.LayerTypeIPv4) + // Decode all layers and return them. The layers up to the first IPv4 layer + // are already decoded, and will not require decoding a second time. + layers := packet.Layers() + +Lazily-decoded packets are not concurrency-safe. Since layers have not all been +decoded, each call to Layer() or Layers() has the potential to mutate the packet +in order to decode the next layer. If a packet is used +in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket +will decode the packet fully, and all future function calls won't mutate the +object. + + +NoCopy Decoding + +By default, gopacket will copy the slice passed to NewPacket and store the +copy within the packet, so future mutations to the bytes underlying the slice +don't affect the packet and its layers. If you can guarantee that the +underlying slice bytes won't be changed, you can use NoCopy to tell +gopacket.NewPacket, and it'll use the passed-in slice itself. + + // This channel returns new byte slices, each of which points to a new + // memory location that's guaranteed immutable for the duration of the + // packet. + for data := range myByteSliceChannel { + p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + doSomethingWithPacket(p) + } + +The fastest method of decoding is to use both Lazy and NoCopy, but note from +the many caveats above that for some implementations either or both may be +dangerous. + + +Pointers To Known Layers + +During decoding, certain layers are stored in the packet as well-known +layer types. For example, IPv4 and IPv6 are both considered NetworkLayer +layers, while TCP and UDP are both TransportLayer layers. We support 4 +layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly +anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these, +you can use the packet.LinkLayer, packet.NetworkLayer, +packet.TransportLayer, and packet.ApplicationLayer functions. Each of +these functions returns a corresponding interface +(gopacket.{Link,Network,Transport,Application}Layer). The first three +provide methods for getting src/dst addresses for that particular layer, +while the final layer provides a Payload function to get payload data. +This is helpful, for example, to get payloads for all packets regardless +of their underlying data type: + + // Get packets from some source + for packet := range someSource { + if app := packet.ApplicationLayer(); app != nil { + if strings.Contains(string(app.Payload()), "magic string") { + fmt.Println("Found magic string in a packet!") + } + } + } + +A particularly useful layer is ErrorLayer, which is set whenever there's +an error parsing part of the packet. + + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default) + if err := packet.ErrorLayer(); err != nil { + fmt.Println("Error decoding some part of the packet:", err) + } + +Note that we don't return an error from NewPacket because we may have decoded +a number of layers successfully before running into our erroneous layer. You +may still be able to get your Ethernet and IPv4 layers correctly, even if +your TCP layer is malformed. + + +Flow And Endpoint + +gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol +independent manner the fact that a packet is coming from A and going to B. +The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide +methods for extracting their flow information, without worrying about the type +of the underlying Layer. + +A Flow is a simple object made up of a set of two Endpoints, one source and one +destination. It details the sender and receiver of the Layer of the Packet. + +An Endpoint is a hashable representation of a source or destination. For +example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4 +IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined +into Flows: + + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + netFlow := packet.NetworkLayer().NetworkFlow() + src, dst := netFlow.Endpoints() + reverseFlow := gopacket.NewFlow(dst, src) + +Both Endpoint and Flow objects can be used as map keys, and the equality +operator can compare them, so you can easily group together all packets +based on endpoint criteria: + + flows := map[gopacket.Endpoint]chan gopacket.Packet + packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy) + // Send all TCP packets to channels based on their destination port. + if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil { + flows[tcp.TransportFlow().Dst()] <- packet + } + // Look for all packets with the same source and destination network address + if net := packet.NetworkLayer(); net != nil { + src, dst := net.NetworkFlow().Endpoints() + if src == dst { + fmt.Println("Fishy packet has same network source and dst: %s", src) + } + } + // Find all packets coming from UDP port 1000 to UDP port 500 + interestingFlow := gopacket.FlowFromEndpoints(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500)) + if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow { + fmt.Println("Found that UDP flow I was looking for!") + } + +For load-balancing purposes, both Flow and Endpoint have FastHash() functions, +which provide quick, non-cryptographic hashes of their contents. Of particular +importance is the fact that Flow FastHash() is symmetric: A->B will have the same +hash as B->A. An example usage could be: + + channels := [8]chan gopacket.Packet + for i := 0; i < 8; i++ { + channels[i] = make(chan gopacket.Packet) + go packetHandler(channels[i]) + } + for packet := range getPackets() { + if net := packet.NetworkLayer(); net != nil { + channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet + } + } + +This allows us to split up a packet stream while still making sure that each +stream sees all packets for a flow (and its bidirectional opposite). + + +Implementing Your Own Decoder + +If your network has some strange encapsulation, you can implement your own +decoder. In this example, we handle Ethernet packets which are encapsulated +in a 4-byte header. + + // Create a layer type, should be unique and high, so it doesn't conflict, + // giving it a name and a decoder to use. + var MyLayerType = gopacket.RegisterLayerType(12345, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)}) + + // Implement my layer + type MyLayer struct { + StrangeHeader []byte + payload []byte + } + func (m MyLayer) LayerType() gopacket.LayerType { return MyLayerType } + func (m MyLayer) LayerContents() []byte { return m.StrangeHeader } + func (m MyLayer) LayerPayload() []byte { return m.payload } + + // Now implement a decoder... this one strips off the first 4 bytes of the + // packet. + func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error { + // Create my layer + p.AddLayer(&MyLayer{data[:4], data[4:]}) + // Determine how to handle the rest of the packet + return p.NextDecoder(layers.LayerTypeEthernet) + } + + // Finally, decode your packets: + p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy) + +See the docs for Decoder and PacketBuilder for more details on how coding +decoders works, or look at RegisterLayerType and RegisterEndpointType to see how +to add layer/endpoint types to gopacket. + + +Fast Decoding With DecodingLayerParser + +TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode +packet data, but only for known packet stacks. + +Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow +due to its need to allocate a new packet and every respective layer. It's very +versatile and can handle all known layer types, but sometimes you really only +care about a specific set of layers regardless, so that versatility is wasted. + +DecodingLayerParser avoids memory allocation altogether by decoding packet +layers directly into preallocated objects, which you can then reference to get +the packet's information. A quick example: + + func main() { + var eth layers.Ethernet + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var tcp layers.TCP + parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp) + decoded := []gopacket.LayerType{} + for packetData := range somehowGetPacketData() { + if err := parser.DecodeLayers(packetData, &decoded); err != nil { + fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err) + continue + } + for _, layerType := range decoded { + switch layerType { + case layers.LayerTypeIPv6: + fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP) + case layers.LayerTypeIPv4: + fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP) + } + } + } + } + +The important thing to note here is that the parser is modifying the passed in +layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly +speeding up the decoding process. It's even branching based on layer type... +it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't +handle any other type... since no other decoders were passed in, an (eth, ip4, +udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet, +LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't +decode a UDP packet). + +Unfortunately, not all layers can be used by DecodingLayerParser... only those +implementing the DecodingLayer interface are usable. Also, it's possible to +create DecodingLayers that are not themselves Layers... see +layers.IPv6ExtensionSkipper for an example of this. + +Faster And Customized Decoding with DecodingLayerContainer + +By default, DecodingLayerParser uses native map to store and search for a layer +to decode. Though being versatile, in some cases this solution may be not so +optimal. For example, if you have only few layers faster operations may be +provided by sparse array indexing or linear array scan. + +To accomodate these scenarios, DecodingLayerContainer interface is introduced +along with its implementations: DecodingLayerSparse, DecodingLayerArray and +DecodingLayerMap. You can specify a container implementation to +DecodingLayerParser with SetDecodingLayerContainer method. Example: + + dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet) + dlp.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil)) + var eth layers.Ethernet + dlp.AddDecodingLayer(ð) + // ... add layers and use DecodingLayerParser as usual... + +To skip one level of indirection (though sacrificing some capabilities) you may +also use DecodingLayerContainer as a decoding tool as it is. In this case you have to +handle unknown layer types and layer panics by yourself. Example: + + func main() { + var eth layers.Ethernet + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var tcp layers.TCP + dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil)) + dlc = dlc.Put(ð) + dlc = dlc.Put(&ip4) + dlc = dlc.Put(&ip6) + dlc = dlc.Put(&tcp) + // you may specify some meaningful DecodeFeedback + decoder := dlc.LayersDecoder(LayerTypeEthernet, gopacket.NilDecodeFeedback) + decoded := make([]gopacket.LayerType, 0, 20) + for packetData := range somehowGetPacketData() { + lt, err := decoder(packetData, &decoded) + if err != nil { + fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err) + continue + } + if lt != gopacket.LayerTypeZero { + fmt.Fprintf(os.Stderr, "unknown layer type: %v\n", lt) + continue + } + for _, layerType := range decoded { + // examine decoded layertypes just as already shown above + } + } + } + +DecodingLayerSparse is the fastest but most effective when LayerType values +that layers in use can decode are not large because otherwise that would lead +to bigger memory footprint. DecodingLayerArray is very compact and primarily +usable if the number of decoding layers is not big (up to ~10-15, but please do +your own benchmarks). DecodingLayerMap is the most versatile one and used by +DecodingLayerParser by default. Please refer to tests and benchmarks in layers +subpackage to further examine usage examples and performance measurements. + +You may also choose to implement your own DecodingLayerContainer if you want to +make use of your own internal packet decoding logic. + +Creating Packet Data + +As well as offering the ability to decode packet data, gopacket will allow you +to create packets from scratch, as well. A number of gopacket layers implement +the SerializableLayer interface; these layers can be serialized to a []byte in +the following manner: + + ip := &layers.IPv4{ + SrcIP: net.IP{1, 2, 3, 4}, + DstIP: net.IP{5, 6, 7, 8}, + // etc... + } + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} // See SerializeOptions for more details. + err := ip.SerializeTo(buf, opts) + if err != nil { panic(err) } + fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer. + +SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat +the current buffer's Bytes() slice as the payload of the serializing layer. +Therefore, you can serialize an entire packet by serializing a set of layers in +reverse order (Payload, then TCP, then IP, then Ethernet, for example). The +SerializeBuffer's SerializeLayers function is a helper that does exactly that. + +To generate a (empty and useless, because no fields are set) +Ethernet(IPv4(TCP(Payload))) packet, for example, you can run: + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{} + gopacket.SerializeLayers(buf, opts, + &layers.Ethernet{}, + &layers.IPv4{}, + &layers.TCP{}, + gopacket.Payload([]byte{1, 2, 3, 4})) + packetData := buf.Bytes() + +A Final Note + +If you use gopacket, you'll almost definitely want to make sure gopacket/layers +is imported, since when imported it sets all the LayerType variables and fills +in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore, +it's recommended that even if you don't use any layers functions directly, you still import with: + + import ( + _ "github.com/google/gopacket/layers" + ) +*/ +package gopacket diff --git a/vendor/github.com/google/gopacket/flows.go b/vendor/github.com/google/gopacket/flows.go new file mode 100644 index 00000000..a00c8839 --- /dev/null +++ b/vendor/github.com/google/gopacket/flows.go @@ -0,0 +1,236 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "bytes" + "fmt" + "strconv" +) + +// MaxEndpointSize determines the maximum size in bytes of an endpoint address. +// +// Endpoints/Flows have a problem: They need to be hashable. Therefore, they +// can't use a byte slice. The two obvious choices are to use a string or a +// byte array. Strings work great, but string creation requires memory +// allocation, which can be slow. Arrays work great, but have a fixed size. We +// originally used the former, now we've switched to the latter. Use of a fixed +// byte-array doubles the speed of constructing a flow (due to not needing to +// allocate). This is a huge increase... too much for us to pass up. +// +// The end result of this, though, is that an endpoint/flow can't be created +// using more than MaxEndpointSize bytes per address. +const MaxEndpointSize = 16 + +// Endpoint is the set of bytes used to address packets at various layers. +// See LinkLayer, NetworkLayer, and TransportLayer specifications. +// Endpoints are usable as map keys. +type Endpoint struct { + typ EndpointType + len int + raw [MaxEndpointSize]byte +} + +// EndpointType returns the endpoint type associated with this endpoint. +func (a Endpoint) EndpointType() EndpointType { return a.typ } + +// Raw returns the raw bytes of this endpoint. These aren't human-readable +// most of the time, but they are faster than calling String. +func (a Endpoint) Raw() []byte { return a.raw[:a.len] } + +// LessThan provides a stable ordering for all endpoints. It sorts first based +// on the EndpointType of an endpoint, then based on the raw bytes of that +// endpoint. +// +// For some endpoints, the actual comparison may not make sense, however this +// ordering does provide useful information for most Endpoint types. +// Ordering is based first on endpoint type, then on raw endpoint bytes. +// Endpoint bytes are sorted lexicographically. +func (a Endpoint) LessThan(b Endpoint) bool { + return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0) +} + +// fnvHash is used by our FastHash functions, and implements the FNV hash +// created by Glenn Fowler, Landon Curt Noll, and Phong Vo. +// See http://isthe.com/chongo/tech/comp/fnv/. +func fnvHash(s []byte) (h uint64) { + h = fnvBasis + for i := 0; i < len(s); i++ { + h ^= uint64(s[i]) + h *= fnvPrime + } + return +} + +const fnvBasis = 14695981039346656037 +const fnvPrime = 1099511628211 + +// FastHash provides a quick hashing function for an endpoint, useful if you'd +// like to split up endpoints by modulos or other load-balancing techniques. +// It uses a variant of Fowler-Noll-Vo hashing. +// +// The output of FastHash is not guaranteed to remain the same through future +// code revisions, so should not be used to key values in persistent storage. +func (a Endpoint) FastHash() (h uint64) { + h = fnvHash(a.raw[:a.len]) + h ^= uint64(a.typ) + h *= fnvPrime + return +} + +// NewEndpoint creates a new Endpoint object. +// +// The size of raw must be less than MaxEndpointSize, otherwise this function +// will panic. +func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) { + e.len = len(raw) + if e.len > MaxEndpointSize { + panic("raw byte length greater than MaxEndpointSize") + } + e.typ = typ + copy(e.raw[:], raw) + return +} + +// EndpointTypeMetadata is used to register a new endpoint type. +type EndpointTypeMetadata struct { + // Name is the string returned by an EndpointType's String function. + Name string + // Formatter is called from an Endpoint's String function to format the raw + // bytes in an Endpoint into a human-readable string. + Formatter func([]byte) string +} + +// EndpointType is the type of a gopacket Endpoint. This type determines how +// the bytes stored in the endpoint should be interpreted. +type EndpointType int64 + +var endpointTypes = map[EndpointType]EndpointTypeMetadata{} + +// RegisterEndpointType creates a new EndpointType and registers it globally. +// It MUST be passed a unique number, or it will panic. Numbers 0-999 are +// reserved for gopacket's use. +func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType { + t := EndpointType(num) + if _, ok := endpointTypes[t]; ok { + panic("Endpoint type number already in use") + } + endpointTypes[t] = meta + return t +} + +func (e EndpointType) String() string { + if t, ok := endpointTypes[e]; ok { + return t.Name + } + return strconv.Itoa(int(e)) +} + +func (a Endpoint) String() string { + if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil { + return t.Formatter(a.raw[:a.len]) + } + return fmt.Sprintf("%v:%v", a.typ, a.raw) +} + +// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint. +// Flows are usable as map keys. +type Flow struct { + typ EndpointType + slen, dlen int + src, dst [MaxEndpointSize]byte +} + +// FlowFromEndpoints creates a new flow by pasting together two endpoints. +// The endpoints must have the same EndpointType, or this function will return +// an error. +func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) { + if src.typ != dst.typ { + err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ) + return + } + return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil +} + +// FastHash provides a quick hashing function for a flow, useful if you'd +// like to split up flows by modulos or other load-balancing techniques. +// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide +// with its reverse flow. IE: the flow A->B will have the same hash as the flow +// B->A. +// +// The output of FastHash is not guaranteed to remain the same through future +// code revisions, so should not be used to key values in persistent storage. +func (f Flow) FastHash() (h uint64) { + // This combination must be commutative. We don't use ^, since that would + // give the same hash for all A->A flows. + h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen]) + h ^= uint64(f.typ) + h *= fnvPrime + return +} + +// String returns a human-readable representation of this flow, in the form +// "Src->Dst" +func (f Flow) String() string { + s, d := f.Endpoints() + return fmt.Sprintf("%v->%v", s, d) +} + +// EndpointType returns the EndpointType for this Flow. +func (f Flow) EndpointType() EndpointType { + return f.typ +} + +// Endpoints returns the two Endpoints for this flow. +func (f Flow) Endpoints() (src, dst Endpoint) { + return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst} +} + +// Src returns the source Endpoint for this flow. +func (f Flow) Src() (src Endpoint) { + src, _ = f.Endpoints() + return +} + +// Dst returns the destination Endpoint for this flow. +func (f Flow) Dst() (dst Endpoint) { + _, dst = f.Endpoints() + return +} + +// Reverse returns a new flow with endpoints reversed. +func (f Flow) Reverse() Flow { + return Flow{f.typ, f.dlen, f.slen, f.dst, f.src} +} + +// NewFlow creates a new flow. +// +// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will +// panic. +func NewFlow(t EndpointType, src, dst []byte) (f Flow) { + f.slen = len(src) + f.dlen = len(dst) + if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize { + panic("flow raw byte length greater than MaxEndpointSize") + } + f.typ = t + copy(f.src[:], src) + copy(f.dst[:], dst) + return +} + +// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints +// that are specified incorrectly during creation. +var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string { + return fmt.Sprintf("%v", b) +}}) + +// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid. +var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil) + +// InvalidFlow is a singleton Flow of type EndpointInvalid. +var InvalidFlow = NewFlow(EndpointInvalid, nil, nil) diff --git a/vendor/github.com/google/gopacket/gc b/vendor/github.com/google/gopacket/gc new file mode 100644 index 00000000..b1d8d2e1 --- /dev/null +++ b/vendor/github.com/google/gopacket/gc @@ -0,0 +1,288 @@ +#!/bin/bash +# Copyright 2012 Google, Inc. All rights reserved. + +# This script provides a simple way to run benchmarks against previous code and +# keep a log of how benchmarks change over time. When used with the --benchmark +# flag, it runs benchmarks from the current code and from the last commit run +# with --benchmark, then stores the results in the git commit description. We +# rerun the old benchmarks along with the new ones, since there's no guarantee +# that git commits will happen on the same machine, so machine differences could +# cause wildly inaccurate results. +# +# If you're making changes to 'gopacket' which could cause performance changes, +# you may be requested to use this commit script to make sure your changes don't +# have large detrimental effects (or to show off how awesome your performance +# improvements are). +# +# If not run with the --benchmark flag, this script is still very useful... it +# makes sure all the correct go formatting, building, and testing work as +# expected. + +function Usage { + cat < + +--benchmark: Run benchmark comparisons against last benchmark'd commit +--root: Run tests that require root priviledges +--gen: Generate code for MACs/ports by pulling down external data + +Note, some 'git commit' flags are necessary, if all else fails, pass in -a +EOF + exit 1 +} + +BENCH="" +GEN="" +ROOT="" +while [ ! -z "$1" ]; do + case "$1" in + "--benchmark") + BENCH="$2" + shift + shift + ;; + "--gen") + GEN="yes" + shift + ;; + "--root") + ROOT="yes" + shift + ;; + "--help") + Usage + ;; + "-h") + Usage + ;; + "help") + Usage + ;; + *) + break + ;; + esac +done + +function Root { + if [ ! -z "$ROOT" ]; then + local exec="$1" + # Some folks (like me) keep source code in places inaccessible by root (like + # NFS), so to make sure things run smoothly we copy them to a /tmp location. + local tmpfile="$(mktemp -t gopacket_XXXXXXXX)" + echo "Running root test executable $exec as $tmpfile" + cp "$exec" "$tmpfile" + chmod a+x "$tmpfile" + shift + sudo "$tmpfile" "$@" + fi +} + +if [ "$#" -eq "0" ]; then + Usage +fi + +cd $(dirname $0) + +# Check for copyright notices. +for filename in $(find ./ -type f -name '*.go'); do + if ! head -n 1 "$filename" | grep -q Copyright; then + echo "File '$filename' may not have copyright notice" + exit 1 + fi +done + +set -e +set -x + +if [ ! -z "$ROOT" ]; then + echo "Running SUDO to get root priviledges for root tests" + sudo echo "have root" +fi + +if [ ! -z "$GEN" ]; then + pushd macs + go run gen.go | gofmt > valid_mac_prefixes.go + popd + pushd layers + go run gen.go | gofmt > iana_ports.go + go run gen2.go | gofmt > enums_generated.go + popd +fi + +# Make sure everything is formatted, compiles, and tests pass. +go fmt ./... +go test -i ./... 2>/dev/null >/dev/null || true +go test +go build +pushd examples/bytediff +go build +popd +if [ -f /usr/include/pcap.h ]; then + pushd pcap + go test ./... + go build ./... + go build pcap_tester.go + Root pcap_tester --mode=basic + Root pcap_tester --mode=filtered + Root pcap_tester --mode=timestamp || echo "You might not support timestamp sources" + popd + pushd examples/afpacket + go build + popd + pushd examples/pcapdump + go build + popd + pushd examples/arpscan + go build + popd + pushd examples/bidirectional + go build + popd + pushd examples/synscan + go build + popd + pushd examples/httpassembly + go build + popd + pushd examples/statsassembly + go build + popd +fi +pushd macs +go test ./... +gofmt -w gen.go +go build gen.go +popd +pushd tcpassembly +go test ./... +popd +pushd reassembly +go test ./... +popd +pushd layers +gofmt -w gen.go +go build gen.go +go test ./... +popd +pushd pcapgo +go test ./... +go build ./... +popd +if [ -f /usr/include/linux/if_packet.h ]; then + if grep -q TPACKET_V3 /usr/include/linux/if_packet.h; then + pushd afpacket + go build ./... + go test ./... + popd + fi +fi +if [ -f /usr/include/pfring.h ]; then + pushd pfring + go test ./... + go build ./... + popd + pushd examples/pfdump + go build + popd +fi +pushd ip4defrag +go test ./... +popd +pushd defrag +go test ./... +popd + +for travis_script in `ls .travis.*.sh`; do + ./$travis_script +done + +# Run our initial commit +git commit "$@" + +if [ -z "$BENCH" ]; then + set +x + echo "We're not benchmarking and we've committed... we're done!" + exit +fi + +### If we get here, we want to run benchmarks from current commit, and compare +### then to benchmarks from the last --benchmark commit. + +# Get our current branch. +BRANCH="$(git branch | grep '^*' | awk '{print $2}')" + +# File we're going to build our commit description in. +COMMIT_FILE="$(mktemp /tmp/tmp.XXXXXXXX)" + +# Add the word "BENCH" to the start of the git commit. +echo -n "BENCH " > $COMMIT_FILE + +# Get the current description... there must be an easier way. +git log -n 1 | grep '^ ' | sed 's/^ //' >> $COMMIT_FILE + +# Get the commit sha for the last benchmark commit +PREV=$(git log -n 1 --grep='BENCHMARK_MARKER_DO_NOT_CHANGE' | head -n 1 | awk '{print $2}') + +## Run current benchmarks + +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +pushd layers +go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE +popd +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +fi + + + +## Reset to last benchmark commit, run benchmarks + +git checkout $PREV + +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +pushd layers +go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE +popd +cat >> $COMMIT_FILE <&1 | tee -a $COMMIT_FILE +fi + + + +## Reset back to the most recent commit, edit the commit message by appending +## benchmark results. +git checkout $BRANCH +git commit --amend -F $COMMIT_FILE diff --git a/vendor/github.com/google/gopacket/layerclass.go b/vendor/github.com/google/gopacket/layerclass.go new file mode 100644 index 00000000..775cd098 --- /dev/null +++ b/vendor/github.com/google/gopacket/layerclass.go @@ -0,0 +1,107 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +// LayerClass is a set of LayerTypes, used for grabbing one of a number of +// different types from a packet. +type LayerClass interface { + // Contains returns true if the given layer type should be considered part + // of this layer class. + Contains(LayerType) bool + // LayerTypes returns the set of all layer types in this layer class. + // Note that this may not be a fast operation on all LayerClass + // implementations. + LayerTypes() []LayerType +} + +// Contains implements LayerClass. +func (l LayerType) Contains(a LayerType) bool { + return l == a +} + +// LayerTypes implements LayerClass. +func (l LayerType) LayerTypes() []LayerType { + return []LayerType{l} +} + +// LayerClassSlice implements a LayerClass with a slice. +type LayerClassSlice []bool + +// Contains returns true if the given layer type should be considered part +// of this layer class. +func (s LayerClassSlice) Contains(t LayerType) bool { + return int(t) < len(s) && s[t] +} + +// LayerTypes returns all layer types in this LayerClassSlice. +// Because of LayerClassSlice's implementation, this could be quite slow. +func (s LayerClassSlice) LayerTypes() (all []LayerType) { + for i := 0; i < len(s); i++ { + if s[i] { + all = append(all, LayerType(i)) + } + } + return +} + +// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of +// size max(types) and setting slice[t] to true for each type t. Note, if +// you implement your own LayerType and give it a high value, this WILL create +// a very large slice. +func NewLayerClassSlice(types []LayerType) LayerClassSlice { + var max LayerType + for _, typ := range types { + if typ > max { + max = typ + } + } + t := make([]bool, int(max+1)) + for _, typ := range types { + t[typ] = true + } + return t +} + +// LayerClassMap implements a LayerClass with a map. +type LayerClassMap map[LayerType]bool + +// Contains returns true if the given layer type should be considered part +// of this layer class. +func (m LayerClassMap) Contains(t LayerType) bool { + return m[t] +} + +// LayerTypes returns all layer types in this LayerClassMap. +func (m LayerClassMap) LayerTypes() (all []LayerType) { + for t := range m { + all = append(all, t) + } + return +} + +// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each +// type in types. +func NewLayerClassMap(types []LayerType) LayerClassMap { + m := LayerClassMap{} + for _, typ := range types { + m[typ] = true + } + return m +} + +// NewLayerClass creates a LayerClass, attempting to be smart about which type +// it creates based on which types are passed in. +func NewLayerClass(types []LayerType) LayerClass { + for _, typ := range types { + if typ > maxLayerType { + // NewLayerClassSlice could create a very large object, so instead create + // a map. + return NewLayerClassMap(types) + } + } + return NewLayerClassSlice(types) +} diff --git a/vendor/github.com/google/gopacket/layers/.lint_blacklist b/vendor/github.com/google/gopacket/layers/.lint_blacklist new file mode 100644 index 00000000..fded4f66 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/.lint_blacklist @@ -0,0 +1,39 @@ +dot11.go +eap.go +endpoints.go +enums_generated.go +enums.go +ethernet.go +geneve.go +icmp4.go +icmp6.go +igmp.go +ip4.go +ip6.go +layertypes.go +linux_sll.go +llc.go +lldp.go +mpls.go +ndp.go +ntp.go +ospf.go +pflog.go +pppoe.go +prism.go +radiotap.go +rudp.go +sctp.go +sflow.go +tcp.go +tcpip.go +tls.go +tls_alert.go +tls_appdata.go +tls_cipherspec.go +tls_hanshake.go +tls_test.go +udp.go +udplite.go +usb.go +vrrp.go diff --git a/vendor/github.com/google/gopacket/layers/arp.go b/vendor/github.com/google/gopacket/layers/arp.go new file mode 100644 index 00000000..0775ac0b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/arp.go @@ -0,0 +1,118 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// Potential values for ARP.Operation. +const ( + ARPRequest = 1 + ARPReply = 2 +) + +// ARP is a ARP packet header. +type ARP struct { + BaseLayer + AddrType LinkType + Protocol EthernetType + HwAddressSize uint8 + ProtAddressSize uint8 + Operation uint16 + SourceHwAddress []byte + SourceProtAddress []byte + DstHwAddress []byte + DstProtAddress []byte +} + +// LayerType returns LayerTypeARP +func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return fmt.Errorf("ARP length %d too short", len(data)) + } + arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2])) + arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + arp.HwAddressSize = data[4] + arp.ProtAddressSize = data[5] + arp.Operation = binary.BigEndian.Uint16(data[6:8]) + arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize + if len(data) < int(arpLength) { + df.SetTruncated() + return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength) + } + arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize] + arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize] + arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize] + arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize] + + arp.Contents = data[:arpLength] + arp.Payload = data[arpLength:] + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress) + bytes, err := b.PrependBytes(size) + if err != nil { + return err + } + if opts.FixLengths { + if len(arp.SourceHwAddress) != len(arp.DstHwAddress) { + return errors.New("mismatched hardware address sizes") + } + arp.HwAddressSize = uint8(len(arp.SourceHwAddress)) + if len(arp.SourceProtAddress) != len(arp.DstProtAddress) { + return errors.New("mismatched prot address sizes") + } + arp.ProtAddressSize = uint8(len(arp.SourceProtAddress)) + } + binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType)) + binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol)) + bytes[4] = arp.HwAddressSize + bytes[5] = arp.ProtAddressSize + binary.BigEndian.PutUint16(bytes[6:], arp.Operation) + start := 8 + for _, addr := range [][]byte{ + arp.SourceHwAddress, + arp.SourceProtAddress, + arp.DstHwAddress, + arp.DstProtAddress, + } { + copy(bytes[start:], addr) + start += len(addr) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (arp *ARP) CanDecode() gopacket.LayerClass { + return LayerTypeARP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (arp *ARP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeARP(data []byte, p gopacket.PacketBuilder) error { + + arp := &ARP{} + return decodingLayerDecoder(arp, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/asf.go b/vendor/github.com/google/gopacket/layers/asf.go new file mode 100644 index 00000000..d698bd0e --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/asf.go @@ -0,0 +1,166 @@ +// Copyright 2019 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file in the root of the source tree. + +package layers + +// This file implements the ASF RMCP payload specified in section 3.2.2.3 of +// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf + +import ( + "encoding/binary" + "fmt" + + "github.com/google/gopacket" +) + +const ( + // ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP. + ASFRMCPEnterprise uint32 = 4542 +) + +// ASFDataIdentifier encapsulates fields used to uniquely identify the format of +// the data block. +// +// While the enterprise number is almost always 4542 (ASF-RMCP), we support +// registering layers using structs of this type as a key in case any users are +// using OEM-extensions. +type ASFDataIdentifier struct { + + // Enterprise is the IANA Enterprise Number associated with the entity that + // defines the message type. A list can be found at + // https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers. + // This can be thought of as the namespace for the message type. + Enterprise uint32 + + // Type is the message type, defined by the entity associated with the + // enterprise above. No pressure, but in the context of EN 4542, 1 byte is + // the difference between sending a ping and telling a machine to do an + // unconditional power down (0x80 and 0x12 respectively). + Type uint8 +} + +// LayerType returns the payload layer type corresponding to an ASF message +// type. +func (a ASFDataIdentifier) LayerType() gopacket.LayerType { + if lt := asfDataLayerTypes[a]; lt != 0 { + return lt + } + + // some layer types don't have a payload, e.g. ASF-RMCP Presence Ping. + return gopacket.LayerTypePayload +} + +// RegisterASFLayerType allows specifying that the data block of ASF packets +// with a given enterprise number and type should be processed by a given layer +// type. This overrides any existing registrations, including defaults. +func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) { + asfDataLayerTypes[a] = l +} + +var ( + // ASFDataIdentifierPresencePong is the message type of the response to a + // Presence Ping message. It indicates the sender is ASF-RMCP-aware. + ASFDataIdentifierPresencePong = ASFDataIdentifier{ + Enterprise: ASFRMCPEnterprise, + Type: 0x40, + } + + // ASFDataIdentifierPresencePing is a message type sent to a managed client + // to solicit a Presence Pong response. Clients may ignore this if the RMCP + // version is unsupported. Sending this message with a sequence number <255 + // is the recommended way of finding out whether an implementation sends + // RMCP ACKs (e.g. iDRAC does, Super Micro does not). + // + // Systems implementing IPMI must respond to this ping to conform to the + // spec, so it is a good substitute for an ICMP ping. + ASFDataIdentifierPresencePing = ASFDataIdentifier{ + Enterprise: ASFRMCPEnterprise, + Type: 0x80, + } + + // asfDataLayerTypes is used to find the next layer for a given ASF header. + asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{ + ASFDataIdentifierPresencePong: LayerTypeASFPresencePong, + } +) + +// ASF defines ASF's generic RMCP message Data block format. See section +// 3.2.2.3. +type ASF struct { + BaseLayer + ASFDataIdentifier + + // Tag is used to match request/response pairs. The tag of a response is set + // to that of the message it is responding to. If a message is + // unidirectional, i.e. not part of a request/response pair, this is set to + // 255. + Tag uint8 + + // 1 byte reserved, set to 0x00. + + // Length is the length of this layer's payload in bytes. + Length uint8 +} + +// LayerType returns LayerTypeASF. It partially satisfies Layer and +// SerializableLayer. +func (*ASF) LayerType() gopacket.LayerType { + return LayerTypeASF +} + +// CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer. +func (a *ASF) CanDecode() gopacket.LayerClass { + return a.LayerType() +} + +// DecodeFromBytes makes the layer represent the provided bytes. It partially +// satisfies DecodingLayer. +func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return fmt.Errorf("invalid ASF data header, length %v less than 8", + len(data)) + } + + a.BaseLayer.Contents = data[:8] + a.BaseLayer.Payload = data[8:] + + a.Enterprise = binary.BigEndian.Uint32(data[:4]) + a.Type = uint8(data[4]) + a.Tag = uint8(data[5]) + // 1 byte reserved + a.Length = uint8(data[7]) + return nil +} + +// NextLayerType returns the layer type corresponding to the message type of +// this ASF data layer. This partially satisfies DecodingLayer. +func (a *ASF) NextLayerType() gopacket.LayerType { + return a.ASFDataIdentifier.LayerType() +} + +// SerializeTo writes the serialized fom of this layer into the SerializeBuffer, +// partially satisfying SerializableLayer. +func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + payload := b.Bytes() + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + binary.BigEndian.PutUint32(bytes[:4], a.Enterprise) + bytes[4] = uint8(a.Type) + bytes[5] = a.Tag + bytes[6] = 0x00 + if opts.FixLengths { + a.Length = uint8(len(payload)) + } + bytes[7] = a.Length + return nil +} + +// decodeASF decodes the byte slice into an RMCP-ASF data struct. +func decodeASF(data []byte, p gopacket.PacketBuilder) error { + return decodingLayerDecoder(&ASF{}, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/asf_presencepong.go b/vendor/github.com/google/gopacket/layers/asf_presencepong.go new file mode 100644 index 00000000..e9a8baf1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/asf_presencepong.go @@ -0,0 +1,194 @@ +// Copyright 2019 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file in the root of the source tree. + +package layers + +// This file implements the RMCP ASF Presence Pong message, specified in section +// 3.2.4.3 of +// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf. It +// also contains non-competing elements from IPMI v2.0, specified in section +// 13.2.4 of +// https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf. + +import ( + "encoding/binary" + "fmt" + + "github.com/google/gopacket" +) + +type ( + // ASFEntity is the type of individual entities that a Presence Pong + // response can indicate support of. The entities currently implemented by + // the spec are IPMI and ASFv1. + ASFEntity uint8 + + // ASFInteraction is the type of individual interactions that a Presence + // Pong response can indicate support for. The interactions currently + // implemented by the spec are RMCP security extensions. Although not + // specified, IPMI uses this field to indicate support for DASH, which is + // supported as well. + ASFInteraction uint8 +) + +const ( + // ASFDCMIEnterprise is the IANA-assigned Enterprise Number of the Data + // Center Manageability Interface Forum. The Presence Pong response's + // Enterprise field being set to this value indicates support for DCMI. The + // DCMI spec regards the OEM field as reserved, so these should be null. + ASFDCMIEnterprise uint32 = 36465 + + // ASFPresencePongEntityIPMI ANDs with Presence Pong's supported entities + // field if the managed system supports IPMI. + ASFPresencePongEntityIPMI ASFEntity = 1 << 7 + + // ASFPresencePongEntityASFv1 ANDs with Presence Pong's supported entities + // field if the managed system supports ASF v1.0. + ASFPresencePongEntityASFv1 ASFEntity = 1 + + // ASFPresencePongInteractionSecurityExtensions ANDs with Presence Pong's + // supported interactions field if the managed system supports RMCP v2.0 + // security extensions. See section 3.2.3. + ASFPresencePongInteractionSecurityExtensions ASFInteraction = 1 << 7 + + // ASFPresencePongInteractionDASH ANDs with Presence Pong's supported + // interactions field if the managed system supports DMTF DASH. See + // https://www.dmtf.org/standards/dash. + ASFPresencePongInteractionDASH ASFInteraction = 1 << 5 +) + +// ASFPresencePong defines the structure of a Presence Pong message's payload. +// See section 3.2.4.3. +type ASFPresencePong struct { + BaseLayer + + // Enterprise is the IANA Enterprise Number of an entity that has defined + // OEM-specific capabilities for the managed client. If no such capabilities + // exist, this is set to ASF's IANA Enterprise Number. + Enterprise uint32 + + // OEM identifies OEM-specific capabilities. Its structure is defined by the + // OEM. This is set to 0s if no OEM-specific capabilities exist. This + // implementation does not change byte order from the wire for this field. + OEM [4]byte + + // We break out entities and interactions into separate booleans as + // discovery is the entire point of this type of message, so we assume they + // are accessed. It also makes gopacket's default layer printing more + // useful. + + // IPMI is true if IPMI is supported by the managed system. There is no + // explicit version in the specification, however given the dates, this is + // assumed to be IPMI v1.0. Support for IPMI is contained in the "supported + // entities" field of the presence pong payload. + IPMI bool + + // ASFv1 indicates support for ASF v1.0. This seems somewhat redundant as + // ASF must be supported in order to receive a response. This is contained + // in the "supported entities" field of the presence pong payload. + ASFv1 bool + + // SecurityExtensions indicates support for RMCP Security Extensions, + // specified in ASF v2.0. This will always be false for v1.x + // implementations. This is contained in the "supported interactions" field + // of the presence pong payload. This field is defined in ASF v1.0, but has + // no useful value. + SecurityExtensions bool + + // DASH is true if DMTF DASH is supported. This is not specified in ASF + // v2.0, but in IPMI v2.0, however the former does not preclude it, so we + // support it. + DASH bool + + // 6 bytes reserved after the entities and interactions fields, set to 0s. +} + +// SupportsDCMI returns whether the Presence Pong message indicates support for +// the Data Center Management Interface, which is an extension of IPMI v2.0. +func (a *ASFPresencePong) SupportsDCMI() bool { + return a.Enterprise == ASFDCMIEnterprise && a.IPMI && a.ASFv1 +} + +// LayerType returns LayerTypeASFPresencePong. It partially satisfies Layer and +// SerializableLayer. +func (*ASFPresencePong) LayerType() gopacket.LayerType { + return LayerTypeASFPresencePong +} + +// CanDecode returns LayerTypeASFPresencePong. It partially satisfies +// DecodingLayer. +func (a *ASFPresencePong) CanDecode() gopacket.LayerClass { + return a.LayerType() +} + +// DecodeFromBytes makes the layer represent the provided bytes. It partially +// satisfies DecodingLayer. +func (a *ASFPresencePong) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 16 { + df.SetTruncated() + return fmt.Errorf("invalid ASF presence pong payload, length %v less than 16", + len(data)) + } + + a.BaseLayer.Contents = data[:16] + a.BaseLayer.Payload = data[16:] + + a.Enterprise = binary.BigEndian.Uint32(data[:4]) + copy(a.OEM[:], data[4:8]) // N.B. no byte order change + a.IPMI = data[8]&uint8(ASFPresencePongEntityIPMI) != 0 + a.ASFv1 = data[8]&uint8(ASFPresencePongEntityASFv1) != 0 + a.SecurityExtensions = data[9]&uint8(ASFPresencePongInteractionSecurityExtensions) != 0 + a.DASH = data[9]&uint8(ASFPresencePongInteractionDASH) != 0 + // ignore remaining 6 bytes; should be set to 0s + return nil +} + +// NextLayerType returns LayerTypePayload, as there are no further layers to +// decode. This partially satisfies DecodingLayer. +func (a *ASFPresencePong) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// SerializeTo writes the serialized fom of this layer into the SerializeBuffer, +// partially satisfying SerializableLayer. +func (a *ASFPresencePong) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(16) + if err != nil { + return err + } + + binary.BigEndian.PutUint32(bytes[:4], a.Enterprise) + + copy(bytes[4:8], a.OEM[:]) + + bytes[8] = 0 + if a.IPMI { + bytes[8] |= uint8(ASFPresencePongEntityIPMI) + } + if a.ASFv1 { + bytes[8] |= uint8(ASFPresencePongEntityASFv1) + } + + bytes[9] = 0 + if a.SecurityExtensions { + bytes[9] |= uint8(ASFPresencePongInteractionSecurityExtensions) + } + if a.DASH { + bytes[9] |= uint8(ASFPresencePongInteractionDASH) + } + + // zero-out remaining 6 bytes + for i := 10; i < len(bytes); i++ { + bytes[i] = 0x00 + } + + return nil +} + +// decodeASFPresencePong decodes the byte slice into an RMCP-ASF Presence Pong +// struct. +func decodeASFPresencePong(data []byte, p gopacket.PacketBuilder) error { + return decodingLayerDecoder(&ASFPresencePong{}, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/base.go b/vendor/github.com/google/gopacket/layers/base.go new file mode 100644 index 00000000..cd59b467 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/base.go @@ -0,0 +1,52 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +// BaseLayer is a convenience struct which implements the LayerData and +// LayerPayload functions of the Layer interface. +type BaseLayer struct { + // Contents is the set of bytes that make up this layer. IE: for an + // Ethernet packet, this would be the set of bytes making up the + // Ethernet frame. + Contents []byte + // Payload is the set of bytes contained by (but not part of) this + // Layer. Again, to take Ethernet as an example, this would be the + // set of bytes encapsulated by the Ethernet protocol. + Payload []byte +} + +// LayerContents returns the bytes of the packet layer. +func (b *BaseLayer) LayerContents() []byte { return b.Contents } + +// LayerPayload returns the bytes contained within the packet layer. +func (b *BaseLayer) LayerPayload() []byte { return b.Payload } + +type layerDecodingLayer interface { + gopacket.Layer + DecodeFromBytes([]byte, gopacket.DecodeFeedback) error + NextLayerType() gopacket.LayerType +} + +func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error { + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(d) + next := d.NextLayerType() + if next == gopacket.LayerTypeZero { + return nil + } + return p.NextDecoder(next) +} + +// hacky way to zero out memory... there must be a better way? +var lotsOfZeros [1024]byte diff --git a/vendor/github.com/google/gopacket/layers/bfd.go b/vendor/github.com/google/gopacket/layers/bfd.go new file mode 100644 index 00000000..43030fb6 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/bfd.go @@ -0,0 +1,481 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// BFD Control Packet Format +// ------------------------- +// The current version of BFD's RFC (RFC 5880) contains the following +// diagram for the BFD Control packet format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | My Discriminator | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Your Discriminator | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Desired Min TX Interval | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Required Min RX Interval | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Required Min Echo RX Interval | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// An optional Authentication Section MAY be present: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Type | Auth Len | Authentication Data... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// Simple Password Authentication Section Format +// --------------------------------------------- +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Type | Auth Len | Auth Key ID | Password... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format +// ---------------------------------------------------------------- +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Type | Auth Len | Auth Key ID | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Key/Digest... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format +// ------------------------------------------------------------------ +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Type | Auth Len | Auth Key ID | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Sequence Number | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Auth Key/Hash... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// From https://tools.ietf.org/rfc/rfc5880.txt +const bfdMinimumRecordSizeInBytes int = 24 + +// BFDVersion represents the version as decoded from the BFD control message +type BFDVersion uint8 + +// BFDDiagnostic represents diagnostic infomation about a BFD session +type BFDDiagnostic uint8 + +// constants that define BFDDiagnostic flags +const ( + BFDDiagnosticNone BFDDiagnostic = 0 // No Diagnostic + BFDDiagnosticTimeExpired BFDDiagnostic = 1 // Control Detection Time Expired + BFDDiagnosticEchoFailed BFDDiagnostic = 2 // Echo Function Failed + BFDDiagnosticNeighborSignalDown BFDDiagnostic = 3 // Neighbor Signaled Session Down + BFDDiagnosticForwardPlaneReset BFDDiagnostic = 4 // Forwarding Plane Reset + BFDDiagnosticPathDown BFDDiagnostic = 5 // Path Down + BFDDiagnosticConcatPathDown BFDDiagnostic = 6 // Concatenated Path Down + BFDDiagnosticAdminDown BFDDiagnostic = 7 // Administratively Down + BFDDiagnosticRevConcatPathDown BFDDiagnostic = 8 // Reverse Concatenated Path Dow +) + +// String returns a string version of BFDDiagnostic +func (bd BFDDiagnostic) String() string { + switch bd { + default: + return "Unknown" + case BFDDiagnosticNone: + return "None" + case BFDDiagnosticTimeExpired: + return "Control Detection Time Expired" + case BFDDiagnosticEchoFailed: + return "Echo Function Failed" + case BFDDiagnosticNeighborSignalDown: + return "Neighbor Signaled Session Down" + case BFDDiagnosticForwardPlaneReset: + return "Forwarding Plane Reset" + case BFDDiagnosticPathDown: + return "Path Down" + case BFDDiagnosticConcatPathDown: + return "Concatenated Path Down" + case BFDDiagnosticAdminDown: + return "Administratively Down" + case BFDDiagnosticRevConcatPathDown: + return "Reverse Concatenated Path Down" + } +} + +// BFDState represents the state of a BFD session +type BFDState uint8 + +// constants that define BFDState +const ( + BFDStateAdminDown BFDState = 0 + BFDStateDown BFDState = 1 + BFDStateInit BFDState = 2 + BFDStateUp BFDState = 3 +) + +// String returns a string version of BFDState +func (s BFDState) String() string { + switch s { + default: + return "Unknown" + case BFDStateAdminDown: + return "Admin Down" + case BFDStateDown: + return "Down" + case BFDStateInit: + return "Init" + case BFDStateUp: + return "Up" + } +} + +// BFDDetectMultiplier represents the negotiated transmit interval, +// multiplied by this value, provides the Detection Time for the +// receiving system in Asynchronous mode. +type BFDDetectMultiplier uint8 + +// BFDDiscriminator is a unique, nonzero discriminator value used +// to demultiplex multiple BFD sessions between the same pair of systems. +type BFDDiscriminator uint32 + +// BFDTimeInterval represents a time interval in microseconds +type BFDTimeInterval uint32 + +// BFDAuthType represents the authentication used in the BFD session +type BFDAuthType uint8 + +// constants that define the BFDAuthType +const ( + BFDAuthTypeNone BFDAuthType = 0 // No Auth + BFDAuthTypePassword BFDAuthType = 1 // Simple Password + BFDAuthTypeKeyedMD5 BFDAuthType = 2 // Keyed MD5 + BFDAuthTypeMeticulousKeyedMD5 BFDAuthType = 3 // Meticulous Keyed MD5 + BFDAuthTypeKeyedSHA1 BFDAuthType = 4 // Keyed SHA1 + BFDAuthTypeMeticulousKeyedSHA1 BFDAuthType = 5 // Meticulous Keyed SHA1 +) + +// String returns a string version of BFDAuthType +func (at BFDAuthType) String() string { + switch at { + default: + return "Unknown" + case BFDAuthTypeNone: + return "No Authentication" + case BFDAuthTypePassword: + return "Simple Password" + case BFDAuthTypeKeyedMD5: + return "Keyed MD5" + case BFDAuthTypeMeticulousKeyedMD5: + return "Meticulous Keyed MD5" + case BFDAuthTypeKeyedSHA1: + return "Keyed SHA1" + case BFDAuthTypeMeticulousKeyedSHA1: + return "Meticulous Keyed SHA1" + } +} + +// BFDAuthKeyID represents the authentication key ID in use for +// this packet. This allows multiple keys to be active simultaneously. +type BFDAuthKeyID uint8 + +// BFDAuthSequenceNumber represents the sequence number for this packet. +// For Keyed Authentication, this value is incremented occasionally. For +// Meticulous Keyed Authentication, this value is incremented for each +// successive packet transmitted for a session. This provides protection +// against replay attacks. +type BFDAuthSequenceNumber uint32 + +// BFDAuthData represents the authentication key or digest +type BFDAuthData []byte + +// BFDAuthHeader represents authentication data used in the BFD session +type BFDAuthHeader struct { + AuthType BFDAuthType + KeyID BFDAuthKeyID + SequenceNumber BFDAuthSequenceNumber + Data BFDAuthData +} + +// Length returns the data length of the BFDAuthHeader based on the +// authentication type +func (h *BFDAuthHeader) Length() int { + switch h.AuthType { + case BFDAuthTypePassword: + return 3 + len(h.Data) + case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5: + return 8 + len(h.Data) + case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1: + return 8 + len(h.Data) + default: + return 0 + } +} + +// BFD represents a BFD control message packet whose payload contains +// the control information required to for a BFD session. +// +// References +// ---------- +// +// Wikipedia's BFD entry: +// https://en.wikipedia.org/wiki/Bidirectional_Forwarding_Detection +// This is the best place to get an overview of BFD. +// +// RFC 5880 "Bidirectional Forwarding Detection (BFD)" (2010) +// https://tools.ietf.org/html/rfc5880 +// This is the original BFD specification. +// +// RFC 5881 "Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop)" (2010) +// https://tools.ietf.org/html/rfc5881 +// Describes the use of the Bidirectional Forwarding Detection (BFD) +// protocol over IPv4 and IPv6 for single IP hops. +type BFD struct { + BaseLayer // Stores the packet bytes and payload bytes. + + Version BFDVersion // Version of the BFD protocol. + Diagnostic BFDDiagnostic // Diagnostic code for last state change + State BFDState // Current state + Poll bool // Requesting verification + Final bool // Responding to a received BFD Control packet that had the Poll (P) bit set. + ControlPlaneIndependent bool // BFD implementation does not share fate with its control plane + AuthPresent bool // Authentication Section is present and the session is to be authenticated + Demand bool // Demand mode is active + Multipoint bool // For future point-to-multipoint extensions. Must always be zero + DetectMultiplier BFDDetectMultiplier // Detection time multiplier + MyDiscriminator BFDDiscriminator // A unique, nonzero discriminator value + YourDiscriminator BFDDiscriminator // discriminator received from the remote system. + DesiredMinTxInterval BFDTimeInterval // Minimum interval, in microseconds, the local system would like to use when transmitting BFD Control packets + RequiredMinRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Control packets that this system is capable of supporting + RequiredMinEchoRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Echo packets that this system is capable of supporting + AuthHeader *BFDAuthHeader // Authentication data, variable length. +} + +// Length returns the data length of a BFD Control message which +// changes based on the presence and type of authentication +// contained in the message +func (d *BFD) Length() int { + if d.AuthPresent && (d.AuthHeader != nil) { + return bfdMinimumRecordSizeInBytes + d.AuthHeader.Length() + } + + return bfdMinimumRecordSizeInBytes +} + +// LayerType returns the layer type of the BFD object, which is LayerTypeBFD. +func (d *BFD) LayerType() gopacket.LayerType { + return LayerTypeBFD +} + +// decodeBFD analyses a byte slice and attempts to decode it as a BFD +// control packet +// +// If it succeeds, it loads p with information about the packet and returns nil. +// If it fails, it returns an error (non nil). +// +// This function is employed in layertypes.go to register the BFD layer. +func decodeBFD(data []byte, p gopacket.PacketBuilder) error { + + // Attempt to decode the byte slice. + d := &BFD{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + + // If the decoding worked, add the layer to the packet and set it + // as the application layer too, if there isn't already one. + p.AddLayer(d) + p.SetApplicationLayer(d) + + return nil +} + +// DecodeFromBytes analyses a byte slice and attempts to decode it as a BFD +// control packet. +// +// Upon succeeds, it loads the BFD object with information about the packet +// and returns nil. +// Upon failure, it returns an error (non nil). +func (d *BFD) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + // If the data block is too short to be a BFD record, then return an error. + if len(data) < bfdMinimumRecordSizeInBytes { + df.SetTruncated() + return errors.New("BFD packet too short") + } + + pLen := uint8(data[3]) + if len(data) != int(pLen) { + return errors.New("BFD packet length does not match") + } + + // BFD type embeds type BaseLayer which contains two fields: + // Contents is supposed to contain the bytes of the data at this level. + // Payload is supposed to contain the payload of this level. + // Here we set the baselayer to be the bytes of the BFD record. + d.BaseLayer = BaseLayer{Contents: data[:len(data)]} + + // Extract the fields from the block of bytes. + // To make sense of this, refer to the packet diagram + // above and the section on endian conventions. + + // The first few fields are all packed into the first 32 bits. Unpack them. + d.Version = BFDVersion(((data[0] & 0xE0) >> 5)) + d.Diagnostic = BFDDiagnostic(data[0] & 0x1F) + data = data[1:] + + d.State = BFDState((data[0] & 0xC0) >> 6) + d.Poll = data[0]&0x20 != 0 + d.Final = data[0]&0x10 != 0 + d.ControlPlaneIndependent = data[0]&0x08 != 0 + d.AuthPresent = data[0]&0x04 != 0 + d.Demand = data[0]&0x02 != 0 + d.Multipoint = data[0]&0x01 != 0 + data = data[1:] + + data, d.DetectMultiplier = data[1:], BFDDetectMultiplier(data[0]) + data, _ = data[1:], uint8(data[0]) // Consume length + + // The remaining fields can just be copied in big endian order. + data, d.MyDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4])) + data, d.YourDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4])) + data, d.DesiredMinTxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4])) + data, d.RequiredMinRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4])) + data, d.RequiredMinEchoRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4])) + + if d.AuthPresent && (len(data) > 2) { + d.AuthHeader = &BFDAuthHeader{} + data, d.AuthHeader.AuthType = data[1:], BFDAuthType(data[0]) + data, _ = data[1:], uint8(data[0]) // Consume length + data, d.AuthHeader.KeyID = data[1:], BFDAuthKeyID(data[0]) + + switch d.AuthHeader.AuthType { + case BFDAuthTypePassword: + d.AuthHeader.Data = BFDAuthData(data) + case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5: + // Skipped reserved byte + data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5])) + d.AuthHeader.Data = BFDAuthData(data) + case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1: + // Skipped reserved byte + data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5])) + d.AuthHeader.Data = BFDAuthData(data) + } + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *BFD) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + data, err := b.PrependBytes(bfdMinimumRecordSizeInBytes) + if err != nil { + return err + } + + // Pack the first few fields into the first 32 bits. + data[0] = byte(byte(d.Version<<5) | byte(d.Diagnostic)) + h := uint8(0) + h |= (uint8(d.State) << 6) + h |= (uint8(bool2uint8(d.Poll)) << 5) + h |= (uint8(bool2uint8(d.Final)) << 4) + h |= (uint8(bool2uint8(d.ControlPlaneIndependent)) << 3) + h |= (uint8(bool2uint8(d.AuthPresent)) << 2) + h |= (uint8(bool2uint8(d.Demand)) << 1) + h |= uint8(bool2uint8(d.Multipoint)) + data[1] = byte(h) + data[2] = byte(d.DetectMultiplier) + data[3] = byte(d.Length()) + + // The remaining fields can just be copied in big endian order. + binary.BigEndian.PutUint32(data[4:], uint32(d.MyDiscriminator)) + binary.BigEndian.PutUint32(data[8:], uint32(d.YourDiscriminator)) + binary.BigEndian.PutUint32(data[12:], uint32(d.DesiredMinTxInterval)) + binary.BigEndian.PutUint32(data[16:], uint32(d.RequiredMinRxInterval)) + binary.BigEndian.PutUint32(data[20:], uint32(d.RequiredMinEchoRxInterval)) + + if d.AuthPresent && (d.AuthHeader != nil) { + auth, err := b.AppendBytes(int(d.AuthHeader.Length())) + if err != nil { + return err + } + + auth[0] = byte(d.AuthHeader.AuthType) + auth[1] = byte(d.AuthHeader.Length()) + auth[2] = byte(d.AuthHeader.KeyID) + + switch d.AuthHeader.AuthType { + case BFDAuthTypePassword: + copy(auth[3:], d.AuthHeader.Data) + case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5: + auth[3] = byte(0) + binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber)) + copy(auth[8:], d.AuthHeader.Data) + case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1: + auth[3] = byte(0) + binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber)) + copy(auth[8:], d.AuthHeader.Data) + } + } + + return nil +} + +// CanDecode returns a set of layers that BFD objects can decode. +// As BFD objects can only decide the BFD layer, we can return just that layer. +// Apparently a single layer type implements LayerClass. +func (d *BFD) CanDecode() gopacket.LayerClass { + return LayerTypeBFD +} + +// NextLayerType specifies the next layer that GoPacket should attempt to +// analyse after this (BFD) layer. As BFD packets do not contain any payload +// bytes, there are no further layers to analyse. +func (d *BFD) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// Payload returns an empty byte slice as BFD packets do not carry a payload +func (d *BFD) Payload() []byte { + return nil +} + +// bool2uint8 converts a bool to uint8 +func bool2uint8(b bool) uint8 { + if b { + return 1 + } + return 0 +} diff --git a/vendor/github.com/google/gopacket/layers/cdp.go b/vendor/github.com/google/gopacket/layers/cdp.go new file mode 100644 index 00000000..095f9261 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/cdp.go @@ -0,0 +1,659 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Enum types courtesy of... +// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm +// https://code.google.com/p/ladvd/ +// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet. +type CDPTLVType uint16 + +// CDPTLVType values. +const ( + CDPTLVDevID CDPTLVType = 0x0001 + CDPTLVAddress CDPTLVType = 0x0002 + CDPTLVPortID CDPTLVType = 0x0003 + CDPTLVCapabilities CDPTLVType = 0x0004 + CDPTLVVersion CDPTLVType = 0x0005 + CDPTLVPlatform CDPTLVType = 0x0006 + CDPTLVIPPrefix CDPTLVType = 0x0007 + CDPTLVHello CDPTLVType = 0x0008 + CDPTLVVTPDomain CDPTLVType = 0x0009 + CDPTLVNativeVLAN CDPTLVType = 0x000a + CDPTLVFullDuplex CDPTLVType = 0x000b + CDPTLVVLANReply CDPTLVType = 0x000e + CDPTLVVLANQuery CDPTLVType = 0x000f + CDPTLVPower CDPTLVType = 0x0010 + CDPTLVMTU CDPTLVType = 0x0011 + CDPTLVExtendedTrust CDPTLVType = 0x0012 + CDPTLVUntrustedCOS CDPTLVType = 0x0013 + CDPTLVSysName CDPTLVType = 0x0014 + CDPTLVSysOID CDPTLVType = 0x0015 + CDPTLVMgmtAddresses CDPTLVType = 0x0016 + CDPTLVLocation CDPTLVType = 0x0017 + CDPTLVExternalPortID CDPTLVType = 0x0018 + CDPTLVPowerRequested CDPTLVType = 0x0019 + CDPTLVPowerAvailable CDPTLVType = 0x001a + CDPTLVPortUnidirectional CDPTLVType = 0x001b + CDPTLVEnergyWise CDPTLVType = 0x001d + CDPTLVSparePairPOE CDPTLVType = 0x001f +) + +// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer. +type CiscoDiscoveryValue struct { + Type CDPTLVType + Length uint16 + Value []byte +} + +// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol. +// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885 +type CiscoDiscovery struct { + BaseLayer + Version byte + TTL byte + Checksum uint16 + Values []CiscoDiscoveryValue +} + +// CDPCapability is the set of capabilities advertised by a CDP device. +type CDPCapability uint32 + +// CDPCapability values. +const ( + CDPCapMaskRouter CDPCapability = 0x0001 + CDPCapMaskTBBridge CDPCapability = 0x0002 + CDPCapMaskSPBridge CDPCapability = 0x0004 + CDPCapMaskSwitch CDPCapability = 0x0008 + CDPCapMaskHost CDPCapability = 0x0010 + CDPCapMaskIGMPFilter CDPCapability = 0x0020 + CDPCapMaskRepeater CDPCapability = 0x0040 + CDPCapMaskPhone CDPCapability = 0x0080 + CDPCapMaskRemote CDPCapability = 0x0100 +) + +// CDPCapabilities represents the capabilities of a device +type CDPCapabilities struct { + L3Router bool + TBBridge bool + SPBridge bool + L2Switch bool + IsHost bool + IGMPFilter bool + L1Repeater bool + IsPhone bool + RemotelyManaged bool +} + +// CDP Power-over-Ethernet values. +const ( + CDPPoEFourWire byte = 0x01 + CDPPoEPDArch byte = 0x02 + CDPPoEPDRequest byte = 0x04 + CDPPoEPSE byte = 0x08 +) + +// CDPSparePairPoE provides information on PoE. +type CDPSparePairPoE struct { + PSEFourWire bool // Supported / Not supported + PDArchShared bool // Shared / Independent + PDRequestOn bool // On / Off + PSEOn bool // On / Off +} + +// CDPVLANDialogue encapsulates a VLAN Query/Reply +type CDPVLANDialogue struct { + ID uint8 + VLAN uint16 +} + +// CDPPowerDialogue encapsulates a Power Query/Reply +type CDPPowerDialogue struct { + ID uint16 + MgmtID uint16 + Values []uint32 +} + +// CDPLocation provides location information for a CDP device. +type CDPLocation struct { + Type uint8 // Undocumented + Location string +} + +// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields) +type CDPHello struct { + OUI []byte + ProtocolID uint16 + ClusterMaster net.IP + Unknown1 net.IP + Version byte + SubVersion byte + Status byte + Unknown2 byte + ClusterCommander net.HardwareAddr + SwitchMAC net.HardwareAddr + Unknown3 byte + ManagementVLAN uint16 +} + +// CDPEnergyWiseSubtype is used within CDP to define TLV values. +type CDPEnergyWiseSubtype uint32 + +// CDPEnergyWiseSubtype values. +const ( + CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007 + CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008 + CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009 + CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017 +) + +// CDPEnergyWise is used by CDP to monitor and control power usage. +type CDPEnergyWise struct { + EncryptedData []byte + Unknown1 uint32 + SequenceNumber uint32 + ModelNumber string + Unknown2 uint16 + HardwareID string + SerialNum string + Unknown3 []byte + Role string + Domain string + Name string + ReplyUnknown1 []byte + ReplyPort []byte + ReplyAddress []byte + ReplyUnknown2 []byte + ReplyUnknown3 []byte +} + +// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues +type CiscoDiscoveryInfo struct { + BaseLayer + CDPHello + DeviceID string + Addresses []net.IP + PortID string + Capabilities CDPCapabilities + Version string + Platform string + IPPrefixes []net.IPNet + VTPDomain string + NativeVLAN uint16 + FullDuplex bool + VLANReply CDPVLANDialogue + VLANQuery CDPVLANDialogue + PowerConsumption uint16 + MTU uint32 + ExtendedTrust uint8 + UntrustedCOS uint8 + SysName string + SysOID string + MgmtAddresses []net.IP + Location CDPLocation + PowerRequest CDPPowerDialogue + PowerAvailable CDPPowerDialogue + SparePairPoe CDPSparePairPoE + EnergyWise CDPEnergyWise + Unknown []CiscoDiscoveryValue +} + +// LayerType returns gopacket.LayerTypeCiscoDiscovery. +func (c *CiscoDiscovery) LayerType() gopacket.LayerType { + return LayerTypeCiscoDiscovery +} + +func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error { + c := &CiscoDiscovery{ + Version: data[0], + TTL: data[1], + Checksum: binary.BigEndian.Uint16(data[2:4]), + } + if c.Version != 1 && c.Version != 2 { + return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version) + } + var err error + c.Values, err = decodeCiscoDiscoveryTLVs(data[4:], p) + if err != nil { + return err + } + c.Contents = data[0:4] + c.Payload = data[4:] + p.AddLayer(c) + return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)) +} + +// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo. +func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType { + return LayerTypeCiscoDiscoveryInfo +} + +func decodeCiscoDiscoveryTLVs(data []byte, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) { + for len(data) > 0 { + if len(data) < 4 { + p.SetTruncated() + return nil, errors.New("CDP TLV < 4 bytes") + } + val := CiscoDiscoveryValue{ + Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])), + Length: binary.BigEndian.Uint16(data[2:4]), + } + if val.Length < 4 { + err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length) + break + } else if len(data) < int(val.Length) { + p.SetTruncated() + return nil, fmt.Errorf("CDP TLV < length %d", val.Length) + } + val.Value = data[4:val.Length] + values = append(values, val) + data = data[val.Length:] + } + return +} + +func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error { + var err error + info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}} + p.AddLayer(info) + values, err := decodeCiscoDiscoveryTLVs(data, p) + if err != nil { // Unlikely, as parent decode will fail, but better safe... + return err + } + for _, val := range values { + switch val.Type { + case CDPTLVDevID: + info.DeviceID = string(val.Value) + case CDPTLVAddress: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.Addresses, err = decodeAddresses(val.Value) + if err != nil { + return err + } + case CDPTLVPortID: + info.PortID = string(val.Value) + case CDPTLVCapabilities: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4])) + info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0) + info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0) + info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0) + info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0) + info.Capabilities.IsHost = (val&CDPCapMaskHost > 0) + info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0) + info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0) + info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0) + info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0) + case CDPTLVVersion: + info.Version = string(val.Value) + case CDPTLVPlatform: + info.Platform = string(val.Value) + case CDPTLVIPPrefix: + v := val.Value + l := len(v) + if l%5 == 0 && l >= 5 { + for len(v) > 0 { + _, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4])) + info.IPPrefixes = append(info.IPPrefixes, *ipnet) + v = v[5:] + } + } else { + return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value)) + } + case CDPTLVHello: + if err = checkCDPTLVLen(val, 32); err != nil { + return err + } + v := val.Value + info.CDPHello.OUI = v[0:3] + info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5]) + info.CDPHello.ClusterMaster = v[5:9] + info.CDPHello.Unknown1 = v[9:13] + info.CDPHello.Version = v[13] + info.CDPHello.SubVersion = v[14] + info.CDPHello.Status = v[15] + info.CDPHello.Unknown2 = v[16] + info.CDPHello.ClusterCommander = v[17:23] + info.CDPHello.SwitchMAC = v[23:29] + info.CDPHello.Unknown3 = v[29] + info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32]) + case CDPTLVVTPDomain: + info.VTPDomain = string(val.Value) + case CDPTLVNativeVLAN: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2]) + case CDPTLVFullDuplex: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.FullDuplex = (val.Value[0] == 1) + case CDPTLVVLANReply: + if err = checkCDPTLVLen(val, 3); err != nil { + return err + } + info.VLANReply.ID = uint8(val.Value[0]) + info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3]) + case CDPTLVVLANQuery: + if err = checkCDPTLVLen(val, 3); err != nil { + return err + } + info.VLANQuery.ID = uint8(val.Value[0]) + info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3]) + case CDPTLVPower: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2]) + case CDPTLVMTU: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.MTU = binary.BigEndian.Uint32(val.Value[0:4]) + case CDPTLVExtendedTrust: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.ExtendedTrust = uint8(val.Value[0]) + case CDPTLVUntrustedCOS: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + info.UntrustedCOS = uint8(val.Value[0]) + case CDPTLVSysName: + info.SysName = string(val.Value) + case CDPTLVSysOID: + info.SysOID = string(val.Value) + case CDPTLVMgmtAddresses: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.MgmtAddresses, err = decodeAddresses(val.Value) + if err != nil { + return err + } + case CDPTLVLocation: + if err = checkCDPTLVLen(val, 2); err != nil { + return err + } + info.Location.Type = uint8(val.Value[0]) + info.Location.Location = string(val.Value[1:]) + + // case CDPTLVLExternalPortID: + // Undocumented + case CDPTLVPowerRequested: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2]) + info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4]) + for n := 4; n < len(val.Value); n += 4 { + info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4])) + } + case CDPTLVPowerAvailable: + if err = checkCDPTLVLen(val, 4); err != nil { + return err + } + info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2]) + info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4]) + for n := 4; n < len(val.Value); n += 4 { + info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4])) + } + // case CDPTLVPortUnidirectional + // Undocumented + case CDPTLVEnergyWise: + if err = checkCDPTLVLen(val, 72); err != nil { + return err + } + info.EnergyWise.EncryptedData = val.Value[0:20] + info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24]) + info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28]) + info.EnergyWise.ModelNumber = string(val.Value[28:44]) + info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46]) + info.EnergyWise.HardwareID = string(val.Value[46:49]) + info.EnergyWise.SerialNum = string(val.Value[49:60]) + info.EnergyWise.Unknown3 = val.Value[60:68] + tlvLen := binary.BigEndian.Uint16(val.Value[68:70]) + tlvNum := binary.BigEndian.Uint16(val.Value[70:72]) + data := val.Value[72:] + if len(data) < int(tlvLen) { + return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data)) + } + numSeen := 0 + for len(data) > 8 { + numSeen++ + if numSeen > int(tlvNum) { // Too many TLV's ? + return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen) + } + tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4])) + tLen := int(binary.BigEndian.Uint32(data[4:8])) + if tLen > len(data)-8 { + return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8) + } + data = data[8:] + switch tType { + case CDPEnergyWiseRole: + info.EnergyWise.Role = string(data[:]) + case CDPEnergyWiseDomain: + info.EnergyWise.Domain = string(data[:]) + case CDPEnergyWiseName: + info.EnergyWise.Name = string(data[:]) + case CDPEnergyWiseReplyTo: + if len(data) >= 18 { + info.EnergyWise.ReplyUnknown1 = data[0:2] + info.EnergyWise.ReplyPort = data[2:4] + info.EnergyWise.ReplyAddress = data[4:8] + info.EnergyWise.ReplyUnknown2 = data[8:10] + info.EnergyWise.ReplyUnknown3 = data[10:14] + } + } + data = data[tLen:] + } + case CDPTLVSparePairPOE: + if err = checkCDPTLVLen(val, 1); err != nil { + return err + } + v := val.Value[0] + info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0) + info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0) + info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0) + info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0) + default: + info.Unknown = append(info.Unknown, val) + } + } + return nil +} + +// CDP Protocol Types +const ( + CDPProtocolTypeNLPID byte = 1 + CDPProtocolType802_2 byte = 2 +) + +// CDPAddressType is used to define TLV values within CDP addresses. +type CDPAddressType uint64 + +// CDP Address types. +const ( + CDPAddressTypeCLNP CDPAddressType = 0x81 + CDPAddressTypeIPV4 CDPAddressType = 0xcc + CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800 + CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003 + CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b + CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137 + CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4 + CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600 + CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019 +) + +func decodeAddresses(v []byte) (addresses []net.IP, err error) { + numaddr := int(binary.BigEndian.Uint32(v[0:4])) + if numaddr < 1 { + return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr) + } + v = v[4:] + if len(v) < numaddr*8 { + return nil, fmt.Errorf("Invalid Address TLV length %d", len(v)) + } + for i := 0; i < numaddr; i++ { + prottype := v[0] + if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type + return nil, fmt.Errorf("Invalid Address Protocol %d", prottype) + } + protlen := int(v[1]) + if (prottype == CDPProtocolTypeNLPID && protlen != 1) || + (prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length + return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen) + } + plen := make([]byte, 8) + copy(plen[8-protlen:], v[2:2+protlen]) + protocol := CDPAddressType(binary.BigEndian.Uint64(plen)) + v = v[2+protlen:] + addrlen := binary.BigEndian.Uint16(v[0:2]) + ab := v[2 : 2+addrlen] + if protocol == CDPAddressTypeIPV4 && addrlen == 4 { + addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3])) + } else if protocol == CDPAddressTypeIPV6 && addrlen == 16 { + addresses = append(addresses, net.IP(ab)) + } else { + // only handle IPV4 & IPV6 for now + } + v = v[2+addrlen:] + if len(v) < 8 { + break + } + } + return +} + +func (t CDPTLVType) String() (s string) { + switch t { + case CDPTLVDevID: + s = "Device ID" + case CDPTLVAddress: + s = "Addresses" + case CDPTLVPortID: + s = "Port ID" + case CDPTLVCapabilities: + s = "Capabilities" + case CDPTLVVersion: + s = "Software Version" + case CDPTLVPlatform: + s = "Platform" + case CDPTLVIPPrefix: + s = "IP Prefix" + case CDPTLVHello: + s = "Protocol Hello" + case CDPTLVVTPDomain: + s = "VTP Management Domain" + case CDPTLVNativeVLAN: + s = "Native VLAN" + case CDPTLVFullDuplex: + s = "Full Duplex" + case CDPTLVVLANReply: + s = "VoIP VLAN Reply" + case CDPTLVVLANQuery: + s = "VLANQuery" + case CDPTLVPower: + s = "Power consumption" + case CDPTLVMTU: + s = "MTU" + case CDPTLVExtendedTrust: + s = "Extended Trust Bitmap" + case CDPTLVUntrustedCOS: + s = "Untrusted Port CoS" + case CDPTLVSysName: + s = "System Name" + case CDPTLVSysOID: + s = "System OID" + case CDPTLVMgmtAddresses: + s = "Management Addresses" + case CDPTLVLocation: + s = "Location" + case CDPTLVExternalPortID: + s = "External Port ID" + case CDPTLVPowerRequested: + s = "Power Requested" + case CDPTLVPowerAvailable: + s = "Power Available" + case CDPTLVPortUnidirectional: + s = "Port Unidirectional" + case CDPTLVEnergyWise: + s = "Energy Wise" + case CDPTLVSparePairPOE: + s = "Spare Pair POE" + default: + s = "Unknown" + } + return +} + +func (a CDPAddressType) String() (s string) { + switch a { + case CDPAddressTypeCLNP: + s = "Connectionless Network Protocol" + case CDPAddressTypeIPV4: + s = "IPv4" + case CDPAddressTypeIPV6: + s = "IPv6" + case CDPAddressTypeDECNET: + s = "DECnet Phase IV" + case CDPAddressTypeAPPLETALK: + s = "Apple Talk" + case CDPAddressTypeIPX: + s = "Novell IPX" + case CDPAddressTypeVINES: + s = "Banyan VINES" + case CDPAddressTypeXNS: + s = "Xerox Network Systems" + case CDPAddressTypeAPOLLO: + s = "Apollo" + default: + s = "Unknown" + } + return +} + +func (t CDPEnergyWiseSubtype) String() (s string) { + switch t { + case CDPEnergyWiseRole: + s = "Role" + case CDPEnergyWiseDomain: + s = "Domain" + case CDPEnergyWiseName: + s = "Name" + case CDPEnergyWiseReplyTo: + s = "ReplyTo" + default: + s = "Unknown" + } + return +} + +func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) { + if len(v.Value) < l { + err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value)) + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/ctp.go b/vendor/github.com/google/gopacket/layers/ctp.go new file mode 100644 index 00000000..82875845 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ctp.go @@ -0,0 +1,109 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each +// EthernetCTP layer. +type EthernetCTPFunction uint16 + +// EthernetCTPFunction values. +const ( + EthernetCTPFunctionReply EthernetCTPFunction = 1 + EthernetCTPFunctionForwardData EthernetCTPFunction = 2 +) + +// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html. +// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more +// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer. +type EthernetCTP struct { + BaseLayer + SkipCount uint16 +} + +// LayerType returns gopacket.LayerTypeEthernetCTP. +func (c *EthernetCTP) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTP +} + +// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more +// details. +type EthernetCTPForwardData struct { + BaseLayer + Function EthernetCTPFunction + ForwardAddress []byte +} + +// LayerType returns gopacket.LayerTypeEthernetCTPForwardData. +func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTPForwardData +} + +// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint. +func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress) +} + +// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details. +type EthernetCTPReply struct { + BaseLayer + Function EthernetCTPFunction + ReceiptNumber uint16 + Data []byte +} + +// LayerType returns gopacket.LayerTypeEthernetCTPReply. +func (c *EthernetCTPReply) LayerType() gopacket.LayerType { + return LayerTypeEthernetCTPReply +} + +// Payload returns the EthernetCTP reply's Data bytes. +func (c *EthernetCTPReply) Payload() []byte { return c.Data } + +func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error { + c := &EthernetCTP{ + SkipCount: binary.LittleEndian.Uint16(data[:2]), + BaseLayer: BaseLayer{data[:2], data[2:]}, + } + if c.SkipCount%2 != 0 { + return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount) + } + p.AddLayer(c) + return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType)) +} + +// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP +// layer type to decode next, then decodes based on that. +func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error { + function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2])) + switch function { + case EthernetCTPFunctionReply: + reply := &EthernetCTPReply{ + Function: function, + ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]), + Data: data[4:], + BaseLayer: BaseLayer{data, nil}, + } + p.AddLayer(reply) + p.SetApplicationLayer(reply) + return nil + case EthernetCTPFunctionForwardData: + forward := &EthernetCTPForwardData{ + Function: function, + ForwardAddress: data[2:8], + BaseLayer: BaseLayer{data[:8], data[8:]}, + } + p.AddLayer(forward) + return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType)) + } + return fmt.Errorf("Unknown EthernetCTP function type %v", function) +} diff --git a/vendor/github.com/google/gopacket/layers/dhcpv4.go b/vendor/github.com/google/gopacket/layers/dhcpv4.go new file mode 100644 index 00000000..d79c5915 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dhcpv4.go @@ -0,0 +1,592 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// DHCPOp rerprents a bootp operation +type DHCPOp byte + +// bootp operations +const ( + DHCPOpRequest DHCPOp = 1 + DHCPOpReply DHCPOp = 2 +) + +// String returns a string version of a DHCPOp. +func (o DHCPOp) String() string { + switch o { + case DHCPOpRequest: + return "Request" + case DHCPOpReply: + return "Reply" + default: + return "Unknown" + } +} + +// DHCPMsgType represents a DHCP operation +type DHCPMsgType byte + +// Constants that represent DHCP operations +const ( + DHCPMsgTypeUnspecified DHCPMsgType = iota + DHCPMsgTypeDiscover + DHCPMsgTypeOffer + DHCPMsgTypeRequest + DHCPMsgTypeDecline + DHCPMsgTypeAck + DHCPMsgTypeNak + DHCPMsgTypeRelease + DHCPMsgTypeInform +) + +// String returns a string version of a DHCPMsgType. +func (o DHCPMsgType) String() string { + switch o { + case DHCPMsgTypeUnspecified: + return "Unspecified" + case DHCPMsgTypeDiscover: + return "Discover" + case DHCPMsgTypeOffer: + return "Offer" + case DHCPMsgTypeRequest: + return "Request" + case DHCPMsgTypeDecline: + return "Decline" + case DHCPMsgTypeAck: + return "Ack" + case DHCPMsgTypeNak: + return "Nak" + case DHCPMsgTypeRelease: + return "Release" + case DHCPMsgTypeInform: + return "Inform" + default: + return "Unknown" + } +} + +//DHCPMagic is the RFC 2131 "magic cooke" for DHCP. +var DHCPMagic uint32 = 0x63825363 + +// DHCPv4 contains data for a single DHCP packet. +type DHCPv4 struct { + BaseLayer + Operation DHCPOp + HardwareType LinkType + HardwareLen uint8 + HardwareOpts uint8 + Xid uint32 + Secs uint16 + Flags uint16 + ClientIP net.IP + YourClientIP net.IP + NextServerIP net.IP + RelayAgentIP net.IP + ClientHWAddr net.HardwareAddr + ServerName []byte + File []byte + Options DHCPOptions +} + +// DHCPOptions is used to get nicely printed option lists which would normally +// be cut off after 5 options. +type DHCPOptions []DHCPOption + +// String returns a string version of the options list. +func (o DHCPOptions) String() string { + buf := &bytes.Buffer{} + buf.WriteByte('[') + for i, opt := range o { + buf.WriteString(opt.String()) + if i+1 != len(o) { + buf.WriteString(", ") + } + } + buf.WriteByte(']') + return buf.String() +} + +// LayerType returns gopacket.LayerTypeDHCPv4 +func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 240 { + df.SetTruncated() + return fmt.Errorf("DHCPv4 length %d too short", len(data)) + } + d.Options = d.Options[:0] + d.Operation = DHCPOp(data[0]) + d.HardwareType = LinkType(data[1]) + d.HardwareLen = data[2] + d.HardwareOpts = data[3] + d.Xid = binary.BigEndian.Uint32(data[4:8]) + d.Secs = binary.BigEndian.Uint16(data[8:10]) + d.Flags = binary.BigEndian.Uint16(data[10:12]) + d.ClientIP = net.IP(data[12:16]) + d.YourClientIP = net.IP(data[16:20]) + d.NextServerIP = net.IP(data[20:24]) + d.RelayAgentIP = net.IP(data[24:28]) + d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen]) + d.ServerName = data[44:108] + d.File = data[108:236] + if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic { + return InvalidMagicCookie + } + + if len(data) <= 240 { + // DHCP Packet could have no option (??) + return nil + } + + options := data[240:] + + stop := len(options) + start := 0 + for start < stop { + o := DHCPOption{} + if err := o.decode(options[start:]); err != nil { + return err + } + if o.Type == DHCPOptEnd { + break + } + d.Options = append(d.Options, o) + // Check if the option is a single byte pad + if o.Type == DHCPOptPad { + start++ + } else { + start += int(o.Length) + 2 + } + } + + d.Contents = data + + return nil +} + +// Len returns the length of a DHCPv4 packet. +func (d *DHCPv4) Len() uint16 { + n := uint16(240) + for _, o := range d.Options { + if o.Type == DHCPOptPad { + n++ + } else { + n += uint16(o.Length) + 2 + } + } + n++ // for opt end + return n +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + plen := int(d.Len()) + + data, err := b.PrependBytes(plen) + if err != nil { + return err + } + + data[0] = byte(d.Operation) + data[1] = byte(d.HardwareType) + if opts.FixLengths { + d.HardwareLen = uint8(len(d.ClientHWAddr)) + } + data[2] = d.HardwareLen + data[3] = d.HardwareOpts + binary.BigEndian.PutUint32(data[4:8], d.Xid) + binary.BigEndian.PutUint16(data[8:10], d.Secs) + binary.BigEndian.PutUint16(data[10:12], d.Flags) + copy(data[12:16], d.ClientIP.To4()) + copy(data[16:20], d.YourClientIP.To4()) + copy(data[20:24], d.NextServerIP.To4()) + copy(data[24:28], d.RelayAgentIP.To4()) + copy(data[28:44], d.ClientHWAddr) + copy(data[44:108], d.ServerName) + copy(data[108:236], d.File) + binary.BigEndian.PutUint32(data[236:240], DHCPMagic) + + if len(d.Options) > 0 { + offset := 240 + for _, o := range d.Options { + if err := o.encode(data[offset:]); err != nil { + return err + } + // A pad option is only a single byte + if o.Type == DHCPOptPad { + offset++ + } else { + offset += 2 + len(o.Data) + } + } + optend := NewDHCPOption(DHCPOptEnd, nil) + if err := optend.encode(data[offset:]); err != nil { + return err + } + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (d *DHCPv4) CanDecode() gopacket.LayerClass { + return LayerTypeDHCPv4 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (d *DHCPv4) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error { + dhcp := &DHCPv4{} + err := dhcp.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(dhcp) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +// DHCPOpt represents a DHCP option or parameter from RFC-2132 +type DHCPOpt byte + +// Constants for the DHCPOpt options. +const ( + DHCPOptPad DHCPOpt = 0 + DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP + DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC) + DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP + DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP + DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP + DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP + DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP + DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP + DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP + DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP + DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP + DHCPOptHostname DHCPOpt = 12 // n, string + DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16 + DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string + DHCPOptDomainName DHCPOpt = 15 // n, string + DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP + DHCPOptRootPath DHCPOpt = 17 // n, string + DHCPOptExtensionsPath DHCPOpt = 18 // n, string + DHCPOptIPForwarding DHCPOpt = 19 // 1, bool + DHCPOptSourceRouting DHCPOpt = 20 // 1, bool + DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP} + DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16 + DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte + DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32 + DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16 + DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16 + DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool + DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP + DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool + DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool + DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool + DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP + DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask + DHCPOptARPTrailers DHCPOpt = 34 // 1, bool + DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32 + DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool + DHCPOptTCPTTL DHCPOpt = 37 // 1, byte + DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32 + DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool + DHCPOptNISDomain DHCPOpt = 40 // n, string + DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP + DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP + DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated. + DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP + DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP + DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte + DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string + DHCPOptXFontServer DHCPOpt = 48 // n, string + DHCPOptXDisplayManager DHCPOpt = 49 // n, string + DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP + DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32 + DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3 + DHCPOptMessageType DHCPOpt = 53 // 1, 1-7 + DHCPOptServerID DHCPOpt = 54 // 4, net.IP + DHCPOptParamsRequest DHCPOpt = 55 // n, []byte + DHCPOptMessage DHCPOpt = 56 // n, 3 + DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16 + DHCPOptT1 DHCPOpt = 58 // 4, uint32 + DHCPOptT2 DHCPOpt = 59 // 4, uint32 + DHCPOptClassID DHCPOpt = 60 // n, []byte + DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte + DHCPOptDomainSearch DHCPOpt = 119 // n, string + DHCPOptSIPServers DHCPOpt = 120 // n, url + DHCPOptClasslessStaticRoute DHCPOpt = 121 // + DHCPOptEnd DHCPOpt = 255 +) + +// String returns a string version of a DHCPOpt. +func (o DHCPOpt) String() string { + switch o { + case DHCPOptPad: + return "(padding)" + case DHCPOptSubnetMask: + return "SubnetMask" + case DHCPOptTimeOffset: + return "TimeOffset" + case DHCPOptRouter: + return "Router" + case DHCPOptTimeServer: + return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP + case DHCPOptNameServer: + return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS + case DHCPOptDNS: + return "DNS" + case DHCPOptLogServer: + return "mitLCS" // MIT LCS server protocol yada yada w. Syslog + case DHCPOptCookieServer: + return "CookieServer" + case DHCPOptLPRServer: + return "LPRServer" + case DHCPOptImpressServer: + return "ImpressServer" + case DHCPOptResLocServer: + return "ResourceLocationServer" + case DHCPOptHostname: + return "Hostname" + case DHCPOptBootfileSize: + return "BootfileSize" + case DHCPOptMeritDumpFile: + return "MeritDumpFile" + case DHCPOptDomainName: + return "DomainName" + case DHCPOptSwapServer: + return "SwapServer" + case DHCPOptRootPath: + return "RootPath" + case DHCPOptExtensionsPath: + return "ExtensionsPath" + case DHCPOptIPForwarding: + return "IPForwarding" + case DHCPOptSourceRouting: + return "SourceRouting" + case DHCPOptPolicyFilter: + return "PolicyFilter" + case DHCPOptDatagramMTU: + return "DatagramMTU" + case DHCPOptDefaultTTL: + return "DefaultTTL" + case DHCPOptPathMTUAgingTimeout: + return "PathMTUAgingTimeout" + case DHCPOptPathPlateuTableOption: + return "PathPlateuTableOption" + case DHCPOptInterfaceMTU: + return "InterfaceMTU" + case DHCPOptAllSubsLocal: + return "AllSubsLocal" + case DHCPOptBroadcastAddr: + return "BroadcastAddress" + case DHCPOptMaskDiscovery: + return "MaskDiscovery" + case DHCPOptMaskSupplier: + return "MaskSupplier" + case DHCPOptRouterDiscovery: + return "RouterDiscovery" + case DHCPOptSolicitAddr: + return "SolicitAddr" + case DHCPOptStaticRoute: + return "StaticRoute" + case DHCPOptARPTrailers: + return "ARPTrailers" + case DHCPOptARPTimeout: + return "ARPTimeout" + case DHCPOptEthernetEncap: + return "EthernetEncap" + case DHCPOptTCPTTL: + return "TCPTTL" + case DHCPOptTCPKeepAliveInt: + return "TCPKeepAliveInt" + case DHCPOptTCPKeepAliveGarbage: + return "TCPKeepAliveGarbage" + case DHCPOptNISDomain: + return "NISDomain" + case DHCPOptNISServers: + return "NISServers" + case DHCPOptNTPServers: + return "NTPServers" + case DHCPOptVendorOption: + return "VendorOption" + case DHCPOptNetBIOSTCPNS: + return "NetBIOSOverTCPNS" + case DHCPOptNetBIOSTCPDDS: + return "NetBiosOverTCPDDS" + case DHCPOptNETBIOSTCPNodeType: + return "NetBIOSOverTCPNodeType" + case DHCPOptNetBIOSTCPScope: + return "NetBIOSOverTCPScope" + case DHCPOptXFontServer: + return "XFontServer" + case DHCPOptXDisplayManager: + return "XDisplayManager" + case DHCPOptEnd: + return "(end)" + case DHCPOptSIPServers: + return "SipServers" + case DHCPOptRequestIP: + return "RequestIP" + case DHCPOptLeaseTime: + return "LeaseTime" + case DHCPOptExtOptions: + return "ExtOpts" + case DHCPOptMessageType: + return "MessageType" + case DHCPOptServerID: + return "ServerID" + case DHCPOptParamsRequest: + return "ParamsRequest" + case DHCPOptMessage: + return "Message" + case DHCPOptMaxMessageSize: + return "MaxDHCPSize" + case DHCPOptT1: + return "Timer1" + case DHCPOptT2: + return "Timer2" + case DHCPOptClassID: + return "ClassID" + case DHCPOptClientID: + return "ClientID" + case DHCPOptDomainSearch: + return "DomainSearch" + case DHCPOptClasslessStaticRoute: + return "ClasslessStaticRoute" + default: + return "Unknown" + } +} + +// DHCPOption rerpresents a DHCP option. +type DHCPOption struct { + Type DHCPOpt + Length uint8 + Data []byte +} + +// String returns a string version of a DHCP Option. +func (o DHCPOption) String() string { + switch o.Type { + + case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath, + DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer, + DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string + return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data)) + + case DHCPOptMessageType: + if len(o.Data) != 1 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0])) + + case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr, + DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP + if len(o.Data) < 4 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data)) + + case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout, + DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32 + if len(o.Data) != 4 { + return fmt.Sprintf("Option(%s:INVALID)", o.Type) + } + return fmt.Sprintf("Option(%s:%d)", o.Type, + uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3])) + + case DHCPOptParamsRequest: + buf := &bytes.Buffer{} + buf.WriteString(fmt.Sprintf("Option(%s:", o.Type)) + for i, v := range o.Data { + buf.WriteString(DHCPOpt(v).String()) + if i+1 != len(o.Data) { + buf.WriteByte(',') + } + } + buf.WriteString(")") + return buf.String() + + default: + return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data) + } +} + +// NewDHCPOption constructs a new DHCPOption with a given type and data. +func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption { + o := DHCPOption{Type: t} + if data != nil { + o.Data = data + o.Length = uint8(len(data)) + } + return o +} + +func (o *DHCPOption) encode(b []byte) error { + switch o.Type { + case DHCPOptPad, DHCPOptEnd: + b[0] = byte(o.Type) + default: + b[0] = byte(o.Type) + b[1] = o.Length + copy(b[2:], o.Data) + } + return nil +} + +func (o *DHCPOption) decode(data []byte) error { + if len(data) < 1 { + // Pad/End have a length of 1 + return DecOptionNotEnoughData + } + o.Type = DHCPOpt(data[0]) + switch o.Type { + case DHCPOptPad, DHCPOptEnd: + o.Data = nil + default: + if len(data) < 2 { + return DecOptionNotEnoughData + } + o.Length = data[1] + if int(o.Length) > len(data[2:]) { + return DecOptionMalformed + } + o.Data = data[2 : 2+int(o.Length)] + } + return nil +} + +// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts. +type DHCPv4Error string + +// DHCPv4Error implements error interface. +func (d DHCPv4Error) Error() string { + return string(d) +} + +const ( + // DecOptionNotEnoughData is returned when there is not enough data during option's decode process + DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode") + // DecOptionMalformed is returned when the option is malformed + DecOptionMalformed = DHCPv4Error("Option is malformed") + // InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header + InvalidMagicCookie = DHCPv4Error("Bad DHCP header") +) diff --git a/vendor/github.com/google/gopacket/layers/dhcpv6.go b/vendor/github.com/google/gopacket/layers/dhcpv6.go new file mode 100644 index 00000000..2698cfb1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dhcpv6.go @@ -0,0 +1,360 @@ +// Copyright 2018 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// DHCPv6MsgType represents a DHCPv6 operation +type DHCPv6MsgType byte + +// Constants that represent DHCP operations +const ( + DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota + DHCPv6MsgTypeSolicit + DHCPv6MsgTypeAdverstise + DHCPv6MsgTypeRequest + DHCPv6MsgTypeConfirm + DHCPv6MsgTypeRenew + DHCPv6MsgTypeRebind + DHCPv6MsgTypeReply + DHCPv6MsgTypeRelease + DHCPv6MsgTypeDecline + DHCPv6MsgTypeReconfigure + DHCPv6MsgTypeInformationRequest + DHCPv6MsgTypeRelayForward + DHCPv6MsgTypeRelayReply +) + +// String returns a string version of a DHCPv6MsgType. +func (o DHCPv6MsgType) String() string { + switch o { + case DHCPv6MsgTypeUnspecified: + return "Unspecified" + case DHCPv6MsgTypeSolicit: + return "Solicit" + case DHCPv6MsgTypeAdverstise: + return "Adverstise" + case DHCPv6MsgTypeRequest: + return "Request" + case DHCPv6MsgTypeConfirm: + return "Confirm" + case DHCPv6MsgTypeRenew: + return "Renew" + case DHCPv6MsgTypeRebind: + return "Rebind" + case DHCPv6MsgTypeReply: + return "Reply" + case DHCPv6MsgTypeRelease: + return "Release" + case DHCPv6MsgTypeDecline: + return "Decline" + case DHCPv6MsgTypeReconfigure: + return "Reconfigure" + case DHCPv6MsgTypeInformationRequest: + return "InformationRequest" + case DHCPv6MsgTypeRelayForward: + return "RelayForward" + case DHCPv6MsgTypeRelayReply: + return "RelayReply" + default: + return "Unknown" + } +} + +// DHCPv6 contains data for a single DHCP packet. +type DHCPv6 struct { + BaseLayer + MsgType DHCPv6MsgType + HopCount uint8 + LinkAddr net.IP + PeerAddr net.IP + TransactionID []byte + Options DHCPv6Options +} + +// LayerType returns gopacket.LayerTypeDHCPv6 +func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("DHCPv6 length %d too short", len(data)) + } + d.BaseLayer = BaseLayer{Contents: data} + d.Options = d.Options[:0] + d.MsgType = DHCPv6MsgType(data[0]) + + offset := 0 + if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { + if len(data) < 34 { + df.SetTruncated() + return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType) + } + d.HopCount = data[1] + d.LinkAddr = net.IP(data[2:18]) + d.PeerAddr = net.IP(data[18:34]) + offset = 34 + } else { + d.TransactionID = data[1:4] + offset = 4 + } + + stop := len(data) + for offset < stop { + o := DHCPv6Option{} + if err := o.decode(data[offset:]); err != nil { + return err + } + d.Options = append(d.Options, o) + offset += int(o.Length) + 4 // 2 from option code, 2 from option length + } + + return nil +} + +// Len returns the length of a DHCPv6 packet. +func (d *DHCPv6) Len() int { + n := 1 + if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { + n += 33 + } else { + n += 3 + } + + for _, o := range d.Options { + n += int(o.Length) + 4 // 2 from option code, 2 from option length + } + + return n +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + plen := int(d.Len()) + + data, err := b.PrependBytes(plen) + if err != nil { + return err + } + + offset := 0 + data[0] = byte(d.MsgType) + if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply { + data[1] = byte(d.HopCount) + copy(data[2:18], d.LinkAddr.To16()) + copy(data[18:34], d.PeerAddr.To16()) + offset = 34 + } else { + copy(data[1:4], d.TransactionID) + offset = 4 + } + + if len(d.Options) > 0 { + for _, o := range d.Options { + if err := o.encode(data[offset:], opts); err != nil { + return err + } + offset += int(o.Length) + 4 // 2 from option code, 2 from option length + } + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (d *DHCPv6) CanDecode() gopacket.LayerClass { + return LayerTypeDHCPv6 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (d *DHCPv6) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error { + dhcp := &DHCPv6{} + err := dhcp.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(dhcp) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +// DHCPv6StatusCode represents a DHCP status code - RFC-3315 +type DHCPv6StatusCode uint16 + +// Constants for the DHCPv6StatusCode. +const ( + DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota + DHCPv6StatusCodeUnspecFail + DHCPv6StatusCodeNoAddrsAvail + DHCPv6StatusCodeNoBinding + DHCPv6StatusCodeNotOnLink + DHCPv6StatusCodeUseMulticast +) + +// String returns a string version of a DHCPv6StatusCode. +func (o DHCPv6StatusCode) String() string { + switch o { + case DHCPv6StatusCodeSuccess: + return "Success" + case DHCPv6StatusCodeUnspecFail: + return "UnspecifiedFailure" + case DHCPv6StatusCodeNoAddrsAvail: + return "NoAddressAvailable" + case DHCPv6StatusCodeNoBinding: + return "NoBinding" + case DHCPv6StatusCodeNotOnLink: + return "NotOnLink" + case DHCPv6StatusCodeUseMulticast: + return "UseMulticast" + default: + return "Unknown" + } +} + +// DHCPv6DUIDType represents a DHCP DUID - RFC-3315 +type DHCPv6DUIDType uint16 + +// Constants for the DHCPv6DUIDType. +const ( + DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1 + DHCPv6DUIDTypeEN + DHCPv6DUIDTypeLL +) + +// String returns a string version of a DHCPv6DUIDType. +func (o DHCPv6DUIDType) String() string { + switch o { + case DHCPv6DUIDTypeLLT: + return "LLT" + case DHCPv6DUIDTypeEN: + return "EN" + case DHCPv6DUIDTypeLL: + return "LL" + default: + return "Unknown" + } +} + +// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19) +type DHCPv6DUID struct { + Type DHCPv6DUIDType + // LLT, LL + HardwareType []byte + // EN + EnterpriseNumber []byte + // LLT + Time []byte + // LLT, LL + LinkLayerAddress net.HardwareAddr + // EN + Identifier []byte +} + +// DecodeFromBytes decodes the given bytes into a DHCPv6DUID +func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error { + if len(data) < 2 { + return fmt.Errorf("Not enough bytes to decode: %d", len(data)) + } + + d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2])) + if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL { + if len(data) < 4 { + return fmt.Errorf("Not enough bytes to decode: %d", len(data)) + } + d.HardwareType = data[2:4] + } + + if d.Type == DHCPv6DUIDTypeLLT { + if len(data) < 8 { + return fmt.Errorf("Not enough bytes to decode: %d", len(data)) + } + d.Time = data[4:8] + d.LinkLayerAddress = net.HardwareAddr(data[8:]) + } else if d.Type == DHCPv6DUIDTypeEN { + if len(data) < 6 { + return fmt.Errorf("Not enough bytes to decode: %d", len(data)) + } + d.EnterpriseNumber = data[2:6] + d.Identifier = data[6:] + } else { // DHCPv6DUIDTypeLL + if len(data) < 4 { + return fmt.Errorf("Not enough bytes to decode: %d", len(data)) + } + d.LinkLayerAddress = net.HardwareAddr(data[4:]) + } + + return nil +} + +// Encode encodes the DHCPv6DUID in a slice of bytes +func (d *DHCPv6DUID) Encode() []byte { + length := d.Len() + data := make([]byte, length) + binary.BigEndian.PutUint16(data[0:2], uint16(d.Type)) + + if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL { + copy(data[2:4], d.HardwareType) + } + + if d.Type == DHCPv6DUIDTypeLLT { + copy(data[4:8], d.Time) + copy(data[8:], d.LinkLayerAddress) + } else if d.Type == DHCPv6DUIDTypeEN { + copy(data[2:6], d.EnterpriseNumber) + copy(data[6:], d.Identifier) + } else { + copy(data[4:], d.LinkLayerAddress) + } + + return data +} + +// Len returns the length of the DHCPv6DUID, respecting the type +func (d *DHCPv6DUID) Len() int { + length := 2 // d.Type + if d.Type == DHCPv6DUIDTypeLLT { + length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress) + } else if d.Type == DHCPv6DUIDTypeEN { + length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier) + } else { // LL + length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress) + } + + return length +} + +func (d *DHCPv6DUID) String() string { + duid := "Type: " + d.Type.String() + ", " + if d.Type == DHCPv6DUIDTypeLLT { + duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress) + } else if d.Type == DHCPv6DUIDTypeEN { + duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier) + } else { // DHCPv6DUIDTypeLL + duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress) + } + return duid +} + +func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) { + duid := &DHCPv6DUID{} + err := duid.DecodeFromBytes(data) + if err != nil { + return nil, err + } + return duid, nil +} diff --git a/vendor/github.com/google/gopacket/layers/dhcpv6_options.go b/vendor/github.com/google/gopacket/layers/dhcpv6_options.go new file mode 100644 index 00000000..5a1f9919 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dhcpv6_options.go @@ -0,0 +1,621 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "github.com/google/gopacket" +) + +// DHCPv6Opt represents a DHCP option or parameter from RFC-3315 +type DHCPv6Opt uint16 + +// Constants for the DHCPv6Opt options. +const ( + DHCPv6OptClientID DHCPv6Opt = 1 + DHCPv6OptServerID DHCPv6Opt = 2 + DHCPv6OptIANA DHCPv6Opt = 3 + DHCPv6OptIATA DHCPv6Opt = 4 + DHCPv6OptIAAddr DHCPv6Opt = 5 + DHCPv6OptOro DHCPv6Opt = 6 + DHCPv6OptPreference DHCPv6Opt = 7 + DHCPv6OptElapsedTime DHCPv6Opt = 8 + DHCPv6OptRelayMessage DHCPv6Opt = 9 + DHCPv6OptAuth DHCPv6Opt = 11 + DHCPv6OptUnicast DHCPv6Opt = 12 + DHCPv6OptStatusCode DHCPv6Opt = 13 + DHCPv6OptRapidCommit DHCPv6Opt = 14 + DHCPv6OptUserClass DHCPv6Opt = 15 + DHCPv6OptVendorClass DHCPv6Opt = 16 + DHCPv6OptVendorOpts DHCPv6Opt = 17 + DHCPv6OptInterfaceID DHCPv6Opt = 18 + DHCPv6OptReconfigureMessage DHCPv6Opt = 19 + DHCPv6OptReconfigureAccept DHCPv6Opt = 20 + + // RFC 3319 Session Initiation Protocol (SIP) + DHCPv6OptSIPServersDomainList DHCPv6Opt = 21 + DHCPv6OptSIPServersAddressList DHCPv6Opt = 22 + + // RFC 3646 DNS Configuration + DHCPv6OptDNSServers DHCPv6Opt = 23 + DHCPv6OptDomainList DHCPv6Opt = 24 + + // RFC 3633 Prefix Delegation + DHCPv6OptIAPD DHCPv6Opt = 25 + DHCPv6OptIAPrefix DHCPv6Opt = 26 + + // RFC 3898 Network Information Service (NIS) + DHCPv6OptNISServers DHCPv6Opt = 27 + DHCPv6OptNISPServers DHCPv6Opt = 28 + DHCPv6OptNISDomainName DHCPv6Opt = 29 + DHCPv6OptNISPDomainName DHCPv6Opt = 30 + + // RFC 4075 Simple Network Time Protocol (SNTP) + DHCPv6OptSNTPServers DHCPv6Opt = 31 + + // RFC 4242 Information Refresh Time Option + DHCPv6OptInformationRefreshTime DHCPv6Opt = 32 + + // RFC 4280 Broadcast and Multicast Control Servers + DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33 + DHCPv6OptBCMCSServerAddressList DHCPv6Opt = 34 + + // RFC 4776 Civic Address ConfigurationOption + DHCPv6OptGeoconfCivic DHCPv6Opt = 36 + + // RFC 4649 Relay Agent Remote-ID + DHCPv6OptRemoteID DHCPv6Opt = 37 + + // RFC 4580 Relay Agent Subscriber-ID + DHCPv6OptSubscriberID DHCPv6Opt = 38 + + // RFC 4704 Client Full Qualified Domain Name (FQDN) + DHCPv6OptClientFQDN DHCPv6Opt = 39 + + // RFC 5192 Protocol for Carrying Authentication for Network Access (PANA) + DHCPv6OptPanaAgent DHCPv6Opt = 40 + + // RFC 4833 Timezone Options + DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41 + DHCPv6OptNewTZDBTimezone DHCPv6Opt = 42 + + // RFC 4994 Relay Agent Echo Request + DHCPv6OptEchoRequestOption DHCPv6Opt = 43 + + // RFC 5007 Leasequery + DHCPv6OptLQQuery DHCPv6Opt = 44 + DHCPv6OptCLTTime DHCPv6Opt = 45 + DHCPv6OptClientData DHCPv6Opt = 46 + DHCPv6OptLQRelayData DHCPv6Opt = 47 + DHCPv6OptLQClientLink DHCPv6Opt = 48 + + // RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6) + DHCPv6OptMIP6HNIDF DHCPv6Opt = 49 + DHCPv6OptMIP6VDINF DHCPv6Opt = 50 + DHCPv6OptMIP6IDINF DHCPv6Opt = 69 + DHCPv6OptMIP6UDINF DHCPv6Opt = 70 + DHCPv6OptMIP6HNP DHCPv6Opt = 71 + DHCPv6OptMIP6HAA DHCPv6Opt = 72 + DHCPv6OptMIP6HAF DHCPv6Opt = 73 + + // RFC 5223 Discovering Location-to-Service Translation (LoST) Servers + DHCPv6OptV6LOST DHCPv6Opt = 51 + + // RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP) + DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52 + + // RFC 5460 Bulk Leasequery + DHCPv6OptRelayID DHCPv6Opt = 53 + + // RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery + DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54 + DHCPv6OptIPv6FQDNMoS DHCPv6Opt = 55 + + // RFC 5908 NTP Server Option + DHCPv6OptNTPServer DHCPv6Opt = 56 + + // RFC 5986 Discovering the Local Location Information Server (LIS) + DHCPv6OptV6AccessDomain DHCPv6Opt = 57 + + // RFC 5986 SIP User Agent + DHCPv6OptSIPUACSList DHCPv6Opt = 58 + + // RFC 5970 Options for Network Boot + DHCPv6OptBootFileURL DHCPv6Opt = 59 + DHCPv6OptBootFileParam DHCPv6Opt = 60 + DHCPv6OptClientArchType DHCPv6Opt = 61 + DHCPv6OptNII DHCPv6Opt = 62 + + // RFC 6225 Coordinate-Based Location Configuration Information + DHCPv6OptGeolocation DHCPv6Opt = 63 + + // RFC 6334 Dual-Stack Lite + DHCPv6OptAFTRName DHCPv6Opt = 64 + + // RFC 6440 EAP Re-authentication Protocol (ERP) + DHCPv6OptERPLocalDomainName DHCPv6Opt = 65 + + // RFC 6422 Relay-Supplied DHCP Options + DHCPv6OptRSOO DHCPv6Opt = 66 + + // RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation + DHCPv6OptPDExclude DHCPv6Opt = 67 + + // RFC 6607 Virtual Subnet Selection + DHCPv6OptVSS DHCPv6Opt = 68 + + // RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes + DHCPv6OptRDNSSSelection DHCPv6Opt = 74 + + // RFC 6784 Kerberos Options for DHCPv6 + DHCPv6OptKRBPrincipalName DHCPv6Opt = 75 + DHCPv6OptKRBRealmName DHCPv6Opt = 76 + DHCPv6OptKRBKDC DHCPv6Opt = 77 + + // RFC 6939 Client Link-Layer Address Option + DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79 + + // RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents + DHCPv6OptLinkAddress DHCPv6Opt = 80 + + // RFC 7037 RADIUS Option for the DHCPv6 Relay Agent + DHCPv6OptRADIUS DHCPv6Opt = 81 + + // RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT + DHCPv6OptSolMaxRt DHCPv6Opt = 82 + DHCPv6OptInfMaxRt DHCPv6Opt = 83 + + // RFC 7078 Distributing Address Selection Policy + DHCPv6OptAddrSel DHCPv6Opt = 84 + DHCPv6OptAddrSelTable DHCPv6Opt = 85 + + // RFC 7291 DHCP Options for the Port Control Protocol (PCP) + DHCPv6OptV6PCPServer DHCPv6Opt = 86 + + // RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport + DHCPv6OptDHCPv4Message DHCPv6Opt = 87 + DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88 + + // RFC 7598 Configuration of Softwire Address and Port-Mapped Clients + DHCPv6OptS46Rule DHCPv6Opt = 89 + DHCPv6OptS46BR DHCPv6Opt = 90 + DHCPv6OptS46DMR DHCPv6Opt = 91 + DHCPv6OptS46V4V4Bind DHCPv6Opt = 92 + DHCPv6OptS46PortParameters DHCPv6Opt = 93 + DHCPv6OptS46ContMAPE DHCPv6Opt = 94 + DHCPv6OptS46ContMAPT DHCPv6Opt = 95 + DHCPv6OptS46ContLW DHCPv6Opt = 96 + + // RFC 7600 IPv4 Residual Deployment via IPv6 + DHCPv6Opt4RD DHCPv6Opt = 97 + DHCPv6Opt4RDMapRule DHCPv6Opt = 98 + DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99 + + // RFC 7653 Active Leasequery + DHCPv6OptLQBaseTime DHCPv6Opt = 100 + DHCPv6OptLQStartTime DHCPv6Opt = 101 + DHCPv6OptLQEndTime DHCPv6Opt = 102 + + // RFC 7710 Captive-Portal Identification + DHCPv6OptCaptivePortal DHCPv6Opt = 103 + + // RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration + DHCPv6OptMPLParameters DHCPv6Opt = 104 + + // RFC 7839 Access-Network-Identifier (ANI) + DHCPv6OptANIATT DHCPv6Opt = 105 + DHCPv6OptANINetworkName DHCPv6Opt = 106 + DHCPv6OptANIAPName DHCPv6Opt = 107 + DHCPv6OptANIAPBSSID DHCPv6Opt = 108 + DHCPv6OptANIOperatorID DHCPv6Opt = 109 + DHCPv6OptANIOperatorRealm DHCPv6Opt = 110 + + // RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE) + DHCPv6OptS46Priority DHCPv6Opt = 111 + + // draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD) + DHCPv6OptMUDURLV6 DHCPv6Opt = 112 + + // RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes + DHCPv6OptV6Prefix64 DHCPv6Opt = 113 + + // RFC 8156 DHCPv6 Failover Protocol + DHCPv6OptFBindingStatus DHCPv6Opt = 114 + DHCPv6OptFConnectFlags DHCPv6Opt = 115 + DHCPv6OptFDNSRemovalInfo DHCPv6Opt = 116 + DHCPv6OptFDNSHostName DHCPv6Opt = 117 + DHCPv6OptFDNSZoneName DHCPv6Opt = 118 + DHCPv6OptFDNSFlags DHCPv6Opt = 119 + DHCPv6OptFExpirationTime DHCPv6Opt = 120 + DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121 + DHCPv6OptFMCLT DHCPv6Opt = 122 + DHCPv6OptFPartnerLifetime DHCPv6Opt = 123 + DHCPv6OptFPartnerLifetimeSent DHCPv6Opt = 124 + DHCPv6OptFPartnerDownTime DHCPv6Opt = 125 + DHCPv6OptFPartnerRawCltTime DHCPv6Opt = 126 + DHCPv6OptFProtocolVersion DHCPv6Opt = 127 + DHCPv6OptFKeepaliveTime DHCPv6Opt = 128 + DHCPv6OptFReconfigureData DHCPv6Opt = 129 + DHCPv6OptFRelationshipName DHCPv6Opt = 130 + DHCPv6OptFServerFlags DHCPv6Opt = 131 + DHCPv6OptFServerState DHCPv6Opt = 132 + DHCPv6OptFStartTimeOfState DHCPv6Opt = 133 + DHCPv6OptFStateExpirationTime DHCPv6Opt = 134 + + // RFC 8357 Generalized UDP Source Port for DHCP Relay + DHCPv6OptRelayPort DHCPv6Opt = 135 + + // draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices + DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136 + + // RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery + DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143 +) + +// String returns a string version of a DHCPv6Opt. +func (o DHCPv6Opt) String() string { + switch o { + case DHCPv6OptClientID: + return "ClientID" + case DHCPv6OptServerID: + return "ServerID" + case DHCPv6OptIANA: + return "IA_NA" + case DHCPv6OptIATA: + return "IA_TA" + case DHCPv6OptIAAddr: + return "IAAddr" + case DHCPv6OptOro: + return "Oro" + case DHCPv6OptPreference: + return "Preference" + case DHCPv6OptElapsedTime: + return "ElapsedTime" + case DHCPv6OptRelayMessage: + return "RelayMessage" + case DHCPv6OptAuth: + return "Auth" + case DHCPv6OptUnicast: + return "Unicast" + case DHCPv6OptStatusCode: + return "StatusCode" + case DHCPv6OptRapidCommit: + return "RapidCommit" + case DHCPv6OptUserClass: + return "UserClass" + case DHCPv6OptVendorClass: + return "VendorClass" + case DHCPv6OptVendorOpts: + return "VendorOpts" + case DHCPv6OptInterfaceID: + return "InterfaceID" + case DHCPv6OptReconfigureMessage: + return "ReconfigureMessage" + case DHCPv6OptReconfigureAccept: + return "ReconfigureAccept" + case DHCPv6OptSIPServersDomainList: + return "SIPServersDomainList" + case DHCPv6OptSIPServersAddressList: + return "SIPServersAddressList" + case DHCPv6OptDNSServers: + return "DNSRecursiveNameServer" + case DHCPv6OptDomainList: + return "DomainSearchList" + case DHCPv6OptIAPD: + return "IdentityAssociationPrefixDelegation" + case DHCPv6OptIAPrefix: + return "IAPDPrefix" + case DHCPv6OptNISServers: + return "NISServers" + case DHCPv6OptNISPServers: + return "NISv2Servers" + case DHCPv6OptNISDomainName: + return "NISDomainName" + case DHCPv6OptNISPDomainName: + return "NISv2DomainName" + case DHCPv6OptSNTPServers: + return "SNTPServers" + case DHCPv6OptInformationRefreshTime: + return "InformationRefreshTime" + case DHCPv6OptBCMCSServerDomainNameList: + return "BCMCSControlServersDomainNameList" + case DHCPv6OptBCMCSServerAddressList: + return "BCMCSControlServersAddressList" + case DHCPv6OptGeoconfCivic: + return "CivicAddress" + case DHCPv6OptRemoteID: + return "RelayAgentRemoteID" + case DHCPv6OptSubscriberID: + return "RelayAgentSubscriberID" + case DHCPv6OptClientFQDN: + return "ClientFQDN" + case DHCPv6OptPanaAgent: + return "PANAAuthenticationAgent" + case DHCPv6OptNewPOSIXTimezone: + return "NewPOSIXTimezone" + case DHCPv6OptNewTZDBTimezone: + return "NewTZDBTimezone" + case DHCPv6OptEchoRequestOption: + return "EchoRequest" + case DHCPv6OptLQQuery: + return "LeasequeryQuery" + case DHCPv6OptClientData: + return "LeasequeryClientData" + case DHCPv6OptCLTTime: + return "LeasequeryClientLastTransactionTime" + case DHCPv6OptLQRelayData: + return "LeasequeryRelayData" + case DHCPv6OptLQClientLink: + return "LeasequeryClientLink" + case DHCPv6OptMIP6HNIDF: + return "MIPv6HomeNetworkIDFQDN" + case DHCPv6OptMIP6VDINF: + return "MIPv6VisitedHomeNetworkInformation" + case DHCPv6OptMIP6IDINF: + return "MIPv6IdentifiedHomeNetworkInformation" + case DHCPv6OptMIP6UDINF: + return "MIPv6UnrestrictedHomeNetworkInformation" + case DHCPv6OptMIP6HNP: + return "MIPv6HomeNetworkPrefix" + case DHCPv6OptMIP6HAA: + return "MIPv6HomeAgentAddress" + case DHCPv6OptMIP6HAF: + return "MIPv6HomeAgentFQDN" + case DHCPv6OptV6LOST: + return "LoST Server" + case DHCPv6OptCAPWAPACV6: + return "CAPWAPAccessControllerV6" + case DHCPv6OptRelayID: + return "LeasequeryRelayID" + case DHCPv6OptIPv6AddressMoS: + return "MoSIPv6Address" + case DHCPv6OptIPv6FQDNMoS: + return "MoSDomainNameList" + case DHCPv6OptNTPServer: + return "NTPServer" + case DHCPv6OptV6AccessDomain: + return "AccessNetworkDomainName" + case DHCPv6OptSIPUACSList: + return "SIPUserAgentConfigurationServiceDomains" + case DHCPv6OptBootFileURL: + return "BootFileURL" + case DHCPv6OptBootFileParam: + return "BootFileParameters" + case DHCPv6OptClientArchType: + return "ClientSystemArchitectureType" + case DHCPv6OptNII: + return "ClientNetworkInterfaceIdentifier" + case DHCPv6OptGeolocation: + return "Geolocation" + case DHCPv6OptAFTRName: + return "AFTRName" + case DHCPv6OptERPLocalDomainName: + return "AFTRName" + case DHCPv6OptRSOO: + return "RSOOption" + case DHCPv6OptPDExclude: + return "PrefixExclude" + case DHCPv6OptVSS: + return "VirtualSubnetSelection" + case DHCPv6OptRDNSSSelection: + return "RDNSSSelection" + case DHCPv6OptKRBPrincipalName: + return "KerberosPrincipalName" + case DHCPv6OptKRBRealmName: + return "KerberosRealmName" + case DHCPv6OptKRBKDC: + return "KerberosKDC" + case DHCPv6OptClientLinkLayerAddress: + return "ClientLinkLayerAddress" + case DHCPv6OptLinkAddress: + return "LinkAddress" + case DHCPv6OptRADIUS: + return "RADIUS" + case DHCPv6OptSolMaxRt: + return "SolMaxRt" + case DHCPv6OptInfMaxRt: + return "InfMaxRt" + case DHCPv6OptAddrSel: + return "AddressSelection" + case DHCPv6OptAddrSelTable: + return "AddressSelectionTable" + case DHCPv6OptV6PCPServer: + return "PCPServer" + case DHCPv6OptDHCPv4Message: + return "DHCPv4Message" + case DHCPv6OptDHCPv4OverDHCPv6Server: + return "DHCP4o6ServerAddress" + case DHCPv6OptS46Rule: + return "S46Rule" + case DHCPv6OptS46BR: + return "S46BR" + case DHCPv6OptS46DMR: + return "S46DMR" + case DHCPv6OptS46V4V4Bind: + return "S46IPv4IPv6AddressBinding" + case DHCPv6OptS46PortParameters: + return "S46PortParameters" + case DHCPv6OptS46ContMAPE: + return "S46MAPEContainer" + case DHCPv6OptS46ContMAPT: + return "S46MAPTContainer" + case DHCPv6OptS46ContLW: + return "S46Lightweight4Over6Container" + case DHCPv6Opt4RD: + return "4RD" + case DHCPv6Opt4RDMapRule: + return "4RDMapRule" + case DHCPv6Opt4RDNonMapRule: + return "4RDNonMapRule" + case DHCPv6OptLQBaseTime: + return "LQBaseTime" + case DHCPv6OptLQStartTime: + return "LQStartTime" + case DHCPv6OptLQEndTime: + return "LQEndTime" + case DHCPv6OptCaptivePortal: + return "CaptivePortal" + case DHCPv6OptMPLParameters: + return "MPLParameterConfiguration" + case DHCPv6OptANIATT: + return "ANIAccessTechnologyType" + case DHCPv6OptANINetworkName: + return "ANINetworkName" + case DHCPv6OptANIAPName: + return "ANIAccessPointName" + case DHCPv6OptANIAPBSSID: + return "ANIAccessPointBSSID" + case DHCPv6OptANIOperatorID: + return "ANIOperatorIdentifier" + case DHCPv6OptANIOperatorRealm: + return "ANIOperatorRealm" + case DHCPv6OptS46Priority: + return "S64Priority" + case DHCPv6OptMUDURLV6: + return "ManufacturerUsageDescriptionURL" + case DHCPv6OptV6Prefix64: + return "V6Prefix64" + case DHCPv6OptFBindingStatus: + return "FailoverBindingStatus" + case DHCPv6OptFConnectFlags: + return "FailoverConnectFlags" + case DHCPv6OptFDNSRemovalInfo: + return "FailoverDNSRemovalInfo" + case DHCPv6OptFDNSHostName: + return "FailoverDNSHostName" + case DHCPv6OptFDNSZoneName: + return "FailoverDNSZoneName" + case DHCPv6OptFDNSFlags: + return "FailoverDNSFlags" + case DHCPv6OptFExpirationTime: + return "FailoverExpirationTime" + case DHCPv6OptFMaxUnacknowledgedBNDUPD: + return "FailoverMaxUnacknowledgedBNDUPDMessages" + case DHCPv6OptFMCLT: + return "FailoverMaximumClientLeadTime" + case DHCPv6OptFPartnerLifetime: + return "FailoverPartnerLifetime" + case DHCPv6OptFPartnerLifetimeSent: + return "FailoverPartnerLifetimeSent" + case DHCPv6OptFPartnerDownTime: + return "FailoverPartnerDownTime" + case DHCPv6OptFPartnerRawCltTime: + return "FailoverPartnerRawClientLeadTime" + case DHCPv6OptFProtocolVersion: + return "FailoverProtocolVersion" + case DHCPv6OptFKeepaliveTime: + return "FailoverKeepaliveTime" + case DHCPv6OptFReconfigureData: + return "FailoverReconfigureData" + case DHCPv6OptFRelationshipName: + return "FailoverRelationshipName" + case DHCPv6OptFServerFlags: + return "FailoverServerFlags" + case DHCPv6OptFServerState: + return "FailoverServerState" + case DHCPv6OptFStartTimeOfState: + return "FailoverStartTimeOfState" + case DHCPv6OptFStateExpirationTime: + return "FailoverStateExpirationTime" + case DHCPv6OptRelayPort: + return "RelayPort" + case DHCPv6OptV6ZeroTouchRedirect: + return "ZeroTouch" + case DHCPv6OptIPV6AddressANDSF: + return "ANDSFIPv6Address" + default: + return fmt.Sprintf("Unknown(%d)", uint16(o)) + } +} + +// DHCPv6Options is used to get nicely printed option lists which would normally +// be cut off after 5 options. +type DHCPv6Options []DHCPv6Option + +// String returns a string version of the options list. +func (o DHCPv6Options) String() string { + buf := &bytes.Buffer{} + buf.WriteByte('[') + for i, opt := range o { + buf.WriteString(opt.String()) + if i+1 != len(o) { + buf.WriteString(", ") + } + } + buf.WriteByte(']') + return buf.String() +} + +// DHCPv6Option rerpresents a DHCP option. +type DHCPv6Option struct { + Code DHCPv6Opt + Length uint16 + Data []byte +} + +// String returns a string version of a DHCP Option. +func (o DHCPv6Option) String() string { + switch o.Code { + case DHCPv6OptClientID, DHCPv6OptServerID: + duid, err := decodeDHCPv6DUID(o.Data) + if err != nil { + return fmt.Sprintf("Option(%s:INVALID)", o.Code) + } + return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String()) + case DHCPv6OptOro: + options := "" + for i := 0; i < int(o.Length); i += 2 { + if options != "" { + options += "," + } + option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2])) + options += option.String() + } + return fmt.Sprintf("Option(%s:[%s])", o.Code, options) + default: + return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data) + } +} + +// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data. +func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option { + o := DHCPv6Option{Code: code} + if data != nil { + o.Data = data + o.Length = uint16(len(data)) + } + + return o +} + +func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error { + binary.BigEndian.PutUint16(b[0:2], uint16(o.Code)) + if opts.FixLengths { + binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data))) + } else { + binary.BigEndian.PutUint16(b[2:4], o.Length) + } + copy(b[4:], o.Data) + + return nil +} + +func (o *DHCPv6Option) decode(data []byte) error { + if len(data) < 4 { + return errors.New("not enough data to decode") + } + o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2])) + o.Length = binary.BigEndian.Uint16(data[2:4]) + if len(data) < 4+int(o.Length) { + return fmt.Errorf("dhcpv6 option size < length %d", 4+o.Length) + } + o.Data = data[4 : 4+o.Length] + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/dns.go b/vendor/github.com/google/gopacket/layers/dns.go new file mode 100644 index 00000000..de55294b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dns.go @@ -0,0 +1,1098 @@ +// Copyright 2014, 2018 GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + "strings" + + "github.com/google/gopacket" +) + +// DNSClass defines the class associated with a request/response. Different DNS +// classes can be thought of as an array of parallel namespace trees. +type DNSClass uint16 + +// DNSClass known values. +const ( + DNSClassIN DNSClass = 1 // Internet + DNSClassCS DNSClass = 2 // the CSNET class (Obsolete) + DNSClassCH DNSClass = 3 // the CHAOS class + DNSClassHS DNSClass = 4 // Hesiod [Dyer 87] + DNSClassAny DNSClass = 255 // AnyClass +) + +func (dc DNSClass) String() string { + switch dc { + default: + return "Unknown" + case DNSClassIN: + return "IN" + case DNSClassCS: + return "CS" + case DNSClassCH: + return "CH" + case DNSClassHS: + return "HS" + case DNSClassAny: + return "Any" + } +} + +// DNSType defines the type of data being requested/returned in a +// question/answer. +type DNSType uint16 + +// DNSType known values. +const ( + DNSTypeA DNSType = 1 // a host address + DNSTypeNS DNSType = 2 // an authoritative name server + DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX) + DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX) + DNSTypeCNAME DNSType = 5 // the canonical name for an alias + DNSTypeSOA DNSType = 6 // marks the start of a zone of authority + DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL) + DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL) + DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL) + DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL) + DNSTypeWKS DNSType = 11 // a well known service description + DNSTypePTR DNSType = 12 // a domain name pointer + DNSTypeHINFO DNSType = 13 // host information + DNSTypeMINFO DNSType = 14 // mailbox or mail list information + DNSTypeMX DNSType = 15 // mail exchange + DNSTypeTXT DNSType = 16 // text strings + DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596] + DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195] + DNSTypeOPT DNSType = 41 // OPT Pseudo-RR [RFC6891] + DNSTypeURI DNSType = 256 // URI RR [RFC7553] +) + +func (dt DNSType) String() string { + switch dt { + default: + return "Unknown" + case DNSTypeA: + return "A" + case DNSTypeNS: + return "NS" + case DNSTypeMD: + return "MD" + case DNSTypeMF: + return "MF" + case DNSTypeCNAME: + return "CNAME" + case DNSTypeSOA: + return "SOA" + case DNSTypeMB: + return "MB" + case DNSTypeMG: + return "MG" + case DNSTypeMR: + return "MR" + case DNSTypeNULL: + return "NULL" + case DNSTypeWKS: + return "WKS" + case DNSTypePTR: + return "PTR" + case DNSTypeHINFO: + return "HINFO" + case DNSTypeMINFO: + return "MINFO" + case DNSTypeMX: + return "MX" + case DNSTypeTXT: + return "TXT" + case DNSTypeAAAA: + return "AAAA" + case DNSTypeSRV: + return "SRV" + case DNSTypeOPT: + return "OPT" + case DNSTypeURI: + return "URI" + } +} + +// DNSResponseCode provides response codes for question answers. +type DNSResponseCode uint8 + +// DNSResponseCode known values. +const ( + DNSResponseCodeNoErr DNSResponseCode = 0 // No error + DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035] + DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035] + DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035] + DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035] + DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035] + DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136] + DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136] + DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136] + DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136] + DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136] + DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671] + DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845] + DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845] + DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845] + DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930] + DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930] + DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930] + DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635] + DNSResponseCodeBadCookie DNSResponseCode = 23 // Bad/missing Server Cookie [RFC7873] +) + +func (drc DNSResponseCode) String() string { + switch drc { + default: + return "Unknown" + case DNSResponseCodeNoErr: + return "No Error" + case DNSResponseCodeFormErr: + return "Format Error" + case DNSResponseCodeServFail: + return "Server Failure " + case DNSResponseCodeNXDomain: + return "Non-Existent Domain" + case DNSResponseCodeNotImp: + return "Not Implemented" + case DNSResponseCodeRefused: + return "Query Refused" + case DNSResponseCodeYXDomain: + return "Name Exists when it should not" + case DNSResponseCodeYXRRSet: + return "RR Set Exists when it should not" + case DNSResponseCodeNXRRSet: + return "RR Set that should exist does not" + case DNSResponseCodeNotAuth: + return "Server Not Authoritative for zone" + case DNSResponseCodeNotZone: + return "Name not contained in zone" + case DNSResponseCodeBadVers: + return "Bad OPT Version" + case DNSResponseCodeBadKey: + return "Key not recognized" + case DNSResponseCodeBadTime: + return "Signature out of time window" + case DNSResponseCodeBadMode: + return "Bad TKEY Mode" + case DNSResponseCodeBadName: + return "Duplicate key name" + case DNSResponseCodeBadAlg: + return "Algorithm not supported" + case DNSResponseCodeBadTruc: + return "Bad Truncation" + case DNSResponseCodeBadCookie: + return "Bad Cookie" + } +} + +// DNSOpCode defines a set of different operation types. +type DNSOpCode uint8 + +// DNSOpCode known values. +const ( + DNSOpCodeQuery DNSOpCode = 0 // Query [RFC1035] + DNSOpCodeIQuery DNSOpCode = 1 // Inverse Query Obsolete [RFC3425] + DNSOpCodeStatus DNSOpCode = 2 // Status [RFC1035] + DNSOpCodeNotify DNSOpCode = 4 // Notify [RFC1996] + DNSOpCodeUpdate DNSOpCode = 5 // Update [RFC2136] +) + +func (doc DNSOpCode) String() string { + switch doc { + default: + return "Unknown" + case DNSOpCodeQuery: + return "Query" + case DNSOpCodeIQuery: + return "Inverse Query" + case DNSOpCodeStatus: + return "Status" + case DNSOpCodeNotify: + return "Notify" + case DNSOpCodeUpdate: + return "Update" + } +} + +// DNS is specified in RFC 1034 / RFC 1035 +// +---------------------+ +// | Header | +// +---------------------+ +// | Question | the question for the name server +// +---------------------+ +// | Answer | RRs answering the question +// +---------------------+ +// | Authority | RRs pointing toward an authority +// +---------------------+ +// | Additional | RRs holding additional information +// +---------------------+ +// +// DNS Header +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ID | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | QDCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ANCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | NSCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | ARCOUNT | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// DNS contains data from a single Domain Name Service packet. +type DNS struct { + BaseLayer + + // Header fields + ID uint16 + QR bool + OpCode DNSOpCode + + AA bool // Authoritative answer + TC bool // Truncated + RD bool // Recursion desired + RA bool // Recursion available + Z uint8 // Reserved for future use + + ResponseCode DNSResponseCode + QDCount uint16 // Number of questions to expect + ANCount uint16 // Number of answers to expect + NSCount uint16 // Number of authorities to expect + ARCount uint16 // Number of additional records to expect + + // Entries + Questions []DNSQuestion + Answers []DNSResourceRecord + Authorities []DNSResourceRecord + Additionals []DNSResourceRecord + + // buffer for doing name decoding. We use a single reusable buffer to avoid + // name decoding on a single object via multiple DecodeFromBytes calls + // requiring constant allocation of small byte slices. + buffer []byte +} + +// LayerType returns gopacket.LayerTypeDNS. +func (d *DNS) LayerType() gopacket.LayerType { return LayerTypeDNS } + +// decodeDNS decodes the byte slice into a DNS type. It also +// setups the application Layer in PacketBuilder. +func decodeDNS(data []byte, p gopacket.PacketBuilder) error { + d := &DNS{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(d) + p.SetApplicationLayer(d) + return nil +} + +// DecodeFromBytes decodes the slice into the DNS struct. +func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + d.buffer = d.buffer[:0] + + if len(data) < 12 { + df.SetTruncated() + return errDNSPacketTooShort + } + + // since there are no further layers, the baselayer's content is + // pointing to this layer + d.BaseLayer = BaseLayer{Contents: data[:len(data)]} + d.ID = binary.BigEndian.Uint16(data[:2]) + d.QR = data[2]&0x80 != 0 + d.OpCode = DNSOpCode(data[2]>>3) & 0x0F + d.AA = data[2]&0x04 != 0 + d.TC = data[2]&0x02 != 0 + d.RD = data[2]&0x01 != 0 + d.RA = data[3]&0x80 != 0 + d.Z = uint8(data[3]>>4) & 0x7 + d.ResponseCode = DNSResponseCode(data[3] & 0xF) + d.QDCount = binary.BigEndian.Uint16(data[4:6]) + d.ANCount = binary.BigEndian.Uint16(data[6:8]) + d.NSCount = binary.BigEndian.Uint16(data[8:10]) + d.ARCount = binary.BigEndian.Uint16(data[10:12]) + + d.Questions = d.Questions[:0] + d.Answers = d.Answers[:0] + d.Authorities = d.Authorities[:0] + d.Additionals = d.Additionals[:0] + + offset := 12 + var err error + for i := 0; i < int(d.QDCount); i++ { + var q DNSQuestion + if offset, err = q.decode(data, offset, df, &d.buffer); err != nil { + return err + } + d.Questions = append(d.Questions, q) + } + + // For some horrible reason, if we do the obvious thing in this loop: + // var r DNSResourceRecord + // if blah := r.decode(blah); err != nil { + // return err + // } + // d.Foo = append(d.Foo, r) + // the Go compiler thinks that 'r' escapes to the heap, causing a malloc for + // every Answer, Authority, and Additional. To get around this, we do + // something really silly: we append an empty resource record to our slice, + // then use the last value in the slice to call decode. Since the value is + // already in the slice, there's no WAY it can escape... on the other hand our + // code is MUCH uglier :( + for i := 0; i < int(d.ANCount); i++ { + d.Answers = append(d.Answers, DNSResourceRecord{}) + if offset, err = d.Answers[i].decode(data, offset, df, &d.buffer); err != nil { + d.Answers = d.Answers[:i] // strip off erroneous value + return err + } + } + for i := 0; i < int(d.NSCount); i++ { + d.Authorities = append(d.Authorities, DNSResourceRecord{}) + if offset, err = d.Authorities[i].decode(data, offset, df, &d.buffer); err != nil { + d.Authorities = d.Authorities[:i] // strip off erroneous value + return err + } + } + for i := 0; i < int(d.ARCount); i++ { + d.Additionals = append(d.Additionals, DNSResourceRecord{}) + if offset, err = d.Additionals[i].decode(data, offset, df, &d.buffer); err != nil { + d.Additionals = d.Additionals[:i] // strip off erroneous value + return err + } + // extract extended RCODE from OPT RRs, RFC 6891 section 6.1.3 + if d.Additionals[i].Type == DNSTypeOPT { + d.ResponseCode = DNSResponseCode(uint8(d.ResponseCode) | uint8(d.Additionals[i].TTL>>20&0xF0)) + } + } + + if uint16(len(d.Questions)) != d.QDCount { + return errDecodeQueryBadQDCount + } else if uint16(len(d.Answers)) != d.ANCount { + return errDecodeQueryBadANCount + } else if uint16(len(d.Authorities)) != d.NSCount { + return errDecodeQueryBadNSCount + } else if uint16(len(d.Additionals)) != d.ARCount { + return errDecodeQueryBadARCount + } + return nil +} + +// CanDecode implements gopacket.DecodingLayer. +func (d *DNS) CanDecode() gopacket.LayerClass { + return LayerTypeDNS +} + +// NextLayerType implements gopacket.DecodingLayer. +func (d *DNS) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// Payload returns nil. +func (d *DNS) Payload() []byte { + return nil +} + +func b2i(b bool) int { + if b { + return 1 + } + return 0 +} + +func recSize(rr *DNSResourceRecord) int { + switch rr.Type { + case DNSTypeA: + return 4 + case DNSTypeAAAA: + return 16 + case DNSTypeNS: + return len(rr.NS) + 2 + case DNSTypeCNAME: + return len(rr.CNAME) + 2 + case DNSTypePTR: + return len(rr.PTR) + 2 + case DNSTypeSOA: + return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20 + case DNSTypeMX: + return 2 + len(rr.MX.Name) + 2 + case DNSTypeTXT: + l := len(rr.TXTs) + for _, txt := range rr.TXTs { + l += len(txt) + } + return l + case DNSTypeSRV: + return 6 + len(rr.SRV.Name) + 2 + case DNSTypeURI: + return 4 + len(rr.URI.Target) + case DNSTypeOPT: + l := len(rr.OPT) * 4 + for _, opt := range rr.OPT { + l += len(opt.Data) + } + return l + } + + return 0 +} + +func computeSize(recs []DNSResourceRecord) int { + sz := 0 + for _, rr := range recs { + v := len(rr.Name) + + if v == 0 { + sz += v + 11 + } else { + sz += v + 12 + } + + sz += recSize(&rr) + } + return sz +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + dsz := 0 + for _, q := range d.Questions { + dsz += len(q.Name) + 6 + } + dsz += computeSize(d.Answers) + dsz += computeSize(d.Authorities) + dsz += computeSize(d.Additionals) + + bytes, err := b.PrependBytes(12 + dsz) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, d.ID) + bytes[2] = byte((b2i(d.QR) << 7) | (int(d.OpCode) << 3) | (b2i(d.AA) << 2) | (b2i(d.TC) << 1) | b2i(d.RD)) + bytes[3] = byte((b2i(d.RA) << 7) | (int(d.Z) << 4) | int(d.ResponseCode)) + + if opts.FixLengths { + d.QDCount = uint16(len(d.Questions)) + d.ANCount = uint16(len(d.Answers)) + d.NSCount = uint16(len(d.Authorities)) + d.ARCount = uint16(len(d.Additionals)) + } + binary.BigEndian.PutUint16(bytes[4:], d.QDCount) + binary.BigEndian.PutUint16(bytes[6:], d.ANCount) + binary.BigEndian.PutUint16(bytes[8:], d.NSCount) + binary.BigEndian.PutUint16(bytes[10:], d.ARCount) + + off := 12 + for _, qd := range d.Questions { + n := qd.encode(bytes, off) + off += n + } + + for i := range d.Answers { + // done this way so we can modify DNSResourceRecord to fix + // lengths if requested + qa := &d.Answers[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + + for i := range d.Authorities { + qa := &d.Authorities[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + for i := range d.Additionals { + qa := &d.Additionals[i] + n, err := qa.encode(bytes, off, opts) + if err != nil { + return err + } + off += n + } + + return nil +} + +const maxRecursionLevel = 255 + +func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) { + if level > maxRecursionLevel { + return nil, 0, errMaxRecursion + } else if offset >= len(data) { + return nil, 0, errDNSNameOffsetTooHigh + } else if offset < 0 { + return nil, 0, errDNSNameOffsetNegative + } + start := len(*buffer) + index := offset + if data[index] == 0x00 { + return nil, index + 1, nil + } +loop: + for data[index] != 0x00 { + switch data[index] & 0xc0 { + default: + /* RFC 1035 + A domain name represented as a sequence of labels, where + each label consists of a length octet followed by that + number of octets. The domain name terminates with the + zero length octet for the null label of the root. Note + that this field may be an odd number of octets; no + padding is used. + */ + index2 := index + int(data[index]) + 1 + if index2-offset > 255 { + return nil, 0, errDNSNameTooLong + } else if index2 < index+1 || index2 > len(data) { + return nil, 0, errDNSNameInvalidIndex + } + *buffer = append(*buffer, '.') + *buffer = append(*buffer, data[index+1:index2]...) + index = index2 + + case 0xc0: + /* RFC 1035 + The pointer takes the form of a two octet sequence. + + The first two bits are ones. This allows a pointer to + be distinguished from a label, since the label must + begin with two zero bits because labels are restricted + to 63 octets or less. (The 10 and 01 combinations are + reserved for future use.) The OFFSET field specifies + an offset from the start of the message (i.e., the + first octet of the ID field in the domain header). A + zero offset specifies the first byte of the ID field, + etc. + + The compression scheme allows a domain name in a message to be + represented as either: + - a sequence of labels ending in a zero octet + - a pointer + - a sequence of labels ending with a pointer + */ + if index+2 > len(data) { + return nil, 0, errDNSPointerOffsetTooHigh + } + offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff) + if offsetp > len(data) { + return nil, 0, errDNSPointerOffsetTooHigh + } + // This looks a little tricky, but actually isn't. Because of how + // decodeName is written, calling it appends the decoded name to the + // current buffer. We already have the start of the buffer, then, so + // once this call is done buffer[start:] will contain our full name. + _, _, err := decodeName(data, offsetp, buffer, level+1) + if err != nil { + return nil, 0, err + } + index++ // pointer is two bytes, so add an extra byte here. + break loop + /* EDNS, or other DNS option ? */ + case 0x40: // RFC 2673 + return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)", + data[index], index) + + case 0x80: + return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)", + data[index], index) + } + if index >= len(data) { + return nil, 0, errDNSIndexOutOfRange + } + } + if len(*buffer) <= start { + return (*buffer)[start:], index + 1, nil + } + return (*buffer)[start+1:], index + 1, nil +} + +// DNSQuestion wraps a single request (question) within a DNS query. +type DNSQuestion struct { + Name []byte + Type DNSType + Class DNSClass +} + +func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return 0, err + } + + q.Name = name + q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) + q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) + + return endq + 4, nil +} + +func (q *DNSQuestion) encode(data []byte, offset int) int { + noff := encodeName(q.Name, data, offset) + nSz := noff - offset + binary.BigEndian.PutUint16(data[noff:], uint16(q.Type)) + binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class)) + return nSz + 4 +} + +// DNSResourceRecord +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | | +// / / +// / NAME / +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TYPE | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | CLASS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TTL | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | RDLENGTH | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| +// / RDATA / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// DNSResourceRecord wraps the data from a single DNS resource within a +// response. +type DNSResourceRecord struct { + // Header + Name []byte + Type DNSType + Class DNSClass + TTL uint32 + + // RDATA Raw Values + DataLength uint16 + Data []byte + + // RDATA Decoded Values + IP net.IP + NS, CNAME, PTR []byte + TXTs [][]byte + SOA DNSSOA + SRV DNSSRV + MX DNSMX + OPT []DNSOPT // See RFC 6891, section 6.1.2 + URI DNSURI + + // Undecoded TXT for backward compatibility + TXT []byte +} + +// decode decodes the resource record, returning the total length of the record. +func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) { + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return 0, err + } + + rr.Name = name + rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2])) + rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4])) + rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8]) + rr.DataLength = binary.BigEndian.Uint16(data[endq+8 : endq+10]) + end := endq + 10 + int(rr.DataLength) + if end > len(data) { + return 0, errDecodeRecordLength + } + rr.Data = data[endq+10 : end] + + if err = rr.decodeRData(data[:end], endq+10, buffer); err != nil { + return 0, err + } + + return endq + 10 + int(rr.DataLength), nil +} + +func encodeName(name []byte, data []byte, offset int) int { + l := 0 + for i := range name { + if name[i] == '.' { + data[offset+i-l] = byte(l) + l = 0 + } else { + // skip one to write the length + data[offset+i+1] = name[i] + l++ + } + } + + if len(name) == 0 { + data[offset] = 0x00 // terminal + return offset + 1 + } + + // length for final portion + data[offset+len(name)-l] = byte(l) + data[offset+len(name)+1] = 0x00 // terminal + return offset + len(name) + 2 +} + +func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) { + + noff := encodeName(rr.Name, data, offset) + nSz := noff - offset + + binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type)) + binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class)) + binary.BigEndian.PutUint32(data[noff+4:], uint32(rr.TTL)) + + switch rr.Type { + case DNSTypeA: + copy(data[noff+10:], rr.IP.To4()) + case DNSTypeAAAA: + copy(data[noff+10:], rr.IP) + case DNSTypeNS: + encodeName(rr.NS, data, noff+10) + case DNSTypeCNAME: + encodeName(rr.CNAME, data, noff+10) + case DNSTypePTR: + encodeName(rr.PTR, data, noff+10) + case DNSTypeSOA: + noff2 := encodeName(rr.SOA.MName, data, noff+10) + noff2 = encodeName(rr.SOA.RName, data, noff2) + binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial) + binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh) + binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry) + binary.BigEndian.PutUint32(data[noff2+12:], rr.SOA.Expire) + binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum) + case DNSTypeMX: + binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference) + encodeName(rr.MX.Name, data, noff+12) + case DNSTypeTXT: + noff2 := noff + 10 + for _, txt := range rr.TXTs { + data[noff2] = byte(len(txt)) + copy(data[noff2+1:], txt) + noff2 += 1 + len(txt) + } + case DNSTypeSRV: + binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority) + binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight) + binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port) + encodeName(rr.SRV.Name, data, noff+16) + case DNSTypeURI: + binary.BigEndian.PutUint16(data[noff+10:], rr.URI.Priority) + binary.BigEndian.PutUint16(data[noff+12:], rr.URI.Weight) + copy(data[noff+14:], rr.URI.Target) + case DNSTypeOPT: + noff2 := noff + 10 + for _, opt := range rr.OPT { + binary.BigEndian.PutUint16(data[noff2:], uint16(opt.Code)) + binary.BigEndian.PutUint16(data[noff2+2:], uint16(len(opt.Data))) + copy(data[noff2+4:], opt.Data) + noff2 += 4 + len(opt.Data) + } + default: + return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type) + } + + // DataLength + dSz := recSize(rr) + binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz)) + + if opts.FixLengths { + rr.DataLength = uint16(dSz) + } + + return nSz + 10 + dSz, nil +} + +func (rr *DNSResourceRecord) String() string { + + if rr.Type == DNSTypeOPT { + opts := make([]string, len(rr.OPT)) + for i, opt := range rr.OPT { + opts[i] = opt.String() + } + return "OPT " + strings.Join(opts, ",") + } + if rr.Type == DNSTypeURI { + return fmt.Sprintf("URI %d %d %s", rr.URI.Priority, rr.URI.Weight, string(rr.URI.Target)) + } + if rr.Class == DNSClassIN { + switch rr.Type { + case DNSTypeA, DNSTypeAAAA: + return rr.IP.String() + case DNSTypeNS: + return "NS " + string(rr.NS) + case DNSTypeCNAME: + return "CNAME " + string(rr.CNAME) + case DNSTypePTR: + return "PTR " + string(rr.PTR) + case DNSTypeTXT: + return "TXT " + string(rr.TXT) + } + } + + return fmt.Sprintf("<%v, %v>", rr.Class, rr.Type) +} + +func decodeCharacterStrings(data []byte) ([][]byte, error) { + strings := make([][]byte, 0, 1) + end := len(data) + for index, index2 := 0, 0; index != end; index = index2 { + index2 = index + 1 + int(data[index]) // index increases by 1..256 and does not overflow + if index2 > end { + return nil, errCharStringMissData + } + strings = append(strings, data[index+1:index2]) + } + return strings, nil +} + +func decodeOPTs(data []byte, offset int) ([]DNSOPT, error) { + allOPT := []DNSOPT{} + end := len(data) + + if offset == end { + return allOPT, nil // There is no data to read + } + + if offset+4 > end { + return allOPT, fmt.Errorf("DNSOPT record is of length %d, it should be at least length 4", end-offset) + } + + for i := offset; i < end; { + opt := DNSOPT{} + if len(data) < i+4 { + return allOPT, fmt.Errorf("Malformed DNSOPT record. Length %d < %d", len(data), i+4) + } + opt.Code = DNSOptionCode(binary.BigEndian.Uint16(data[i : i+2])) + l := binary.BigEndian.Uint16(data[i+2 : i+4]) + if i+4+int(l) > end { + return allOPT, fmt.Errorf("Malformed DNSOPT record. The length (%d) field implies a packet larger than the one received", l) + } + opt.Data = data[i+4 : i+4+int(l)] + allOPT = append(allOPT, opt) + i += int(l) + 4 + } + return allOPT, nil +} + +func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error { + switch rr.Type { + case DNSTypeA: + rr.IP = rr.Data + case DNSTypeAAAA: + rr.IP = rr.Data + case DNSTypeTXT, DNSTypeHINFO: + rr.TXT = rr.Data + txts, err := decodeCharacterStrings(rr.Data) + if err != nil { + return err + } + rr.TXTs = txts + case DNSTypeNS: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.NS = name + case DNSTypeCNAME: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.CNAME = name + case DNSTypePTR: + name, _, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.PTR = name + case DNSTypeSOA: + name, endq, err := decodeName(data, offset, buffer, 1) + if err != nil { + return err + } + rr.SOA.MName = name + name, endq, err = decodeName(data, endq, buffer, 1) + if err != nil { + return err + } + if len(data) < endq+20 { + return errors.New("SOA too small") + } + rr.SOA.RName = name + rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4]) + rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8]) + rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12]) + rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16]) + rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20]) + case DNSTypeMX: + if len(data) < offset+2 { + return errors.New("MX too small") + } + rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2]) + name, _, err := decodeName(data, offset+2, buffer, 1) + if err != nil { + return err + } + rr.MX.Name = name + case DNSTypeURI: + if len(rr.Data) < 4 { + return errors.New("URI too small") + } + rr.URI.Priority = binary.BigEndian.Uint16(data[offset : offset+2]) + rr.URI.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + rr.URI.Target = rr.Data[4:] + case DNSTypeSRV: + if len(data) < offset+6 { + return errors.New("SRV too small") + } + rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2]) + rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6]) + name, _, err := decodeName(data, offset+6, buffer, 1) + if err != nil { + return err + } + rr.SRV.Name = name + case DNSTypeOPT: + allOPT, err := decodeOPTs(data, offset) + if err != nil { + return err + } + rr.OPT = allOPT + } + return nil +} + +// DNSSOA is a Start of Authority record. Each domain requires a SOA record at +// the cutover where a domain is delegated from its parent. +type DNSSOA struct { + MName, RName []byte + Serial, Refresh, Retry, Expire, Minimum uint32 +} + +// DNSSRV is a Service record, defining a location (hostname/port) of a +// server/service. +type DNSSRV struct { + Priority, Weight, Port uint16 + Name []byte +} + +// DNSMX is a mail exchange record, defining a mail server for a recipient's +// domain. +type DNSMX struct { + Preference uint16 + Name []byte +} + +// DNSURI is a URI record, defining a target (URI) of a server/service +type DNSURI struct { + Priority, Weight uint16 + Target []byte +} + +// DNSOptionCode represents the code of a DNS Option, see RFC6891, section 6.1.2 +type DNSOptionCode uint16 + +func (doc DNSOptionCode) String() string { + switch doc { + default: + return "Unknown" + case DNSOptionCodeNSID: + return "NSID" + case DNSOptionCodeDAU: + return "DAU" + case DNSOptionCodeDHU: + return "DHU" + case DNSOptionCodeN3U: + return "N3U" + case DNSOptionCodeEDNSClientSubnet: + return "EDNSClientSubnet" + case DNSOptionCodeEDNSExpire: + return "EDNSExpire" + case DNSOptionCodeCookie: + return "Cookie" + case DNSOptionCodeEDNSKeepAlive: + return "EDNSKeepAlive" + case DNSOptionCodePadding: + return "CodePadding" + case DNSOptionCodeChain: + return "CodeChain" + case DNSOptionCodeEDNSKeyTag: + return "CodeEDNSKeyTag" + case DNSOptionCodeEDNSClientTag: + return "EDNSClientTag" + case DNSOptionCodeEDNSServerTag: + return "EDNSServerTag" + case DNSOptionCodeDeviceID: + return "DeviceID" + } +} + +// DNSOptionCode known values. See IANA +const ( + DNSOptionCodeNSID DNSOptionCode = 3 + DNSOptionCodeDAU DNSOptionCode = 5 + DNSOptionCodeDHU DNSOptionCode = 6 + DNSOptionCodeN3U DNSOptionCode = 7 + DNSOptionCodeEDNSClientSubnet DNSOptionCode = 8 + DNSOptionCodeEDNSExpire DNSOptionCode = 9 + DNSOptionCodeCookie DNSOptionCode = 10 + DNSOptionCodeEDNSKeepAlive DNSOptionCode = 11 + DNSOptionCodePadding DNSOptionCode = 12 + DNSOptionCodeChain DNSOptionCode = 13 + DNSOptionCodeEDNSKeyTag DNSOptionCode = 14 + DNSOptionCodeEDNSClientTag DNSOptionCode = 16 + DNSOptionCodeEDNSServerTag DNSOptionCode = 17 + DNSOptionCodeDeviceID DNSOptionCode = 26946 +) + +// DNSOPT is a DNS Option, see RFC6891, section 6.1.2 +type DNSOPT struct { + Code DNSOptionCode + Data []byte +} + +func (opt DNSOPT) String() string { + return fmt.Sprintf("%s=%x", opt.Code, opt.Data) +} + +var ( + errMaxRecursion = errors.New("max DNS recursion level hit") + + errDNSNameOffsetTooHigh = errors.New("dns name offset too high") + errDNSNameOffsetNegative = errors.New("dns name offset is negative") + errDNSPacketTooShort = errors.New("DNS packet too short") + errDNSNameTooLong = errors.New("dns name is too long") + errDNSNameInvalidIndex = errors.New("dns name uncomputable: invalid index") + errDNSPointerOffsetTooHigh = errors.New("dns offset pointer too high") + errDNSIndexOutOfRange = errors.New("dns index walked out of range") + errDNSNameHasNoData = errors.New("no dns data found for name") + + errCharStringMissData = errors.New("Insufficient data for a ") + + errDecodeRecordLength = errors.New("resource record length exceeds data") + + errDecodeQueryBadQDCount = errors.New("Invalid query decoding, not the right number of questions") + errDecodeQueryBadANCount = errors.New("Invalid query decoding, not the right number of answers") + errDecodeQueryBadNSCount = errors.New("Invalid query decoding, not the right number of authorities") + errDecodeQueryBadARCount = errors.New("Invalid query decoding, not the right number of additionals info") +) diff --git a/vendor/github.com/google/gopacket/layers/doc.go b/vendor/github.com/google/gopacket/layers/doc.go new file mode 100644 index 00000000..3c882c3f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/doc.go @@ -0,0 +1,61 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +Package layers provides decoding layers for many common protocols. + +The layers package contains decode implementations for a number of different +types of packet layers. Users of gopacket will almost always want to also use +layers to actually decode packet data into useful pieces. To see the set of +protocols that gopacket/layers is currently able to decode, +look at the set of LayerTypes defined in the Variables sections. The +layers package also defines endpoints for many of the common packet layers +that have source/destination addresses associated with them, for example IPv4/6 +(IPs) and TCP/UDP (ports). +Finally, layers contains a number of useful enumerations (IPProtocol, +EthernetType, LinkType, PPPType, etc...). Many of these implement the +gopacket.Decoder interface, so they can be passed into gopacket as decoders. + +Most common protocol layers are named using acronyms or other industry-common +names (IPv4, TCP, PPP). Some of the less common ones have their names expanded +(CiscoDiscoveryProtocol). +For certain protocols, sub-parts of the protocol are split out into their own +layers (SCTP, for example). This is done mostly in cases where portions of the +protocol may fulfill the capabilities of interesting layers (SCTPData implements +ApplicationLayer, while base SCTP implements TransportLayer), or possibly +because splitting a protocol into a few layers makes decoding easier. + +This package is meant to be used with its parent, +http://github.com/google/gopacket. + +Port Types + +Instead of using raw uint16 or uint8 values for ports, we use a different port +type for every protocol, for example TCPPort and UDPPort. This allows us to +override string behavior for each port, which we do by setting up port name +maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with +their protocol names, and their String function displays these names: + + p := TCPPort(80) + fmt.Printf("Number: %d String: %v", p, p) + // Prints: "Number: 80 String: 80(http)" + +Modifying Decode Behavior + +layers links together decoding through its enumerations. For example, after +decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder. +All enumerations that act as decoders, like EthernetType, can be modified by +users depending on their preferences. For example, if you have a spiffy new +IPv4 decoder that works way better than the one built into layers, you can do +this: + + var mySpiffyIPv4Decoder gopacket.Decoder = ... + layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder + +This will make all future ethernet packets use your new decoder to decode IPv4 +packets, instead of the built-in decoder used by gopacket. +*/ +package layers diff --git a/vendor/github.com/google/gopacket/layers/dot11.go b/vendor/github.com/google/gopacket/layers/dot11.go new file mode 100644 index 00000000..3e649106 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dot11.go @@ -0,0 +1,2118 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// See http://standards.ieee.org/findstds/standard/802.11-2012.html for info on +// all of the layers in this file. + +package layers + +import ( + "bytes" + "encoding/binary" + "fmt" + "hash/crc32" + "net" + + "github.com/google/gopacket" +) + +// Dot11Flags contains the set of 8 flags in the IEEE 802.11 frame control +// header, all in one place. +type Dot11Flags uint8 + +const ( + Dot11FlagsToDS Dot11Flags = 1 << iota + Dot11FlagsFromDS + Dot11FlagsMF + Dot11FlagsRetry + Dot11FlagsPowerManagement + Dot11FlagsMD + Dot11FlagsWEP + Dot11FlagsOrder +) + +func (d Dot11Flags) ToDS() bool { + return d&Dot11FlagsToDS != 0 +} +func (d Dot11Flags) FromDS() bool { + return d&Dot11FlagsFromDS != 0 +} +func (d Dot11Flags) MF() bool { + return d&Dot11FlagsMF != 0 +} +func (d Dot11Flags) Retry() bool { + return d&Dot11FlagsRetry != 0 +} +func (d Dot11Flags) PowerManagement() bool { + return d&Dot11FlagsPowerManagement != 0 +} +func (d Dot11Flags) MD() bool { + return d&Dot11FlagsMD != 0 +} +func (d Dot11Flags) WEP() bool { + return d&Dot11FlagsWEP != 0 +} +func (d Dot11Flags) Order() bool { + return d&Dot11FlagsOrder != 0 +} + +// String provides a human readable string for Dot11Flags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Flags value, not its string. +func (a Dot11Flags) String() string { + var out bytes.Buffer + if a.ToDS() { + out.WriteString("TO-DS,") + } + if a.FromDS() { + out.WriteString("FROM-DS,") + } + if a.MF() { + out.WriteString("MF,") + } + if a.Retry() { + out.WriteString("Retry,") + } + if a.PowerManagement() { + out.WriteString("PowerManagement,") + } + if a.MD() { + out.WriteString("MD,") + } + if a.WEP() { + out.WriteString("WEP,") + } + if a.Order() { + out.WriteString("Order,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type Dot11Reason uint16 + +// TODO: Verify these reasons, and append more reasons if necessary. + +const ( + Dot11ReasonReserved Dot11Reason = 1 + Dot11ReasonUnspecified Dot11Reason = 2 + Dot11ReasonAuthExpired Dot11Reason = 3 + Dot11ReasonDeauthStLeaving Dot11Reason = 4 + Dot11ReasonInactivity Dot11Reason = 5 + Dot11ReasonApFull Dot11Reason = 6 + Dot11ReasonClass2FromNonAuth Dot11Reason = 7 + Dot11ReasonClass3FromNonAss Dot11Reason = 8 + Dot11ReasonDisasStLeaving Dot11Reason = 9 + Dot11ReasonStNotAuth Dot11Reason = 10 +) + +// String provides a human readable string for Dot11Reason. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Reason value, not its string. +func (a Dot11Reason) String() string { + switch a { + case Dot11ReasonReserved: + return "Reserved" + case Dot11ReasonUnspecified: + return "Unspecified" + case Dot11ReasonAuthExpired: + return "Auth. expired" + case Dot11ReasonDeauthStLeaving: + return "Deauth. st. leaving" + case Dot11ReasonInactivity: + return "Inactivity" + case Dot11ReasonApFull: + return "Ap. full" + case Dot11ReasonClass2FromNonAuth: + return "Class2 from non auth." + case Dot11ReasonClass3FromNonAss: + return "Class3 from non ass." + case Dot11ReasonDisasStLeaving: + return "Disass st. leaving" + case Dot11ReasonStNotAuth: + return "St. not auth." + default: + return "Unknown reason" + } +} + +type Dot11Status uint16 + +const ( + Dot11StatusSuccess Dot11Status = 0 + Dot11StatusFailure Dot11Status = 1 // Unspecified failure + Dot11StatusCannotSupportAllCapabilities Dot11Status = 10 // Cannot support all requested capabilities in the Capability Information field + Dot11StatusInabilityExistsAssociation Dot11Status = 11 // Reassociation denied due to inability to confirm that association exists + Dot11StatusAssociationDenied Dot11Status = 12 // Association denied due to reason outside the scope of this standard + Dot11StatusAlgorithmUnsupported Dot11Status = 13 // Responding station does not support the specified authentication algorithm + Dot11StatusOufOfExpectedSequence Dot11Status = 14 // Received an Authentication frame with authentication transaction sequence number out of expected sequence + Dot11StatusChallengeFailure Dot11Status = 15 // Authentication rejected because of challenge failure + Dot11StatusTimeout Dot11Status = 16 // Authentication rejected due to timeout waiting for next frame in sequence + Dot11StatusAPUnableToHandle Dot11Status = 17 // Association denied because AP is unable to handle additional associated stations + Dot11StatusRateUnsupported Dot11Status = 18 // Association denied due to requesting station not supporting all of the data rates in the BSSBasicRateSet parameter +) + +// String provides a human readable string for Dot11Status. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Status value, not its string. +func (a Dot11Status) String() string { + switch a { + case Dot11StatusSuccess: + return "success" + case Dot11StatusFailure: + return "failure" + case Dot11StatusCannotSupportAllCapabilities: + return "cannot-support-all-capabilities" + case Dot11StatusInabilityExistsAssociation: + return "inability-exists-association" + case Dot11StatusAssociationDenied: + return "association-denied" + case Dot11StatusAlgorithmUnsupported: + return "algorithm-unsupported" + case Dot11StatusOufOfExpectedSequence: + return "out-of-expected-sequence" + case Dot11StatusChallengeFailure: + return "challenge-failure" + case Dot11StatusTimeout: + return "timeout" + case Dot11StatusAPUnableToHandle: + return "ap-unable-to-handle" + case Dot11StatusRateUnsupported: + return "rate-unsupported" + default: + return "unknown status" + } +} + +type Dot11AckPolicy uint8 + +const ( + Dot11AckPolicyNormal Dot11AckPolicy = 0 + Dot11AckPolicyNone Dot11AckPolicy = 1 + Dot11AckPolicyNoExplicit Dot11AckPolicy = 2 + Dot11AckPolicyBlock Dot11AckPolicy = 3 +) + +// String provides a human readable string for Dot11AckPolicy. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11AckPolicy value, not its string. +func (a Dot11AckPolicy) String() string { + switch a { + case Dot11AckPolicyNormal: + return "normal-ack" + case Dot11AckPolicyNone: + return "no-ack" + case Dot11AckPolicyNoExplicit: + return "no-explicit-ack" + case Dot11AckPolicyBlock: + return "block-ack" + default: + return "unknown-ack-policy" + } +} + +type Dot11Algorithm uint16 + +const ( + Dot11AlgorithmOpen Dot11Algorithm = 0 + Dot11AlgorithmSharedKey Dot11Algorithm = 1 +) + +// String provides a human readable string for Dot11Algorithm. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11Algorithm value, not its string. +func (a Dot11Algorithm) String() string { + switch a { + case Dot11AlgorithmOpen: + return "open" + case Dot11AlgorithmSharedKey: + return "shared-key" + default: + return "unknown-algorithm" + } +} + +type Dot11InformationElementID uint8 + +const ( + Dot11InformationElementIDSSID Dot11InformationElementID = 0 + Dot11InformationElementIDRates Dot11InformationElementID = 1 + Dot11InformationElementIDFHSet Dot11InformationElementID = 2 + Dot11InformationElementIDDSSet Dot11InformationElementID = 3 + Dot11InformationElementIDCFSet Dot11InformationElementID = 4 + Dot11InformationElementIDTIM Dot11InformationElementID = 5 + Dot11InformationElementIDIBSSSet Dot11InformationElementID = 6 + Dot11InformationElementIDCountryInfo Dot11InformationElementID = 7 + Dot11InformationElementIDHoppingPatternParam Dot11InformationElementID = 8 + Dot11InformationElementIDHoppingPatternTable Dot11InformationElementID = 9 + Dot11InformationElementIDRequest Dot11InformationElementID = 10 + Dot11InformationElementIDQBSSLoadElem Dot11InformationElementID = 11 + Dot11InformationElementIDEDCAParamSet Dot11InformationElementID = 12 + Dot11InformationElementIDTrafficSpec Dot11InformationElementID = 13 + Dot11InformationElementIDTrafficClass Dot11InformationElementID = 14 + Dot11InformationElementIDSchedule Dot11InformationElementID = 15 + Dot11InformationElementIDChallenge Dot11InformationElementID = 16 + Dot11InformationElementIDPowerConst Dot11InformationElementID = 32 + Dot11InformationElementIDPowerCapability Dot11InformationElementID = 33 + Dot11InformationElementIDTPCRequest Dot11InformationElementID = 34 + Dot11InformationElementIDTPCReport Dot11InformationElementID = 35 + Dot11InformationElementIDSupportedChannels Dot11InformationElementID = 36 + Dot11InformationElementIDSwitchChannelAnnounce Dot11InformationElementID = 37 + Dot11InformationElementIDMeasureRequest Dot11InformationElementID = 38 + Dot11InformationElementIDMeasureReport Dot11InformationElementID = 39 + Dot11InformationElementIDQuiet Dot11InformationElementID = 40 + Dot11InformationElementIDIBSSDFS Dot11InformationElementID = 41 + Dot11InformationElementIDERPInfo Dot11InformationElementID = 42 + Dot11InformationElementIDTSDelay Dot11InformationElementID = 43 + Dot11InformationElementIDTCLASProcessing Dot11InformationElementID = 44 + Dot11InformationElementIDHTCapabilities Dot11InformationElementID = 45 + Dot11InformationElementIDQOSCapability Dot11InformationElementID = 46 + Dot11InformationElementIDERPInfo2 Dot11InformationElementID = 47 + Dot11InformationElementIDRSNInfo Dot11InformationElementID = 48 + Dot11InformationElementIDESRates Dot11InformationElementID = 50 + Dot11InformationElementIDAPChannelReport Dot11InformationElementID = 51 + Dot11InformationElementIDNeighborReport Dot11InformationElementID = 52 + Dot11InformationElementIDRCPI Dot11InformationElementID = 53 + Dot11InformationElementIDMobilityDomain Dot11InformationElementID = 54 + Dot11InformationElementIDFastBSSTrans Dot11InformationElementID = 55 + Dot11InformationElementIDTimeoutInt Dot11InformationElementID = 56 + Dot11InformationElementIDRICData Dot11InformationElementID = 57 + Dot11InformationElementIDDSERegisteredLoc Dot11InformationElementID = 58 + Dot11InformationElementIDSuppOperatingClass Dot11InformationElementID = 59 + Dot11InformationElementIDExtChanSwitchAnnounce Dot11InformationElementID = 60 + Dot11InformationElementIDHTInfo Dot11InformationElementID = 61 + Dot11InformationElementIDSecChanOffset Dot11InformationElementID = 62 + Dot11InformationElementIDBSSAverageAccessDelay Dot11InformationElementID = 63 + Dot11InformationElementIDAntenna Dot11InformationElementID = 64 + Dot11InformationElementIDRSNI Dot11InformationElementID = 65 + Dot11InformationElementIDMeasurePilotTrans Dot11InformationElementID = 66 + Dot11InformationElementIDBSSAvailAdmCapacity Dot11InformationElementID = 67 + Dot11InformationElementIDBSSACAccDelayWAPIParam Dot11InformationElementID = 68 + Dot11InformationElementIDTimeAdvertisement Dot11InformationElementID = 69 + Dot11InformationElementIDRMEnabledCapabilities Dot11InformationElementID = 70 + Dot11InformationElementIDMultipleBSSID Dot11InformationElementID = 71 + Dot11InformationElementID2040BSSCoExist Dot11InformationElementID = 72 + Dot11InformationElementID2040BSSIntChanReport Dot11InformationElementID = 73 + Dot11InformationElementIDOverlapBSSScanParam Dot11InformationElementID = 74 + Dot11InformationElementIDRICDescriptor Dot11InformationElementID = 75 + Dot11InformationElementIDManagementMIC Dot11InformationElementID = 76 + Dot11InformationElementIDEventRequest Dot11InformationElementID = 78 + Dot11InformationElementIDEventReport Dot11InformationElementID = 79 + Dot11InformationElementIDDiagnosticRequest Dot11InformationElementID = 80 + Dot11InformationElementIDDiagnosticReport Dot11InformationElementID = 81 + Dot11InformationElementIDLocationParam Dot11InformationElementID = 82 + Dot11InformationElementIDNonTransBSSIDCapability Dot11InformationElementID = 83 + Dot11InformationElementIDSSIDList Dot11InformationElementID = 84 + Dot11InformationElementIDMultipleBSSIDIndex Dot11InformationElementID = 85 + Dot11InformationElementIDFMSDescriptor Dot11InformationElementID = 86 + Dot11InformationElementIDFMSRequest Dot11InformationElementID = 87 + Dot11InformationElementIDFMSResponse Dot11InformationElementID = 88 + Dot11InformationElementIDQOSTrafficCapability Dot11InformationElementID = 89 + Dot11InformationElementIDBSSMaxIdlePeriod Dot11InformationElementID = 90 + Dot11InformationElementIDTFSRequest Dot11InformationElementID = 91 + Dot11InformationElementIDTFSResponse Dot11InformationElementID = 92 + Dot11InformationElementIDWNMSleepMode Dot11InformationElementID = 93 + Dot11InformationElementIDTIMBroadcastRequest Dot11InformationElementID = 94 + Dot11InformationElementIDTIMBroadcastResponse Dot11InformationElementID = 95 + Dot11InformationElementIDCollInterferenceReport Dot11InformationElementID = 96 + Dot11InformationElementIDChannelUsage Dot11InformationElementID = 97 + Dot11InformationElementIDTimeZone Dot11InformationElementID = 98 + Dot11InformationElementIDDMSRequest Dot11InformationElementID = 99 + Dot11InformationElementIDDMSResponse Dot11InformationElementID = 100 + Dot11InformationElementIDLinkIdentifier Dot11InformationElementID = 101 + Dot11InformationElementIDWakeupSchedule Dot11InformationElementID = 102 + Dot11InformationElementIDChannelSwitchTiming Dot11InformationElementID = 104 + Dot11InformationElementIDPTIControl Dot11InformationElementID = 105 + Dot11InformationElementIDPUBufferStatus Dot11InformationElementID = 106 + Dot11InformationElementIDInterworking Dot11InformationElementID = 107 + Dot11InformationElementIDAdvertisementProtocol Dot11InformationElementID = 108 + Dot11InformationElementIDExpBWRequest Dot11InformationElementID = 109 + Dot11InformationElementIDQOSMapSet Dot11InformationElementID = 110 + Dot11InformationElementIDRoamingConsortium Dot11InformationElementID = 111 + Dot11InformationElementIDEmergencyAlertIdentifier Dot11InformationElementID = 112 + Dot11InformationElementIDMeshConfiguration Dot11InformationElementID = 113 + Dot11InformationElementIDMeshID Dot11InformationElementID = 114 + Dot11InformationElementIDMeshLinkMetricReport Dot11InformationElementID = 115 + Dot11InformationElementIDCongestionNotification Dot11InformationElementID = 116 + Dot11InformationElementIDMeshPeeringManagement Dot11InformationElementID = 117 + Dot11InformationElementIDMeshChannelSwitchParam Dot11InformationElementID = 118 + Dot11InformationElementIDMeshAwakeWindows Dot11InformationElementID = 119 + Dot11InformationElementIDBeaconTiming Dot11InformationElementID = 120 + Dot11InformationElementIDMCCAOPSetupRequest Dot11InformationElementID = 121 + Dot11InformationElementIDMCCAOPSetupReply Dot11InformationElementID = 122 + Dot11InformationElementIDMCCAOPAdvertisement Dot11InformationElementID = 123 + Dot11InformationElementIDMCCAOPTeardown Dot11InformationElementID = 124 + Dot11InformationElementIDGateAnnouncement Dot11InformationElementID = 125 + Dot11InformationElementIDRootAnnouncement Dot11InformationElementID = 126 + Dot11InformationElementIDExtCapability Dot11InformationElementID = 127 + Dot11InformationElementIDAgereProprietary Dot11InformationElementID = 128 + Dot11InformationElementIDPathRequest Dot11InformationElementID = 130 + Dot11InformationElementIDPathReply Dot11InformationElementID = 131 + Dot11InformationElementIDPathError Dot11InformationElementID = 132 + Dot11InformationElementIDCiscoCCX1CKIPDeviceName Dot11InformationElementID = 133 + Dot11InformationElementIDCiscoCCX2 Dot11InformationElementID = 136 + Dot11InformationElementIDProxyUpdate Dot11InformationElementID = 137 + Dot11InformationElementIDProxyUpdateConfirmation Dot11InformationElementID = 138 + Dot11InformationElementIDAuthMeshPerringExch Dot11InformationElementID = 139 + Dot11InformationElementIDMIC Dot11InformationElementID = 140 + Dot11InformationElementIDDestinationURI Dot11InformationElementID = 141 + Dot11InformationElementIDUAPSDCoexistence Dot11InformationElementID = 142 + Dot11InformationElementIDWakeupSchedule80211ad Dot11InformationElementID = 143 + Dot11InformationElementIDExtendedSchedule Dot11InformationElementID = 144 + Dot11InformationElementIDSTAAvailability Dot11InformationElementID = 145 + Dot11InformationElementIDDMGTSPEC Dot11InformationElementID = 146 + Dot11InformationElementIDNextDMGATI Dot11InformationElementID = 147 + Dot11InformationElementIDDMSCapabilities Dot11InformationElementID = 148 + Dot11InformationElementIDCiscoUnknown95 Dot11InformationElementID = 149 + Dot11InformationElementIDVendor2 Dot11InformationElementID = 150 + Dot11InformationElementIDDMGOperating Dot11InformationElementID = 151 + Dot11InformationElementIDDMGBSSParamChange Dot11InformationElementID = 152 + Dot11InformationElementIDDMGBeamRefinement Dot11InformationElementID = 153 + Dot11InformationElementIDChannelMeasFeedback Dot11InformationElementID = 154 + Dot11InformationElementIDAwakeWindow Dot11InformationElementID = 157 + Dot11InformationElementIDMultiBand Dot11InformationElementID = 158 + Dot11InformationElementIDADDBAExtension Dot11InformationElementID = 159 + Dot11InformationElementIDNEXTPCPList Dot11InformationElementID = 160 + Dot11InformationElementIDPCPHandover Dot11InformationElementID = 161 + Dot11InformationElementIDDMGLinkMargin Dot11InformationElementID = 162 + Dot11InformationElementIDSwitchingStream Dot11InformationElementID = 163 + Dot11InformationElementIDSessionTransmission Dot11InformationElementID = 164 + Dot11InformationElementIDDynamicTonePairReport Dot11InformationElementID = 165 + Dot11InformationElementIDClusterReport Dot11InformationElementID = 166 + Dot11InformationElementIDRelayCapabilities Dot11InformationElementID = 167 + Dot11InformationElementIDRelayTransferParameter Dot11InformationElementID = 168 + Dot11InformationElementIDBeamlinkMaintenance Dot11InformationElementID = 169 + Dot11InformationElementIDMultipleMacSublayers Dot11InformationElementID = 170 + Dot11InformationElementIDUPID Dot11InformationElementID = 171 + Dot11InformationElementIDDMGLinkAdaptionAck Dot11InformationElementID = 172 + Dot11InformationElementIDSymbolProprietary Dot11InformationElementID = 173 + Dot11InformationElementIDMCCAOPAdvertOverview Dot11InformationElementID = 174 + Dot11InformationElementIDQuietPeriodRequest Dot11InformationElementID = 175 + Dot11InformationElementIDQuietPeriodResponse Dot11InformationElementID = 177 + Dot11InformationElementIDECPACPolicy Dot11InformationElementID = 182 + Dot11InformationElementIDClusterTimeOffset Dot11InformationElementID = 183 + Dot11InformationElementIDAntennaSectorID Dot11InformationElementID = 190 + Dot11InformationElementIDVHTCapabilities Dot11InformationElementID = 191 + Dot11InformationElementIDVHTOperation Dot11InformationElementID = 192 + Dot11InformationElementIDExtendedBSSLoad Dot11InformationElementID = 193 + Dot11InformationElementIDWideBWChannelSwitch Dot11InformationElementID = 194 + Dot11InformationElementIDVHTTxPowerEnvelope Dot11InformationElementID = 195 + Dot11InformationElementIDChannelSwitchWrapper Dot11InformationElementID = 196 + Dot11InformationElementIDOperatingModeNotification Dot11InformationElementID = 199 + Dot11InformationElementIDUPSIM Dot11InformationElementID = 200 + Dot11InformationElementIDReducedNeighborReport Dot11InformationElementID = 201 + Dot11InformationElementIDTVHTOperation Dot11InformationElementID = 202 + Dot11InformationElementIDDeviceLocation Dot11InformationElementID = 204 + Dot11InformationElementIDWhiteSpaceMap Dot11InformationElementID = 205 + Dot11InformationElementIDFineTuningMeasureParams Dot11InformationElementID = 206 + Dot11InformationElementIDVendor Dot11InformationElementID = 221 +) + +// String provides a human readable string for Dot11InformationElementID. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the Dot11InformationElementID value, +// not its string. +func (a Dot11InformationElementID) String() string { + switch a { + case Dot11InformationElementIDSSID: + return "SSID parameter set" + case Dot11InformationElementIDRates: + return "Supported Rates" + case Dot11InformationElementIDFHSet: + return "FH Parameter set" + case Dot11InformationElementIDDSSet: + return "DS Parameter set" + case Dot11InformationElementIDCFSet: + return "CF Parameter set" + case Dot11InformationElementIDTIM: + return "Traffic Indication Map (TIM)" + case Dot11InformationElementIDIBSSSet: + return "IBSS Parameter set" + case Dot11InformationElementIDCountryInfo: + return "Country Information" + case Dot11InformationElementIDHoppingPatternParam: + return "Hopping Pattern Parameters" + case Dot11InformationElementIDHoppingPatternTable: + return "Hopping Pattern Table" + case Dot11InformationElementIDRequest: + return "Request" + case Dot11InformationElementIDQBSSLoadElem: + return "QBSS Load Element" + case Dot11InformationElementIDEDCAParamSet: + return "EDCA Parameter Set" + case Dot11InformationElementIDTrafficSpec: + return "Traffic Specification" + case Dot11InformationElementIDTrafficClass: + return "Traffic Classification" + case Dot11InformationElementIDSchedule: + return "Schedule" + case Dot11InformationElementIDChallenge: + return "Challenge text" + case Dot11InformationElementIDPowerConst: + return "Power Constraint" + case Dot11InformationElementIDPowerCapability: + return "Power Capability" + case Dot11InformationElementIDTPCRequest: + return "TPC Request" + case Dot11InformationElementIDTPCReport: + return "TPC Report" + case Dot11InformationElementIDSupportedChannels: + return "Supported Channels" + case Dot11InformationElementIDSwitchChannelAnnounce: + return "Channel Switch Announcement" + case Dot11InformationElementIDMeasureRequest: + return "Measurement Request" + case Dot11InformationElementIDMeasureReport: + return "Measurement Report" + case Dot11InformationElementIDQuiet: + return "Quiet" + case Dot11InformationElementIDIBSSDFS: + return "IBSS DFS" + case Dot11InformationElementIDERPInfo: + return "ERP Information" + case Dot11InformationElementIDTSDelay: + return "TS Delay" + case Dot11InformationElementIDTCLASProcessing: + return "TCLAS Processing" + case Dot11InformationElementIDHTCapabilities: + return "HT Capabilities (802.11n D1.10)" + case Dot11InformationElementIDQOSCapability: + return "QOS Capability" + case Dot11InformationElementIDERPInfo2: + return "ERP Information-2" + case Dot11InformationElementIDRSNInfo: + return "RSN Information" + case Dot11InformationElementIDESRates: + return "Extended Supported Rates" + case Dot11InformationElementIDAPChannelReport: + return "AP Channel Report" + case Dot11InformationElementIDNeighborReport: + return "Neighbor Report" + case Dot11InformationElementIDRCPI: + return "RCPI" + case Dot11InformationElementIDMobilityDomain: + return "Mobility Domain" + case Dot11InformationElementIDFastBSSTrans: + return "Fast BSS Transition" + case Dot11InformationElementIDTimeoutInt: + return "Timeout Interval" + case Dot11InformationElementIDRICData: + return "RIC Data" + case Dot11InformationElementIDDSERegisteredLoc: + return "DSE Registered Location" + case Dot11InformationElementIDSuppOperatingClass: + return "Supported Operating Classes" + case Dot11InformationElementIDExtChanSwitchAnnounce: + return "Extended Channel Switch Announcement" + case Dot11InformationElementIDHTInfo: + return "HT Information (802.11n D1.10)" + case Dot11InformationElementIDSecChanOffset: + return "Secondary Channel Offset (802.11n D1.10)" + case Dot11InformationElementIDBSSAverageAccessDelay: + return "BSS Average Access Delay" + case Dot11InformationElementIDAntenna: + return "Antenna" + case Dot11InformationElementIDRSNI: + return "RSNI" + case Dot11InformationElementIDMeasurePilotTrans: + return "Measurement Pilot Transmission" + case Dot11InformationElementIDBSSAvailAdmCapacity: + return "BSS Available Admission Capacity" + case Dot11InformationElementIDBSSACAccDelayWAPIParam: + return "BSS AC Access Delay/WAPI Parameter Set" + case Dot11InformationElementIDTimeAdvertisement: + return "Time Advertisement" + case Dot11InformationElementIDRMEnabledCapabilities: + return "RM Enabled Capabilities" + case Dot11InformationElementIDMultipleBSSID: + return "Multiple BSSID" + case Dot11InformationElementID2040BSSCoExist: + return "20/40 BSS Coexistence" + case Dot11InformationElementID2040BSSIntChanReport: + return "20/40 BSS Intolerant Channel Report" + case Dot11InformationElementIDOverlapBSSScanParam: + return "Overlapping BSS Scan Parameters" + case Dot11InformationElementIDRICDescriptor: + return "RIC Descriptor" + case Dot11InformationElementIDManagementMIC: + return "Management MIC" + case Dot11InformationElementIDEventRequest: + return "Event Request" + case Dot11InformationElementIDEventReport: + return "Event Report" + case Dot11InformationElementIDDiagnosticRequest: + return "Diagnostic Request" + case Dot11InformationElementIDDiagnosticReport: + return "Diagnostic Report" + case Dot11InformationElementIDLocationParam: + return "Location Parameters" + case Dot11InformationElementIDNonTransBSSIDCapability: + return "Non Transmitted BSSID Capability" + case Dot11InformationElementIDSSIDList: + return "SSID List" + case Dot11InformationElementIDMultipleBSSIDIndex: + return "Multiple BSSID Index" + case Dot11InformationElementIDFMSDescriptor: + return "FMS Descriptor" + case Dot11InformationElementIDFMSRequest: + return "FMS Request" + case Dot11InformationElementIDFMSResponse: + return "FMS Response" + case Dot11InformationElementIDQOSTrafficCapability: + return "QoS Traffic Capability" + case Dot11InformationElementIDBSSMaxIdlePeriod: + return "BSS Max Idle Period" + case Dot11InformationElementIDTFSRequest: + return "TFS Request" + case Dot11InformationElementIDTFSResponse: + return "TFS Response" + case Dot11InformationElementIDWNMSleepMode: + return "WNM-Sleep Mode" + case Dot11InformationElementIDTIMBroadcastRequest: + return "TIM Broadcast Request" + case Dot11InformationElementIDTIMBroadcastResponse: + return "TIM Broadcast Response" + case Dot11InformationElementIDCollInterferenceReport: + return "Collocated Interference Report" + case Dot11InformationElementIDChannelUsage: + return "Channel Usage" + case Dot11InformationElementIDTimeZone: + return "Time Zone" + case Dot11InformationElementIDDMSRequest: + return "DMS Request" + case Dot11InformationElementIDDMSResponse: + return "DMS Response" + case Dot11InformationElementIDLinkIdentifier: + return "Link Identifier" + case Dot11InformationElementIDWakeupSchedule: + return "Wakeup Schedule" + case Dot11InformationElementIDChannelSwitchTiming: + return "Channel Switch Timing" + case Dot11InformationElementIDPTIControl: + return "PTI Control" + case Dot11InformationElementIDPUBufferStatus: + return "PU Buffer Status" + case Dot11InformationElementIDInterworking: + return "Interworking" + case Dot11InformationElementIDAdvertisementProtocol: + return "Advertisement Protocol" + case Dot11InformationElementIDExpBWRequest: + return "Expedited Bandwidth Request" + case Dot11InformationElementIDQOSMapSet: + return "QoS Map Set" + case Dot11InformationElementIDRoamingConsortium: + return "Roaming Consortium" + case Dot11InformationElementIDEmergencyAlertIdentifier: + return "Emergency Alert Identifier" + case Dot11InformationElementIDMeshConfiguration: + return "Mesh Configuration" + case Dot11InformationElementIDMeshID: + return "Mesh ID" + case Dot11InformationElementIDMeshLinkMetricReport: + return "Mesh Link Metric Report" + case Dot11InformationElementIDCongestionNotification: + return "Congestion Notification" + case Dot11InformationElementIDMeshPeeringManagement: + return "Mesh Peering Management" + case Dot11InformationElementIDMeshChannelSwitchParam: + return "Mesh Channel Switch Parameters" + case Dot11InformationElementIDMeshAwakeWindows: + return "Mesh Awake Windows" + case Dot11InformationElementIDBeaconTiming: + return "Beacon Timing" + case Dot11InformationElementIDMCCAOPSetupRequest: + return "MCCAOP Setup Request" + case Dot11InformationElementIDMCCAOPSetupReply: + return "MCCAOP SETUP Reply" + case Dot11InformationElementIDMCCAOPAdvertisement: + return "MCCAOP Advertisement" + case Dot11InformationElementIDMCCAOPTeardown: + return "MCCAOP Teardown" + case Dot11InformationElementIDGateAnnouncement: + return "Gate Announcement" + case Dot11InformationElementIDRootAnnouncement: + return "Root Announcement" + case Dot11InformationElementIDExtCapability: + return "Extended Capabilities" + case Dot11InformationElementIDAgereProprietary: + return "Agere Proprietary" + case Dot11InformationElementIDPathRequest: + return "Path Request" + case Dot11InformationElementIDPathReply: + return "Path Reply" + case Dot11InformationElementIDPathError: + return "Path Error" + case Dot11InformationElementIDCiscoCCX1CKIPDeviceName: + return "Cisco CCX1 CKIP + Device Name" + case Dot11InformationElementIDCiscoCCX2: + return "Cisco CCX2" + case Dot11InformationElementIDProxyUpdate: + return "Proxy Update" + case Dot11InformationElementIDProxyUpdateConfirmation: + return "Proxy Update Confirmation" + case Dot11InformationElementIDAuthMeshPerringExch: + return "Auhenticated Mesh Perring Exchange" + case Dot11InformationElementIDMIC: + return "MIC (Message Integrity Code)" + case Dot11InformationElementIDDestinationURI: + return "Destination URI" + case Dot11InformationElementIDUAPSDCoexistence: + return "U-APSD Coexistence" + case Dot11InformationElementIDWakeupSchedule80211ad: + return "Wakeup Schedule 802.11ad" + case Dot11InformationElementIDExtendedSchedule: + return "Extended Schedule" + case Dot11InformationElementIDSTAAvailability: + return "STA Availability" + case Dot11InformationElementIDDMGTSPEC: + return "DMG TSPEC" + case Dot11InformationElementIDNextDMGATI: + return "Next DMG ATI" + case Dot11InformationElementIDDMSCapabilities: + return "DMG Capabilities" + case Dot11InformationElementIDCiscoUnknown95: + return "Cisco Unknown 95" + case Dot11InformationElementIDVendor2: + return "Vendor Specific" + case Dot11InformationElementIDDMGOperating: + return "DMG Operating" + case Dot11InformationElementIDDMGBSSParamChange: + return "DMG BSS Parameter Change" + case Dot11InformationElementIDDMGBeamRefinement: + return "DMG Beam Refinement" + case Dot11InformationElementIDChannelMeasFeedback: + return "Channel Measurement Feedback" + case Dot11InformationElementIDAwakeWindow: + return "Awake Window" + case Dot11InformationElementIDMultiBand: + return "Multi Band" + case Dot11InformationElementIDADDBAExtension: + return "ADDBA Extension" + case Dot11InformationElementIDNEXTPCPList: + return "NEXTPCP List" + case Dot11InformationElementIDPCPHandover: + return "PCP Handover" + case Dot11InformationElementIDDMGLinkMargin: + return "DMG Link Margin" + case Dot11InformationElementIDSwitchingStream: + return "Switching Stream" + case Dot11InformationElementIDSessionTransmission: + return "Session Transmission" + case Dot11InformationElementIDDynamicTonePairReport: + return "Dynamic Tone Pairing Report" + case Dot11InformationElementIDClusterReport: + return "Cluster Report" + case Dot11InformationElementIDRelayCapabilities: + return "Relay Capabilities" + case Dot11InformationElementIDRelayTransferParameter: + return "Relay Transfer Parameter" + case Dot11InformationElementIDBeamlinkMaintenance: + return "Beamlink Maintenance" + case Dot11InformationElementIDMultipleMacSublayers: + return "Multiple MAC Sublayers" + case Dot11InformationElementIDUPID: + return "U-PID" + case Dot11InformationElementIDDMGLinkAdaptionAck: + return "DMG Link Adaption Acknowledgment" + case Dot11InformationElementIDSymbolProprietary: + return "Symbol Proprietary" + case Dot11InformationElementIDMCCAOPAdvertOverview: + return "MCCAOP Advertisement Overview" + case Dot11InformationElementIDQuietPeriodRequest: + return "Quiet Period Request" + case Dot11InformationElementIDQuietPeriodResponse: + return "Quiet Period Response" + case Dot11InformationElementIDECPACPolicy: + return "ECPAC Policy" + case Dot11InformationElementIDClusterTimeOffset: + return "Cluster Time Offset" + case Dot11InformationElementIDAntennaSectorID: + return "Antenna Sector ID" + case Dot11InformationElementIDVHTCapabilities: + return "VHT Capabilities (IEEE Std 802.11ac/D3.1)" + case Dot11InformationElementIDVHTOperation: + return "VHT Operation (IEEE Std 802.11ac/D3.1)" + case Dot11InformationElementIDExtendedBSSLoad: + return "Extended BSS Load" + case Dot11InformationElementIDWideBWChannelSwitch: + return "Wide Bandwidth Channel Switch" + case Dot11InformationElementIDVHTTxPowerEnvelope: + return "VHT Tx Power Envelope (IEEE Std 802.11ac/D5.0)" + case Dot11InformationElementIDChannelSwitchWrapper: + return "Channel Switch Wrapper" + case Dot11InformationElementIDOperatingModeNotification: + return "Operating Mode Notification" + case Dot11InformationElementIDUPSIM: + return "UP SIM" + case Dot11InformationElementIDReducedNeighborReport: + return "Reduced Neighbor Report" + case Dot11InformationElementIDTVHTOperation: + return "TVHT Op" + case Dot11InformationElementIDDeviceLocation: + return "Device Location" + case Dot11InformationElementIDWhiteSpaceMap: + return "White Space Map" + case Dot11InformationElementIDFineTuningMeasureParams: + return "Fine Tuning Measure Parameters" + case Dot11InformationElementIDVendor: + return "Vendor" + default: + return "Unknown information element id" + } +} + +// Dot11 provides an IEEE 802.11 base packet header. +// See http://standards.ieee.org/findstds/standard/802.11-2012.html +// for excruciating detail. +type Dot11 struct { + BaseLayer + Type Dot11Type + Proto uint8 + Flags Dot11Flags + DurationID uint16 + Address1 net.HardwareAddr + Address2 net.HardwareAddr + Address3 net.HardwareAddr + Address4 net.HardwareAddr + SequenceNumber uint16 + FragmentNumber uint16 + Checksum uint32 + QOS *Dot11QOS + HTControl *Dot11HTControl + DataLayer gopacket.Layer +} + +type Dot11QOS struct { + TID uint8 /* Traffic IDentifier */ + EOSP bool /* End of service period */ + AckPolicy Dot11AckPolicy + TXOP uint8 +} + +type Dot11HTControl struct { + ACConstraint bool + RDGMorePPDU bool + + VHT *Dot11HTControlVHT + HT *Dot11HTControlHT +} + +type Dot11HTControlHT struct { + LinkAdapationControl *Dot11LinkAdapationControl + CalibrationPosition uint8 + CalibrationSequence uint8 + CSISteering uint8 + NDPAnnouncement bool + DEI bool +} + +type Dot11HTControlVHT struct { + MRQ bool + UnsolicitedMFB bool + MSI *uint8 + MFB Dot11HTControlMFB + CompressedMSI *uint8 + STBCIndication bool + MFSI *uint8 + GID *uint8 + CodingType *Dot11CodingType + FbTXBeamformed bool +} + +type Dot11HTControlMFB struct { + NumSTS uint8 + VHTMCS uint8 + BW uint8 + SNR int8 +} + +type Dot11LinkAdapationControl struct { + TRQ bool + MRQ bool + MSI uint8 + MFSI uint8 + ASEL *Dot11ASEL + MFB *uint8 +} + +type Dot11ASEL struct { + Command uint8 + Data uint8 +} + +type Dot11CodingType uint8 + +const ( + Dot11CodingTypeBCC = 0 + Dot11CodingTypeLDPC = 1 +) + +func (a Dot11CodingType) String() string { + switch a { + case Dot11CodingTypeBCC: + return "BCC" + case Dot11CodingTypeLDPC: + return "LDPC" + default: + return "Unknown coding type" + } +} + +func (m *Dot11HTControlMFB) NoFeedBackPresent() bool { + return m.VHTMCS == 15 && m.NumSTS == 7 +} + +func decodeDot11(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(d) + if d.DataLayer != nil { + p.AddLayer(d.DataLayer) + } + return p.NextDecoder(d.NextLayerType()) +} + +func (m *Dot11) LayerType() gopacket.LayerType { return LayerTypeDot11 } +func (m *Dot11) CanDecode() gopacket.LayerClass { return LayerTypeDot11 } +func (m *Dot11) NextLayerType() gopacket.LayerType { + if m.DataLayer != nil { + if m.Flags.WEP() { + return LayerTypeDot11WEP + } + return m.DataLayer.(gopacket.DecodingLayer).NextLayerType() + } + return m.Type.LayerType() +} + +func createU8(x uint8) *uint8 { + return &x +} + +var dataDecodeMap = map[Dot11Type]func() gopacket.DecodingLayer{ + Dot11TypeData: func() gopacket.DecodingLayer { return &Dot11Data{} }, + Dot11TypeDataCFAck: func() gopacket.DecodingLayer { return &Dot11DataCFAck{} }, + Dot11TypeDataCFPoll: func() gopacket.DecodingLayer { return &Dot11DataCFPoll{} }, + Dot11TypeDataCFAckPoll: func() gopacket.DecodingLayer { return &Dot11DataCFAckPoll{} }, + Dot11TypeDataNull: func() gopacket.DecodingLayer { return &Dot11DataNull{} }, + Dot11TypeDataCFAckNoData: func() gopacket.DecodingLayer { return &Dot11DataCFAckNoData{} }, + Dot11TypeDataCFPollNoData: func() gopacket.DecodingLayer { return &Dot11DataCFPollNoData{} }, + Dot11TypeDataCFAckPollNoData: func() gopacket.DecodingLayer { return &Dot11DataCFAckPollNoData{} }, + Dot11TypeDataQOSData: func() gopacket.DecodingLayer { return &Dot11DataQOSData{} }, + Dot11TypeDataQOSDataCFAck: func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFAck{} }, + Dot11TypeDataQOSDataCFPoll: func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFPoll{} }, + Dot11TypeDataQOSDataCFAckPoll: func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFAckPoll{} }, + Dot11TypeDataQOSNull: func() gopacket.DecodingLayer { return &Dot11DataQOSNull{} }, + Dot11TypeDataQOSCFPollNoData: func() gopacket.DecodingLayer { return &Dot11DataQOSCFPollNoData{} }, + Dot11TypeDataQOSCFAckPollNoData: func() gopacket.DecodingLayer { return &Dot11DataQOSCFAckPollNoData{} }, +} + +func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 10 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), 10) + } + m.Type = Dot11Type((data[0])&0xFC) >> 2 + + m.DataLayer = nil + m.Proto = uint8(data[0]) & 0x0003 + m.Flags = Dot11Flags(data[1]) + m.DurationID = binary.LittleEndian.Uint16(data[2:4]) + m.Address1 = net.HardwareAddr(data[4:10]) + + offset := 10 + + mainType := m.Type.MainType() + + switch mainType { + case Dot11TypeCtrl: + switch m.Type { + case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck: + if len(data) < offset+6 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + m.Address2 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + } + case Dot11TypeMgmt, Dot11TypeData: + if len(data) < offset+14 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+14) + } + m.Address2 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + m.Address3 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + + m.SequenceNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0xFFF0) >> 4 + m.FragmentNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0x000F) + offset += 2 + } + + if mainType == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() { + if len(data) < offset+6 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + m.Address4 = net.HardwareAddr(data[offset : offset+6]) + offset += 6 + } + + if m.Type.QOS() { + if len(data) < offset+2 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + m.QOS = &Dot11QOS{ + TID: (uint8(data[offset]) & 0x0F), + EOSP: (uint8(data[offset]) & 0x10) == 0x10, + AckPolicy: Dot11AckPolicy((uint8(data[offset]) & 0x60) >> 5), + TXOP: uint8(data[offset+1]), + } + offset += 2 + } + if m.Flags.Order() && (m.Type.QOS() || mainType == Dot11TypeMgmt) { + if len(data) < offset+4 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6) + } + + htc := &Dot11HTControl{ + ACConstraint: data[offset+3]&0x40 != 0, + RDGMorePPDU: data[offset+3]&0x80 != 0, + } + m.HTControl = htc + + if data[offset]&0x1 != 0 { // VHT Variant + vht := &Dot11HTControlVHT{} + htc.VHT = vht + vht.MRQ = data[offset]&0x4 != 0 + vht.UnsolicitedMFB = data[offset+3]&0x20 != 0 + vht.MFB = Dot11HTControlMFB{ + NumSTS: uint8(data[offset+1] >> 1 & 0x7), + VHTMCS: uint8(data[offset+1] >> 4 & 0xF), + BW: uint8(data[offset+2] & 0x3), + SNR: int8((-(data[offset+2] >> 2 & 0x20))+data[offset+2]>>2&0x1F) + 22, + } + + if vht.UnsolicitedMFB { + if !vht.MFB.NoFeedBackPresent() { + vht.CompressedMSI = createU8(data[offset] >> 3 & 0x3) + vht.STBCIndication = data[offset]&0x20 != 0 + vht.CodingType = (*Dot11CodingType)(createU8(data[offset+3] >> 3 & 0x1)) + vht.FbTXBeamformed = data[offset+3]&0x10 != 0 + vht.GID = createU8( + data[offset]>>6 + + (data[offset+1] & 0x1 << 2) + + data[offset+3]&0x7<<3) + } + } else { + if vht.MRQ { + vht.MSI = createU8((data[offset] >> 3) & 0x07) + } + vht.MFSI = createU8(data[offset]>>6 + (data[offset+1] & 0x1 << 2)) + } + + } else { // HT Variant + ht := &Dot11HTControlHT{} + htc.HT = ht + + lac := &Dot11LinkAdapationControl{} + ht.LinkAdapationControl = lac + lac.TRQ = data[offset]&0x2 != 0 + lac.MFSI = data[offset]>>6&0x3 + data[offset+1]&0x1<<3 + if data[offset]&0x3C == 0x38 { // ASEL + lac.ASEL = &Dot11ASEL{ + Command: data[offset+1] >> 1 & 0x7, + Data: data[offset+1] >> 4 & 0xF, + } + } else { + lac.MRQ = data[offset]&0x4 != 0 + if lac.MRQ { + lac.MSI = data[offset] >> 3 & 0x7 + } + lac.MFB = createU8(data[offset+1] >> 1) + } + ht.CalibrationPosition = data[offset+2] & 0x3 + ht.CalibrationSequence = data[offset+2] >> 2 & 0x3 + ht.CSISteering = data[offset+2] >> 6 & 0x3 + ht.NDPAnnouncement = data[offset+3]&0x1 != 0 + if mainType != Dot11TypeMgmt { + ht.DEI = data[offset+3]&0x20 != 0 + } + } + + offset += 4 + } + + if len(data) < offset+4 { + df.SetTruncated() + return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+4) + } + + m.BaseLayer = BaseLayer{ + Contents: data[0:offset], + Payload: data[offset : len(data)-4], + } + + if mainType == Dot11TypeData { + d := dataDecodeMap[m.Type] + if d == nil { + return fmt.Errorf("unsupported type: %v", m.Type) + } + l := d() + err := l.DecodeFromBytes(m.BaseLayer.Payload, df) + if err != nil { + return err + } + m.DataLayer = l.(gopacket.Layer) + } + + m.Checksum = binary.LittleEndian.Uint32(data[len(data)-4 : len(data)]) + return nil +} + +func (m *Dot11) ChecksumValid() bool { + // only for CTRL and MGMT frames + h := crc32.NewIEEE() + h.Write(m.Contents) + h.Write(m.Payload) + return m.Checksum == h.Sum32() +} + +func (m Dot11) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(24) + + if err != nil { + return err + } + + buf[0] = (uint8(m.Type) << 2) | m.Proto + buf[1] = uint8(m.Flags) + + binary.LittleEndian.PutUint16(buf[2:4], m.DurationID) + + copy(buf[4:10], m.Address1) + + offset := 10 + + switch m.Type.MainType() { + case Dot11TypeCtrl: + switch m.Type { + case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck: + copy(buf[offset:offset+6], m.Address2) + offset += 6 + } + case Dot11TypeMgmt, Dot11TypeData: + copy(buf[offset:offset+6], m.Address2) + offset += 6 + copy(buf[offset:offset+6], m.Address3) + offset += 6 + + binary.LittleEndian.PutUint16(buf[offset:offset+2], (m.SequenceNumber<<4)|m.FragmentNumber) + offset += 2 + } + + if m.Type.MainType() == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() { + copy(buf[offset:offset+6], m.Address4) + offset += 6 + } + + return nil +} + +// Dot11Mgmt is a base for all IEEE 802.11 management layers. +type Dot11Mgmt struct { + BaseLayer +} + +func (m *Dot11Mgmt) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } +func (m *Dot11Mgmt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +// Dot11Ctrl is a base for all IEEE 802.11 control layers. +type Dot11Ctrl struct { + BaseLayer +} + +func (m *Dot11Ctrl) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } + +func (m *Dot11Ctrl) LayerType() gopacket.LayerType { return LayerTypeDot11Ctrl } +func (m *Dot11Ctrl) CanDecode() gopacket.LayerClass { return LayerTypeDot11Ctrl } +func (m *Dot11Ctrl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeDot11Ctrl(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11Ctrl{} + return decodingLayerDecoder(d, data, p) +} + +// Dot11WEP contains WEP encrpted IEEE 802.11 data. +type Dot11WEP struct { + BaseLayer +} + +func (m *Dot11WEP) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } + +func (m *Dot11WEP) LayerType() gopacket.LayerType { return LayerTypeDot11WEP } +func (m *Dot11WEP) CanDecode() gopacket.LayerClass { return LayerTypeDot11WEP } +func (m *Dot11WEP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeDot11WEP(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11WEP{} + return decodingLayerDecoder(d, data, p) +} + +// Dot11Data is a base for all IEEE 802.11 data layers. +type Dot11Data struct { + BaseLayer +} + +func (m *Dot11Data) NextLayerType() gopacket.LayerType { + return LayerTypeLLC +} + +func (m *Dot11Data) LayerType() gopacket.LayerType { return LayerTypeDot11Data } +func (m *Dot11Data) CanDecode() gopacket.LayerClass { return LayerTypeDot11Data } +func (m *Dot11Data) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Payload = data + return nil +} + +func decodeDot11Data(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11Data{} + return decodingLayerDecoder(d, data, p) +} + +type Dot11DataCFAck struct { + Dot11Data +} + +func decodeDot11DataCFAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck } +func (m *Dot11DataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAck } +func (m *Dot11DataCFAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFPoll struct { + Dot11Data +} + +func decodeDot11DataCFPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFPoll) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPoll } +func (m *Dot11DataCFPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPoll } +func (m *Dot11DataCFPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckPoll struct { + Dot11Data +} + +func decodeDot11DataCFAckPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckPoll) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAckPoll } +func (m *Dot11DataCFAckPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckPoll } +func (m *Dot11DataCFAckPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataNull struct { + Dot11Data +} + +func decodeDot11DataNull(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataNull{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataNull) LayerType() gopacket.LayerType { return LayerTypeDot11DataNull } +func (m *Dot11DataNull) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataNull } +func (m *Dot11DataNull) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckNoData struct { + Dot11Data +} + +func decodeDot11DataCFAckNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFAckNoData } +func (m *Dot11DataCFAckNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckNoData } +func (m *Dot11DataCFAckNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFPollNoData struct { + Dot11Data +} + +func decodeDot11DataCFPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData } +func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataCFPollNoData +} +func (m *Dot11DataCFPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataCFAckPollNoData struct { + Dot11Data +} + +func decodeDot11DataCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataCFAckPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataCFAckPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPollNoData +} +func (m *Dot11DataCFAckPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataCFAckPollNoData +} +func (m *Dot11DataCFAckPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Data.DecodeFromBytes(data, df) +} + +type Dot11DataQOS struct { + Dot11Ctrl +} + +func (m *Dot11DataQOS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.BaseLayer = BaseLayer{Payload: data} + return nil +} + +type Dot11DataQOSData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSData) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSData } +func (m *Dot11DataQOSData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSData } + +func (m *Dot11DataQOSData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11Data +} + +type Dot11DataQOSDataCFAck struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck } +func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSDataCFAck +} +func (m *Dot11DataQOSDataCFAck) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck } + +type Dot11DataQOSDataCFPoll struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFPoll) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSDataCFPoll +} +func (m *Dot11DataQOSDataCFPoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSDataCFPoll +} +func (m *Dot11DataQOSDataCFPoll) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFPoll } + +type Dot11DataQOSDataCFAckPoll struct { + Dot11DataQOS +} + +func decodeDot11DataQOSDataCFAckPoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSDataCFAckPoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSDataCFAckPoll) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSDataCFAckPoll +} +func (m *Dot11DataQOSDataCFAckPoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSDataCFAckPoll +} +func (m *Dot11DataQOSDataCFAckPoll) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPoll +} + +type Dot11DataQOSNull struct { + Dot11DataQOS +} + +func decodeDot11DataQOSNull(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSNull{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSNull) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSNull } +func (m *Dot11DataQOSNull) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSNull } +func (m *Dot11DataQOSNull) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataNull } + +type Dot11DataQOSCFPollNoData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSCFPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSCFPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSCFPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSCFPollNoData +} +func (m *Dot11DataQOSCFPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSCFPollNoData +} +func (m *Dot11DataQOSCFPollNoData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFPollNoData +} + +type Dot11DataQOSCFAckPollNoData struct { + Dot11DataQOS +} + +func decodeDot11DataQOSCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11DataQOSCFAckPollNoData{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11DataQOSCFAckPollNoData) LayerType() gopacket.LayerType { + return LayerTypeDot11DataQOSCFAckPollNoData +} +func (m *Dot11DataQOSCFAckPollNoData) CanDecode() gopacket.LayerClass { + return LayerTypeDot11DataQOSCFAckPollNoData +} +func (m *Dot11DataQOSCFAckPollNoData) NextLayerType() gopacket.LayerType { + return LayerTypeDot11DataCFAckPollNoData +} + +type Dot11InformationElement struct { + BaseLayer + ID Dot11InformationElementID + Length uint8 + OUI []byte + Info []byte +} + +func (m *Dot11InformationElement) LayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11InformationElement) CanDecode() gopacket.LayerClass { + return LayerTypeDot11InformationElement +} + +func (m *Dot11InformationElement) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), 2) + } + m.ID = Dot11InformationElementID(data[0]) + m.Length = data[1] + offset := int(2) + + if len(data) < offset+int(m.Length) { + df.SetTruncated() + return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), offset+int(m.Length)) + } + if len(data) < offset+4 { + df.SetTruncated() + return fmt.Errorf("vendor extension size < %d", offset+int(m.Length)) + } + if m.ID == 221 { + // Vendor extension + m.OUI = data[offset : offset+4] + m.Info = data[offset+4 : offset+int(m.Length)] + } else { + m.Info = data[offset : offset+int(m.Length)] + } + + offset += int(m.Length) + + m.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]} + return nil +} + +func (d *Dot11InformationElement) String() string { + if d.ID == 0 { + return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, SSID: %v)", d.ID, d.Length, string(d.Info)) + } else if d.ID == 1 { + rates := "" + for i := 0; i < len(d.Info); i++ { + if d.Info[i]&0x80 == 0 { + rates += fmt.Sprintf("%.1f ", float32(d.Info[i])*0.5) + } else { + rates += fmt.Sprintf("%.1f* ", float32(d.Info[i]&0x7F)*0.5) + } + } + return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, Rates: %s Mbit)", d.ID, d.Length, rates) + } else if d.ID == 221 { + return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, OUI: %X, Info: %X)", d.ID, d.Length, d.OUI, d.Info) + } else { + return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, Info: %X)", d.ID, d.Length, d.Info) + } +} + +func (m Dot11InformationElement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := len(m.Info) + len(m.OUI) + if buf, err := b.PrependBytes(2 + length); err != nil { + return err + } else { + buf[0] = uint8(m.ID) + buf[1] = uint8(length) + copy(buf[2:], m.OUI) + copy(buf[2+len(m.OUI):], m.Info) + } + return nil +} + +func decodeDot11InformationElement(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11InformationElement{} + return decodingLayerDecoder(d, data, p) +} + +type Dot11CtrlCTS struct { + Dot11Ctrl +} + +func decodeDot11CtrlCTS(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCTS{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCTS) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCTS +} +func (m *Dot11CtrlCTS) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCTS +} +func (m *Dot11CtrlCTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlRTS struct { + Dot11Ctrl +} + +func decodeDot11CtrlRTS(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlRTS{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlRTS) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlRTS +} +func (m *Dot11CtrlRTS) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlRTS +} +func (m *Dot11CtrlRTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlBlockAckReq struct { + Dot11Ctrl +} + +func decodeDot11CtrlBlockAckReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlBlockAckReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlBlockAckReq) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlBlockAckReq +} +func (m *Dot11CtrlBlockAckReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlBlockAckReq +} +func (m *Dot11CtrlBlockAckReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlBlockAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlBlockAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlBlockAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlBlockAck) LayerType() gopacket.LayerType { return LayerTypeDot11CtrlBlockAck } +func (m *Dot11CtrlBlockAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlBlockAck } +func (m *Dot11CtrlBlockAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlPowersavePoll struct { + Dot11Ctrl +} + +func decodeDot11CtrlPowersavePoll(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlPowersavePoll{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlPowersavePoll) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlPowersavePoll +} +func (m *Dot11CtrlPowersavePoll) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlPowersavePoll +} +func (m *Dot11CtrlPowersavePoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlAck) LayerType() gopacket.LayerType { return LayerTypeDot11CtrlAck } +func (m *Dot11CtrlAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlAck } +func (m *Dot11CtrlAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlCFEnd struct { + Dot11Ctrl +} + +func decodeDot11CtrlCFEnd(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCFEnd{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCFEnd) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCFEnd +} +func (m *Dot11CtrlCFEnd) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCFEnd +} +func (m *Dot11CtrlCFEnd) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11CtrlCFEndAck struct { + Dot11Ctrl +} + +func decodeDot11CtrlCFEndAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11CtrlCFEndAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11CtrlCFEndAck) LayerType() gopacket.LayerType { + return LayerTypeDot11CtrlCFEndAck +} +func (m *Dot11CtrlCFEndAck) CanDecode() gopacket.LayerClass { + return LayerTypeDot11CtrlCFEndAck +} +func (m *Dot11CtrlCFEndAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + return m.Dot11Ctrl.DecodeFromBytes(data, df) +} + +type Dot11MgmtAssociationReq struct { + Dot11Mgmt + CapabilityInfo uint16 + ListenInterval uint16 +} + +func decodeDot11MgmtAssociationReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAssociationReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAssociationReq) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAssociationReq +} +func (m *Dot11MgmtAssociationReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAssociationReq +} +func (m *Dot11MgmtAssociationReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAssociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAssociationReq length %v too short, %v required", len(data), 4) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.ListenInterval = binary.LittleEndian.Uint16(data[2:4]) + m.Payload = data[4:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAssociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(4) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval) + + return nil +} + +type Dot11MgmtAssociationResp struct { + Dot11Mgmt + CapabilityInfo uint16 + Status Dot11Status + AID uint16 +} + +func decodeDot11MgmtAssociationResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAssociationResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAssociationResp) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAssociationResp +} +func (m *Dot11MgmtAssociationResp) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAssociationResp +} +func (m *Dot11MgmtAssociationResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAssociationResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 6 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAssociationResp length %v too short, %v required", len(data), 6) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.Status = Dot11Status(binary.LittleEndian.Uint16(data[2:4])) + m.AID = binary.LittleEndian.Uint16(data[4:6]) + m.Payload = data[6:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAssociationResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(6) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], uint16(m.Status)) + binary.LittleEndian.PutUint16(buf[4:6], m.AID) + + return nil +} + +type Dot11MgmtReassociationReq struct { + Dot11Mgmt + CapabilityInfo uint16 + ListenInterval uint16 + CurrentApAddress net.HardwareAddr +} + +func decodeDot11MgmtReassociationReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtReassociationReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtReassociationReq) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtReassociationReq +} +func (m *Dot11MgmtReassociationReq) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtReassociationReq +} +func (m *Dot11MgmtReassociationReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtReassociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 10 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtReassociationReq length %v too short, %v required", len(data), 10) + } + m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2]) + m.ListenInterval = binary.LittleEndian.Uint16(data[2:4]) + m.CurrentApAddress = net.HardwareAddr(data[4:10]) + m.Payload = data[10:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtReassociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(10) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo) + binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval) + + copy(buf[4:10], m.CurrentApAddress) + + return nil +} + +type Dot11MgmtReassociationResp struct { + Dot11Mgmt +} + +func decodeDot11MgmtReassociationResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtReassociationResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtReassociationResp) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtReassociationResp +} +func (m *Dot11MgmtReassociationResp) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtReassociationResp +} +func (m *Dot11MgmtReassociationResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +type Dot11MgmtProbeReq struct { + Dot11Mgmt +} + +func decodeDot11MgmtProbeReq(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtProbeReq{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtProbeReq) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtProbeReq } +func (m *Dot11MgmtProbeReq) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeReq } +func (m *Dot11MgmtProbeReq) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +type Dot11MgmtProbeResp struct { + Dot11Mgmt + Timestamp uint64 + Interval uint16 + Flags uint16 +} + +func decodeDot11MgmtProbeResp(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtProbeResp{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtProbeResp) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtProbeResp } +func (m *Dot11MgmtProbeResp) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeResp } +func (m *Dot11MgmtProbeResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + df.SetTruncated() + + return fmt.Errorf("Dot11MgmtProbeResp length %v too short, %v required", len(data), 12) + } + + m.Timestamp = binary.LittleEndian.Uint64(data[0:8]) + m.Interval = binary.LittleEndian.Uint16(data[8:10]) + m.Flags = binary.LittleEndian.Uint16(data[10:12]) + m.Payload = data[12:] + + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m *Dot11MgmtProbeResp) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} + +func (m Dot11MgmtProbeResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(12) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp) + binary.LittleEndian.PutUint16(buf[8:10], m.Interval) + binary.LittleEndian.PutUint16(buf[10:12], m.Flags) + + return nil +} + +type Dot11MgmtMeasurementPilot struct { + Dot11Mgmt +} + +func decodeDot11MgmtMeasurementPilot(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtMeasurementPilot{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtMeasurementPilot) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtMeasurementPilot +} +func (m *Dot11MgmtMeasurementPilot) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtMeasurementPilot +} + +type Dot11MgmtBeacon struct { + Dot11Mgmt + Timestamp uint64 + Interval uint16 + Flags uint16 +} + +func decodeDot11MgmtBeacon(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtBeacon{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtBeacon) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtBeacon } +func (m *Dot11MgmtBeacon) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtBeacon } +func (m *Dot11MgmtBeacon) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtBeacon length %v too short, %v required", len(data), 12) + } + m.Timestamp = binary.LittleEndian.Uint64(data[0:8]) + m.Interval = binary.LittleEndian.Uint16(data[8:10]) + m.Flags = binary.LittleEndian.Uint16(data[10:12]) + m.Payload = data[12:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m *Dot11MgmtBeacon) NextLayerType() gopacket.LayerType { return LayerTypeDot11InformationElement } + +func (m Dot11MgmtBeacon) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(12) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp) + binary.LittleEndian.PutUint16(buf[8:10], m.Interval) + binary.LittleEndian.PutUint16(buf[10:12], m.Flags) + + return nil +} + +type Dot11MgmtATIM struct { + Dot11Mgmt +} + +func decodeDot11MgmtATIM(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtATIM{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtATIM) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtATIM } +func (m *Dot11MgmtATIM) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtATIM } + +type Dot11MgmtDisassociation struct { + Dot11Mgmt + Reason Dot11Reason +} + +func decodeDot11MgmtDisassociation(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtDisassociation{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtDisassociation) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtDisassociation +} +func (m *Dot11MgmtDisassociation) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtDisassociation +} +func (m *Dot11MgmtDisassociation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtDisassociation length %v too short, %v required", len(data), 2) + } + m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2])) + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtDisassociation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(2) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason)) + + return nil +} + +type Dot11MgmtAuthentication struct { + Dot11Mgmt + Algorithm Dot11Algorithm + Sequence uint16 + Status Dot11Status +} + +func decodeDot11MgmtAuthentication(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAuthentication{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAuthentication) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtAuthentication +} +func (m *Dot11MgmtAuthentication) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtAuthentication +} +func (m *Dot11MgmtAuthentication) NextLayerType() gopacket.LayerType { + return LayerTypeDot11InformationElement +} +func (m *Dot11MgmtAuthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 6 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtAuthentication length %v too short, %v required", len(data), 6) + } + m.Algorithm = Dot11Algorithm(binary.LittleEndian.Uint16(data[0:2])) + m.Sequence = binary.LittleEndian.Uint16(data[2:4]) + m.Status = Dot11Status(binary.LittleEndian.Uint16(data[4:6])) + m.Payload = data[6:] + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtAuthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(6) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Algorithm)) + binary.LittleEndian.PutUint16(buf[2:4], m.Sequence) + binary.LittleEndian.PutUint16(buf[4:6], uint16(m.Status)) + + return nil +} + +type Dot11MgmtDeauthentication struct { + Dot11Mgmt + Reason Dot11Reason +} + +func decodeDot11MgmtDeauthentication(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtDeauthentication{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtDeauthentication) LayerType() gopacket.LayerType { + return LayerTypeDot11MgmtDeauthentication +} +func (m *Dot11MgmtDeauthentication) CanDecode() gopacket.LayerClass { + return LayerTypeDot11MgmtDeauthentication +} +func (m *Dot11MgmtDeauthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Dot11MgmtDeauthentication length %v too short, %v required", len(data), 2) + } + m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2])) + return m.Dot11Mgmt.DecodeFromBytes(data, df) +} + +func (m Dot11MgmtDeauthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(2) + + if err != nil { + return err + } + + binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason)) + + return nil +} + +type Dot11MgmtAction struct { + Dot11Mgmt +} + +func decodeDot11MgmtAction(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtAction{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtAction) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtAction } +func (m *Dot11MgmtAction) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtAction } + +type Dot11MgmtActionNoAck struct { + Dot11Mgmt +} + +func decodeDot11MgmtActionNoAck(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtActionNoAck{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtActionNoAck) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtActionNoAck } +func (m *Dot11MgmtActionNoAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtActionNoAck } + +type Dot11MgmtArubaWLAN struct { + Dot11Mgmt +} + +func decodeDot11MgmtArubaWLAN(data []byte, p gopacket.PacketBuilder) error { + d := &Dot11MgmtArubaWLAN{} + return decodingLayerDecoder(d, data, p) +} + +func (m *Dot11MgmtArubaWLAN) LayerType() gopacket.LayerType { return LayerTypeDot11MgmtArubaWLAN } +func (m *Dot11MgmtArubaWLAN) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtArubaWLAN } diff --git a/vendor/github.com/google/gopacket/layers/dot1q.go b/vendor/github.com/google/gopacket/layers/dot1q.go new file mode 100644 index 00000000..5cdd2f8d --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/dot1q.go @@ -0,0 +1,75 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// Dot1Q is the packet layer for 802.1Q VLAN headers. +type Dot1Q struct { + BaseLayer + Priority uint8 + DropEligible bool + VLANIdentifier uint16 + Type EthernetType +} + +// LayerType returns gopacket.LayerTypeDot1Q +func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q } + +// DecodeFromBytes decodes the given bytes into this layer. +func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("802.1Q tag length %d too short", len(data)) + } + d.Priority = (data[0] & 0xE0) >> 5 + d.DropEligible = data[0]&0x10 != 0 + d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF + d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4])) + d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (d *Dot1Q) CanDecode() gopacket.LayerClass { + return LayerTypeDot1Q +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (d *Dot1Q) NextLayerType() gopacket.LayerType { + return d.Type.LayerType() +} + +func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error { + d := &Dot1Q{} + return decodingLayerDecoder(d, data, p) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + if d.VLANIdentifier > 0xFFF { + return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier) + } + firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier + if d.DropEligible { + firstBytes |= 0x1000 + } + binary.BigEndian.PutUint16(bytes, firstBytes) + binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type)) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/eap.go b/vendor/github.com/google/gopacket/layers/eap.go new file mode 100644 index 00000000..54238e8c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/eap.go @@ -0,0 +1,114 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +type EAPCode uint8 +type EAPType uint8 + +const ( + EAPCodeRequest EAPCode = 1 + EAPCodeResponse EAPCode = 2 + EAPCodeSuccess EAPCode = 3 + EAPCodeFailure EAPCode = 4 + + // EAPTypeNone means that this EAP layer has no Type or TypeData. + // Success and Failure EAPs will have this set. + EAPTypeNone EAPType = 0 + + EAPTypeIdentity EAPType = 1 + EAPTypeNotification EAPType = 2 + EAPTypeNACK EAPType = 3 + EAPTypeOTP EAPType = 4 + EAPTypeTokenCard EAPType = 5 +) + +// EAP defines an Extensible Authentication Protocol (rfc 3748) layer. +type EAP struct { + BaseLayer + Code EAPCode + Id uint8 + Length uint16 + Type EAPType + TypeData []byte +} + +// LayerType returns LayerTypeEAP. +func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("EAP length %d too short", len(data)) + } + e.Code = EAPCode(data[0]) + e.Id = data[1] + e.Length = binary.BigEndian.Uint16(data[2:4]) + if len(data) < int(e.Length) { + df.SetTruncated() + return fmt.Errorf("EAP length %d too short, %d expected", len(data), e.Length) + } + switch { + case e.Length > 4: + e.Type = EAPType(data[4]) + e.TypeData = data[5:] + case e.Length == 4: + e.Type = 0 + e.TypeData = nil + default: + return fmt.Errorf("invalid EAP length %d", e.Length) + } + e.BaseLayer.Contents = data[:e.Length] + e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if opts.FixLengths { + e.Length = uint16(len(e.TypeData) + 1) + } + size := len(e.TypeData) + 4 + if size > 4 { + size++ + } + bytes, err := b.PrependBytes(size) + if err != nil { + return err + } + bytes[0] = byte(e.Code) + bytes[1] = e.Id + binary.BigEndian.PutUint16(bytes[2:], e.Length) + if size > 4 { + bytes[4] = byte(e.Type) + copy(bytes[5:], e.TypeData) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EAP) CanDecode() gopacket.LayerClass { + return LayerTypeEAP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EAP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +func decodeEAP(data []byte, p gopacket.PacketBuilder) error { + e := &EAP{} + return decodingLayerDecoder(e, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/eapol.go b/vendor/github.com/google/gopacket/layers/eapol.go new file mode 100644 index 00000000..902598a2 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/eapol.go @@ -0,0 +1,302 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +// EAPOL defines an EAP over LAN (802.1x) layer. +type EAPOL struct { + BaseLayer + Version uint8 + Type EAPOLType + Length uint16 +} + +// LayerType returns LayerTypeEAPOL. +func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("EAPOL length %d too short", len(data)) + } + e.Version = data[0] + e.Type = EAPOLType(data[1]) + e.Length = binary.BigEndian.Uint16(data[2:4]) + e.BaseLayer = BaseLayer{data[:4], data[4:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer +func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, _ := b.PrependBytes(4) + bytes[0] = e.Version + bytes[1] = byte(e.Type) + binary.BigEndian.PutUint16(bytes[2:], e.Length) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EAPOL) CanDecode() gopacket.LayerClass { + return LayerTypeEAPOL +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EAPOL) NextLayerType() gopacket.LayerType { + return e.Type.LayerType() +} + +func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error { + e := &EAPOL{} + return decodingLayerDecoder(e, data, p) +} + +// EAPOLKeyDescriptorType is an enumeration of key descriptor types +// as specified by 802.1x in the EAPOL-Key frame +type EAPOLKeyDescriptorType uint8 + +// Enumeration of EAPOLKeyDescriptorType +const ( + EAPOLKeyDescriptorTypeRC4 EAPOLKeyDescriptorType = 1 + EAPOLKeyDescriptorTypeDot11 EAPOLKeyDescriptorType = 2 + EAPOLKeyDescriptorTypeWPA EAPOLKeyDescriptorType = 254 +) + +func (kdt EAPOLKeyDescriptorType) String() string { + switch kdt { + case EAPOLKeyDescriptorTypeRC4: + return "RC4" + case EAPOLKeyDescriptorTypeDot11: + return "802.11" + case EAPOLKeyDescriptorTypeWPA: + return "WPA" + default: + return fmt.Sprintf("unknown descriptor type %d", kdt) + } +} + +// EAPOLKeyDescriptorVersion is an enumeration of versions specifying the +// encryption algorithm for the key data and the authentication for the +// message integrity code (MIC) +type EAPOLKeyDescriptorVersion uint8 + +// Enumeration of EAPOLKeyDescriptorVersion +const ( + EAPOLKeyDescriptorVersionOther EAPOLKeyDescriptorVersion = 0 + EAPOLKeyDescriptorVersionRC4HMACMD5 EAPOLKeyDescriptorVersion = 1 + EAPOLKeyDescriptorVersionAESHMACSHA1 EAPOLKeyDescriptorVersion = 2 + EAPOLKeyDescriptorVersionAES128CMAC EAPOLKeyDescriptorVersion = 3 +) + +func (v EAPOLKeyDescriptorVersion) String() string { + switch v { + case EAPOLKeyDescriptorVersionOther: + return "Other" + case EAPOLKeyDescriptorVersionRC4HMACMD5: + return "RC4-HMAC-MD5" + case EAPOLKeyDescriptorVersionAESHMACSHA1: + return "AES-HMAC-SHA1-128" + case EAPOLKeyDescriptorVersionAES128CMAC: + return "AES-128-CMAC" + default: + return fmt.Sprintf("unknown version %d", v) + } +} + +// EAPOLKeyType is an enumeration of key derivation types describing +// the purpose of the keys being derived. +type EAPOLKeyType uint8 + +// Enumeration of EAPOLKeyType +const ( + EAPOLKeyTypeGroupSMK EAPOLKeyType = 0 + EAPOLKeyTypePairwise EAPOLKeyType = 1 +) + +func (kt EAPOLKeyType) String() string { + switch kt { + case EAPOLKeyTypeGroupSMK: + return "Group/SMK" + case EAPOLKeyTypePairwise: + return "Pairwise" + default: + return fmt.Sprintf("unknown key type %d", kt) + } +} + +// EAPOLKey defines an EAPOL-Key frame for 802.1x authentication +type EAPOLKey struct { + BaseLayer + KeyDescriptorType EAPOLKeyDescriptorType + KeyDescriptorVersion EAPOLKeyDescriptorVersion + KeyType EAPOLKeyType + KeyIndex uint8 + Install bool + KeyACK bool + KeyMIC bool + Secure bool + MICError bool + Request bool + HasEncryptedKeyData bool + SMKMessage bool + KeyLength uint16 + ReplayCounter uint64 + Nonce []byte + IV []byte + RSC uint64 + ID uint64 + MIC []byte + KeyDataLength uint16 + EncryptedKeyData []byte +} + +// LayerType returns LayerTypeEAPOLKey. +func (ek *EAPOLKey) LayerType() gopacket.LayerType { + return LayerTypeEAPOLKey +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (ek *EAPOLKey) CanDecode() gopacket.LayerType { + return LayerTypeEAPOLKey +} + +// NextLayerType returns layers.LayerTypeDot11InformationElement if the key +// data exists and is unencrypted, otherwise it does not expect a next layer. +func (ek *EAPOLKey) NextLayerType() gopacket.LayerType { + if !ek.HasEncryptedKeyData && ek.KeyDataLength > 0 { + return LayerTypeDot11InformationElement + } + return gopacket.LayerTypePayload +} + +const eapolKeyFrameLen = 95 + +// DecodeFromBytes decodes the given bytes into this layer. +func (ek *EAPOLKey) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < eapolKeyFrameLen { + df.SetTruncated() + return fmt.Errorf("EAPOLKey length %v too short, %v required", + len(data), eapolKeyFrameLen) + } + + ek.KeyDescriptorType = EAPOLKeyDescriptorType(data[0]) + + info := binary.BigEndian.Uint16(data[1:3]) + ek.KeyDescriptorVersion = EAPOLKeyDescriptorVersion(info & 0x0007) + ek.KeyType = EAPOLKeyType((info & 0x0008) >> 3) + ek.KeyIndex = uint8((info & 0x0030) >> 4) + ek.Install = (info & 0x0040) != 0 + ek.KeyACK = (info & 0x0080) != 0 + ek.KeyMIC = (info & 0x0100) != 0 + ek.Secure = (info & 0x0200) != 0 + ek.MICError = (info & 0x0400) != 0 + ek.Request = (info & 0x0800) != 0 + ek.HasEncryptedKeyData = (info & 0x1000) != 0 + ek.SMKMessage = (info & 0x2000) != 0 + + ek.KeyLength = binary.BigEndian.Uint16(data[3:5]) + ek.ReplayCounter = binary.BigEndian.Uint64(data[5:13]) + + ek.Nonce = data[13:45] + ek.IV = data[45:61] + ek.RSC = binary.BigEndian.Uint64(data[61:69]) + ek.ID = binary.BigEndian.Uint64(data[69:77]) + ek.MIC = data[77:93] + + ek.KeyDataLength = binary.BigEndian.Uint16(data[93:95]) + + totalLength := eapolKeyFrameLen + int(ek.KeyDataLength) + if len(data) < totalLength { + df.SetTruncated() + return fmt.Errorf("EAPOLKey data length %d too short, %d required", + len(data)-eapolKeyFrameLen, ek.KeyDataLength) + } + + if ek.HasEncryptedKeyData { + ek.EncryptedKeyData = data[eapolKeyFrameLen:totalLength] + ek.BaseLayer = BaseLayer{ + Contents: data[:totalLength], + Payload: data[totalLength:], + } + } else { + ek.BaseLayer = BaseLayer{ + Contents: data[:eapolKeyFrameLen], + Payload: data[eapolKeyFrameLen:], + } + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (ek *EAPOLKey) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(eapolKeyFrameLen + len(ek.EncryptedKeyData)) + if err != nil { + return err + } + + buf[0] = byte(ek.KeyDescriptorType) + + var info uint16 + info |= uint16(ek.KeyDescriptorVersion) + info |= uint16(ek.KeyType) << 3 + info |= uint16(ek.KeyIndex) << 4 + if ek.Install { + info |= 0x0040 + } + if ek.KeyACK { + info |= 0x0080 + } + if ek.KeyMIC { + info |= 0x0100 + } + if ek.Secure { + info |= 0x0200 + } + if ek.MICError { + info |= 0x0400 + } + if ek.Request { + info |= 0x0800 + } + if ek.HasEncryptedKeyData { + info |= 0x1000 + } + if ek.SMKMessage { + info |= 0x2000 + } + binary.BigEndian.PutUint16(buf[1:3], info) + + binary.BigEndian.PutUint16(buf[3:5], ek.KeyLength) + binary.BigEndian.PutUint64(buf[5:13], ek.ReplayCounter) + + copy(buf[13:45], ek.Nonce) + copy(buf[45:61], ek.IV) + binary.BigEndian.PutUint64(buf[61:69], ek.RSC) + binary.BigEndian.PutUint64(buf[69:77], ek.ID) + copy(buf[77:93], ek.MIC) + + binary.BigEndian.PutUint16(buf[93:95], ek.KeyDataLength) + if len(ek.EncryptedKeyData) > 0 { + copy(buf[95:95+len(ek.EncryptedKeyData)], ek.EncryptedKeyData) + } + + return nil +} + +func decodeEAPOLKey(data []byte, p gopacket.PacketBuilder) error { + ek := &EAPOLKey{} + return decodingLayerDecoder(ek, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/endpoints.go b/vendor/github.com/google/gopacket/layers/endpoints.go new file mode 100644 index 00000000..4c91cc33 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/endpoints.go @@ -0,0 +1,97 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" + "net" + "strconv" +) + +var ( + // We use two different endpoint types for IPv4 vs IPv6 addresses, so that + // ordering with endpointA.LessThan(endpointB) sanely groups all IPv4 + // addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses. + EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string { + return net.IP(b).String() + }}) + EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string { + return net.IP(b).String() + }}) + + EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string { + return net.HardwareAddr(b).String() + }}) + EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string { + return strconv.Itoa(int(b[0])) + }}) + EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string { + return strconv.Itoa(int(binary.BigEndian.Uint16(b))) + }}) + EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string { + return "point" + }}) +) + +// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address. +// It returns gopacket.InvalidEndpoint if the IP address is invalid. +func NewIPEndpoint(a net.IP) gopacket.Endpoint { + ipv4 := a.To4() + if ipv4 != nil { + return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4)) + } + + ipv6 := a.To16() + if ipv6 != nil { + return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6)) + } + + return gopacket.InvalidEndpoint +} + +// NewMACEndpoint returns a new MAC address endpoint. +func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointMAC, []byte(a)) +} +func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint { + return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)}) +} + +// NewTCPPortEndpoint returns an endpoint based on a TCP port. +func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointTCPPort, uint16(p)) +} + +// NewUDPPortEndpoint returns an endpoint based on a UDP port. +func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointUDPPort, uint16(p)) +} + +// NewSCTPPortEndpoint returns an endpoint based on a SCTP port. +func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint { + return newPortEndpoint(EndpointSCTPPort, uint16(p)) +} + +// NewRUDPPortEndpoint returns an endpoint based on a RUDP port. +func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint { + return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)}) +} + +// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port. +func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint { + return newPortEndpoint(EndpointUDPLitePort, uint16(p)) +} diff --git a/vendor/github.com/google/gopacket/layers/enums.go b/vendor/github.com/google/gopacket/layers/enums.go new file mode 100644 index 00000000..8427bdaf --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/enums.go @@ -0,0 +1,443 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "fmt" + "runtime" + + "github.com/google/gopacket" +) + +// EnumMetadata keeps track of a set of metadata for each enumeration value +// for protocol enumerations. +type EnumMetadata struct { + // DecodeWith is the decoder to use to decode this protocol's data. + DecodeWith gopacket.Decoder + // Name is the name of the enumeration value. + Name string + // LayerType is the layer type implied by the given enum. + LayerType gopacket.LayerType +} + +// EthernetType is an enumeration of ethernet type values, and acts as a decoder +// for any type it supports. +type EthernetType uint16 + +const ( + // EthernetTypeLLC is not an actual ethernet type. It is instead a + // placeholder we use in Ethernet frames that use the 802.3 standard of + // srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype. + EthernetTypeLLC EthernetType = 0 + EthernetTypeIPv4 EthernetType = 0x0800 + EthernetTypeARP EthernetType = 0x0806 + EthernetTypeIPv6 EthernetType = 0x86DD + EthernetTypeCiscoDiscovery EthernetType = 0x2000 + EthernetTypeNortelDiscovery EthernetType = 0x01a2 + EthernetTypeTransparentEthernetBridging EthernetType = 0x6558 + EthernetTypeDot1Q EthernetType = 0x8100 + EthernetTypePPP EthernetType = 0x880b + EthernetTypePPPoEDiscovery EthernetType = 0x8863 + EthernetTypePPPoESession EthernetType = 0x8864 + EthernetTypeMPLSUnicast EthernetType = 0x8847 + EthernetTypeMPLSMulticast EthernetType = 0x8848 + EthernetTypeEAPOL EthernetType = 0x888e + EthernetTypeERSPAN EthernetType = 0x88be + EthernetTypeQinQ EthernetType = 0x88a8 + EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc + EthernetTypeEthernetCTP EthernetType = 0x9000 +) + +// IPProtocol is an enumeration of IP protocol values, and acts as a decoder +// for any type it supports. +type IPProtocol uint8 + +const ( + IPProtocolIPv6HopByHop IPProtocol = 0 + IPProtocolICMPv4 IPProtocol = 1 + IPProtocolIGMP IPProtocol = 2 + IPProtocolIPv4 IPProtocol = 4 + IPProtocolTCP IPProtocol = 6 + IPProtocolUDP IPProtocol = 17 + IPProtocolRUDP IPProtocol = 27 + IPProtocolIPv6 IPProtocol = 41 + IPProtocolIPv6Routing IPProtocol = 43 + IPProtocolIPv6Fragment IPProtocol = 44 + IPProtocolGRE IPProtocol = 47 + IPProtocolESP IPProtocol = 50 + IPProtocolAH IPProtocol = 51 + IPProtocolICMPv6 IPProtocol = 58 + IPProtocolNoNextHeader IPProtocol = 59 + IPProtocolIPv6Destination IPProtocol = 60 + IPProtocolOSPF IPProtocol = 89 + IPProtocolIPIP IPProtocol = 94 + IPProtocolEtherIP IPProtocol = 97 + IPProtocolVRRP IPProtocol = 112 + IPProtocolSCTP IPProtocol = 132 + IPProtocolUDPLite IPProtocol = 136 + IPProtocolMPLSInIP IPProtocol = 137 +) + +// LinkType is an enumeration of link types, and acts as a decoder for any +// link type it supports. +type LinkType uint8 + +const ( + // According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html + LinkTypeNull LinkType = 0 + LinkTypeEthernet LinkType = 1 + LinkTypeAX25 LinkType = 3 + LinkTypeTokenRing LinkType = 6 + LinkTypeArcNet LinkType = 7 + LinkTypeSLIP LinkType = 8 + LinkTypePPP LinkType = 9 + LinkTypeFDDI LinkType = 10 + LinkTypePPP_HDLC LinkType = 50 + LinkTypePPPEthernet LinkType = 51 + LinkTypeATM_RFC1483 LinkType = 100 + LinkTypeRaw LinkType = 101 + LinkTypeC_HDLC LinkType = 104 + LinkTypeIEEE802_11 LinkType = 105 + LinkTypeFRelay LinkType = 107 + LinkTypeLoop LinkType = 108 + LinkTypeLinuxSLL LinkType = 113 + LinkTypeLTalk LinkType = 114 + LinkTypePFLog LinkType = 117 + LinkTypePrismHeader LinkType = 119 + LinkTypeIPOverFC LinkType = 122 + LinkTypeSunATM LinkType = 123 + LinkTypeIEEE80211Radio LinkType = 127 + LinkTypeARCNetLinux LinkType = 129 + LinkTypeIPOver1394 LinkType = 138 + LinkTypeMTP2Phdr LinkType = 139 + LinkTypeMTP2 LinkType = 140 + LinkTypeMTP3 LinkType = 141 + LinkTypeSCCP LinkType = 142 + LinkTypeDOCSIS LinkType = 143 + LinkTypeLinuxIRDA LinkType = 144 + LinkTypeLinuxLAPD LinkType = 177 + LinkTypeLinuxUSB LinkType = 220 + LinkTypeFC2 LinkType = 224 + LinkTypeFC2Framed LinkType = 225 + LinkTypeIPv4 LinkType = 228 + LinkTypeIPv6 LinkType = 229 +) + +// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516 +type PPPoECode uint8 + +const ( + PPPoECodePADI PPPoECode = 0x09 + PPPoECodePADO PPPoECode = 0x07 + PPPoECodePADR PPPoECode = 0x19 + PPPoECodePADS PPPoECode = 0x65 + PPPoECodePADT PPPoECode = 0xA7 + PPPoECodeSession PPPoECode = 0x00 +) + +// PPPType is an enumeration of PPP type values, and acts as a decoder for any +// type it supports. +type PPPType uint16 + +const ( + PPPTypeIPv4 PPPType = 0x0021 + PPPTypeIPv6 PPPType = 0x0057 + PPPTypeMPLSUnicast PPPType = 0x0281 + PPPTypeMPLSMulticast PPPType = 0x0283 +) + +// SCTPChunkType is an enumeration of chunk types inside SCTP packets. +type SCTPChunkType uint8 + +const ( + SCTPChunkTypeData SCTPChunkType = 0 + SCTPChunkTypeInit SCTPChunkType = 1 + SCTPChunkTypeInitAck SCTPChunkType = 2 + SCTPChunkTypeSack SCTPChunkType = 3 + SCTPChunkTypeHeartbeat SCTPChunkType = 4 + SCTPChunkTypeHeartbeatAck SCTPChunkType = 5 + SCTPChunkTypeAbort SCTPChunkType = 6 + SCTPChunkTypeShutdown SCTPChunkType = 7 + SCTPChunkTypeShutdownAck SCTPChunkType = 8 + SCTPChunkTypeError SCTPChunkType = 9 + SCTPChunkTypeCookieEcho SCTPChunkType = 10 + SCTPChunkTypeCookieAck SCTPChunkType = 11 + SCTPChunkTypeShutdownComplete SCTPChunkType = 14 +) + +// FDDIFrameControl is an enumeration of FDDI frame control bytes. +type FDDIFrameControl uint8 + +const ( + FDDIFrameControlLLC FDDIFrameControl = 0x50 +) + +// EAPOLType is an enumeration of EAPOL packet types. +type EAPOLType uint8 + +const ( + EAPOLTypeEAP EAPOLType = 0 + EAPOLTypeStart EAPOLType = 1 + EAPOLTypeLogOff EAPOLType = 2 + EAPOLTypeKey EAPOLType = 3 + EAPOLTypeASFAlert EAPOLType = 4 +) + +// ProtocolFamily is the set of values defined as PF_* in sys/socket.h +type ProtocolFamily uint8 + +const ( + ProtocolFamilyIPv4 ProtocolFamily = 2 + // BSDs use different values for INET6... glory be. These values taken from + // tcpdump 4.3.0. + ProtocolFamilyIPv6BSD ProtocolFamily = 24 + ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28 + ProtocolFamilyIPv6Darwin ProtocolFamily = 30 + ProtocolFamilyIPv6Linux ProtocolFamily = 10 +) + +// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields. +// By combining these two fields together into a single type, we're able to +// provide a String function that correctly displays the subtype given the +// top-level type. +// +// If you just care about the top-level type, use the MainType function. +type Dot11Type uint8 + +// MainType strips the subtype information from the given type, +// returning just the overarching type (Mgmt, Ctrl, Data, Reserved). +func (d Dot11Type) MainType() Dot11Type { + return d & dot11TypeMask +} + +func (d Dot11Type) QOS() bool { + return d&dot11QOSMask == Dot11TypeDataQOSData +} + +const ( + Dot11TypeMgmt Dot11Type = 0x00 + Dot11TypeCtrl Dot11Type = 0x01 + Dot11TypeData Dot11Type = 0x02 + Dot11TypeReserved Dot11Type = 0x03 + dot11TypeMask = 0x03 + dot11QOSMask = 0x23 + + // The following are type/subtype conglomerations. + + // Management + Dot11TypeMgmtAssociationReq Dot11Type = 0x00 + Dot11TypeMgmtAssociationResp Dot11Type = 0x04 + Dot11TypeMgmtReassociationReq Dot11Type = 0x08 + Dot11TypeMgmtReassociationResp Dot11Type = 0x0c + Dot11TypeMgmtProbeReq Dot11Type = 0x10 + Dot11TypeMgmtProbeResp Dot11Type = 0x14 + Dot11TypeMgmtMeasurementPilot Dot11Type = 0x18 + Dot11TypeMgmtBeacon Dot11Type = 0x20 + Dot11TypeMgmtATIM Dot11Type = 0x24 + Dot11TypeMgmtDisassociation Dot11Type = 0x28 + Dot11TypeMgmtAuthentication Dot11Type = 0x2c + Dot11TypeMgmtDeauthentication Dot11Type = 0x30 + Dot11TypeMgmtAction Dot11Type = 0x34 + Dot11TypeMgmtActionNoAck Dot11Type = 0x38 + + // Control + Dot11TypeCtrlWrapper Dot11Type = 0x1d + Dot11TypeCtrlBlockAckReq Dot11Type = 0x21 + Dot11TypeCtrlBlockAck Dot11Type = 0x25 + Dot11TypeCtrlPowersavePoll Dot11Type = 0x29 + Dot11TypeCtrlRTS Dot11Type = 0x2d + Dot11TypeCtrlCTS Dot11Type = 0x31 + Dot11TypeCtrlAck Dot11Type = 0x35 + Dot11TypeCtrlCFEnd Dot11Type = 0x39 + Dot11TypeCtrlCFEndAck Dot11Type = 0x3d + + // Data + Dot11TypeDataCFAck Dot11Type = 0x06 + Dot11TypeDataCFPoll Dot11Type = 0x0a + Dot11TypeDataCFAckPoll Dot11Type = 0x0e + Dot11TypeDataNull Dot11Type = 0x12 + Dot11TypeDataCFAckNoData Dot11Type = 0x16 + Dot11TypeDataCFPollNoData Dot11Type = 0x1a + Dot11TypeDataCFAckPollNoData Dot11Type = 0x1e + Dot11TypeDataQOSData Dot11Type = 0x22 + Dot11TypeDataQOSDataCFAck Dot11Type = 0x26 + Dot11TypeDataQOSDataCFPoll Dot11Type = 0x2a + Dot11TypeDataQOSDataCFAckPoll Dot11Type = 0x2e + Dot11TypeDataQOSNull Dot11Type = 0x32 + Dot11TypeDataQOSCFPollNoData Dot11Type = 0x3a + Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e +) + +// Decode a raw v4 or v6 IP packet. +func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error { + version := data[0] >> 4 + switch version { + case 4: + return decodeIPv4(data, p) + case 6: + return decodeIPv6(data, p) + } + return fmt.Errorf("Invalid IP packet version %v", version) +} + +func initActualTypeData() { + // Each of the XXXTypeMetadata arrays contains mappings of how to handle enum + // values for various enum types in gopacket/layers. + // These arrays are actually created by gen2.go and stored in + // enums_generated.go. + // + // So, EthernetTypeMetadata[2] contains information on how to handle EthernetType + // 2, including which name to give it and which decoder to use to decode + // packet data of that type. These arrays are filled by default with all of the + // protocols gopacket/layers knows how to handle, but users of the library can + // add new decoders or override existing ones. For example, if you write a better + // TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith + // with your new decoder, and all gopacket/layers decoding will use your new + // decoder whenever they encounter that IPProtocol. + + // Here we link up all enumerations with their respective names and decoders. + EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC} + EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP} + EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q} + EthernetTypeMetadata[EthernetTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP", LayerType: LayerTypePPP} + EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE} + EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE} + EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP} + EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery} + EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery} + EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery} + EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS} + EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS} + EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL} + EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q} + EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet} + EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII} + + IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP} + IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP} + IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4} + IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6} + IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP} + IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP} + IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP} + IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE} + IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop} + IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing} + IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment} + IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination} + IPProtocolMetadata[IPProtocolOSPF] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeOSPF), Name: "OSPF", LayerType: LayerTypeOSPF} + IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH} + IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP} + IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite} + IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS} + IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload} + IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP} + IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP} + + SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"} + SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"} + SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"} + SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"} + SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"} + SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"} + SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"} + SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"} + + PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"} + PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"} + PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"} + PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"} + + PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"} + + LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"} + LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"} + LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"} + LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"} + LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"} + LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"} + LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"} + LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"} + // See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85 + // Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508 + if runtime.GOOS == "openbsd" { + LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"} + } else { + LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"} + } + LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"} + LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"} + LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"} + LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"} + LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"} + + FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"} + + EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP} + EAPOLTypeMetadata[EAPOLTypeKey] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOLKey), Name: "EAPOLKey", LayerType: LayerTypeEAPOLKey} + + ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4} + ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6} + + Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq} + Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp} + Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq} + Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp} + Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq} + Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp} + Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot} + Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon} + Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM} + Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation} + Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication} + Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication} + Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction} + Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck} + Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl} + Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl} + Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq} + Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck} + Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll} + Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS} + Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS} + Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck} + Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd} + Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck} + Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data} + Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck} + Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll} + Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll} + Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull} + Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData} + Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData} + Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData} + Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll} + Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll} + Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull} + Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData} + Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData} + + USBTransportTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt} + USBTransportTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl} + USBTransportTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk} +} diff --git a/vendor/github.com/google/gopacket/layers/enums_generated.go b/vendor/github.com/google/gopacket/layers/enums_generated.go new file mode 100644 index 00000000..bf77aac5 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/enums_generated.go @@ -0,0 +1,434 @@ +// Copyright 2012 Google, Inc. All rights reserved. + +package layers + +// Created by gen2.go, don't edit manually +// Generated at 2017-10-23 10:20:24.458771856 -0600 MDT m=+0.001159033 + +import ( + "fmt" + + "github.com/google/gopacket" +) + +func init() { + initUnknownTypesForLinkType() + initUnknownTypesForEthernetType() + initUnknownTypesForPPPType() + initUnknownTypesForIPProtocol() + initUnknownTypesForSCTPChunkType() + initUnknownTypesForPPPoECode() + initUnknownTypesForFDDIFrameControl() + initUnknownTypesForEAPOLType() + initUnknownTypesForProtocolFamily() + initUnknownTypesForDot11Type() + initUnknownTypesForUSBTransportType() + initActualTypeData() +} + +// Decoder calls LinkTypeMetadata.DecodeWith's decoder. +func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return LinkTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns LinkTypeMetadata.Name. +func (a LinkType) String() string { + return LinkTypeMetadata[a].Name +} + +// LayerType returns LinkTypeMetadata.LayerType. +func (a LinkType) LayerType() gopacket.LayerType { + return LinkTypeMetadata[a].LayerType +} + +type errorDecoderForLinkType int + +func (a *errorDecoderForLinkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForLinkType) Error() string { + return fmt.Sprintf("Unable to decode LinkType %d", int(*a)) +} + +var errorDecodersForLinkType [256]errorDecoderForLinkType +var LinkTypeMetadata [256]EnumMetadata + +func initUnknownTypesForLinkType() { + for i := 0; i < 256; i++ { + errorDecodersForLinkType[i] = errorDecoderForLinkType(i) + LinkTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForLinkType[i], + Name: "UnknownLinkType", + } + } +} + +// Decoder calls EthernetTypeMetadata.DecodeWith's decoder. +func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error { + return EthernetTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns EthernetTypeMetadata.Name. +func (a EthernetType) String() string { + return EthernetTypeMetadata[a].Name +} + +// LayerType returns EthernetTypeMetadata.LayerType. +func (a EthernetType) LayerType() gopacket.LayerType { + return EthernetTypeMetadata[a].LayerType +} + +type errorDecoderForEthernetType int + +func (a *errorDecoderForEthernetType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForEthernetType) Error() string { + return fmt.Sprintf("Unable to decode EthernetType %d", int(*a)) +} + +var errorDecodersForEthernetType [65536]errorDecoderForEthernetType +var EthernetTypeMetadata [65536]EnumMetadata + +func initUnknownTypesForEthernetType() { + for i := 0; i < 65536; i++ { + errorDecodersForEthernetType[i] = errorDecoderForEthernetType(i) + EthernetTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForEthernetType[i], + Name: "UnknownEthernetType", + } + } +} + +// Decoder calls PPPTypeMetadata.DecodeWith's decoder. +func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error { + return PPPTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns PPPTypeMetadata.Name. +func (a PPPType) String() string { + return PPPTypeMetadata[a].Name +} + +// LayerType returns PPPTypeMetadata.LayerType. +func (a PPPType) LayerType() gopacket.LayerType { + return PPPTypeMetadata[a].LayerType +} + +type errorDecoderForPPPType int + +func (a *errorDecoderForPPPType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForPPPType) Error() string { + return fmt.Sprintf("Unable to decode PPPType %d", int(*a)) +} + +var errorDecodersForPPPType [65536]errorDecoderForPPPType +var PPPTypeMetadata [65536]EnumMetadata + +func initUnknownTypesForPPPType() { + for i := 0; i < 65536; i++ { + errorDecodersForPPPType[i] = errorDecoderForPPPType(i) + PPPTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForPPPType[i], + Name: "UnknownPPPType", + } + } +} + +// Decoder calls IPProtocolMetadata.DecodeWith's decoder. +func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error { + return IPProtocolMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns IPProtocolMetadata.Name. +func (a IPProtocol) String() string { + return IPProtocolMetadata[a].Name +} + +// LayerType returns IPProtocolMetadata.LayerType. +func (a IPProtocol) LayerType() gopacket.LayerType { + return IPProtocolMetadata[a].LayerType +} + +type errorDecoderForIPProtocol int + +func (a *errorDecoderForIPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForIPProtocol) Error() string { + return fmt.Sprintf("Unable to decode IPProtocol %d", int(*a)) +} + +var errorDecodersForIPProtocol [256]errorDecoderForIPProtocol +var IPProtocolMetadata [256]EnumMetadata + +func initUnknownTypesForIPProtocol() { + for i := 0; i < 256; i++ { + errorDecodersForIPProtocol[i] = errorDecoderForIPProtocol(i) + IPProtocolMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForIPProtocol[i], + Name: "UnknownIPProtocol", + } + } +} + +// Decoder calls SCTPChunkTypeMetadata.DecodeWith's decoder. +func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns SCTPChunkTypeMetadata.Name. +func (a SCTPChunkType) String() string { + return SCTPChunkTypeMetadata[a].Name +} + +// LayerType returns SCTPChunkTypeMetadata.LayerType. +func (a SCTPChunkType) LayerType() gopacket.LayerType { + return SCTPChunkTypeMetadata[a].LayerType +} + +type errorDecoderForSCTPChunkType int + +func (a *errorDecoderForSCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForSCTPChunkType) Error() string { + return fmt.Sprintf("Unable to decode SCTPChunkType %d", int(*a)) +} + +var errorDecodersForSCTPChunkType [256]errorDecoderForSCTPChunkType +var SCTPChunkTypeMetadata [256]EnumMetadata + +func initUnknownTypesForSCTPChunkType() { + for i := 0; i < 256; i++ { + errorDecodersForSCTPChunkType[i] = errorDecoderForSCTPChunkType(i) + SCTPChunkTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForSCTPChunkType[i], + Name: "UnknownSCTPChunkType", + } + } +} + +// Decoder calls PPPoECodeMetadata.DecodeWith's decoder. +func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error { + return PPPoECodeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns PPPoECodeMetadata.Name. +func (a PPPoECode) String() string { + return PPPoECodeMetadata[a].Name +} + +// LayerType returns PPPoECodeMetadata.LayerType. +func (a PPPoECode) LayerType() gopacket.LayerType { + return PPPoECodeMetadata[a].LayerType +} + +type errorDecoderForPPPoECode int + +func (a *errorDecoderForPPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForPPPoECode) Error() string { + return fmt.Sprintf("Unable to decode PPPoECode %d", int(*a)) +} + +var errorDecodersForPPPoECode [256]errorDecoderForPPPoECode +var PPPoECodeMetadata [256]EnumMetadata + +func initUnknownTypesForPPPoECode() { + for i := 0; i < 256; i++ { + errorDecodersForPPPoECode[i] = errorDecoderForPPPoECode(i) + PPPoECodeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForPPPoECode[i], + Name: "UnknownPPPoECode", + } + } +} + +// Decoder calls FDDIFrameControlMetadata.DecodeWith's decoder. +func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error { + return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns FDDIFrameControlMetadata.Name. +func (a FDDIFrameControl) String() string { + return FDDIFrameControlMetadata[a].Name +} + +// LayerType returns FDDIFrameControlMetadata.LayerType. +func (a FDDIFrameControl) LayerType() gopacket.LayerType { + return FDDIFrameControlMetadata[a].LayerType +} + +type errorDecoderForFDDIFrameControl int + +func (a *errorDecoderForFDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForFDDIFrameControl) Error() string { + return fmt.Sprintf("Unable to decode FDDIFrameControl %d", int(*a)) +} + +var errorDecodersForFDDIFrameControl [256]errorDecoderForFDDIFrameControl +var FDDIFrameControlMetadata [256]EnumMetadata + +func initUnknownTypesForFDDIFrameControl() { + for i := 0; i < 256; i++ { + errorDecodersForFDDIFrameControl[i] = errorDecoderForFDDIFrameControl(i) + FDDIFrameControlMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForFDDIFrameControl[i], + Name: "UnknownFDDIFrameControl", + } + } +} + +// Decoder calls EAPOLTypeMetadata.DecodeWith's decoder. +func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error { + return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns EAPOLTypeMetadata.Name. +func (a EAPOLType) String() string { + return EAPOLTypeMetadata[a].Name +} + +// LayerType returns EAPOLTypeMetadata.LayerType. +func (a EAPOLType) LayerType() gopacket.LayerType { + return EAPOLTypeMetadata[a].LayerType +} + +type errorDecoderForEAPOLType int + +func (a *errorDecoderForEAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForEAPOLType) Error() string { + return fmt.Sprintf("Unable to decode EAPOLType %d", int(*a)) +} + +var errorDecodersForEAPOLType [256]errorDecoderForEAPOLType +var EAPOLTypeMetadata [256]EnumMetadata + +func initUnknownTypesForEAPOLType() { + for i := 0; i < 256; i++ { + errorDecodersForEAPOLType[i] = errorDecoderForEAPOLType(i) + EAPOLTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForEAPOLType[i], + Name: "UnknownEAPOLType", + } + } +} + +// Decoder calls ProtocolFamilyMetadata.DecodeWith's decoder. +func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error { + return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns ProtocolFamilyMetadata.Name. +func (a ProtocolFamily) String() string { + return ProtocolFamilyMetadata[a].Name +} + +// LayerType returns ProtocolFamilyMetadata.LayerType. +func (a ProtocolFamily) LayerType() gopacket.LayerType { + return ProtocolFamilyMetadata[a].LayerType +} + +type errorDecoderForProtocolFamily int + +func (a *errorDecoderForProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForProtocolFamily) Error() string { + return fmt.Sprintf("Unable to decode ProtocolFamily %d", int(*a)) +} + +var errorDecodersForProtocolFamily [256]errorDecoderForProtocolFamily +var ProtocolFamilyMetadata [256]EnumMetadata + +func initUnknownTypesForProtocolFamily() { + for i := 0; i < 256; i++ { + errorDecodersForProtocolFamily[i] = errorDecoderForProtocolFamily(i) + ProtocolFamilyMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForProtocolFamily[i], + Name: "UnknownProtocolFamily", + } + } +} + +// Decoder calls Dot11TypeMetadata.DecodeWith's decoder. +func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error { + return Dot11TypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns Dot11TypeMetadata.Name. +func (a Dot11Type) String() string { + return Dot11TypeMetadata[a].Name +} + +// LayerType returns Dot11TypeMetadata.LayerType. +func (a Dot11Type) LayerType() gopacket.LayerType { + return Dot11TypeMetadata[a].LayerType +} + +type errorDecoderForDot11Type int + +func (a *errorDecoderForDot11Type) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForDot11Type) Error() string { + return fmt.Sprintf("Unable to decode Dot11Type %d", int(*a)) +} + +var errorDecodersForDot11Type [256]errorDecoderForDot11Type +var Dot11TypeMetadata [256]EnumMetadata + +func initUnknownTypesForDot11Type() { + for i := 0; i < 256; i++ { + errorDecodersForDot11Type[i] = errorDecoderForDot11Type(i) + Dot11TypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForDot11Type[i], + Name: "UnknownDot11Type", + } + } +} + +// Decoder calls USBTransportTypeMetadata.DecodeWith's decoder. +func (a USBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error { + return USBTransportTypeMetadata[a].DecodeWith.Decode(data, p) +} + +// String returns USBTransportTypeMetadata.Name. +func (a USBTransportType) String() string { + return USBTransportTypeMetadata[a].Name +} + +// LayerType returns USBTransportTypeMetadata.LayerType. +func (a USBTransportType) LayerType() gopacket.LayerType { + return USBTransportTypeMetadata[a].LayerType +} + +type errorDecoderForUSBTransportType int + +func (a *errorDecoderForUSBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error { + return a +} +func (a *errorDecoderForUSBTransportType) Error() string { + return fmt.Sprintf("Unable to decode USBTransportType %d", int(*a)) +} + +var errorDecodersForUSBTransportType [256]errorDecoderForUSBTransportType +var USBTransportTypeMetadata [256]EnumMetadata + +func initUnknownTypesForUSBTransportType() { + for i := 0; i < 256; i++ { + errorDecodersForUSBTransportType[i] = errorDecoderForUSBTransportType(i) + USBTransportTypeMetadata[i] = EnumMetadata{ + DecodeWith: &errorDecodersForUSBTransportType[i], + Name: "UnknownUSBTransportType", + } + } +} diff --git a/vendor/github.com/google/gopacket/layers/erspan2.go b/vendor/github.com/google/gopacket/layers/erspan2.go new file mode 100644 index 00000000..15443620 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/erspan2.go @@ -0,0 +1,86 @@ +// Copyright 2018 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + + "github.com/google/gopacket" +) + +const ( + //ERSPANIIVersionObsolete - The obsolete value for the version field + ERSPANIIVersionObsolete = 0x0 + // ERSPANIIVersion - The current value for the version field + ERSPANIIVersion = 0x1 +) + +// ERSPANII contains all of the fields found in an ERSPAN Type II header +// https://tools.ietf.org/html/draft-foschiano-erspan-03 +type ERSPANII struct { + BaseLayer + IsTruncated bool + Version, CoS, TrunkEncap uint8 + VLANIdentifier, SessionID, Reserved uint16 + Index uint32 +} + +func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPANII } + +// DecodeFromBytes decodes the given bytes into this layer. +func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + erspan2Length := 8 + erspan2.Version = data[0] & 0xF0 >> 4 + erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF + erspan2.CoS = data[2] & 0xE0 >> 5 + erspan2.TrunkEncap = data[2] & 0x18 >> 3 + erspan2.IsTruncated = data[2]&0x4>>2 != 0 + erspan2.SessionID = binary.BigEndian.Uint16(data[2:4]) & 0x03FF + erspan2.Reserved = binary.BigEndian.Uint16(data[4:6]) & 0xFFF0 >> 4 + erspan2.Index = binary.BigEndian.Uint32(data[4:8]) & 0x000FFFFF + erspan2.Contents = data[:erspan2Length] + erspan2.Payload = data[erspan2Length:] + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (erspan2 *ERSPANII) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + + twoByteInt := uint16(erspan2.Version&0xF)<<12 | erspan2.VLANIdentifier&0x0FFF + binary.BigEndian.PutUint16(bytes, twoByteInt) + + twoByteInt = uint16(erspan2.CoS&0x7)<<13 | uint16(erspan2.TrunkEncap&0x3)<<11 | erspan2.SessionID&0x03FF + if erspan2.IsTruncated { + twoByteInt |= 0x400 + } + binary.BigEndian.PutUint16(bytes[2:], twoByteInt) + + fourByteInt := uint32(erspan2.Reserved&0x0FFF)<<20 | erspan2.Index&0x000FFFFF + binary.BigEndian.PutUint32(bytes[4:], fourByteInt) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (erspan2 *ERSPANII) CanDecode() gopacket.LayerClass { + return LayerTypeERSPANII +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (erspan2 *ERSPANII) NextLayerType() gopacket.LayerType { + return LayerTypeEthernet +} + +func decodeERSPANII(data []byte, p gopacket.PacketBuilder) error { + erspan2 := &ERSPANII{} + return decodingLayerDecoder(erspan2, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/etherip.go b/vendor/github.com/google/gopacket/layers/etherip.go new file mode 100644 index 00000000..5b7b7229 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/etherip.go @@ -0,0 +1,45 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// EtherIP is the struct for storing RFC 3378 EtherIP packet headers. +type EtherIP struct { + BaseLayer + Version uint8 + Reserved uint16 +} + +// LayerType returns gopacket.LayerTypeEtherIP. +func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + e.Version = data[0] >> 4 + e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff + e.BaseLayer = BaseLayer{data[:2], data[2:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (e *EtherIP) CanDecode() gopacket.LayerClass { + return LayerTypeEtherIP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (e *EtherIP) NextLayerType() gopacket.LayerType { + return LayerTypeEthernet +} + +func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error { + e := &EtherIP{} + return decodingLayerDecoder(e, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/ethernet.go b/vendor/github.com/google/gopacket/layers/ethernet.go new file mode 100644 index 00000000..b73748f2 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ethernet.go @@ -0,0 +1,123 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "github.com/google/gopacket" + "net" +) + +// EthernetBroadcast is the broadcast MAC address used by Ethernet. +var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + +// Ethernet is the layer for Ethernet frame headers. +type Ethernet struct { + BaseLayer + SrcMAC, DstMAC net.HardwareAddr + EthernetType EthernetType + // Length is only set if a length field exists within this header. Ethernet + // headers follow two different standards, one that uses an EthernetType, the + // other which defines a length the follows with a LLC header (802.3). If the + // former is the case, we set EthernetType and Length stays 0. In the latter + // case, we set Length and EthernetType = EthernetTypeLLC. + Length uint16 +} + +// LayerType returns LayerTypeEthernet +func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet } + +func (e *Ethernet) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC) +} + +func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 14 { + return errors.New("Ethernet packet too small") + } + eth.DstMAC = net.HardwareAddr(data[0:6]) + eth.SrcMAC = net.HardwareAddr(data[6:12]) + eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14])) + eth.BaseLayer = BaseLayer{data[:14], data[14:]} + eth.Length = 0 + if eth.EthernetType < 0x0600 { + eth.Length = uint16(eth.EthernetType) + eth.EthernetType = EthernetTypeLLC + if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 { + df.SetTruncated() + } else if cmp > 0 { + // Strip off bytes at the end, since we have too many bytes + eth.Payload = eth.Payload[:len(eth.Payload)-cmp] + } + // fmt.Println(eth) + } + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if len(eth.DstMAC) != 6 { + return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC) + } + if len(eth.SrcMAC) != 6 { + return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC) + } + payload := b.Bytes() + bytes, err := b.PrependBytes(14) + if err != nil { + return err + } + copy(bytes, eth.DstMAC) + copy(bytes[6:], eth.SrcMAC) + if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC { + if opts.FixLengths { + eth.Length = uint16(len(payload)) + } + if eth.EthernetType != EthernetTypeLLC { + return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length) + } else if eth.Length > 0x0600 { + return fmt.Errorf("invalid ethernet length %v", eth.Length) + } + binary.BigEndian.PutUint16(bytes[12:], eth.Length) + } else { + binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType)) + } + length := len(b.Bytes()) + if length < 60 { + // Pad out to 60 bytes. + padding, err := b.AppendBytes(60 - length) + if err != nil { + return err + } + copy(padding, lotsOfZeros[:]) + } + return nil +} + +func (eth *Ethernet) CanDecode() gopacket.LayerClass { + return LayerTypeEthernet +} + +func (eth *Ethernet) NextLayerType() gopacket.LayerType { + return eth.EthernetType.LayerType() +} + +func decodeEthernet(data []byte, p gopacket.PacketBuilder) error { + eth := &Ethernet{} + err := eth.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(eth) + p.SetLinkLayer(eth) + return p.NextDecoder(eth.EthernetType) +} diff --git a/vendor/github.com/google/gopacket/layers/fddi.go b/vendor/github.com/google/gopacket/layers/fddi.go new file mode 100644 index 00000000..ed9e1957 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/fddi.go @@ -0,0 +1,41 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" + "net" +) + +// FDDI contains the header for FDDI frames. +type FDDI struct { + BaseLayer + FrameControl FDDIFrameControl + Priority uint8 + SrcMAC, DstMAC net.HardwareAddr +} + +// LayerType returns LayerTypeFDDI. +func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI } + +// LinkFlow returns a new flow of type EndpointMAC. +func (f *FDDI) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC) +} + +func decodeFDDI(data []byte, p gopacket.PacketBuilder) error { + f := &FDDI{ + FrameControl: FDDIFrameControl(data[0] & 0xF8), + Priority: data[0] & 0x07, + SrcMAC: net.HardwareAddr(data[1:7]), + DstMAC: net.HardwareAddr(data[7:13]), + BaseLayer: BaseLayer{data[:13], data[13:]}, + } + p.SetLinkLayer(f) + p.AddLayer(f) + return p.NextDecoder(f.FrameControl) +} diff --git a/vendor/github.com/google/gopacket/layers/fuzz_layer.go b/vendor/github.com/google/gopacket/layers/fuzz_layer.go new file mode 100644 index 00000000..606e45d2 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/fuzz_layer.go @@ -0,0 +1,39 @@ +// Copyright 2019 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file in the root of the source tree. + +package layers + +import ( + "encoding/binary" + + "github.com/google/gopacket" +) + +// FuzzLayer is a fuzz target for the layers package of gopacket +// A fuzz target is a function processing a binary blob (byte slice) +// The process here is to interpret this data as a packet, and print the layers contents. +// The decoding options and the starting layer are encoded in the first bytes. +// The function returns 1 if this is a valid packet (no error layer) +func FuzzLayer(data []byte) int { + if len(data) < 3 { + return 0 + } + // use the first two bytes to choose the top level layer + startLayer := binary.BigEndian.Uint16(data[:2]) + var fuzzOpts = gopacket.DecodeOptions{ + Lazy: data[2]&0x1 != 0, + NoCopy: data[2]&0x2 != 0, + SkipDecodeRecovery: data[2]&0x4 != 0, + DecodeStreamsAsDatagrams: data[2]&0x8 != 0, + } + p := gopacket.NewPacket(data[3:], gopacket.LayerType(startLayer), fuzzOpts) + for _, l := range p.Layers() { + gopacket.LayerString(l) + } + if p.ErrorLayer() != nil { + return 0 + } + return 1 +} diff --git a/vendor/github.com/google/gopacket/layers/gen_linted.sh b/vendor/github.com/google/gopacket/layers/gen_linted.sh new file mode 100644 index 00000000..75c701f4 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gen_linted.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +for i in *.go; do golint $i | grep -q . || echo $i; done > .linted diff --git a/vendor/github.com/google/gopacket/layers/geneve.go b/vendor/github.com/google/gopacket/layers/geneve.go new file mode 100644 index 00000000..e9a14288 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/geneve.go @@ -0,0 +1,121 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03 +// Geneve Header: +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Ver| Opt Len |O|C| Rsvd. | Protocol Type | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Virtual Network Identifier (VNI) | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Variable Length Options | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type Geneve struct { + BaseLayer + Version uint8 // 2 bits + OptionsLength uint8 // 6 bits + OAMPacket bool // 1 bits + CriticalOption bool // 1 bits + Protocol EthernetType // 16 bits + VNI uint32 // 24bits + Options []*GeneveOption +} + +// Geneve Tunnel Options +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Option Class | Type |R|R|R| Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Variable Option Data | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type GeneveOption struct { + Class uint16 // 16 bits + Type uint8 // 8 bits + Flags uint8 // 3 bits + Length uint8 // 5 bits + Data []byte +} + +// LayerType returns LayerTypeGeneve +func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve } + +func decodeGeneveOption(data []byte, gn *Geneve, df gopacket.DecodeFeedback) (*GeneveOption, uint8, error) { + if len(data) < 3 { + df.SetTruncated() + return nil, 0, errors.New("geneve option too small") + } + opt := &GeneveOption{} + + opt.Class = binary.BigEndian.Uint16(data[0:2]) + opt.Type = data[2] + opt.Flags = data[3] >> 4 + opt.Length = (data[3]&0xf)*4 + 4 + + if len(data) < int(opt.Length) { + df.SetTruncated() + return nil, 0, errors.New("geneve option too small") + } + opt.Data = make([]byte, opt.Length-4) + copy(opt.Data, data[4:opt.Length]) + + return opt, opt.Length, nil +} + +func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 7 { + df.SetTruncated() + return errors.New("geneve packet too short") + } + + gn.Version = data[0] >> 7 + gn.OptionsLength = (data[0] & 0x3f) * 4 + + gn.OAMPacket = data[1]&0x80 > 0 + gn.CriticalOption = data[1]&0x40 > 0 + gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + + var buf [4]byte + copy(buf[1:], data[4:7]) + gn.VNI = binary.BigEndian.Uint32(buf[:]) + + offset, length := uint8(8), int32(gn.OptionsLength) + if len(data) < int(length+7) { + df.SetTruncated() + return errors.New("geneve packet too short") + } + + for length > 0 { + opt, len, err := decodeGeneveOption(data[offset:], gn, df) + if err != nil { + return err + } + gn.Options = append(gn.Options, opt) + + length -= int32(len) + offset += len + } + + gn.BaseLayer = BaseLayer{data[:offset], data[offset:]} + + return nil +} + +func (gn *Geneve) NextLayerType() gopacket.LayerType { + return gn.Protocol.LayerType() +} + +func decodeGeneve(data []byte, p gopacket.PacketBuilder) error { + gn := &Geneve{} + return decodingLayerDecoder(gn, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/gre.go b/vendor/github.com/google/gopacket/layers/gre.go new file mode 100644 index 00000000..9c5e7d24 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gre.go @@ -0,0 +1,200 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + + "github.com/google/gopacket" +) + +// GRE is a Generic Routing Encapsulation header. +type GRE struct { + BaseLayer + ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute, AckPresent bool + RecursionControl, Flags, Version uint8 + Protocol EthernetType + Checksum, Offset uint16 + Key, Seq, Ack uint32 + *GRERouting +} + +// GRERouting is GRE routing information, present if the RoutingPresent flag is +// set. +type GRERouting struct { + AddressFamily uint16 + SREOffset, SRELength uint8 + RoutingInformation []byte + Next *GRERouting +} + +// LayerType returns gopacket.LayerTypeGRE. +func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE } + +// DecodeFromBytes decodes the given bytes into this layer. +func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + g.ChecksumPresent = data[0]&0x80 != 0 + g.RoutingPresent = data[0]&0x40 != 0 + g.KeyPresent = data[0]&0x20 != 0 + g.SeqPresent = data[0]&0x10 != 0 + g.StrictSourceRoute = data[0]&0x08 != 0 + g.AckPresent = data[1]&0x80 != 0 + g.RecursionControl = data[0] & 0x7 + g.Flags = data[1] >> 3 + g.Version = data[1] & 0x7 + g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4])) + offset := 4 + if g.ChecksumPresent || g.RoutingPresent { + g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2]) + g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4]) + offset += 4 + } + if g.KeyPresent { + g.Key = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + if g.SeqPresent { + g.Seq = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + if g.RoutingPresent { + tail := &g.GRERouting + for { + sre := &GRERouting{ + AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]), + SREOffset: data[offset+2], + SRELength: data[offset+3], + } + sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)] + offset += 4 + int(sre.SRELength) + if sre.AddressFamily == 0 && sre.SRELength == 0 { + break + } + (*tail) = sre + tail = &sre.Next + } + } + if g.AckPresent { + g.Ack = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + } + g.BaseLayer = BaseLayer{data[:offset], data[offset:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the SerializationBuffer, +// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info. +func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + size := 4 + if g.ChecksumPresent || g.RoutingPresent { + size += 4 + } + if g.KeyPresent { + size += 4 + } + if g.SeqPresent { + size += 4 + } + if g.RoutingPresent { + r := g.GRERouting + for r != nil { + size += 4 + int(r.SRELength) + r = r.Next + } + size += 4 + } + if g.AckPresent { + size += 4 + } + buf, err := b.PrependBytes(size) + if err != nil { + return err + } + // Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags. + buf[0] = 0 + buf[1] = 0 + if g.ChecksumPresent { + buf[0] |= 0x80 + } + if g.RoutingPresent { + buf[0] |= 0x40 + } + if g.KeyPresent { + buf[0] |= 0x20 + } + if g.SeqPresent { + buf[0] |= 0x10 + } + if g.StrictSourceRoute { + buf[0] |= 0x08 + } + if g.AckPresent { + buf[1] |= 0x80 + } + buf[0] |= g.RecursionControl + buf[1] |= g.Flags << 3 + buf[1] |= g.Version + binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol)) + offset := 4 + if g.ChecksumPresent || g.RoutingPresent { + // Don't write the checksum value yet, as we may need to compute it, + // which requires the entire header be complete. + // Instead we zeroize the memory in case it is dirty. + buf[offset] = 0 + buf[offset+1] = 0 + binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset) + offset += 4 + } + if g.KeyPresent { + binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key) + offset += 4 + } + if g.SeqPresent { + binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq) + offset += 4 + } + if g.RoutingPresent { + sre := g.GRERouting + for sre != nil { + binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily) + buf[offset+2] = sre.SREOffset + buf[offset+3] = sre.SRELength + copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation) + offset += 4 + int(sre.SRELength) + sre = sre.Next + } + // Terminate routing field with a "NULL" SRE. + binary.BigEndian.PutUint32(buf[offset:offset+4], 0) + } + if g.AckPresent { + binary.BigEndian.PutUint32(buf[offset:offset+4], g.Ack) + offset += 4 + } + if g.ChecksumPresent { + if opts.ComputeChecksums { + g.Checksum = tcpipChecksum(b.Bytes(), 0) + } + + binary.BigEndian.PutUint16(buf[4:6], g.Checksum) + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (g *GRE) CanDecode() gopacket.LayerClass { + return LayerTypeGRE +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (g *GRE) NextLayerType() gopacket.LayerType { + return g.Protocol.LayerType() +} + +func decodeGRE(data []byte, p gopacket.PacketBuilder) error { + g := &GRE{} + return decodingLayerDecoder(g, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/gtp.go b/vendor/github.com/google/gopacket/layers/gtp.go new file mode 100644 index 00000000..fe3054a6 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/gtp.go @@ -0,0 +1,184 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +const gtpMinimumSizeInBytes int = 8 + +// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number. +type GTPExtensionHeader struct { + Type uint8 + Content []byte +} + +// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces. +// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595 +type GTPv1U struct { + BaseLayer + Version uint8 + ProtocolType uint8 + Reserved uint8 + ExtensionHeaderFlag bool + SequenceNumberFlag bool + NPDUFlag bool + MessageType uint8 + MessageLength uint16 + TEID uint32 + SequenceNumber uint16 + NPDU uint8 + GTPExtensionHeaders []GTPExtensionHeader +} + +// LayerType returns LayerTypeGTPV1U +func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U } + +// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet +func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + hLen := gtpMinimumSizeInBytes + dLen := len(data) + if dLen < hLen { + return fmt.Errorf("GTP packet too small: %d bytes", dLen) + } + g.Version = (data[0] >> 5) & 0x07 + g.ProtocolType = (data[0] >> 4) & 0x01 + g.Reserved = (data[0] >> 3) & 0x01 + g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1 + g.NPDUFlag = (data[0] & 0x01) == 1 + g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1 + g.MessageType = data[1] + g.MessageLength = binary.BigEndian.Uint16(data[2:4]) + pLen := 8 + g.MessageLength + if uint16(dLen) < pLen { + return fmt.Errorf("GTP packet too small: %d bytes", dLen) + } + // Field used to multiplex different connections in the same GTP tunnel. + g.TEID = binary.BigEndian.Uint32(data[4:8]) + cIndex := uint16(hLen) + if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag { + hLen += 4 + cIndex += 4 + if dLen < hLen { + return fmt.Errorf("GTP packet too small: %d bytes", dLen) + } + if g.SequenceNumberFlag { + g.SequenceNumber = binary.BigEndian.Uint16(data[8:10]) + } + if g.NPDUFlag { + g.NPDU = data[10] + } + if g.ExtensionHeaderFlag { + extensionFlag := true + for extensionFlag { + extensionType := uint8(data[cIndex-1]) + extensionLength := uint(data[cIndex]) + if extensionLength == 0 { + return fmt.Errorf("GTP packet with invalid extension header") + } + // extensionLength is in 4-octet units + lIndex := cIndex + (uint16(extensionLength) * 4) + if uint16(dLen) < lIndex { + fmt.Println(dLen, lIndex) + return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen) + } + content := data[cIndex+1 : lIndex-1] + eh := GTPExtensionHeader{Type: extensionType, Content: content} + g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh) + cIndex = lIndex + // Check if coming bytes are from an extension header + extensionFlag = data[cIndex-1] != 0 + + } + } + } + g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]} + return nil + +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (g *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + data, err := b.PrependBytes(gtpMinimumSizeInBytes) + if err != nil { + return err + } + data[0] |= (g.Version << 5) + data[0] |= (1 << 4) + if len(g.GTPExtensionHeaders) > 0 { + data[0] |= 0x04 + g.ExtensionHeaderFlag = true + } + if g.SequenceNumberFlag { + data[0] |= 0x02 + } + if g.NPDUFlag { + data[0] |= 0x01 + } + data[1] = g.MessageType + binary.BigEndian.PutUint16(data[2:4], g.MessageLength) + binary.BigEndian.PutUint32(data[4:8], g.TEID) + if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag { + data, err := b.AppendBytes(4) + if err != nil { + return err + } + binary.BigEndian.PutUint16(data[:2], g.SequenceNumber) + data[2] = g.NPDU + for _, eh := range g.GTPExtensionHeaders { + data[len(data)-1] = eh.Type + lContent := len(eh.Content) + // extensionLength is in 4-octet units + extensionLength := (lContent + 2) / 4 + // Get two extra byte for the next extension header type and length + data, err = b.AppendBytes(lContent + 2) + if err != nil { + return err + } + data[0] = byte(extensionLength) + copy(data[1:lContent+1], eh.Content) + } + } + return nil + +} + +// CanDecode returns a set of layers that GTP objects can decode. +func (g *GTPv1U) CanDecode() gopacket.LayerClass { + return LayerTypeGTPv1U +} + +// NextLayerType specifies the next layer that GoPacket should attempt to +func (g *GTPv1U) NextLayerType() gopacket.LayerType { + if len(g.LayerPayload()) == 0 { + return gopacket.LayerTypeZero + } + version := uint8(g.LayerPayload()[0]) >> 4 + if version == 4 { + return LayerTypeIPv4 + } else if version == 6 { + return LayerTypeIPv6 + } else { + return LayerTypePPP + } +} + +func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error { + gtp := >Pv1U{} + err := gtp.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(gtp) + return p.NextDecoder(gtp.NextLayerType()) +} diff --git a/vendor/github.com/google/gopacket/layers/iana_ports.go b/vendor/github.com/google/gopacket/layers/iana_ports.go new file mode 100644 index 00000000..ddcf3ecd --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/iana_ports.go @@ -0,0 +1,11351 @@ +// Copyright 2012 Google, Inc. All rights reserved. + +package layers + +// Created by gen.go, don't edit manually +// Generated at 2017-10-23 09:57:28.214859163 -0600 MDT m=+1.011679290 +// Fetched from "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml" + +// TCPPortNames contains the port names for all TCP ports. +var TCPPortNames = tcpPortNames + +// UDPPortNames contains the port names for all UDP ports. +var UDPPortNames = udpPortNames + +// SCTPPortNames contains the port names for all SCTP ports. +var SCTPPortNames = sctpPortNames + +var tcpPortNames = map[TCPPort]string{ + 1: "tcpmux", + 2: "compressnet", + 3: "compressnet", + 5: "rje", + 7: "echo", + 9: "discard", + 11: "systat", + 13: "daytime", + 17: "qotd", + 18: "msp", + 19: "chargen", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 23: "telnet", + 25: "smtp", + 27: "nsw-fe", + 29: "msg-icp", + 31: "msg-auth", + 33: "dsp", + 37: "time", + 38: "rap", + 39: "rlp", + 41: "graphics", + 42: "name", + 43: "nicname", + 44: "mpm-flags", + 45: "mpm", + 46: "mpm-snd", + 48: "auditd", + 49: "tacacs", + 50: "re-mail-ck", + 52: "xns-time", + 53: "domain", + 54: "xns-ch", + 55: "isi-gl", + 56: "xns-auth", + 58: "xns-mail", + 62: "acas", + 63: "whoispp", + 64: "covia", + 65: "tacacs-ds", + 66: "sql-net", + 67: "bootps", + 68: "bootpc", + 69: "tftp", + 70: "gopher", + 71: "netrjs-1", + 72: "netrjs-2", + 73: "netrjs-3", + 74: "netrjs-4", + 76: "deos", + 78: "vettcp", + 79: "finger", + 80: "http", + 82: "xfer", + 83: "mit-ml-dev", + 84: "ctf", + 85: "mit-ml-dev", + 86: "mfcobol", + 88: "kerberos", + 89: "su-mit-tg", + 90: "dnsix", + 91: "mit-dov", + 92: "npp", + 93: "dcp", + 94: "objcall", + 95: "supdup", + 96: "dixie", + 97: "swift-rvf", + 98: "tacnews", + 99: "metagram", + 101: "hostname", + 102: "iso-tsap", + 103: "gppitnp", + 104: "acr-nema", + 105: "cso", + 106: "3com-tsmux", + 107: "rtelnet", + 108: "snagas", + 109: "pop2", + 110: "pop3", + 111: "sunrpc", + 112: "mcidas", + 113: "ident", + 115: "sftp", + 116: "ansanotify", + 117: "uucp-path", + 118: "sqlserv", + 119: "nntp", + 120: "cfdptkt", + 121: "erpc", + 122: "smakynet", + 123: "ntp", + 124: "ansatrader", + 125: "locus-map", + 126: "nxedit", + 127: "locus-con", + 128: "gss-xlicen", + 129: "pwdgen", + 130: "cisco-fna", + 131: "cisco-tna", + 132: "cisco-sys", + 133: "statsrv", + 134: "ingres-net", + 135: "epmap", + 136: "profile", + 137: "netbios-ns", + 138: "netbios-dgm", + 139: "netbios-ssn", + 140: "emfis-data", + 141: "emfis-cntl", + 142: "bl-idm", + 143: "imap", + 144: "uma", + 145: "uaac", + 146: "iso-tp0", + 147: "iso-ip", + 148: "jargon", + 149: "aed-512", + 150: "sql-net", + 151: "hems", + 152: "bftp", + 153: "sgmp", + 154: "netsc-prod", + 155: "netsc-dev", + 156: "sqlsrv", + 157: "knet-cmp", + 158: "pcmail-srv", + 159: "nss-routing", + 160: "sgmp-traps", + 161: "snmp", + 162: "snmptrap", + 163: "cmip-man", + 164: "cmip-agent", + 165: "xns-courier", + 166: "s-net", + 167: "namp", + 168: "rsvd", + 169: "send", + 170: "print-srv", + 171: "multiplex", + 172: "cl-1", + 173: "xyplex-mux", + 174: "mailq", + 175: "vmnet", + 176: "genrad-mux", + 177: "xdmcp", + 178: "nextstep", + 179: "bgp", + 180: "ris", + 181: "unify", + 182: "audit", + 183: "ocbinder", + 184: "ocserver", + 185: "remote-kis", + 186: "kis", + 187: "aci", + 188: "mumps", + 189: "qft", + 190: "gacp", + 191: "prospero", + 192: "osu-nms", + 193: "srmp", + 194: "irc", + 195: "dn6-nlm-aud", + 196: "dn6-smm-red", + 197: "dls", + 198: "dls-mon", + 199: "smux", + 200: "src", + 201: "at-rtmp", + 202: "at-nbp", + 203: "at-3", + 204: "at-echo", + 205: "at-5", + 206: "at-zis", + 207: "at-7", + 208: "at-8", + 209: "qmtp", + 210: "z39-50", + 211: "914c-g", + 212: "anet", + 213: "ipx", + 214: "vmpwscs", + 215: "softpc", + 216: "CAIlic", + 217: "dbase", + 218: "mpp", + 219: "uarps", + 220: "imap3", + 221: "fln-spx", + 222: "rsh-spx", + 223: "cdc", + 224: "masqdialer", + 242: "direct", + 243: "sur-meas", + 244: "inbusiness", + 245: "link", + 246: "dsp3270", + 247: "subntbcst-tftp", + 248: "bhfhs", + 256: "rap", + 257: "set", + 259: "esro-gen", + 260: "openport", + 261: "nsiiops", + 262: "arcisdms", + 263: "hdap", + 264: "bgmp", + 265: "x-bone-ctl", + 266: "sst", + 267: "td-service", + 268: "td-replica", + 269: "manet", + 271: "pt-tls", + 280: "http-mgmt", + 281: "personal-link", + 282: "cableport-ax", + 283: "rescap", + 284: "corerjd", + 286: "fxp", + 287: "k-block", + 308: "novastorbakcup", + 309: "entrusttime", + 310: "bhmds", + 311: "asip-webadmin", + 312: "vslmp", + 313: "magenta-logic", + 314: "opalis-robot", + 315: "dpsi", + 316: "decauth", + 317: "zannet", + 318: "pkix-timestamp", + 319: "ptp-event", + 320: "ptp-general", + 321: "pip", + 322: "rtsps", + 323: "rpki-rtr", + 324: "rpki-rtr-tls", + 333: "texar", + 344: "pdap", + 345: "pawserv", + 346: "zserv", + 347: "fatserv", + 348: "csi-sgwp", + 349: "mftp", + 350: "matip-type-a", + 351: "matip-type-b", + 352: "dtag-ste-sb", + 353: "ndsauth", + 354: "bh611", + 355: "datex-asn", + 356: "cloanto-net-1", + 357: "bhevent", + 358: "shrinkwrap", + 359: "nsrmp", + 360: "scoi2odialog", + 361: "semantix", + 362: "srssend", + 363: "rsvp-tunnel", + 364: "aurora-cmgr", + 365: "dtk", + 366: "odmr", + 367: "mortgageware", + 368: "qbikgdp", + 369: "rpc2portmap", + 370: "codaauth2", + 371: "clearcase", + 372: "ulistproc", + 373: "legent-1", + 374: "legent-2", + 375: "hassle", + 376: "nip", + 377: "tnETOS", + 378: "dsETOS", + 379: "is99c", + 380: "is99s", + 381: "hp-collector", + 382: "hp-managed-node", + 383: "hp-alarm-mgr", + 384: "arns", + 385: "ibm-app", + 386: "asa", + 387: "aurp", + 388: "unidata-ldm", + 389: "ldap", + 390: "uis", + 391: "synotics-relay", + 392: "synotics-broker", + 393: "meta5", + 394: "embl-ndt", + 395: "netcp", + 396: "netware-ip", + 397: "mptn", + 398: "kryptolan", + 399: "iso-tsap-c2", + 400: "osb-sd", + 401: "ups", + 402: "genie", + 403: "decap", + 404: "nced", + 405: "ncld", + 406: "imsp", + 407: "timbuktu", + 408: "prm-sm", + 409: "prm-nm", + 410: "decladebug", + 411: "rmt", + 412: "synoptics-trap", + 413: "smsp", + 414: "infoseek", + 415: "bnet", + 416: "silverplatter", + 417: "onmux", + 418: "hyper-g", + 419: "ariel1", + 420: "smpte", + 421: "ariel2", + 422: "ariel3", + 423: "opc-job-start", + 424: "opc-job-track", + 425: "icad-el", + 426: "smartsdp", + 427: "svrloc", + 428: "ocs-cmu", + 429: "ocs-amu", + 430: "utmpsd", + 431: "utmpcd", + 432: "iasd", + 433: "nnsp", + 434: "mobileip-agent", + 435: "mobilip-mn", + 436: "dna-cml", + 437: "comscm", + 438: "dsfgw", + 439: "dasp", + 440: "sgcp", + 441: "decvms-sysmgt", + 442: "cvc-hostd", + 443: "https", + 444: "snpp", + 445: "microsoft-ds", + 446: "ddm-rdb", + 447: "ddm-dfm", + 448: "ddm-ssl", + 449: "as-servermap", + 450: "tserver", + 451: "sfs-smp-net", + 452: "sfs-config", + 453: "creativeserver", + 454: "contentserver", + 455: "creativepartnr", + 456: "macon-tcp", + 457: "scohelp", + 458: "appleqtc", + 459: "ampr-rcmd", + 460: "skronk", + 461: "datasurfsrv", + 462: "datasurfsrvsec", + 463: "alpes", + 464: "kpasswd", + 465: "urd", + 466: "digital-vrc", + 467: "mylex-mapd", + 468: "photuris", + 469: "rcp", + 470: "scx-proxy", + 471: "mondex", + 472: "ljk-login", + 473: "hybrid-pop", + 474: "tn-tl-w1", + 475: "tcpnethaspsrv", + 476: "tn-tl-fd1", + 477: "ss7ns", + 478: "spsc", + 479: "iafserver", + 480: "iafdbase", + 481: "ph", + 482: "bgs-nsi", + 483: "ulpnet", + 484: "integra-sme", + 485: "powerburst", + 486: "avian", + 487: "saft", + 488: "gss-http", + 489: "nest-protocol", + 490: "micom-pfs", + 491: "go-login", + 492: "ticf-1", + 493: "ticf-2", + 494: "pov-ray", + 495: "intecourier", + 496: "pim-rp-disc", + 497: "retrospect", + 498: "siam", + 499: "iso-ill", + 500: "isakmp", + 501: "stmf", + 502: "mbap", + 503: "intrinsa", + 504: "citadel", + 505: "mailbox-lm", + 506: "ohimsrv", + 507: "crs", + 508: "xvttp", + 509: "snare", + 510: "fcp", + 511: "passgo", + 512: "exec", + 513: "login", + 514: "shell", + 515: "printer", + 516: "videotex", + 517: "talk", + 518: "ntalk", + 519: "utime", + 520: "efs", + 521: "ripng", + 522: "ulp", + 523: "ibm-db2", + 524: "ncp", + 525: "timed", + 526: "tempo", + 527: "stx", + 528: "custix", + 529: "irc-serv", + 530: "courier", + 531: "conference", + 532: "netnews", + 533: "netwall", + 534: "windream", + 535: "iiop", + 536: "opalis-rdv", + 537: "nmsp", + 538: "gdomap", + 539: "apertus-ldp", + 540: "uucp", + 541: "uucp-rlogin", + 542: "commerce", + 543: "klogin", + 544: "kshell", + 545: "appleqtcsrvr", + 546: "dhcpv6-client", + 547: "dhcpv6-server", + 548: "afpovertcp", + 549: "idfp", + 550: "new-rwho", + 551: "cybercash", + 552: "devshr-nts", + 553: "pirp", + 554: "rtsp", + 555: "dsf", + 556: "remotefs", + 557: "openvms-sysipc", + 558: "sdnskmp", + 559: "teedtap", + 560: "rmonitor", + 561: "monitor", + 562: "chshell", + 563: "nntps", + 564: "9pfs", + 565: "whoami", + 566: "streettalk", + 567: "banyan-rpc", + 568: "ms-shuttle", + 569: "ms-rome", + 570: "meter", + 571: "meter", + 572: "sonar", + 573: "banyan-vip", + 574: "ftp-agent", + 575: "vemmi", + 576: "ipcd", + 577: "vnas", + 578: "ipdd", + 579: "decbsrv", + 580: "sntp-heartbeat", + 581: "bdp", + 582: "scc-security", + 583: "philips-vc", + 584: "keyserver", + 586: "password-chg", + 587: "submission", + 588: "cal", + 589: "eyelink", + 590: "tns-cml", + 591: "http-alt", + 592: "eudora-set", + 593: "http-rpc-epmap", + 594: "tpip", + 595: "cab-protocol", + 596: "smsd", + 597: "ptcnameservice", + 598: "sco-websrvrmg3", + 599: "acp", + 600: "ipcserver", + 601: "syslog-conn", + 602: "xmlrpc-beep", + 603: "idxp", + 604: "tunnel", + 605: "soap-beep", + 606: "urm", + 607: "nqs", + 608: "sift-uft", + 609: "npmp-trap", + 610: "npmp-local", + 611: "npmp-gui", + 612: "hmmp-ind", + 613: "hmmp-op", + 614: "sshell", + 615: "sco-inetmgr", + 616: "sco-sysmgr", + 617: "sco-dtmgr", + 618: "dei-icda", + 619: "compaq-evm", + 620: "sco-websrvrmgr", + 621: "escp-ip", + 622: "collaborator", + 623: "oob-ws-http", + 624: "cryptoadmin", + 625: "dec-dlm", + 626: "asia", + 627: "passgo-tivoli", + 628: "qmqp", + 629: "3com-amp3", + 630: "rda", + 631: "ipp", + 632: "bmpp", + 633: "servstat", + 634: "ginad", + 635: "rlzdbase", + 636: "ldaps", + 637: "lanserver", + 638: "mcns-sec", + 639: "msdp", + 640: "entrust-sps", + 641: "repcmd", + 642: "esro-emsdp", + 643: "sanity", + 644: "dwr", + 645: "pssc", + 646: "ldp", + 647: "dhcp-failover", + 648: "rrp", + 649: "cadview-3d", + 650: "obex", + 651: "ieee-mms", + 652: "hello-port", + 653: "repscmd", + 654: "aodv", + 655: "tinc", + 656: "spmp", + 657: "rmc", + 658: "tenfold", + 660: "mac-srvr-admin", + 661: "hap", + 662: "pftp", + 663: "purenoise", + 664: "oob-ws-https", + 665: "sun-dr", + 666: "mdqs", + 667: "disclose", + 668: "mecomm", + 669: "meregister", + 670: "vacdsm-sws", + 671: "vacdsm-app", + 672: "vpps-qua", + 673: "cimplex", + 674: "acap", + 675: "dctp", + 676: "vpps-via", + 677: "vpp", + 678: "ggf-ncp", + 679: "mrm", + 680: "entrust-aaas", + 681: "entrust-aams", + 682: "xfr", + 683: "corba-iiop", + 684: "corba-iiop-ssl", + 685: "mdc-portmapper", + 686: "hcp-wismar", + 687: "asipregistry", + 688: "realm-rusd", + 689: "nmap", + 690: "vatp", + 691: "msexch-routing", + 692: "hyperwave-isp", + 693: "connendp", + 694: "ha-cluster", + 695: "ieee-mms-ssl", + 696: "rushd", + 697: "uuidgen", + 698: "olsr", + 699: "accessnetwork", + 700: "epp", + 701: "lmp", + 702: "iris-beep", + 704: "elcsd", + 705: "agentx", + 706: "silc", + 707: "borland-dsj", + 709: "entrust-kmsh", + 710: "entrust-ash", + 711: "cisco-tdp", + 712: "tbrpf", + 713: "iris-xpc", + 714: "iris-xpcs", + 715: "iris-lwz", + 729: "netviewdm1", + 730: "netviewdm2", + 731: "netviewdm3", + 741: "netgw", + 742: "netrcs", + 744: "flexlm", + 747: "fujitsu-dev", + 748: "ris-cm", + 749: "kerberos-adm", + 750: "rfile", + 751: "pump", + 752: "qrh", + 753: "rrh", + 754: "tell", + 758: "nlogin", + 759: "con", + 760: "ns", + 761: "rxe", + 762: "quotad", + 763: "cycleserv", + 764: "omserv", + 765: "webster", + 767: "phonebook", + 769: "vid", + 770: "cadlock", + 771: "rtip", + 772: "cycleserv2", + 773: "submit", + 774: "rpasswd", + 775: "entomb", + 776: "wpages", + 777: "multiling-http", + 780: "wpgs", + 800: "mdbs-daemon", + 801: "device", + 802: "mbap-s", + 810: "fcp-udp", + 828: "itm-mcell-s", + 829: "pkix-3-ca-ra", + 830: "netconf-ssh", + 831: "netconf-beep", + 832: "netconfsoaphttp", + 833: "netconfsoapbeep", + 847: "dhcp-failover2", + 848: "gdoi", + 853: "domain-s", + 854: "dlep", + 860: "iscsi", + 861: "owamp-control", + 862: "twamp-control", + 873: "rsync", + 886: "iclcnet-locate", + 887: "iclcnet-svinfo", + 888: "accessbuilder", + 900: "omginitialrefs", + 901: "smpnameres", + 902: "ideafarm-door", + 903: "ideafarm-panic", + 910: "kink", + 911: "xact-backup", + 912: "apex-mesh", + 913: "apex-edge", + 953: "rndc", + 989: "ftps-data", + 990: "ftps", + 991: "nas", + 992: "telnets", + 993: "imaps", + 995: "pop3s", + 996: "vsinet", + 997: "maitrd", + 998: "busboy", + 999: "garcon", + 1000: "cadlock2", + 1001: "webpush", + 1010: "surf", + 1021: "exp1", + 1022: "exp2", + 1025: "blackjack", + 1026: "cap", + 1029: "solid-mux", + 1033: "netinfo-local", + 1034: "activesync", + 1035: "mxxrlogin", + 1036: "nsstp", + 1037: "ams", + 1038: "mtqp", + 1039: "sbl", + 1040: "netarx", + 1041: "danf-ak2", + 1042: "afrog", + 1043: "boinc-client", + 1044: "dcutility", + 1045: "fpitp", + 1046: "wfremotertm", + 1047: "neod1", + 1048: "neod2", + 1049: "td-postman", + 1050: "cma", + 1051: "optima-vnet", + 1052: "ddt", + 1053: "remote-as", + 1054: "brvread", + 1055: "ansyslmd", + 1056: "vfo", + 1057: "startron", + 1058: "nim", + 1059: "nimreg", + 1060: "polestar", + 1061: "kiosk", + 1062: "veracity", + 1063: "kyoceranetdev", + 1064: "jstel", + 1065: "syscomlan", + 1066: "fpo-fns", + 1067: "instl-boots", + 1068: "instl-bootc", + 1069: "cognex-insight", + 1070: "gmrupdateserv", + 1071: "bsquare-voip", + 1072: "cardax", + 1073: "bridgecontrol", + 1074: "warmspotMgmt", + 1075: "rdrmshc", + 1076: "dab-sti-c", + 1077: "imgames", + 1078: "avocent-proxy", + 1079: "asprovatalk", + 1080: "socks", + 1081: "pvuniwien", + 1082: "amt-esd-prot", + 1083: "ansoft-lm-1", + 1084: "ansoft-lm-2", + 1085: "webobjects", + 1086: "cplscrambler-lg", + 1087: "cplscrambler-in", + 1088: "cplscrambler-al", + 1089: "ff-annunc", + 1090: "ff-fms", + 1091: "ff-sm", + 1092: "obrpd", + 1093: "proofd", + 1094: "rootd", + 1095: "nicelink", + 1096: "cnrprotocol", + 1097: "sunclustermgr", + 1098: "rmiactivation", + 1099: "rmiregistry", + 1100: "mctp", + 1101: "pt2-discover", + 1102: "adobeserver-1", + 1103: "adobeserver-2", + 1104: "xrl", + 1105: "ftranhc", + 1106: "isoipsigport-1", + 1107: "isoipsigport-2", + 1108: "ratio-adp", + 1110: "webadmstart", + 1111: "lmsocialserver", + 1112: "icp", + 1113: "ltp-deepspace", + 1114: "mini-sql", + 1115: "ardus-trns", + 1116: "ardus-cntl", + 1117: "ardus-mtrns", + 1118: "sacred", + 1119: "bnetgame", + 1120: "bnetfile", + 1121: "rmpp", + 1122: "availant-mgr", + 1123: "murray", + 1124: "hpvmmcontrol", + 1125: "hpvmmagent", + 1126: "hpvmmdata", + 1127: "kwdb-commn", + 1128: "saphostctrl", + 1129: "saphostctrls", + 1130: "casp", + 1131: "caspssl", + 1132: "kvm-via-ip", + 1133: "dfn", + 1134: "aplx", + 1135: "omnivision", + 1136: "hhb-gateway", + 1137: "trim", + 1138: "encrypted-admin", + 1139: "evm", + 1140: "autonoc", + 1141: "mxomss", + 1142: "edtools", + 1143: "imyx", + 1144: "fuscript", + 1145: "x9-icue", + 1146: "audit-transfer", + 1147: "capioverlan", + 1148: "elfiq-repl", + 1149: "bvtsonar", + 1150: "blaze", + 1151: "unizensus", + 1152: "winpoplanmess", + 1153: "c1222-acse", + 1154: "resacommunity", + 1155: "nfa", + 1156: "iascontrol-oms", + 1157: "iascontrol", + 1158: "dbcontrol-oms", + 1159: "oracle-oms", + 1160: "olsv", + 1161: "health-polling", + 1162: "health-trap", + 1163: "sddp", + 1164: "qsm-proxy", + 1165: "qsm-gui", + 1166: "qsm-remote", + 1167: "cisco-ipsla", + 1168: "vchat", + 1169: "tripwire", + 1170: "atc-lm", + 1171: "atc-appserver", + 1172: "dnap", + 1173: "d-cinema-rrp", + 1174: "fnet-remote-ui", + 1175: "dossier", + 1176: "indigo-server", + 1177: "dkmessenger", + 1178: "sgi-storman", + 1179: "b2n", + 1180: "mc-client", + 1181: "3comnetman", + 1182: "accelenet", + 1183: "llsurfup-http", + 1184: "llsurfup-https", + 1185: "catchpole", + 1186: "mysql-cluster", + 1187: "alias", + 1188: "hp-webadmin", + 1189: "unet", + 1190: "commlinx-avl", + 1191: "gpfs", + 1192: "caids-sensor", + 1193: "fiveacross", + 1194: "openvpn", + 1195: "rsf-1", + 1196: "netmagic", + 1197: "carrius-rshell", + 1198: "cajo-discovery", + 1199: "dmidi", + 1200: "scol", + 1201: "nucleus-sand", + 1202: "caiccipc", + 1203: "ssslic-mgr", + 1204: "ssslog-mgr", + 1205: "accord-mgc", + 1206: "anthony-data", + 1207: "metasage", + 1208: "seagull-ais", + 1209: "ipcd3", + 1210: "eoss", + 1211: "groove-dpp", + 1212: "lupa", + 1213: "mpc-lifenet", + 1214: "kazaa", + 1215: "scanstat-1", + 1216: "etebac5", + 1217: "hpss-ndapi", + 1218: "aeroflight-ads", + 1219: "aeroflight-ret", + 1220: "qt-serveradmin", + 1221: "sweetware-apps", + 1222: "nerv", + 1223: "tgp", + 1224: "vpnz", + 1225: "slinkysearch", + 1226: "stgxfws", + 1227: "dns2go", + 1228: "florence", + 1229: "zented", + 1230: "periscope", + 1231: "menandmice-lpm", + 1232: "first-defense", + 1233: "univ-appserver", + 1234: "search-agent", + 1235: "mosaicsyssvc1", + 1236: "bvcontrol", + 1237: "tsdos390", + 1238: "hacl-qs", + 1239: "nmsd", + 1240: "instantia", + 1241: "nessus", + 1242: "nmasoverip", + 1243: "serialgateway", + 1244: "isbconference1", + 1245: "isbconference2", + 1246: "payrouter", + 1247: "visionpyramid", + 1248: "hermes", + 1249: "mesavistaco", + 1250: "swldy-sias", + 1251: "servergraph", + 1252: "bspne-pcc", + 1253: "q55-pcc", + 1254: "de-noc", + 1255: "de-cache-query", + 1256: "de-server", + 1257: "shockwave2", + 1258: "opennl", + 1259: "opennl-voice", + 1260: "ibm-ssd", + 1261: "mpshrsv", + 1262: "qnts-orb", + 1263: "dka", + 1264: "prat", + 1265: "dssiapi", + 1266: "dellpwrappks", + 1267: "epc", + 1268: "propel-msgsys", + 1269: "watilapp", + 1270: "opsmgr", + 1271: "excw", + 1272: "cspmlockmgr", + 1273: "emc-gateway", + 1274: "t1distproc", + 1275: "ivcollector", + 1277: "miva-mqs", + 1278: "dellwebadmin-1", + 1279: "dellwebadmin-2", + 1280: "pictrography", + 1281: "healthd", + 1282: "emperion", + 1283: "productinfo", + 1284: "iee-qfx", + 1285: "neoiface", + 1286: "netuitive", + 1287: "routematch", + 1288: "navbuddy", + 1289: "jwalkserver", + 1290: "winjaserver", + 1291: "seagulllms", + 1292: "dsdn", + 1293: "pkt-krb-ipsec", + 1294: "cmmdriver", + 1295: "ehtp", + 1296: "dproxy", + 1297: "sdproxy", + 1298: "lpcp", + 1299: "hp-sci", + 1300: "h323hostcallsc", + 1301: "ci3-software-1", + 1302: "ci3-software-2", + 1303: "sftsrv", + 1304: "boomerang", + 1305: "pe-mike", + 1306: "re-conn-proto", + 1307: "pacmand", + 1308: "odsi", + 1309: "jtag-server", + 1310: "husky", + 1311: "rxmon", + 1312: "sti-envision", + 1313: "bmc-patroldb", + 1314: "pdps", + 1315: "els", + 1316: "exbit-escp", + 1317: "vrts-ipcserver", + 1318: "krb5gatekeeper", + 1319: "amx-icsp", + 1320: "amx-axbnet", + 1321: "pip", + 1322: "novation", + 1323: "brcd", + 1324: "delta-mcp", + 1325: "dx-instrument", + 1326: "wimsic", + 1327: "ultrex", + 1328: "ewall", + 1329: "netdb-export", + 1330: "streetperfect", + 1331: "intersan", + 1332: "pcia-rxp-b", + 1333: "passwrd-policy", + 1334: "writesrv", + 1335: "digital-notary", + 1336: "ischat", + 1337: "menandmice-dns", + 1338: "wmc-log-svc", + 1339: "kjtsiteserver", + 1340: "naap", + 1341: "qubes", + 1342: "esbroker", + 1343: "re101", + 1344: "icap", + 1345: "vpjp", + 1346: "alta-ana-lm", + 1347: "bbn-mmc", + 1348: "bbn-mmx", + 1349: "sbook", + 1350: "editbench", + 1351: "equationbuilder", + 1352: "lotusnote", + 1353: "relief", + 1354: "XSIP-network", + 1355: "intuitive-edge", + 1356: "cuillamartin", + 1357: "pegboard", + 1358: "connlcli", + 1359: "ftsrv", + 1360: "mimer", + 1361: "linx", + 1362: "timeflies", + 1363: "ndm-requester", + 1364: "ndm-server", + 1365: "adapt-sna", + 1366: "netware-csp", + 1367: "dcs", + 1368: "screencast", + 1369: "gv-us", + 1370: "us-gv", + 1371: "fc-cli", + 1372: "fc-ser", + 1373: "chromagrafx", + 1374: "molly", + 1375: "bytex", + 1376: "ibm-pps", + 1377: "cichlid", + 1378: "elan", + 1379: "dbreporter", + 1380: "telesis-licman", + 1381: "apple-licman", + 1382: "udt-os", + 1383: "gwha", + 1384: "os-licman", + 1385: "atex-elmd", + 1386: "checksum", + 1387: "cadsi-lm", + 1388: "objective-dbc", + 1389: "iclpv-dm", + 1390: "iclpv-sc", + 1391: "iclpv-sas", + 1392: "iclpv-pm", + 1393: "iclpv-nls", + 1394: "iclpv-nlc", + 1395: "iclpv-wsm", + 1396: "dvl-activemail", + 1397: "audio-activmail", + 1398: "video-activmail", + 1399: "cadkey-licman", + 1400: "cadkey-tablet", + 1401: "goldleaf-licman", + 1402: "prm-sm-np", + 1403: "prm-nm-np", + 1404: "igi-lm", + 1405: "ibm-res", + 1406: "netlabs-lm", + 1407: "tibet-server", + 1408: "sophia-lm", + 1409: "here-lm", + 1410: "hiq", + 1411: "af", + 1412: "innosys", + 1413: "innosys-acl", + 1414: "ibm-mqseries", + 1415: "dbstar", + 1416: "novell-lu6-2", + 1417: "timbuktu-srv1", + 1418: "timbuktu-srv2", + 1419: "timbuktu-srv3", + 1420: "timbuktu-srv4", + 1421: "gandalf-lm", + 1422: "autodesk-lm", + 1423: "essbase", + 1424: "hybrid", + 1425: "zion-lm", + 1426: "sais", + 1427: "mloadd", + 1428: "informatik-lm", + 1429: "nms", + 1430: "tpdu", + 1431: "rgtp", + 1432: "blueberry-lm", + 1433: "ms-sql-s", + 1434: "ms-sql-m", + 1435: "ibm-cics", + 1436: "saism", + 1437: "tabula", + 1438: "eicon-server", + 1439: "eicon-x25", + 1440: "eicon-slp", + 1441: "cadis-1", + 1442: "cadis-2", + 1443: "ies-lm", + 1444: "marcam-lm", + 1445: "proxima-lm", + 1446: "ora-lm", + 1447: "apri-lm", + 1448: "oc-lm", + 1449: "peport", + 1450: "dwf", + 1451: "infoman", + 1452: "gtegsc-lm", + 1453: "genie-lm", + 1454: "interhdl-elmd", + 1455: "esl-lm", + 1456: "dca", + 1457: "valisys-lm", + 1458: "nrcabq-lm", + 1459: "proshare1", + 1460: "proshare2", + 1461: "ibm-wrless-lan", + 1462: "world-lm", + 1463: "nucleus", + 1464: "msl-lmd", + 1465: "pipes", + 1466: "oceansoft-lm", + 1467: "csdmbase", + 1468: "csdm", + 1469: "aal-lm", + 1470: "uaiact", + 1471: "csdmbase", + 1472: "csdm", + 1473: "openmath", + 1474: "telefinder", + 1475: "taligent-lm", + 1476: "clvm-cfg", + 1477: "ms-sna-server", + 1478: "ms-sna-base", + 1479: "dberegister", + 1480: "pacerforum", + 1481: "airs", + 1482: "miteksys-lm", + 1483: "afs", + 1484: "confluent", + 1485: "lansource", + 1486: "nms-topo-serv", + 1487: "localinfosrvr", + 1488: "docstor", + 1489: "dmdocbroker", + 1490: "insitu-conf", + 1492: "stone-design-1", + 1493: "netmap-lm", + 1494: "ica", + 1495: "cvc", + 1496: "liberty-lm", + 1497: "rfx-lm", + 1498: "sybase-sqlany", + 1499: "fhc", + 1500: "vlsi-lm", + 1501: "saiscm", + 1502: "shivadiscovery", + 1503: "imtc-mcs", + 1504: "evb-elm", + 1505: "funkproxy", + 1506: "utcd", + 1507: "symplex", + 1508: "diagmond", + 1509: "robcad-lm", + 1510: "mvx-lm", + 1511: "3l-l1", + 1512: "wins", + 1513: "fujitsu-dtc", + 1514: "fujitsu-dtcns", + 1515: "ifor-protocol", + 1516: "vpad", + 1517: "vpac", + 1518: "vpvd", + 1519: "vpvc", + 1520: "atm-zip-office", + 1521: "ncube-lm", + 1522: "ricardo-lm", + 1523: "cichild-lm", + 1524: "ingreslock", + 1525: "orasrv", + 1526: "pdap-np", + 1527: "tlisrv", + 1529: "coauthor", + 1530: "rap-service", + 1531: "rap-listen", + 1532: "miroconnect", + 1533: "virtual-places", + 1534: "micromuse-lm", + 1535: "ampr-info", + 1536: "ampr-inter", + 1537: "sdsc-lm", + 1538: "3ds-lm", + 1539: "intellistor-lm", + 1540: "rds", + 1541: "rds2", + 1542: "gridgen-elmd", + 1543: "simba-cs", + 1544: "aspeclmd", + 1545: "vistium-share", + 1546: "abbaccuray", + 1547: "laplink", + 1548: "axon-lm", + 1549: "shivahose", + 1550: "3m-image-lm", + 1551: "hecmtl-db", + 1552: "pciarray", + 1553: "sna-cs", + 1554: "caci-lm", + 1555: "livelan", + 1556: "veritas-pbx", + 1557: "arbortext-lm", + 1558: "xingmpeg", + 1559: "web2host", + 1560: "asci-val", + 1561: "facilityview", + 1562: "pconnectmgr", + 1563: "cadabra-lm", + 1564: "pay-per-view", + 1565: "winddlb", + 1566: "corelvideo", + 1567: "jlicelmd", + 1568: "tsspmap", + 1569: "ets", + 1570: "orbixd", + 1571: "rdb-dbs-disp", + 1572: "chip-lm", + 1573: "itscomm-ns", + 1574: "mvel-lm", + 1575: "oraclenames", + 1576: "moldflow-lm", + 1577: "hypercube-lm", + 1578: "jacobus-lm", + 1579: "ioc-sea-lm", + 1580: "tn-tl-r1", + 1581: "mil-2045-47001", + 1582: "msims", + 1583: "simbaexpress", + 1584: "tn-tl-fd2", + 1585: "intv", + 1586: "ibm-abtact", + 1587: "pra-elmd", + 1588: "triquest-lm", + 1589: "vqp", + 1590: "gemini-lm", + 1591: "ncpm-pm", + 1592: "commonspace", + 1593: "mainsoft-lm", + 1594: "sixtrak", + 1595: "radio", + 1596: "radio-sm", + 1597: "orbplus-iiop", + 1598: "picknfs", + 1599: "simbaservices", + 1600: "issd", + 1601: "aas", + 1602: "inspect", + 1603: "picodbc", + 1604: "icabrowser", + 1605: "slp", + 1606: "slm-api", + 1607: "stt", + 1608: "smart-lm", + 1609: "isysg-lm", + 1610: "taurus-wh", + 1611: "ill", + 1612: "netbill-trans", + 1613: "netbill-keyrep", + 1614: "netbill-cred", + 1615: "netbill-auth", + 1616: "netbill-prod", + 1617: "nimrod-agent", + 1618: "skytelnet", + 1619: "xs-openstorage", + 1620: "faxportwinport", + 1621: "softdataphone", + 1622: "ontime", + 1623: "jaleosnd", + 1624: "udp-sr-port", + 1625: "svs-omagent", + 1626: "shockwave", + 1627: "t128-gateway", + 1628: "lontalk-norm", + 1629: "lontalk-urgnt", + 1630: "oraclenet8cman", + 1631: "visitview", + 1632: "pammratc", + 1633: "pammrpc", + 1634: "loaprobe", + 1635: "edb-server1", + 1636: "isdc", + 1637: "islc", + 1638: "ismc", + 1639: "cert-initiator", + 1640: "cert-responder", + 1641: "invision", + 1642: "isis-am", + 1643: "isis-ambc", + 1644: "saiseh", + 1645: "sightline", + 1646: "sa-msg-port", + 1647: "rsap", + 1648: "concurrent-lm", + 1649: "kermit", + 1650: "nkd", + 1651: "shiva-confsrvr", + 1652: "xnmp", + 1653: "alphatech-lm", + 1654: "stargatealerts", + 1655: "dec-mbadmin", + 1656: "dec-mbadmin-h", + 1657: "fujitsu-mmpdc", + 1658: "sixnetudr", + 1659: "sg-lm", + 1660: "skip-mc-gikreq", + 1661: "netview-aix-1", + 1662: "netview-aix-2", + 1663: "netview-aix-3", + 1664: "netview-aix-4", + 1665: "netview-aix-5", + 1666: "netview-aix-6", + 1667: "netview-aix-7", + 1668: "netview-aix-8", + 1669: "netview-aix-9", + 1670: "netview-aix-10", + 1671: "netview-aix-11", + 1672: "netview-aix-12", + 1673: "proshare-mc-1", + 1674: "proshare-mc-2", + 1675: "pdp", + 1676: "netcomm1", + 1677: "groupwise", + 1678: "prolink", + 1679: "darcorp-lm", + 1680: "microcom-sbp", + 1681: "sd-elmd", + 1682: "lanyon-lantern", + 1683: "ncpm-hip", + 1684: "snaresecure", + 1685: "n2nremote", + 1686: "cvmon", + 1687: "nsjtp-ctrl", + 1688: "nsjtp-data", + 1689: "firefox", + 1690: "ng-umds", + 1691: "empire-empuma", + 1692: "sstsys-lm", + 1693: "rrirtr", + 1694: "rrimwm", + 1695: "rrilwm", + 1696: "rrifmm", + 1697: "rrisat", + 1698: "rsvp-encap-1", + 1699: "rsvp-encap-2", + 1700: "mps-raft", + 1701: "l2f", + 1702: "deskshare", + 1703: "hb-engine", + 1704: "bcs-broker", + 1705: "slingshot", + 1706: "jetform", + 1707: "vdmplay", + 1708: "gat-lmd", + 1709: "centra", + 1710: "impera", + 1711: "pptconference", + 1712: "registrar", + 1713: "conferencetalk", + 1714: "sesi-lm", + 1715: "houdini-lm", + 1716: "xmsg", + 1717: "fj-hdnet", + 1718: "h323gatedisc", + 1719: "h323gatestat", + 1720: "h323hostcall", + 1721: "caicci", + 1722: "hks-lm", + 1723: "pptp", + 1724: "csbphonemaster", + 1725: "iden-ralp", + 1726: "iberiagames", + 1727: "winddx", + 1728: "telindus", + 1729: "citynl", + 1730: "roketz", + 1731: "msiccp", + 1732: "proxim", + 1733: "siipat", + 1734: "cambertx-lm", + 1735: "privatechat", + 1736: "street-stream", + 1737: "ultimad", + 1738: "gamegen1", + 1739: "webaccess", + 1740: "encore", + 1741: "cisco-net-mgmt", + 1742: "3Com-nsd", + 1743: "cinegrfx-lm", + 1744: "ncpm-ft", + 1745: "remote-winsock", + 1746: "ftrapid-1", + 1747: "ftrapid-2", + 1748: "oracle-em1", + 1749: "aspen-services", + 1750: "sslp", + 1751: "swiftnet", + 1752: "lofr-lm", + 1753: "predatar-comms", + 1754: "oracle-em2", + 1755: "ms-streaming", + 1756: "capfast-lmd", + 1757: "cnhrp", + 1758: "tftp-mcast", + 1759: "spss-lm", + 1760: "www-ldap-gw", + 1761: "cft-0", + 1762: "cft-1", + 1763: "cft-2", + 1764: "cft-3", + 1765: "cft-4", + 1766: "cft-5", + 1767: "cft-6", + 1768: "cft-7", + 1769: "bmc-net-adm", + 1770: "bmc-net-svc", + 1771: "vaultbase", + 1772: "essweb-gw", + 1773: "kmscontrol", + 1774: "global-dtserv", + 1775: "vdab", + 1776: "femis", + 1777: "powerguardian", + 1778: "prodigy-intrnet", + 1779: "pharmasoft", + 1780: "dpkeyserv", + 1781: "answersoft-lm", + 1782: "hp-hcip", + 1784: "finle-lm", + 1785: "windlm", + 1786: "funk-logger", + 1787: "funk-license", + 1788: "psmond", + 1789: "hello", + 1790: "nmsp", + 1791: "ea1", + 1792: "ibm-dt-2", + 1793: "rsc-robot", + 1794: "cera-bcm", + 1795: "dpi-proxy", + 1796: "vocaltec-admin", + 1797: "uma", + 1798: "etp", + 1799: "netrisk", + 1800: "ansys-lm", + 1801: "msmq", + 1802: "concomp1", + 1803: "hp-hcip-gwy", + 1804: "enl", + 1805: "enl-name", + 1806: "musiconline", + 1807: "fhsp", + 1808: "oracle-vp2", + 1809: "oracle-vp1", + 1810: "jerand-lm", + 1811: "scientia-sdb", + 1812: "radius", + 1813: "radius-acct", + 1814: "tdp-suite", + 1815: "mmpft", + 1816: "harp", + 1817: "rkb-oscs", + 1818: "etftp", + 1819: "plato-lm", + 1820: "mcagent", + 1821: "donnyworld", + 1822: "es-elmd", + 1823: "unisys-lm", + 1824: "metrics-pas", + 1825: "direcpc-video", + 1826: "ardt", + 1827: "asi", + 1828: "itm-mcell-u", + 1829: "optika-emedia", + 1830: "net8-cman", + 1831: "myrtle", + 1832: "tht-treasure", + 1833: "udpradio", + 1834: "ardusuni", + 1835: "ardusmul", + 1836: "ste-smsc", + 1837: "csoft1", + 1838: "talnet", + 1839: "netopia-vo1", + 1840: "netopia-vo2", + 1841: "netopia-vo3", + 1842: "netopia-vo4", + 1843: "netopia-vo5", + 1844: "direcpc-dll", + 1845: "altalink", + 1846: "tunstall-pnc", + 1847: "slp-notify", + 1848: "fjdocdist", + 1849: "alpha-sms", + 1850: "gsi", + 1851: "ctcd", + 1852: "virtual-time", + 1853: "vids-avtp", + 1854: "buddy-draw", + 1855: "fiorano-rtrsvc", + 1856: "fiorano-msgsvc", + 1857: "datacaptor", + 1858: "privateark", + 1859: "gammafetchsvr", + 1860: "sunscalar-svc", + 1861: "lecroy-vicp", + 1862: "mysql-cm-agent", + 1863: "msnp", + 1864: "paradym-31port", + 1865: "entp", + 1866: "swrmi", + 1867: "udrive", + 1868: "viziblebrowser", + 1869: "transact", + 1870: "sunscalar-dns", + 1871: "canocentral0", + 1872: "canocentral1", + 1873: "fjmpjps", + 1874: "fjswapsnp", + 1875: "westell-stats", + 1876: "ewcappsrv", + 1877: "hp-webqosdb", + 1878: "drmsmc", + 1879: "nettgain-nms", + 1880: "vsat-control", + 1881: "ibm-mqseries2", + 1882: "ecsqdmn", + 1883: "mqtt", + 1884: "idmaps", + 1885: "vrtstrapserver", + 1886: "leoip", + 1887: "filex-lport", + 1888: "ncconfig", + 1889: "unify-adapter", + 1890: "wilkenlistener", + 1891: "childkey-notif", + 1892: "childkey-ctrl", + 1893: "elad", + 1894: "o2server-port", + 1896: "b-novative-ls", + 1897: "metaagent", + 1898: "cymtec-port", + 1899: "mc2studios", + 1900: "ssdp", + 1901: "fjicl-tep-a", + 1902: "fjicl-tep-b", + 1903: "linkname", + 1904: "fjicl-tep-c", + 1905: "sugp", + 1906: "tpmd", + 1907: "intrastar", + 1908: "dawn", + 1909: "global-wlink", + 1910: "ultrabac", + 1911: "mtp", + 1912: "rhp-iibp", + 1913: "armadp", + 1914: "elm-momentum", + 1915: "facelink", + 1916: "persona", + 1917: "noagent", + 1918: "can-nds", + 1919: "can-dch", + 1920: "can-ferret", + 1921: "noadmin", + 1922: "tapestry", + 1923: "spice", + 1924: "xiip", + 1925: "discovery-port", + 1926: "egs", + 1927: "videte-cipc", + 1928: "emsd-port", + 1929: "bandwiz-system", + 1930: "driveappserver", + 1931: "amdsched", + 1932: "ctt-broker", + 1933: "xmapi", + 1934: "xaapi", + 1935: "macromedia-fcs", + 1936: "jetcmeserver", + 1937: "jwserver", + 1938: "jwclient", + 1939: "jvserver", + 1940: "jvclient", + 1941: "dic-aida", + 1942: "res", + 1943: "beeyond-media", + 1944: "close-combat", + 1945: "dialogic-elmd", + 1946: "tekpls", + 1947: "sentinelsrm", + 1948: "eye2eye", + 1949: "ismaeasdaqlive", + 1950: "ismaeasdaqtest", + 1951: "bcs-lmserver", + 1952: "mpnjsc", + 1953: "rapidbase", + 1954: "abr-api", + 1955: "abr-secure", + 1956: "vrtl-vmf-ds", + 1957: "unix-status", + 1958: "dxadmind", + 1959: "simp-all", + 1960: "nasmanager", + 1961: "bts-appserver", + 1962: "biap-mp", + 1963: "webmachine", + 1964: "solid-e-engine", + 1965: "tivoli-npm", + 1966: "slush", + 1967: "sns-quote", + 1968: "lipsinc", + 1969: "lipsinc1", + 1970: "netop-rc", + 1971: "netop-school", + 1972: "intersys-cache", + 1973: "dlsrap", + 1974: "drp", + 1975: "tcoflashagent", + 1976: "tcoregagent", + 1977: "tcoaddressbook", + 1978: "unisql", + 1979: "unisql-java", + 1980: "pearldoc-xact", + 1981: "p2pq", + 1982: "estamp", + 1983: "lhtp", + 1984: "bb", + 1985: "hsrp", + 1986: "licensedaemon", + 1987: "tr-rsrb-p1", + 1988: "tr-rsrb-p2", + 1989: "tr-rsrb-p3", + 1990: "stun-p1", + 1991: "stun-p2", + 1992: "stun-p3", + 1993: "snmp-tcp-port", + 1994: "stun-port", + 1995: "perf-port", + 1996: "tr-rsrb-port", + 1997: "gdp-port", + 1998: "x25-svc-port", + 1999: "tcp-id-port", + 2000: "cisco-sccp", + 2001: "dc", + 2002: "globe", + 2003: "brutus", + 2004: "mailbox", + 2005: "berknet", + 2006: "invokator", + 2007: "dectalk", + 2008: "conf", + 2009: "news", + 2010: "search", + 2011: "raid-cc", + 2012: "ttyinfo", + 2013: "raid-am", + 2014: "troff", + 2015: "cypress", + 2016: "bootserver", + 2017: "cypress-stat", + 2018: "terminaldb", + 2019: "whosockami", + 2020: "xinupageserver", + 2021: "servexec", + 2022: "down", + 2023: "xinuexpansion3", + 2024: "xinuexpansion4", + 2025: "ellpack", + 2026: "scrabble", + 2027: "shadowserver", + 2028: "submitserver", + 2029: "hsrpv6", + 2030: "device2", + 2031: "mobrien-chat", + 2032: "blackboard", + 2033: "glogger", + 2034: "scoremgr", + 2035: "imsldoc", + 2036: "e-dpnet", + 2037: "applus", + 2038: "objectmanager", + 2039: "prizma", + 2040: "lam", + 2041: "interbase", + 2042: "isis", + 2043: "isis-bcast", + 2044: "rimsl", + 2045: "cdfunc", + 2046: "sdfunc", + 2047: "dls", + 2048: "dls-monitor", + 2049: "shilp", + 2050: "av-emb-config", + 2051: "epnsdp", + 2052: "clearvisn", + 2053: "lot105-ds-upd", + 2054: "weblogin", + 2055: "iop", + 2056: "omnisky", + 2057: "rich-cp", + 2058: "newwavesearch", + 2059: "bmc-messaging", + 2060: "teleniumdaemon", + 2061: "netmount", + 2062: "icg-swp", + 2063: "icg-bridge", + 2064: "icg-iprelay", + 2065: "dlsrpn", + 2066: "aura", + 2067: "dlswpn", + 2068: "avauthsrvprtcl", + 2069: "event-port", + 2070: "ah-esp-encap", + 2071: "acp-port", + 2072: "msync", + 2073: "gxs-data-port", + 2074: "vrtl-vmf-sa", + 2075: "newlixengine", + 2076: "newlixconfig", + 2077: "tsrmagt", + 2078: "tpcsrvr", + 2079: "idware-router", + 2080: "autodesk-nlm", + 2081: "kme-trap-port", + 2082: "infowave", + 2083: "radsec", + 2084: "sunclustergeo", + 2085: "ada-cip", + 2086: "gnunet", + 2087: "eli", + 2088: "ip-blf", + 2089: "sep", + 2090: "lrp", + 2091: "prp", + 2092: "descent3", + 2093: "nbx-cc", + 2094: "nbx-au", + 2095: "nbx-ser", + 2096: "nbx-dir", + 2097: "jetformpreview", + 2098: "dialog-port", + 2099: "h2250-annex-g", + 2100: "amiganetfs", + 2101: "rtcm-sc104", + 2102: "zephyr-srv", + 2103: "zephyr-clt", + 2104: "zephyr-hm", + 2105: "minipay", + 2106: "mzap", + 2107: "bintec-admin", + 2108: "comcam", + 2109: "ergolight", + 2110: "umsp", + 2111: "dsatp", + 2112: "idonix-metanet", + 2113: "hsl-storm", + 2114: "newheights", + 2115: "kdm", + 2116: "ccowcmr", + 2117: "mentaclient", + 2118: "mentaserver", + 2119: "gsigatekeeper", + 2120: "qencp", + 2121: "scientia-ssdb", + 2122: "caupc-remote", + 2123: "gtp-control", + 2124: "elatelink", + 2125: "lockstep", + 2126: "pktcable-cops", + 2127: "index-pc-wb", + 2128: "net-steward", + 2129: "cs-live", + 2130: "xds", + 2131: "avantageb2b", + 2132: "solera-epmap", + 2133: "zymed-zpp", + 2134: "avenue", + 2135: "gris", + 2136: "appworxsrv", + 2137: "connect", + 2138: "unbind-cluster", + 2139: "ias-auth", + 2140: "ias-reg", + 2141: "ias-admind", + 2142: "tdmoip", + 2143: "lv-jc", + 2144: "lv-ffx", + 2145: "lv-pici", + 2146: "lv-not", + 2147: "lv-auth", + 2148: "veritas-ucl", + 2149: "acptsys", + 2150: "dynamic3d", + 2151: "docent", + 2152: "gtp-user", + 2153: "ctlptc", + 2154: "stdptc", + 2155: "brdptc", + 2156: "trp", + 2157: "xnds", + 2158: "touchnetplus", + 2159: "gdbremote", + 2160: "apc-2160", + 2161: "apc-2161", + 2162: "navisphere", + 2163: "navisphere-sec", + 2164: "ddns-v3", + 2165: "x-bone-api", + 2166: "iwserver", + 2167: "raw-serial", + 2168: "easy-soft-mux", + 2169: "brain", + 2170: "eyetv", + 2171: "msfw-storage", + 2172: "msfw-s-storage", + 2173: "msfw-replica", + 2174: "msfw-array", + 2175: "airsync", + 2176: "rapi", + 2177: "qwave", + 2178: "bitspeer", + 2179: "vmrdp", + 2180: "mc-gt-srv", + 2181: "eforward", + 2182: "cgn-stat", + 2183: "cgn-config", + 2184: "nvd", + 2185: "onbase-dds", + 2186: "gtaua", + 2187: "ssmc", + 2188: "radware-rpm", + 2189: "radware-rpm-s", + 2190: "tivoconnect", + 2191: "tvbus", + 2192: "asdis", + 2193: "drwcs", + 2197: "mnp-exchange", + 2198: "onehome-remote", + 2199: "onehome-help", + 2200: "ici", + 2201: "ats", + 2202: "imtc-map", + 2203: "b2-runtime", + 2204: "b2-license", + 2205: "jps", + 2206: "hpocbus", + 2207: "hpssd", + 2208: "hpiod", + 2209: "rimf-ps", + 2210: "noaaport", + 2211: "emwin", + 2212: "leecoposserver", + 2213: "kali", + 2214: "rpi", + 2215: "ipcore", + 2216: "vtu-comms", + 2217: "gotodevice", + 2218: "bounzza", + 2219: "netiq-ncap", + 2220: "netiq", + 2221: "ethernet-ip-s", + 2222: "EtherNet-IP-1", + 2223: "rockwell-csp2", + 2224: "efi-mg", + 2225: "rcip-itu", + 2226: "di-drm", + 2227: "di-msg", + 2228: "ehome-ms", + 2229: "datalens", + 2230: "queueadm", + 2231: "wimaxasncp", + 2232: "ivs-video", + 2233: "infocrypt", + 2234: "directplay", + 2235: "sercomm-wlink", + 2236: "nani", + 2237: "optech-port1-lm", + 2238: "aviva-sna", + 2239: "imagequery", + 2240: "recipe", + 2241: "ivsd", + 2242: "foliocorp", + 2243: "magicom", + 2244: "nmsserver", + 2245: "hao", + 2246: "pc-mta-addrmap", + 2247: "antidotemgrsvr", + 2248: "ums", + 2249: "rfmp", + 2250: "remote-collab", + 2251: "dif-port", + 2252: "njenet-ssl", + 2253: "dtv-chan-req", + 2254: "seispoc", + 2255: "vrtp", + 2256: "pcc-mfp", + 2257: "simple-tx-rx", + 2258: "rcts", + 2260: "apc-2260", + 2261: "comotionmaster", + 2262: "comotionback", + 2263: "ecwcfg", + 2264: "apx500api-1", + 2265: "apx500api-2", + 2266: "mfserver", + 2267: "ontobroker", + 2268: "amt", + 2269: "mikey", + 2270: "starschool", + 2271: "mmcals", + 2272: "mmcal", + 2273: "mysql-im", + 2274: "pcttunnell", + 2275: "ibridge-data", + 2276: "ibridge-mgmt", + 2277: "bluectrlproxy", + 2278: "s3db", + 2279: "xmquery", + 2280: "lnvpoller", + 2281: "lnvconsole", + 2282: "lnvalarm", + 2283: "lnvstatus", + 2284: "lnvmaps", + 2285: "lnvmailmon", + 2286: "nas-metering", + 2287: "dna", + 2288: "netml", + 2289: "dict-lookup", + 2290: "sonus-logging", + 2291: "eapsp", + 2292: "mib-streaming", + 2293: "npdbgmngr", + 2294: "konshus-lm", + 2295: "advant-lm", + 2296: "theta-lm", + 2297: "d2k-datamover1", + 2298: "d2k-datamover2", + 2299: "pc-telecommute", + 2300: "cvmmon", + 2301: "cpq-wbem", + 2302: "binderysupport", + 2303: "proxy-gateway", + 2304: "attachmate-uts", + 2305: "mt-scaleserver", + 2306: "tappi-boxnet", + 2307: "pehelp", + 2308: "sdhelp", + 2309: "sdserver", + 2310: "sdclient", + 2311: "messageservice", + 2312: "wanscaler", + 2313: "iapp", + 2314: "cr-websystems", + 2315: "precise-sft", + 2316: "sent-lm", + 2317: "attachmate-g32", + 2318: "cadencecontrol", + 2319: "infolibria", + 2320: "siebel-ns", + 2321: "rdlap", + 2322: "ofsd", + 2323: "3d-nfsd", + 2324: "cosmocall", + 2325: "ansysli", + 2326: "idcp", + 2327: "xingcsm", + 2328: "netrix-sftm", + 2329: "nvd", + 2330: "tscchat", + 2331: "agentview", + 2332: "rcc-host", + 2333: "snapp", + 2334: "ace-client", + 2335: "ace-proxy", + 2336: "appleugcontrol", + 2337: "ideesrv", + 2338: "norton-lambert", + 2339: "3com-webview", + 2340: "wrs-registry", + 2341: "xiostatus", + 2342: "manage-exec", + 2343: "nati-logos", + 2344: "fcmsys", + 2345: "dbm", + 2346: "redstorm-join", + 2347: "redstorm-find", + 2348: "redstorm-info", + 2349: "redstorm-diag", + 2350: "psbserver", + 2351: "psrserver", + 2352: "pslserver", + 2353: "pspserver", + 2354: "psprserver", + 2355: "psdbserver", + 2356: "gxtelmd", + 2357: "unihub-server", + 2358: "futrix", + 2359: "flukeserver", + 2360: "nexstorindltd", + 2361: "tl1", + 2362: "digiman", + 2363: "mediacntrlnfsd", + 2364: "oi-2000", + 2365: "dbref", + 2366: "qip-login", + 2367: "service-ctrl", + 2368: "opentable", + 2370: "l3-hbmon", + 2371: "hp-rda", + 2372: "lanmessenger", + 2373: "remographlm", + 2374: "hydra", + 2375: "docker", + 2376: "docker-s", + 2377: "swarm", + 2379: "etcd-client", + 2380: "etcd-server", + 2381: "compaq-https", + 2382: "ms-olap3", + 2383: "ms-olap4", + 2384: "sd-request", + 2385: "sd-data", + 2386: "virtualtape", + 2387: "vsamredirector", + 2388: "mynahautostart", + 2389: "ovsessionmgr", + 2390: "rsmtp", + 2391: "3com-net-mgmt", + 2392: "tacticalauth", + 2393: "ms-olap1", + 2394: "ms-olap2", + 2395: "lan900-remote", + 2396: "wusage", + 2397: "ncl", + 2398: "orbiter", + 2399: "fmpro-fdal", + 2400: "opequus-server", + 2401: "cvspserver", + 2402: "taskmaster2000", + 2403: "taskmaster2000", + 2404: "iec-104", + 2405: "trc-netpoll", + 2406: "jediserver", + 2407: "orion", + 2408: "railgun-webaccl", + 2409: "sns-protocol", + 2410: "vrts-registry", + 2411: "netwave-ap-mgmt", + 2412: "cdn", + 2413: "orion-rmi-reg", + 2414: "beeyond", + 2415: "codima-rtp", + 2416: "rmtserver", + 2417: "composit-server", + 2418: "cas", + 2419: "attachmate-s2s", + 2420: "dslremote-mgmt", + 2421: "g-talk", + 2422: "crmsbits", + 2423: "rnrp", + 2424: "kofax-svr", + 2425: "fjitsuappmgr", + 2426: "vcmp", + 2427: "mgcp-gateway", + 2428: "ott", + 2429: "ft-role", + 2430: "venus", + 2431: "venus-se", + 2432: "codasrv", + 2433: "codasrv-se", + 2434: "pxc-epmap", + 2435: "optilogic", + 2436: "topx", + 2437: "unicontrol", + 2438: "msp", + 2439: "sybasedbsynch", + 2440: "spearway", + 2441: "pvsw-inet", + 2442: "netangel", + 2443: "powerclientcsf", + 2444: "btpp2sectrans", + 2445: "dtn1", + 2446: "bues-service", + 2447: "ovwdb", + 2448: "hpppssvr", + 2449: "ratl", + 2450: "netadmin", + 2451: "netchat", + 2452: "snifferclient", + 2453: "madge-ltd", + 2454: "indx-dds", + 2455: "wago-io-system", + 2456: "altav-remmgt", + 2457: "rapido-ip", + 2458: "griffin", + 2459: "community", + 2460: "ms-theater", + 2461: "qadmifoper", + 2462: "qadmifevent", + 2463: "lsi-raid-mgmt", + 2464: "direcpc-si", + 2465: "lbm", + 2466: "lbf", + 2467: "high-criteria", + 2468: "qip-msgd", + 2469: "mti-tcs-comm", + 2470: "taskman-port", + 2471: "seaodbc", + 2472: "c3", + 2473: "aker-cdp", + 2474: "vitalanalysis", + 2475: "ace-server", + 2476: "ace-svr-prop", + 2477: "ssm-cvs", + 2478: "ssm-cssps", + 2479: "ssm-els", + 2480: "powerexchange", + 2481: "giop", + 2482: "giop-ssl", + 2483: "ttc", + 2484: "ttc-ssl", + 2485: "netobjects1", + 2486: "netobjects2", + 2487: "pns", + 2488: "moy-corp", + 2489: "tsilb", + 2490: "qip-qdhcp", + 2491: "conclave-cpp", + 2492: "groove", + 2493: "talarian-mqs", + 2494: "bmc-ar", + 2495: "fast-rem-serv", + 2496: "dirgis", + 2497: "quaddb", + 2498: "odn-castraq", + 2499: "unicontrol", + 2500: "rtsserv", + 2501: "rtsclient", + 2502: "kentrox-prot", + 2503: "nms-dpnss", + 2504: "wlbs", + 2505: "ppcontrol", + 2506: "jbroker", + 2507: "spock", + 2508: "jdatastore", + 2509: "fjmpss", + 2510: "fjappmgrbulk", + 2511: "metastorm", + 2512: "citrixima", + 2513: "citrixadmin", + 2514: "facsys-ntp", + 2515: "facsys-router", + 2516: "maincontrol", + 2517: "call-sig-trans", + 2518: "willy", + 2519: "globmsgsvc", + 2520: "pvsw", + 2521: "adaptecmgr", + 2522: "windb", + 2523: "qke-llc-v3", + 2524: "optiwave-lm", + 2525: "ms-v-worlds", + 2526: "ema-sent-lm", + 2527: "iqserver", + 2528: "ncr-ccl", + 2529: "utsftp", + 2530: "vrcommerce", + 2531: "ito-e-gui", + 2532: "ovtopmd", + 2533: "snifferserver", + 2534: "combox-web-acc", + 2535: "madcap", + 2536: "btpp2audctr1", + 2537: "upgrade", + 2538: "vnwk-prapi", + 2539: "vsiadmin", + 2540: "lonworks", + 2541: "lonworks2", + 2542: "udrawgraph", + 2543: "reftek", + 2544: "novell-zen", + 2545: "sis-emt", + 2546: "vytalvaultbrtp", + 2547: "vytalvaultvsmp", + 2548: "vytalvaultpipe", + 2549: "ipass", + 2550: "ads", + 2551: "isg-uda-server", + 2552: "call-logging", + 2553: "efidiningport", + 2554: "vcnet-link-v10", + 2555: "compaq-wcp", + 2556: "nicetec-nmsvc", + 2557: "nicetec-mgmt", + 2558: "pclemultimedia", + 2559: "lstp", + 2560: "labrat", + 2561: "mosaixcc", + 2562: "delibo", + 2563: "cti-redwood", + 2564: "hp-3000-telnet", + 2565: "coord-svr", + 2566: "pcs-pcw", + 2567: "clp", + 2568: "spamtrap", + 2569: "sonuscallsig", + 2570: "hs-port", + 2571: "cecsvc", + 2572: "ibp", + 2573: "trustestablish", + 2574: "blockade-bpsp", + 2575: "hl7", + 2576: "tclprodebugger", + 2577: "scipticslsrvr", + 2578: "rvs-isdn-dcp", + 2579: "mpfoncl", + 2580: "tributary", + 2581: "argis-te", + 2582: "argis-ds", + 2583: "mon", + 2584: "cyaserv", + 2585: "netx-server", + 2586: "netx-agent", + 2587: "masc", + 2588: "privilege", + 2589: "quartus-tcl", + 2590: "idotdist", + 2591: "maytagshuffle", + 2592: "netrek", + 2593: "mns-mail", + 2594: "dts", + 2595: "worldfusion1", + 2596: "worldfusion2", + 2597: "homesteadglory", + 2598: "citriximaclient", + 2599: "snapd", + 2600: "hpstgmgr", + 2601: "discp-client", + 2602: "discp-server", + 2603: "servicemeter", + 2604: "nsc-ccs", + 2605: "nsc-posa", + 2606: "netmon", + 2607: "connection", + 2608: "wag-service", + 2609: "system-monitor", + 2610: "versa-tek", + 2611: "lionhead", + 2612: "qpasa-agent", + 2613: "smntubootstrap", + 2614: "neveroffline", + 2615: "firepower", + 2616: "appswitch-emp", + 2617: "cmadmin", + 2618: "priority-e-com", + 2619: "bruce", + 2620: "lpsrecommender", + 2621: "miles-apart", + 2622: "metricadbc", + 2623: "lmdp", + 2624: "aria", + 2625: "blwnkl-port", + 2626: "gbjd816", + 2627: "moshebeeri", + 2628: "dict", + 2629: "sitaraserver", + 2630: "sitaramgmt", + 2631: "sitaradir", + 2632: "irdg-post", + 2633: "interintelli", + 2634: "pk-electronics", + 2635: "backburner", + 2636: "solve", + 2637: "imdocsvc", + 2638: "sybaseanywhere", + 2639: "aminet", + 2640: "ami-control", + 2641: "hdl-srv", + 2642: "tragic", + 2643: "gte-samp", + 2644: "travsoft-ipx-t", + 2645: "novell-ipx-cmd", + 2646: "and-lm", + 2647: "syncserver", + 2648: "upsnotifyprot", + 2649: "vpsipport", + 2650: "eristwoguns", + 2651: "ebinsite", + 2652: "interpathpanel", + 2653: "sonus", + 2654: "corel-vncadmin", + 2655: "unglue", + 2656: "kana", + 2657: "sns-dispatcher", + 2658: "sns-admin", + 2659: "sns-query", + 2660: "gcmonitor", + 2661: "olhost", + 2662: "bintec-capi", + 2663: "bintec-tapi", + 2664: "patrol-mq-gm", + 2665: "patrol-mq-nm", + 2666: "extensis", + 2667: "alarm-clock-s", + 2668: "alarm-clock-c", + 2669: "toad", + 2670: "tve-announce", + 2671: "newlixreg", + 2672: "nhserver", + 2673: "firstcall42", + 2674: "ewnn", + 2675: "ttc-etap", + 2676: "simslink", + 2677: "gadgetgate1way", + 2678: "gadgetgate2way", + 2679: "syncserverssl", + 2680: "pxc-sapxom", + 2681: "mpnjsomb", + 2683: "ncdloadbalance", + 2684: "mpnjsosv", + 2685: "mpnjsocl", + 2686: "mpnjsomg", + 2687: "pq-lic-mgmt", + 2688: "md-cg-http", + 2689: "fastlynx", + 2690: "hp-nnm-data", + 2691: "itinternet", + 2692: "admins-lms", + 2694: "pwrsevent", + 2695: "vspread", + 2696: "unifyadmin", + 2697: "oce-snmp-trap", + 2698: "mck-ivpip", + 2699: "csoft-plusclnt", + 2700: "tqdata", + 2701: "sms-rcinfo", + 2702: "sms-xfer", + 2703: "sms-chat", + 2704: "sms-remctrl", + 2705: "sds-admin", + 2706: "ncdmirroring", + 2707: "emcsymapiport", + 2708: "banyan-net", + 2709: "supermon", + 2710: "sso-service", + 2711: "sso-control", + 2712: "aocp", + 2713: "raventbs", + 2714: "raventdm", + 2715: "hpstgmgr2", + 2716: "inova-ip-disco", + 2717: "pn-requester", + 2718: "pn-requester2", + 2719: "scan-change", + 2720: "wkars", + 2721: "smart-diagnose", + 2722: "proactivesrvr", + 2723: "watchdog-nt", + 2724: "qotps", + 2725: "msolap-ptp2", + 2726: "tams", + 2727: "mgcp-callagent", + 2728: "sqdr", + 2729: "tcim-control", + 2730: "nec-raidplus", + 2731: "fyre-messanger", + 2732: "g5m", + 2733: "signet-ctf", + 2734: "ccs-software", + 2735: "netiq-mc", + 2736: "radwiz-nms-srv", + 2737: "srp-feedback", + 2738: "ndl-tcp-ois-gw", + 2739: "tn-timing", + 2740: "alarm", + 2741: "tsb", + 2742: "tsb2", + 2743: "murx", + 2744: "honyaku", + 2745: "urbisnet", + 2746: "cpudpencap", + 2747: "fjippol-swrly", + 2748: "fjippol-polsvr", + 2749: "fjippol-cnsl", + 2750: "fjippol-port1", + 2751: "fjippol-port2", + 2752: "rsisysaccess", + 2753: "de-spot", + 2754: "apollo-cc", + 2755: "expresspay", + 2756: "simplement-tie", + 2757: "cnrp", + 2758: "apollo-status", + 2759: "apollo-gms", + 2760: "sabams", + 2761: "dicom-iscl", + 2762: "dicom-tls", + 2763: "desktop-dna", + 2764: "data-insurance", + 2765: "qip-audup", + 2766: "compaq-scp", + 2767: "uadtc", + 2768: "uacs", + 2769: "exce", + 2770: "veronica", + 2771: "vergencecm", + 2772: "auris", + 2773: "rbakcup1", + 2774: "rbakcup2", + 2775: "smpp", + 2776: "ridgeway1", + 2777: "ridgeway2", + 2778: "gwen-sonya", + 2779: "lbc-sync", + 2780: "lbc-control", + 2781: "whosells", + 2782: "everydayrc", + 2783: "aises", + 2784: "www-dev", + 2785: "aic-np", + 2786: "aic-oncrpc", + 2787: "piccolo", + 2788: "fryeserv", + 2789: "media-agent", + 2790: "plgproxy", + 2791: "mtport-regist", + 2792: "f5-globalsite", + 2793: "initlsmsad", + 2795: "livestats", + 2796: "ac-tech", + 2797: "esp-encap", + 2798: "tmesis-upshot", + 2799: "icon-discover", + 2800: "acc-raid", + 2801: "igcp", + 2802: "veritas-tcp1", + 2803: "btprjctrl", + 2804: "dvr-esm", + 2805: "wta-wsp-s", + 2806: "cspuni", + 2807: "cspmulti", + 2808: "j-lan-p", + 2809: "corbaloc", + 2810: "netsteward", + 2811: "gsiftp", + 2812: "atmtcp", + 2813: "llm-pass", + 2814: "llm-csv", + 2815: "lbc-measure", + 2816: "lbc-watchdog", + 2817: "nmsigport", + 2818: "rmlnk", + 2819: "fc-faultnotify", + 2820: "univision", + 2821: "vrts-at-port", + 2822: "ka0wuc", + 2823: "cqg-netlan", + 2824: "cqg-netlan-1", + 2826: "slc-systemlog", + 2827: "slc-ctrlrloops", + 2828: "itm-lm", + 2829: "silkp1", + 2830: "silkp2", + 2831: "silkp3", + 2832: "silkp4", + 2833: "glishd", + 2834: "evtp", + 2835: "evtp-data", + 2836: "catalyst", + 2837: "repliweb", + 2838: "starbot", + 2839: "nmsigport", + 2840: "l3-exprt", + 2841: "l3-ranger", + 2842: "l3-hawk", + 2843: "pdnet", + 2844: "bpcp-poll", + 2845: "bpcp-trap", + 2846: "aimpp-hello", + 2847: "aimpp-port-req", + 2848: "amt-blc-port", + 2849: "fxp", + 2850: "metaconsole", + 2851: "webemshttp", + 2852: "bears-01", + 2853: "ispipes", + 2854: "infomover", + 2855: "msrp", + 2856: "cesdinv", + 2857: "simctlp", + 2858: "ecnp", + 2859: "activememory", + 2860: "dialpad-voice1", + 2861: "dialpad-voice2", + 2862: "ttg-protocol", + 2863: "sonardata", + 2864: "astromed-main", + 2865: "pit-vpn", + 2866: "iwlistener", + 2867: "esps-portal", + 2868: "npep-messaging", + 2869: "icslap", + 2870: "daishi", + 2871: "msi-selectplay", + 2872: "radix", + 2874: "dxmessagebase1", + 2875: "dxmessagebase2", + 2876: "sps-tunnel", + 2877: "bluelance", + 2878: "aap", + 2879: "ucentric-ds", + 2880: "synapse", + 2881: "ndsp", + 2882: "ndtp", + 2883: "ndnp", + 2884: "flashmsg", + 2885: "topflow", + 2886: "responselogic", + 2887: "aironetddp", + 2888: "spcsdlobby", + 2889: "rsom", + 2890: "cspclmulti", + 2891: "cinegrfx-elmd", + 2892: "snifferdata", + 2893: "vseconnector", + 2894: "abacus-remote", + 2895: "natuslink", + 2896: "ecovisiong6-1", + 2897: "citrix-rtmp", + 2898: "appliance-cfg", + 2899: "powergemplus", + 2900: "quicksuite", + 2901: "allstorcns", + 2902: "netaspi", + 2903: "suitcase", + 2904: "m2ua", + 2905: "m3ua", + 2906: "caller9", + 2907: "webmethods-b2b", + 2908: "mao", + 2909: "funk-dialout", + 2910: "tdaccess", + 2911: "blockade", + 2912: "epicon", + 2913: "boosterware", + 2914: "gamelobby", + 2915: "tksocket", + 2916: "elvin-server", + 2917: "elvin-client", + 2918: "kastenchasepad", + 2919: "roboer", + 2920: "roboeda", + 2921: "cesdcdman", + 2922: "cesdcdtrn", + 2923: "wta-wsp-wtp-s", + 2924: "precise-vip", + 2926: "mobile-file-dl", + 2927: "unimobilectrl", + 2928: "redstone-cpss", + 2929: "amx-webadmin", + 2930: "amx-weblinx", + 2931: "circle-x", + 2932: "incp", + 2933: "4-tieropmgw", + 2934: "4-tieropmcli", + 2935: "qtp", + 2936: "otpatch", + 2937: "pnaconsult-lm", + 2938: "sm-pas-1", + 2939: "sm-pas-2", + 2940: "sm-pas-3", + 2941: "sm-pas-4", + 2942: "sm-pas-5", + 2943: "ttnrepository", + 2944: "megaco-h248", + 2945: "h248-binary", + 2946: "fjsvmpor", + 2947: "gpsd", + 2948: "wap-push", + 2949: "wap-pushsecure", + 2950: "esip", + 2951: "ottp", + 2952: "mpfwsas", + 2953: "ovalarmsrv", + 2954: "ovalarmsrv-cmd", + 2955: "csnotify", + 2956: "ovrimosdbman", + 2957: "jmact5", + 2958: "jmact6", + 2959: "rmopagt", + 2960: "dfoxserver", + 2961: "boldsoft-lm", + 2962: "iph-policy-cli", + 2963: "iph-policy-adm", + 2964: "bullant-srap", + 2965: "bullant-rap", + 2966: "idp-infotrieve", + 2967: "ssc-agent", + 2968: "enpp", + 2969: "essp", + 2970: "index-net", + 2971: "netclip", + 2972: "pmsm-webrctl", + 2973: "svnetworks", + 2974: "signal", + 2975: "fjmpcm", + 2976: "cns-srv-port", + 2977: "ttc-etap-ns", + 2978: "ttc-etap-ds", + 2979: "h263-video", + 2980: "wimd", + 2981: "mylxamport", + 2982: "iwb-whiteboard", + 2983: "netplan", + 2984: "hpidsadmin", + 2985: "hpidsagent", + 2986: "stonefalls", + 2987: "identify", + 2988: "hippad", + 2989: "zarkov", + 2990: "boscap", + 2991: "wkstn-mon", + 2992: "avenyo", + 2993: "veritas-vis1", + 2994: "veritas-vis2", + 2995: "idrs", + 2996: "vsixml", + 2997: "rebol", + 2998: "realsecure", + 2999: "remoteware-un", + 3000: "hbci", + 3001: "origo-native", + 3002: "exlm-agent", + 3003: "cgms", + 3004: "csoftragent", + 3005: "geniuslm", + 3006: "ii-admin", + 3007: "lotusmtap", + 3008: "midnight-tech", + 3009: "pxc-ntfy", + 3010: "gw", + 3011: "trusted-web", + 3012: "twsdss", + 3013: "gilatskysurfer", + 3014: "broker-service", + 3015: "nati-dstp", + 3016: "notify-srvr", + 3017: "event-listener", + 3018: "srvc-registry", + 3019: "resource-mgr", + 3020: "cifs", + 3021: "agriserver", + 3022: "csregagent", + 3023: "magicnotes", + 3024: "nds-sso", + 3025: "arepa-raft", + 3026: "agri-gateway", + 3027: "LiebDevMgmt-C", + 3028: "LiebDevMgmt-DM", + 3029: "LiebDevMgmt-A", + 3030: "arepa-cas", + 3031: "eppc", + 3032: "redwood-chat", + 3033: "pdb", + 3034: "osmosis-aeea", + 3035: "fjsv-gssagt", + 3036: "hagel-dump", + 3037: "hp-san-mgmt", + 3038: "santak-ups", + 3039: "cogitate", + 3040: "tomato-springs", + 3041: "di-traceware", + 3042: "journee", + 3043: "brp", + 3044: "epp", + 3045: "responsenet", + 3046: "di-ase", + 3047: "hlserver", + 3048: "pctrader", + 3049: "nsws", + 3050: "gds-db", + 3051: "galaxy-server", + 3052: "apc-3052", + 3053: "dsom-server", + 3054: "amt-cnf-prot", + 3055: "policyserver", + 3056: "cdl-server", + 3057: "goahead-fldup", + 3058: "videobeans", + 3059: "qsoft", + 3060: "interserver", + 3061: "cautcpd", + 3062: "ncacn-ip-tcp", + 3063: "ncadg-ip-udp", + 3064: "rprt", + 3065: "slinterbase", + 3066: "netattachsdmp", + 3067: "fjhpjp", + 3068: "ls3bcast", + 3069: "ls3", + 3070: "mgxswitch", + 3071: "xplat-replicate", + 3072: "csd-monitor", + 3073: "vcrp", + 3074: "xbox", + 3075: "orbix-locator", + 3076: "orbix-config", + 3077: "orbix-loc-ssl", + 3078: "orbix-cfg-ssl", + 3079: "lv-frontpanel", + 3080: "stm-pproc", + 3081: "tl1-lv", + 3082: "tl1-raw", + 3083: "tl1-telnet", + 3084: "itm-mccs", + 3085: "pcihreq", + 3086: "jdl-dbkitchen", + 3087: "asoki-sma", + 3088: "xdtp", + 3089: "ptk-alink", + 3090: "stss", + 3091: "1ci-smcs", + 3093: "rapidmq-center", + 3094: "rapidmq-reg", + 3095: "panasas", + 3096: "ndl-aps", + 3098: "umm-port", + 3099: "chmd", + 3100: "opcon-xps", + 3101: "hp-pxpib", + 3102: "slslavemon", + 3103: "autocuesmi", + 3104: "autocuelog", + 3105: "cardbox", + 3106: "cardbox-http", + 3107: "business", + 3108: "geolocate", + 3109: "personnel", + 3110: "sim-control", + 3111: "wsynch", + 3112: "ksysguard", + 3113: "cs-auth-svr", + 3114: "ccmad", + 3115: "mctet-master", + 3116: "mctet-gateway", + 3117: "mctet-jserv", + 3118: "pkagent", + 3119: "d2000kernel", + 3120: "d2000webserver", + 3121: "pcmk-remote", + 3122: "vtr-emulator", + 3123: "edix", + 3124: "beacon-port", + 3125: "a13-an", + 3127: "ctx-bridge", + 3128: "ndl-aas", + 3129: "netport-id", + 3130: "icpv2", + 3131: "netbookmark", + 3132: "ms-rule-engine", + 3133: "prism-deploy", + 3134: "ecp", + 3135: "peerbook-port", + 3136: "grubd", + 3137: "rtnt-1", + 3138: "rtnt-2", + 3139: "incognitorv", + 3140: "ariliamulti", + 3141: "vmodem", + 3142: "rdc-wh-eos", + 3143: "seaview", + 3144: "tarantella", + 3145: "csi-lfap", + 3146: "bears-02", + 3147: "rfio", + 3148: "nm-game-admin", + 3149: "nm-game-server", + 3150: "nm-asses-admin", + 3151: "nm-assessor", + 3152: "feitianrockey", + 3153: "s8-client-port", + 3154: "ccmrmi", + 3155: "jpegmpeg", + 3156: "indura", + 3157: "e3consultants", + 3158: "stvp", + 3159: "navegaweb-port", + 3160: "tip-app-server", + 3161: "doc1lm", + 3162: "sflm", + 3163: "res-sap", + 3164: "imprs", + 3165: "newgenpay", + 3166: "sossecollector", + 3167: "nowcontact", + 3168: "poweronnud", + 3169: "serverview-as", + 3170: "serverview-asn", + 3171: "serverview-gf", + 3172: "serverview-rm", + 3173: "serverview-icc", + 3174: "armi-server", + 3175: "t1-e1-over-ip", + 3176: "ars-master", + 3177: "phonex-port", + 3178: "radclientport", + 3179: "h2gf-w-2m", + 3180: "mc-brk-srv", + 3181: "bmcpatrolagent", + 3182: "bmcpatrolrnvu", + 3183: "cops-tls", + 3184: "apogeex-port", + 3185: "smpppd", + 3186: "iiw-port", + 3187: "odi-port", + 3188: "brcm-comm-port", + 3189: "pcle-infex", + 3190: "csvr-proxy", + 3191: "csvr-sslproxy", + 3192: "firemonrcc", + 3193: "spandataport", + 3194: "magbind", + 3195: "ncu-1", + 3196: "ncu-2", + 3197: "embrace-dp-s", + 3198: "embrace-dp-c", + 3199: "dmod-workspace", + 3200: "tick-port", + 3201: "cpq-tasksmart", + 3202: "intraintra", + 3203: "netwatcher-mon", + 3204: "netwatcher-db", + 3205: "isns", + 3206: "ironmail", + 3207: "vx-auth-port", + 3208: "pfu-prcallback", + 3209: "netwkpathengine", + 3210: "flamenco-proxy", + 3211: "avsecuremgmt", + 3212: "surveyinst", + 3213: "neon24x7", + 3214: "jmq-daemon-1", + 3215: "jmq-daemon-2", + 3216: "ferrari-foam", + 3217: "unite", + 3218: "smartpackets", + 3219: "wms-messenger", + 3220: "xnm-ssl", + 3221: "xnm-clear-text", + 3222: "glbp", + 3223: "digivote", + 3224: "aes-discovery", + 3225: "fcip-port", + 3226: "isi-irp", + 3227: "dwnmshttp", + 3228: "dwmsgserver", + 3229: "global-cd-port", + 3230: "sftdst-port", + 3231: "vidigo", + 3232: "mdtp", + 3233: "whisker", + 3234: "alchemy", + 3235: "mdap-port", + 3236: "apparenet-ts", + 3237: "apparenet-tps", + 3238: "apparenet-as", + 3239: "apparenet-ui", + 3240: "triomotion", + 3241: "sysorb", + 3242: "sdp-id-port", + 3243: "timelot", + 3244: "onesaf", + 3245: "vieo-fe", + 3246: "dvt-system", + 3247: "dvt-data", + 3248: "procos-lm", + 3249: "ssp", + 3250: "hicp", + 3251: "sysscanner", + 3252: "dhe", + 3253: "pda-data", + 3254: "pda-sys", + 3255: "semaphore", + 3256: "cpqrpm-agent", + 3257: "cpqrpm-server", + 3258: "ivecon-port", + 3259: "epncdp2", + 3260: "iscsi-target", + 3261: "winshadow", + 3262: "necp", + 3263: "ecolor-imager", + 3264: "ccmail", + 3265: "altav-tunnel", + 3266: "ns-cfg-server", + 3267: "ibm-dial-out", + 3268: "msft-gc", + 3269: "msft-gc-ssl", + 3270: "verismart", + 3271: "csoft-prev", + 3272: "user-manager", + 3273: "sxmp", + 3274: "ordinox-server", + 3275: "samd", + 3276: "maxim-asics", + 3277: "awg-proxy", + 3278: "lkcmserver", + 3279: "admind", + 3280: "vs-server", + 3281: "sysopt", + 3282: "datusorb", + 3283: "Apple Remote Desktop (Net Assistant)", + 3284: "4talk", + 3285: "plato", + 3286: "e-net", + 3287: "directvdata", + 3288: "cops", + 3289: "enpc", + 3290: "caps-lm", + 3291: "sah-lm", + 3292: "cart-o-rama", + 3293: "fg-fps", + 3294: "fg-gip", + 3295: "dyniplookup", + 3296: "rib-slm", + 3297: "cytel-lm", + 3298: "deskview", + 3299: "pdrncs", + 3300: "ceph", + 3302: "mcs-fastmail", + 3303: "opsession-clnt", + 3304: "opsession-srvr", + 3305: "odette-ftp", + 3306: "mysql", + 3307: "opsession-prxy", + 3308: "tns-server", + 3309: "tns-adv", + 3310: "dyna-access", + 3311: "mcns-tel-ret", + 3312: "appman-server", + 3313: "uorb", + 3314: "uohost", + 3315: "cdid", + 3316: "aicc-cmi", + 3317: "vsaiport", + 3318: "ssrip", + 3319: "sdt-lmd", + 3320: "officelink2000", + 3321: "vnsstr", + 3326: "sftu", + 3327: "bbars", + 3328: "egptlm", + 3329: "hp-device-disc", + 3330: "mcs-calypsoicf", + 3331: "mcs-messaging", + 3332: "mcs-mailsvr", + 3333: "dec-notes", + 3334: "directv-web", + 3335: "directv-soft", + 3336: "directv-tick", + 3337: "directv-catlg", + 3338: "anet-b", + 3339: "anet-l", + 3340: "anet-m", + 3341: "anet-h", + 3342: "webtie", + 3343: "ms-cluster-net", + 3344: "bnt-manager", + 3345: "influence", + 3346: "trnsprntproxy", + 3347: "phoenix-rpc", + 3348: "pangolin-laser", + 3349: "chevinservices", + 3350: "findviatv", + 3351: "btrieve", + 3352: "ssql", + 3353: "fatpipe", + 3354: "suitjd", + 3355: "ordinox-dbase", + 3356: "upnotifyps", + 3357: "adtech-test", + 3358: "mpsysrmsvr", + 3359: "wg-netforce", + 3360: "kv-server", + 3361: "kv-agent", + 3362: "dj-ilm", + 3363: "nati-vi-server", + 3364: "creativeserver", + 3365: "contentserver", + 3366: "creativepartnr", + 3372: "tip2", + 3373: "lavenir-lm", + 3374: "cluster-disc", + 3375: "vsnm-agent", + 3376: "cdbroker", + 3377: "cogsys-lm", + 3378: "wsicopy", + 3379: "socorfs", + 3380: "sns-channels", + 3381: "geneous", + 3382: "fujitsu-neat", + 3383: "esp-lm", + 3384: "hp-clic", + 3385: "qnxnetman", + 3386: "gprs-data", + 3387: "backroomnet", + 3388: "cbserver", + 3389: "ms-wbt-server", + 3390: "dsc", + 3391: "savant", + 3392: "efi-lm", + 3393: "d2k-tapestry1", + 3394: "d2k-tapestry2", + 3395: "dyna-lm", + 3396: "printer-agent", + 3397: "cloanto-lm", + 3398: "mercantile", + 3399: "csms", + 3400: "csms2", + 3401: "filecast", + 3402: "fxaengine-net", + 3405: "nokia-ann-ch1", + 3406: "nokia-ann-ch2", + 3407: "ldap-admin", + 3408: "BESApi", + 3409: "networklens", + 3410: "networklenss", + 3411: "biolink-auth", + 3412: "xmlblaster", + 3413: "svnet", + 3414: "wip-port", + 3415: "bcinameservice", + 3416: "commandport", + 3417: "csvr", + 3418: "rnmap", + 3419: "softaudit", + 3420: "ifcp-port", + 3421: "bmap", + 3422: "rusb-sys-port", + 3423: "xtrm", + 3424: "xtrms", + 3425: "agps-port", + 3426: "arkivio", + 3427: "websphere-snmp", + 3428: "twcss", + 3429: "gcsp", + 3430: "ssdispatch", + 3431: "ndl-als", + 3432: "osdcp", + 3433: "opnet-smp", + 3434: "opencm", + 3435: "pacom", + 3436: "gc-config", + 3437: "autocueds", + 3438: "spiral-admin", + 3439: "hri-port", + 3440: "ans-console", + 3441: "connect-client", + 3442: "connect-server", + 3443: "ov-nnm-websrv", + 3444: "denali-server", + 3445: "monp", + 3446: "3comfaxrpc", + 3447: "directnet", + 3448: "dnc-port", + 3449: "hotu-chat", + 3450: "castorproxy", + 3451: "asam", + 3452: "sabp-signal", + 3453: "pscupd", + 3454: "mira", + 3455: "prsvp", + 3456: "vat", + 3457: "vat-control", + 3458: "d3winosfi", + 3459: "integral", + 3460: "edm-manager", + 3461: "edm-stager", + 3462: "edm-std-notify", + 3463: "edm-adm-notify", + 3464: "edm-mgr-sync", + 3465: "edm-mgr-cntrl", + 3466: "workflow", + 3467: "rcst", + 3468: "ttcmremotectrl", + 3469: "pluribus", + 3470: "jt400", + 3471: "jt400-ssl", + 3472: "jaugsremotec-1", + 3473: "jaugsremotec-2", + 3474: "ttntspauto", + 3475: "genisar-port", + 3476: "nppmp", + 3477: "ecomm", + 3478: "stun", + 3479: "twrpc", + 3480: "plethora", + 3481: "cleanerliverc", + 3482: "vulture", + 3483: "slim-devices", + 3484: "gbs-stp", + 3485: "celatalk", + 3486: "ifsf-hb-port", + 3487: "ltctcp", + 3488: "fs-rh-srv", + 3489: "dtp-dia", + 3490: "colubris", + 3491: "swr-port", + 3492: "tvdumtray-port", + 3493: "nut", + 3494: "ibm3494", + 3495: "seclayer-tcp", + 3496: "seclayer-tls", + 3497: "ipether232port", + 3498: "dashpas-port", + 3499: "sccip-media", + 3500: "rtmp-port", + 3501: "isoft-p2p", + 3502: "avinstalldisc", + 3503: "lsp-ping", + 3504: "ironstorm", + 3505: "ccmcomm", + 3506: "apc-3506", + 3507: "nesh-broker", + 3508: "interactionweb", + 3509: "vt-ssl", + 3510: "xss-port", + 3511: "webmail-2", + 3512: "aztec", + 3513: "arcpd", + 3514: "must-p2p", + 3515: "must-backplane", + 3516: "smartcard-port", + 3517: "802-11-iapp", + 3518: "artifact-msg", + 3519: "nvmsgd", + 3520: "galileolog", + 3521: "mc3ss", + 3522: "nssocketport", + 3523: "odeumservlink", + 3524: "ecmport", + 3525: "eisport", + 3526: "starquiz-port", + 3527: "beserver-msg-q", + 3528: "jboss-iiop", + 3529: "jboss-iiop-ssl", + 3530: "gf", + 3531: "joltid", + 3532: "raven-rmp", + 3533: "raven-rdp", + 3534: "urld-port", + 3535: "ms-la", + 3536: "snac", + 3537: "ni-visa-remote", + 3538: "ibm-diradm", + 3539: "ibm-diradm-ssl", + 3540: "pnrp-port", + 3541: "voispeed-port", + 3542: "hacl-monitor", + 3543: "qftest-lookup", + 3544: "teredo", + 3545: "camac", + 3547: "symantec-sim", + 3548: "interworld", + 3549: "tellumat-nms", + 3550: "ssmpp", + 3551: "apcupsd", + 3552: "taserver", + 3553: "rbr-discovery", + 3554: "questnotify", + 3555: "razor", + 3556: "sky-transport", + 3557: "personalos-001", + 3558: "mcp-port", + 3559: "cctv-port", + 3560: "iniserve-port", + 3561: "bmc-onekey", + 3562: "sdbproxy", + 3563: "watcomdebug", + 3564: "esimport", + 3565: "m2pa", + 3566: "quest-data-hub", + 3567: "dof-eps", + 3568: "dof-tunnel-sec", + 3569: "mbg-ctrl", + 3570: "mccwebsvr-port", + 3571: "megardsvr-port", + 3572: "megaregsvrport", + 3573: "tag-ups-1", + 3574: "dmaf-server", + 3575: "ccm-port", + 3576: "cmc-port", + 3577: "config-port", + 3578: "data-port", + 3579: "ttat3lb", + 3580: "nati-svrloc", + 3581: "kfxaclicensing", + 3582: "press", + 3583: "canex-watch", + 3584: "u-dbap", + 3585: "emprise-lls", + 3586: "emprise-lsc", + 3587: "p2pgroup", + 3588: "sentinel", + 3589: "isomair", + 3590: "wv-csp-sms", + 3591: "gtrack-server", + 3592: "gtrack-ne", + 3593: "bpmd", + 3594: "mediaspace", + 3595: "shareapp", + 3596: "iw-mmogame", + 3597: "a14", + 3598: "a15", + 3599: "quasar-server", + 3600: "trap-daemon", + 3601: "visinet-gui", + 3602: "infiniswitchcl", + 3603: "int-rcv-cntrl", + 3604: "bmc-jmx-port", + 3605: "comcam-io", + 3606: "splitlock", + 3607: "precise-i3", + 3608: "trendchip-dcp", + 3609: "cpdi-pidas-cm", + 3610: "echonet", + 3611: "six-degrees", + 3612: "hp-dataprotect", + 3613: "alaris-disc", + 3614: "sigma-port", + 3615: "start-network", + 3616: "cd3o-protocol", + 3617: "sharp-server", + 3618: "aairnet-1", + 3619: "aairnet-2", + 3620: "ep-pcp", + 3621: "ep-nsp", + 3622: "ff-lr-port", + 3623: "haipe-discover", + 3624: "dist-upgrade", + 3625: "volley", + 3626: "bvcdaemon-port", + 3627: "jamserverport", + 3628: "ept-machine", + 3629: "escvpnet", + 3630: "cs-remote-db", + 3631: "cs-services", + 3632: "distcc", + 3633: "wacp", + 3634: "hlibmgr", + 3635: "sdo", + 3636: "servistaitsm", + 3637: "scservp", + 3638: "ehp-backup", + 3639: "xap-ha", + 3640: "netplay-port1", + 3641: "netplay-port2", + 3642: "juxml-port", + 3643: "audiojuggler", + 3644: "ssowatch", + 3645: "cyc", + 3646: "xss-srv-port", + 3647: "splitlock-gw", + 3648: "fjcp", + 3649: "nmmp", + 3650: "prismiq-plugin", + 3651: "xrpc-registry", + 3652: "vxcrnbuport", + 3653: "tsp", + 3654: "vaprtm", + 3655: "abatemgr", + 3656: "abatjss", + 3657: "immedianet-bcn", + 3658: "ps-ams", + 3659: "apple-sasl", + 3660: "can-nds-ssl", + 3661: "can-ferret-ssl", + 3662: "pserver", + 3663: "dtp", + 3664: "ups-engine", + 3665: "ent-engine", + 3666: "eserver-pap", + 3667: "infoexch", + 3668: "dell-rm-port", + 3669: "casanswmgmt", + 3670: "smile", + 3671: "efcp", + 3672: "lispworks-orb", + 3673: "mediavault-gui", + 3674: "wininstall-ipc", + 3675: "calltrax", + 3676: "va-pacbase", + 3677: "roverlog", + 3678: "ipr-dglt", + 3679: "Escale (Newton Dock)", + 3680: "npds-tracker", + 3681: "bts-x73", + 3682: "cas-mapi", + 3683: "bmc-ea", + 3684: "faxstfx-port", + 3685: "dsx-agent", + 3686: "tnmpv2", + 3687: "simple-push", + 3688: "simple-push-s", + 3689: "daap", + 3690: "svn", + 3691: "magaya-network", + 3692: "intelsync", + 3693: "easl", + 3695: "bmc-data-coll", + 3696: "telnetcpcd", + 3697: "nw-license", + 3698: "sagectlpanel", + 3699: "kpn-icw", + 3700: "lrs-paging", + 3701: "netcelera", + 3702: "ws-discovery", + 3703: "adobeserver-3", + 3704: "adobeserver-4", + 3705: "adobeserver-5", + 3706: "rt-event", + 3707: "rt-event-s", + 3708: "sun-as-iiops", + 3709: "ca-idms", + 3710: "portgate-auth", + 3711: "edb-server2", + 3712: "sentinel-ent", + 3713: "tftps", + 3714: "delos-dms", + 3715: "anoto-rendezv", + 3716: "wv-csp-sms-cir", + 3717: "wv-csp-udp-cir", + 3718: "opus-services", + 3719: "itelserverport", + 3720: "ufastro-instr", + 3721: "xsync", + 3722: "xserveraid", + 3723: "sychrond", + 3724: "blizwow", + 3725: "na-er-tip", + 3726: "array-manager", + 3727: "e-mdu", + 3728: "e-woa", + 3729: "fksp-audit", + 3730: "client-ctrl", + 3731: "smap", + 3732: "m-wnn", + 3733: "multip-msg", + 3734: "synel-data", + 3735: "pwdis", + 3736: "rs-rmi", + 3737: "xpanel", + 3738: "versatalk", + 3739: "launchbird-lm", + 3740: "heartbeat", + 3741: "wysdma", + 3742: "cst-port", + 3743: "ipcs-command", + 3744: "sasg", + 3745: "gw-call-port", + 3746: "linktest", + 3747: "linktest-s", + 3748: "webdata", + 3749: "cimtrak", + 3750: "cbos-ip-port", + 3751: "gprs-cube", + 3752: "vipremoteagent", + 3753: "nattyserver", + 3754: "timestenbroker", + 3755: "sas-remote-hlp", + 3756: "canon-capt", + 3757: "grf-port", + 3758: "apw-registry", + 3759: "exapt-lmgr", + 3760: "adtempusclient", + 3761: "gsakmp", + 3762: "gbs-smp", + 3763: "xo-wave", + 3764: "mni-prot-rout", + 3765: "rtraceroute", + 3766: "sitewatch-s", + 3767: "listmgr-port", + 3768: "rblcheckd", + 3769: "haipe-otnk", + 3770: "cindycollab", + 3771: "paging-port", + 3772: "ctp", + 3773: "ctdhercules", + 3774: "zicom", + 3775: "ispmmgr", + 3776: "dvcprov-port", + 3777: "jibe-eb", + 3778: "c-h-it-port", + 3779: "cognima", + 3780: "nnp", + 3781: "abcvoice-port", + 3782: "iso-tp0s", + 3783: "bim-pem", + 3784: "bfd-control", + 3785: "bfd-echo", + 3786: "upstriggervsw", + 3787: "fintrx", + 3788: "isrp-port", + 3789: "remotedeploy", + 3790: "quickbooksrds", + 3791: "tvnetworkvideo", + 3792: "sitewatch", + 3793: "dcsoftware", + 3794: "jaus", + 3795: "myblast", + 3796: "spw-dialer", + 3797: "idps", + 3798: "minilock", + 3799: "radius-dynauth", + 3800: "pwgpsi", + 3801: "ibm-mgr", + 3802: "vhd", + 3803: "soniqsync", + 3804: "iqnet-port", + 3805: "tcpdataserver", + 3806: "wsmlb", + 3807: "spugna", + 3808: "sun-as-iiops-ca", + 3809: "apocd", + 3810: "wlanauth", + 3811: "amp", + 3812: "neto-wol-server", + 3813: "rap-ip", + 3814: "neto-dcs", + 3815: "lansurveyorxml", + 3816: "sunlps-http", + 3817: "tapeware", + 3818: "crinis-hb", + 3819: "epl-slp", + 3820: "scp", + 3821: "pmcp", + 3822: "acp-discovery", + 3823: "acp-conduit", + 3824: "acp-policy", + 3825: "ffserver", + 3826: "warmux", + 3827: "netmpi", + 3828: "neteh", + 3829: "neteh-ext", + 3830: "cernsysmgmtagt", + 3831: "dvapps", + 3832: "xxnetserver", + 3833: "aipn-auth", + 3834: "spectardata", + 3835: "spectardb", + 3836: "markem-dcp", + 3837: "mkm-discovery", + 3838: "sos", + 3839: "amx-rms", + 3840: "flirtmitmir", + 3841: "shiprush-db-svr", + 3842: "nhci", + 3843: "quest-agent", + 3844: "rnm", + 3845: "v-one-spp", + 3846: "an-pcp", + 3847: "msfw-control", + 3848: "item", + 3849: "spw-dnspreload", + 3850: "qtms-bootstrap", + 3851: "spectraport", + 3852: "sse-app-config", + 3853: "sscan", + 3854: "stryker-com", + 3855: "opentrac", + 3856: "informer", + 3857: "trap-port", + 3858: "trap-port-mom", + 3859: "nav-port", + 3860: "sasp", + 3861: "winshadow-hd", + 3862: "giga-pocket", + 3863: "asap-tcp", + 3864: "asap-tcp-tls", + 3865: "xpl", + 3866: "dzdaemon", + 3867: "dzoglserver", + 3868: "diameter", + 3869: "ovsam-mgmt", + 3870: "ovsam-d-agent", + 3871: "avocent-adsap", + 3872: "oem-agent", + 3873: "fagordnc", + 3874: "sixxsconfig", + 3875: "pnbscada", + 3876: "dl-agent", + 3877: "xmpcr-interface", + 3878: "fotogcad", + 3879: "appss-lm", + 3880: "igrs", + 3881: "idac", + 3882: "msdts1", + 3883: "vrpn", + 3884: "softrack-meter", + 3885: "topflow-ssl", + 3886: "nei-management", + 3887: "ciphire-data", + 3888: "ciphire-serv", + 3889: "dandv-tester", + 3890: "ndsconnect", + 3891: "rtc-pm-port", + 3892: "pcc-image-port", + 3893: "cgi-starapi", + 3894: "syam-agent", + 3895: "syam-smc", + 3896: "sdo-tls", + 3897: "sdo-ssh", + 3898: "senip", + 3899: "itv-control", + 3900: "udt-os", + 3901: "nimsh", + 3902: "nimaux", + 3903: "charsetmgr", + 3904: "omnilink-port", + 3905: "mupdate", + 3906: "topovista-data", + 3907: "imoguia-port", + 3908: "hppronetman", + 3909: "surfcontrolcpa", + 3910: "prnrequest", + 3911: "prnstatus", + 3912: "gbmt-stars", + 3913: "listcrt-port", + 3914: "listcrt-port-2", + 3915: "agcat", + 3916: "wysdmc", + 3917: "aftmux", + 3918: "pktcablemmcops", + 3919: "hyperip", + 3920: "exasoftport1", + 3921: "herodotus-net", + 3922: "sor-update", + 3923: "symb-sb-port", + 3924: "mpl-gprs-port", + 3925: "zmp", + 3926: "winport", + 3927: "natdataservice", + 3928: "netboot-pxe", + 3929: "smauth-port", + 3930: "syam-webserver", + 3931: "msr-plugin-port", + 3932: "dyn-site", + 3933: "plbserve-port", + 3934: "sunfm-port", + 3935: "sdp-portmapper", + 3936: "mailprox", + 3937: "dvbservdsc", + 3938: "dbcontrol-agent", + 3939: "aamp", + 3940: "xecp-node", + 3941: "homeportal-web", + 3942: "srdp", + 3943: "tig", + 3944: "sops", + 3945: "emcads", + 3946: "backupedge", + 3947: "ccp", + 3948: "apdap", + 3949: "drip", + 3950: "namemunge", + 3951: "pwgippfax", + 3952: "i3-sessionmgr", + 3953: "xmlink-connect", + 3954: "adrep", + 3955: "p2pcommunity", + 3956: "gvcp", + 3957: "mqe-broker", + 3958: "mqe-agent", + 3959: "treehopper", + 3960: "bess", + 3961: "proaxess", + 3962: "sbi-agent", + 3963: "thrp", + 3964: "sasggprs", + 3965: "ati-ip-to-ncpe", + 3966: "bflckmgr", + 3967: "ppsms", + 3968: "ianywhere-dbns", + 3969: "landmarks", + 3970: "lanrevagent", + 3971: "lanrevserver", + 3972: "iconp", + 3973: "progistics", + 3974: "citysearch", + 3975: "airshot", + 3976: "opswagent", + 3977: "opswmanager", + 3978: "secure-cfg-svr", + 3979: "smwan", + 3980: "acms", + 3981: "starfish", + 3982: "eis", + 3983: "eisp", + 3984: "mapper-nodemgr", + 3985: "mapper-mapethd", + 3986: "mapper-ws-ethd", + 3987: "centerline", + 3988: "dcs-config", + 3989: "bv-queryengine", + 3990: "bv-is", + 3991: "bv-smcsrv", + 3992: "bv-ds", + 3993: "bv-agent", + 3995: "iss-mgmt-ssl", + 3996: "abcsoftware", + 3997: "agentsease-db", + 3998: "dnx", + 3999: "nvcnet", + 4000: "terabase", + 4001: "newoak", + 4002: "pxc-spvr-ft", + 4003: "pxc-splr-ft", + 4004: "pxc-roid", + 4005: "pxc-pin", + 4006: "pxc-spvr", + 4007: "pxc-splr", + 4008: "netcheque", + 4009: "chimera-hwm", + 4010: "samsung-unidex", + 4011: "altserviceboot", + 4012: "pda-gate", + 4013: "acl-manager", + 4014: "taiclock", + 4015: "talarian-mcast1", + 4016: "talarian-mcast2", + 4017: "talarian-mcast3", + 4018: "talarian-mcast4", + 4019: "talarian-mcast5", + 4020: "trap", + 4021: "nexus-portal", + 4022: "dnox", + 4023: "esnm-zoning", + 4024: "tnp1-port", + 4025: "partimage", + 4026: "as-debug", + 4027: "bxp", + 4028: "dtserver-port", + 4029: "ip-qsig", + 4030: "jdmn-port", + 4031: "suucp", + 4032: "vrts-auth-port", + 4033: "sanavigator", + 4034: "ubxd", + 4035: "wap-push-http", + 4036: "wap-push-https", + 4037: "ravehd", + 4038: "fazzt-ptp", + 4039: "fazzt-admin", + 4040: "yo-main", + 4041: "houston", + 4042: "ldxp", + 4043: "nirp", + 4044: "ltp", + 4045: "npp", + 4046: "acp-proto", + 4047: "ctp-state", + 4049: "wafs", + 4050: "cisco-wafs", + 4051: "cppdp", + 4052: "interact", + 4053: "ccu-comm-1", + 4054: "ccu-comm-2", + 4055: "ccu-comm-3", + 4056: "lms", + 4057: "wfm", + 4058: "kingfisher", + 4059: "dlms-cosem", + 4060: "dsmeter-iatc", + 4061: "ice-location", + 4062: "ice-slocation", + 4063: "ice-router", + 4064: "ice-srouter", + 4065: "avanti-cdp", + 4066: "pmas", + 4067: "idp", + 4068: "ipfltbcst", + 4069: "minger", + 4070: "tripe", + 4071: "aibkup", + 4072: "zieto-sock", + 4073: "iRAPP", + 4074: "cequint-cityid", + 4075: "perimlan", + 4076: "seraph", + 4078: "cssp", + 4079: "santools", + 4080: "lorica-in", + 4081: "lorica-in-sec", + 4082: "lorica-out", + 4083: "lorica-out-sec", + 4085: "ezmessagesrv", + 4087: "applusservice", + 4088: "npsp", + 4089: "opencore", + 4090: "omasgport", + 4091: "ewinstaller", + 4092: "ewdgs", + 4093: "pvxpluscs", + 4094: "sysrqd", + 4095: "xtgui", + 4096: "bre", + 4097: "patrolview", + 4098: "drmsfsd", + 4099: "dpcp", + 4100: "igo-incognito", + 4101: "brlp-0", + 4102: "brlp-1", + 4103: "brlp-2", + 4104: "brlp-3", + 4105: "shofar", + 4106: "synchronite", + 4107: "j-ac", + 4108: "accel", + 4109: "izm", + 4110: "g2tag", + 4111: "xgrid", + 4112: "apple-vpns-rp", + 4113: "aipn-reg", + 4114: "jomamqmonitor", + 4115: "cds", + 4116: "smartcard-tls", + 4117: "hillrserv", + 4118: "netscript", + 4119: "assuria-slm", + 4120: "minirem", + 4121: "e-builder", + 4122: "fprams", + 4123: "z-wave", + 4124: "tigv2", + 4125: "opsview-envoy", + 4126: "ddrepl", + 4127: "unikeypro", + 4128: "nufw", + 4129: "nuauth", + 4130: "fronet", + 4131: "stars", + 4132: "nuts-dem", + 4133: "nuts-bootp", + 4134: "nifty-hmi", + 4135: "cl-db-attach", + 4136: "cl-db-request", + 4137: "cl-db-remote", + 4138: "nettest", + 4139: "thrtx", + 4140: "cedros-fds", + 4141: "oirtgsvc", + 4142: "oidocsvc", + 4143: "oidsr", + 4145: "vvr-control", + 4146: "tgcconnect", + 4147: "vrxpservman", + 4148: "hhb-handheld", + 4149: "agslb", + 4150: "PowerAlert-nsa", + 4151: "menandmice-noh", + 4152: "idig-mux", + 4153: "mbl-battd", + 4154: "atlinks", + 4155: "bzr", + 4156: "stat-results", + 4157: "stat-scanner", + 4158: "stat-cc", + 4159: "nss", + 4160: "jini-discovery", + 4161: "omscontact", + 4162: "omstopology", + 4163: "silverpeakpeer", + 4164: "silverpeakcomm", + 4165: "altcp", + 4166: "joost", + 4167: "ddgn", + 4168: "pslicser", + 4169: "iadt", + 4170: "d-cinema-csp", + 4171: "ml-svnet", + 4172: "pcoip", + 4174: "smcluster", + 4175: "bccp", + 4176: "tl-ipcproxy", + 4177: "wello", + 4178: "storman", + 4179: "MaxumSP", + 4180: "httpx", + 4181: "macbak", + 4182: "pcptcpservice", + 4183: "cyborgnet", + 4184: "universe-suite", + 4185: "wcpp", + 4186: "boxbackupstore", + 4187: "csc-proxy", + 4188: "vatata", + 4189: "pcep", + 4190: "sieve", + 4192: "azeti", + 4193: "pvxplusio", + 4197: "hctl", + 4199: "eims-admin", + 4300: "corelccam", + 4301: "d-data", + 4302: "d-data-control", + 4303: "srcp", + 4304: "owserver", + 4305: "batman", + 4306: "pinghgl", + 4307: "trueconf", + 4308: "compx-lockview", + 4309: "dserver", + 4310: "mirrtex", + 4311: "p6ssmc", + 4312: "pscl-mgt", + 4313: "perrla", + 4314: "choiceview-agt", + 4316: "choiceview-clt", + 4320: "fdt-rcatp", + 4321: "rwhois", + 4322: "trim-event", + 4323: "trim-ice", + 4325: "geognosisman", + 4326: "geognosis", + 4327: "jaxer-web", + 4328: "jaxer-manager", + 4329: "publiqare-sync", + 4330: "dey-sapi", + 4331: "ktickets-rest", + 4333: "ahsp", + 4334: "netconf-ch-ssh", + 4335: "netconf-ch-tls", + 4336: "restconf-ch-tls", + 4340: "gaia", + 4341: "lisp-data", + 4342: "lisp-cons", + 4343: "unicall", + 4344: "vinainstall", + 4345: "m4-network-as", + 4346: "elanlm", + 4347: "lansurveyor", + 4348: "itose", + 4349: "fsportmap", + 4350: "net-device", + 4351: "plcy-net-svcs", + 4352: "pjlink", + 4353: "f5-iquery", + 4354: "qsnet-trans", + 4355: "qsnet-workst", + 4356: "qsnet-assist", + 4357: "qsnet-cond", + 4358: "qsnet-nucl", + 4359: "omabcastltkm", + 4360: "matrix-vnet", + 4368: "wxbrief", + 4369: "epmd", + 4370: "elpro-tunnel", + 4371: "l2c-control", + 4372: "l2c-data", + 4373: "remctl", + 4374: "psi-ptt", + 4375: "tolteces", + 4376: "bip", + 4377: "cp-spxsvr", + 4378: "cp-spxdpy", + 4379: "ctdb", + 4389: "xandros-cms", + 4390: "wiegand", + 4391: "apwi-imserver", + 4392: "apwi-rxserver", + 4393: "apwi-rxspooler", + 4395: "omnivisionesx", + 4396: "fly", + 4400: "ds-srv", + 4401: "ds-srvr", + 4402: "ds-clnt", + 4403: "ds-user", + 4404: "ds-admin", + 4405: "ds-mail", + 4406: "ds-slp", + 4407: "nacagent", + 4408: "slscc", + 4409: "netcabinet-com", + 4410: "itwo-server", + 4411: "found", + 4413: "avi-nms", + 4414: "updog", + 4415: "brcd-vr-req", + 4416: "pjj-player", + 4417: "workflowdir", + 4419: "cbp", + 4420: "nvm-express", + 4421: "scaleft", + 4422: "tsepisp", + 4423: "thingkit", + 4425: "netrockey6", + 4426: "beacon-port-2", + 4427: "drizzle", + 4428: "omviserver", + 4429: "omviagent", + 4430: "rsqlserver", + 4431: "wspipe", + 4432: "l-acoustics", + 4433: "vop", + 4442: "saris", + 4443: "pharos", + 4444: "krb524", + 4445: "upnotifyp", + 4446: "n1-fwp", + 4447: "n1-rmgmt", + 4448: "asc-slmd", + 4449: "privatewire", + 4450: "camp", + 4451: "ctisystemmsg", + 4452: "ctiprogramload", + 4453: "nssalertmgr", + 4454: "nssagentmgr", + 4455: "prchat-user", + 4456: "prchat-server", + 4457: "prRegister", + 4458: "mcp", + 4484: "hpssmgmt", + 4485: "assyst-dr", + 4486: "icms", + 4487: "prex-tcp", + 4488: "awacs-ice", + 4500: "ipsec-nat-t", + 4535: "ehs", + 4536: "ehs-ssl", + 4537: "wssauthsvc", + 4538: "swx-gate", + 4545: "worldscores", + 4546: "sf-lm", + 4547: "lanner-lm", + 4548: "synchromesh", + 4549: "aegate", + 4550: "gds-adppiw-db", + 4551: "ieee-mih", + 4552: "menandmice-mon", + 4553: "icshostsvc", + 4554: "msfrs", + 4555: "rsip", + 4556: "dtn-bundle", + 4559: "hylafax", + 4563: "amahi-anywhere", + 4566: "kwtc", + 4567: "tram", + 4568: "bmc-reporting", + 4569: "iax", + 4570: "deploymentmap", + 4573: "cardifftec-back", + 4590: "rid", + 4591: "l3t-at-an", + 4593: "ipt-anri-anri", + 4594: "ias-session", + 4595: "ias-paging", + 4596: "ias-neighbor", + 4597: "a21-an-1xbs", + 4598: "a16-an-an", + 4599: "a17-an-an", + 4600: "piranha1", + 4601: "piranha2", + 4602: "mtsserver", + 4603: "menandmice-upg", + 4604: "irp", + 4605: "sixchat", + 4658: "playsta2-app", + 4659: "playsta2-lob", + 4660: "smaclmgr", + 4661: "kar2ouche", + 4662: "oms", + 4663: "noteit", + 4664: "ems", + 4665: "contclientms", + 4666: "eportcomm", + 4667: "mmacomm", + 4668: "mmaeds", + 4669: "eportcommdata", + 4670: "light", + 4671: "acter", + 4672: "rfa", + 4673: "cxws", + 4674: "appiq-mgmt", + 4675: "dhct-status", + 4676: "dhct-alerts", + 4677: "bcs", + 4678: "traversal", + 4679: "mgesupervision", + 4680: "mgemanagement", + 4681: "parliant", + 4682: "finisar", + 4683: "spike", + 4684: "rfid-rp1", + 4685: "autopac", + 4686: "msp-os", + 4687: "nst", + 4688: "mobile-p2p", + 4689: "altovacentral", + 4690: "prelude", + 4691: "mtn", + 4692: "conspiracy", + 4700: "netxms-agent", + 4701: "netxms-mgmt", + 4702: "netxms-sync", + 4703: "npqes-test", + 4704: "assuria-ins", + 4711: "trinity-dist", + 4725: "truckstar", + 4727: "fcis", + 4728: "capmux", + 4730: "gearman", + 4731: "remcap", + 4733: "resorcs", + 4737: "ipdr-sp", + 4738: "solera-lpn", + 4739: "ipfix", + 4740: "ipfixs", + 4741: "lumimgrd", + 4742: "sicct", + 4743: "openhpid", + 4744: "ifsp", + 4745: "fmp", + 4749: "profilemac", + 4750: "ssad", + 4751: "spocp", + 4752: "snap", + 4753: "simon", + 4756: "RDCenter", + 4774: "converge", + 4784: "bfd-multi-ctl", + 4786: "smart-install", + 4787: "sia-ctrl-plane", + 4788: "xmcp", + 4800: "iims", + 4801: "iwec", + 4802: "ilss", + 4803: "notateit", + 4827: "htcp", + 4837: "varadero-0", + 4838: "varadero-1", + 4839: "varadero-2", + 4840: "opcua-tcp", + 4841: "quosa", + 4842: "gw-asv", + 4843: "opcua-tls", + 4844: "gw-log", + 4845: "wcr-remlib", + 4846: "contamac-icm", + 4847: "wfc", + 4848: "appserv-http", + 4849: "appserv-https", + 4850: "sun-as-nodeagt", + 4851: "derby-repli", + 4867: "unify-debug", + 4868: "phrelay", + 4869: "phrelaydbg", + 4870: "cc-tracking", + 4871: "wired", + 4876: "tritium-can", + 4877: "lmcs", + 4879: "wsdl-event", + 4880: "hislip", + 4883: "wmlserver", + 4884: "hivestor", + 4885: "abbs", + 4894: "lyskom", + 4899: "radmin-port", + 4900: "hfcs", + 4901: "flr-agent", + 4902: "magiccontrol", + 4912: "lutap", + 4913: "lutcp", + 4914: "bones", + 4915: "frcs", + 4940: "eq-office-4940", + 4941: "eq-office-4941", + 4942: "eq-office-4942", + 4949: "munin", + 4950: "sybasesrvmon", + 4951: "pwgwims", + 4952: "sagxtsds", + 4953: "dbsyncarbiter", + 4969: "ccss-qmm", + 4970: "ccss-qsm", + 4971: "burp", + 4984: "webyast", + 4985: "gerhcs", + 4986: "mrip", + 4987: "smar-se-port1", + 4988: "smar-se-port2", + 4989: "parallel", + 4990: "busycal", + 4991: "vrt", + 4999: "hfcs-manager", + 5000: "commplex-main", + 5001: "commplex-link", + 5002: "rfe", + 5003: "fmpro-internal", + 5004: "avt-profile-1", + 5005: "avt-profile-2", + 5006: "wsm-server", + 5007: "wsm-server-ssl", + 5008: "synapsis-edge", + 5009: "winfs", + 5010: "telelpathstart", + 5011: "telelpathattack", + 5012: "nsp", + 5013: "fmpro-v6", + 5015: "fmwp", + 5020: "zenginkyo-1", + 5021: "zenginkyo-2", + 5022: "mice", + 5023: "htuilsrv", + 5024: "scpi-telnet", + 5025: "scpi-raw", + 5026: "strexec-d", + 5027: "strexec-s", + 5028: "qvr", + 5029: "infobright", + 5030: "surfpass", + 5032: "signacert-agent", + 5033: "jtnetd-server", + 5034: "jtnetd-status", + 5042: "asnaacceler8db", + 5043: "swxadmin", + 5044: "lxi-evntsvc", + 5045: "osp", + 5048: "texai", + 5049: "ivocalize", + 5050: "mmcc", + 5051: "ita-agent", + 5052: "ita-manager", + 5053: "rlm", + 5054: "rlm-admin", + 5055: "unot", + 5056: "intecom-ps1", + 5057: "intecom-ps2", + 5059: "sds", + 5060: "sip", + 5061: "sips", + 5062: "na-localise", + 5063: "csrpc", + 5064: "ca-1", + 5065: "ca-2", + 5066: "stanag-5066", + 5067: "authentx", + 5068: "bitforestsrv", + 5069: "i-net-2000-npr", + 5070: "vtsas", + 5071: "powerschool", + 5072: "ayiya", + 5073: "tag-pm", + 5074: "alesquery", + 5075: "pvaccess", + 5080: "onscreen", + 5081: "sdl-ets", + 5082: "qcp", + 5083: "qfp", + 5084: "llrp", + 5085: "encrypted-llrp", + 5086: "aprigo-cs", + 5087: "biotic", + 5093: "sentinel-lm", + 5094: "hart-ip", + 5099: "sentlm-srv2srv", + 5100: "socalia", + 5101: "talarian-tcp", + 5102: "oms-nonsecure", + 5103: "actifio-c2c", + 5106: "actifioudsagent", + 5107: "actifioreplic", + 5111: "taep-as-svc", + 5112: "pm-cmdsvr", + 5114: "ev-services", + 5115: "autobuild", + 5117: "gradecam", + 5120: "barracuda-bbs", + 5133: "nbt-pc", + 5134: "ppactivation", + 5135: "erp-scale", + 5137: "ctsd", + 5145: "rmonitor-secure", + 5146: "social-alarm", + 5150: "atmp", + 5151: "esri-sde", + 5152: "sde-discovery", + 5153: "toruxserver", + 5154: "bzflag", + 5155: "asctrl-agent", + 5156: "rugameonline", + 5157: "mediat", + 5161: "snmpssh", + 5162: "snmpssh-trap", + 5163: "sbackup", + 5164: "vpa", + 5165: "ife-icorp", + 5166: "winpcs", + 5167: "scte104", + 5168: "scte30", + 5172: "pcoip-mgmt", + 5190: "aol", + 5191: "aol-1", + 5192: "aol-2", + 5193: "aol-3", + 5194: "cpscomm", + 5195: "ampl-lic", + 5196: "ampl-tableproxy", + 5197: "tunstall-lwp", + 5200: "targus-getdata", + 5201: "targus-getdata1", + 5202: "targus-getdata2", + 5203: "targus-getdata3", + 5209: "nomad", + 5215: "noteza", + 5221: "3exmp", + 5222: "xmpp-client", + 5223: "hpvirtgrp", + 5224: "hpvirtctrl", + 5225: "hp-server", + 5226: "hp-status", + 5227: "perfd", + 5228: "hpvroom", + 5229: "jaxflow", + 5230: "jaxflow-data", + 5231: "crusecontrol", + 5232: "csedaemon", + 5233: "enfs", + 5234: "eenet", + 5235: "galaxy-network", + 5236: "padl2sim", + 5237: "mnet-discovery", + 5245: "downtools", + 5248: "caacws", + 5249: "caaclang2", + 5250: "soagateway", + 5251: "caevms", + 5252: "movaz-ssc", + 5253: "kpdp", + 5254: "logcabin", + 5264: "3com-njack-1", + 5265: "3com-njack-2", + 5269: "xmpp-server", + 5270: "cartographerxmp", + 5271: "cuelink", + 5272: "pk", + 5280: "xmpp-bosh", + 5281: "undo-lm", + 5282: "transmit-port", + 5298: "presence", + 5299: "nlg-data", + 5300: "hacl-hb", + 5301: "hacl-gs", + 5302: "hacl-cfg", + 5303: "hacl-probe", + 5304: "hacl-local", + 5305: "hacl-test", + 5306: "sun-mc-grp", + 5307: "sco-aip", + 5308: "cfengine", + 5309: "jprinter", + 5310: "outlaws", + 5312: "permabit-cs", + 5313: "rrdp", + 5314: "opalis-rbt-ipc", + 5315: "hacl-poll", + 5316: "hpbladems", + 5317: "hpdevms", + 5318: "pkix-cmc", + 5320: "bsfserver-zn", + 5321: "bsfsvr-zn-ssl", + 5343: "kfserver", + 5344: "xkotodrcp", + 5349: "stuns", + 5352: "dns-llq", + 5353: "mdns", + 5354: "mdnsresponder", + 5355: "llmnr", + 5356: "ms-smlbiz", + 5357: "wsdapi", + 5358: "wsdapi-s", + 5359: "ms-alerter", + 5360: "ms-sideshow", + 5361: "ms-s-sideshow", + 5362: "serverwsd2", + 5363: "net-projection", + 5397: "stresstester", + 5398: "elektron-admin", + 5399: "securitychase", + 5400: "excerpt", + 5401: "excerpts", + 5402: "mftp", + 5403: "hpoms-ci-lstn", + 5404: "hpoms-dps-lstn", + 5405: "netsupport", + 5406: "systemics-sox", + 5407: "foresyte-clear", + 5408: "foresyte-sec", + 5409: "salient-dtasrv", + 5410: "salient-usrmgr", + 5411: "actnet", + 5412: "continuus", + 5413: "wwiotalk", + 5414: "statusd", + 5415: "ns-server", + 5416: "sns-gateway", + 5417: "sns-agent", + 5418: "mcntp", + 5419: "dj-ice", + 5420: "cylink-c", + 5421: "netsupport2", + 5422: "salient-mux", + 5423: "virtualuser", + 5424: "beyond-remote", + 5425: "br-channel", + 5426: "devbasic", + 5427: "sco-peer-tta", + 5428: "telaconsole", + 5429: "base", + 5430: "radec-corp", + 5431: "park-agent", + 5432: "postgresql", + 5433: "pyrrho", + 5434: "sgi-arrayd", + 5435: "sceanics", + 5443: "spss", + 5445: "smbdirect", + 5450: "tiepie", + 5453: "surebox", + 5454: "apc-5454", + 5455: "apc-5455", + 5456: "apc-5456", + 5461: "silkmeter", + 5462: "ttl-publisher", + 5463: "ttlpriceproxy", + 5464: "quailnet", + 5465: "netops-broker", + 5470: "apsolab-col", + 5471: "apsolab-cols", + 5472: "apsolab-tag", + 5473: "apsolab-tags", + 5475: "apsolab-data", + 5500: "fcp-addr-srvr1", + 5501: "fcp-addr-srvr2", + 5502: "fcp-srvr-inst1", + 5503: "fcp-srvr-inst2", + 5504: "fcp-cics-gw1", + 5505: "checkoutdb", + 5506: "amc", + 5507: "psl-management", + 5550: "cbus", + 5553: "sgi-eventmond", + 5554: "sgi-esphttp", + 5555: "personal-agent", + 5556: "freeciv", + 5557: "farenet", + 5565: "hpe-dp-bura", + 5566: "westec-connect", + 5567: "dof-dps-mc-sec", + 5568: "sdt", + 5569: "rdmnet-ctrl", + 5573: "sdmmp", + 5574: "lsi-bobcat", + 5575: "ora-oap", + 5579: "fdtracks", + 5580: "tmosms0", + 5581: "tmosms1", + 5582: "fac-restore", + 5583: "tmo-icon-sync", + 5584: "bis-web", + 5585: "bis-sync", + 5586: "att-mt-sms", + 5597: "ininmessaging", + 5598: "mctfeed", + 5599: "esinstall", + 5600: "esmmanager", + 5601: "esmagent", + 5602: "a1-msc", + 5603: "a1-bs", + 5604: "a3-sdunode", + 5605: "a4-sdunode", + 5618: "efr", + 5627: "ninaf", + 5628: "htrust", + 5629: "symantec-sfdb", + 5630: "precise-comm", + 5631: "pcanywheredata", + 5632: "pcanywherestat", + 5633: "beorl", + 5634: "xprtld", + 5635: "sfmsso", + 5636: "sfm-db-server", + 5637: "cssc", + 5638: "flcrs", + 5639: "ics", + 5646: "vfmobile", + 5666: "nrpe", + 5670: "filemq", + 5671: "amqps", + 5672: "amqp", + 5673: "jms", + 5674: "hyperscsi-port", + 5675: "v5ua", + 5676: "raadmin", + 5677: "questdb2-lnchr", + 5678: "rrac", + 5679: "dccm", + 5680: "auriga-router", + 5681: "ncxcp", + 5688: "ggz", + 5689: "qmvideo", + 5693: "rbsystem", + 5696: "kmip", + 5700: "supportassist", + 5705: "storageos", + 5713: "proshareaudio", + 5714: "prosharevideo", + 5715: "prosharedata", + 5716: "prosharerequest", + 5717: "prosharenotify", + 5718: "dpm", + 5719: "dpm-agent", + 5720: "ms-licensing", + 5721: "dtpt", + 5722: "msdfsr", + 5723: "omhs", + 5724: "omsdk", + 5725: "ms-ilm", + 5726: "ms-ilm-sts", + 5727: "asgenf", + 5728: "io-dist-data", + 5729: "openmail", + 5730: "unieng", + 5741: "ida-discover1", + 5742: "ida-discover2", + 5743: "watchdoc-pod", + 5744: "watchdoc", + 5745: "fcopy-server", + 5746: "fcopys-server", + 5747: "tunatic", + 5748: "tunalyzer", + 5750: "rscd", + 5755: "openmailg", + 5757: "x500ms", + 5766: "openmailns", + 5767: "s-openmail", + 5768: "openmailpxy", + 5769: "spramsca", + 5770: "spramsd", + 5771: "netagent", + 5777: "dali-port", + 5780: "vts-rpc", + 5781: "3par-evts", + 5782: "3par-mgmt", + 5783: "3par-mgmt-ssl", + 5785: "3par-rcopy", + 5793: "xtreamx", + 5813: "icmpd", + 5814: "spt-automation", + 5841: "shiprush-d-ch", + 5842: "reversion", + 5859: "wherehoo", + 5863: "ppsuitemsg", + 5868: "diameters", + 5883: "jute", + 5900: "rfb", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 5963: "indy", + 5968: "mppolicy-v5", + 5969: "mppolicy-mgr", + 5984: "couchdb", + 5985: "wsman", + 5986: "wsmans", + 5987: "wbem-rmi", + 5988: "wbem-http", + 5989: "wbem-https", + 5990: "wbem-exp-https", + 5991: "nuxsl", + 5992: "consul-insight", + 5993: "cim-rs", + 5999: "cvsup", + 6064: "ndl-ahp-svc", + 6065: "winpharaoh", + 6066: "ewctsp", + 6068: "gsmp-ancp", + 6069: "trip", + 6070: "messageasap", + 6071: "ssdtp", + 6072: "diagnose-proc", + 6073: "directplay8", + 6074: "max", + 6075: "dpm-acm", + 6076: "msft-dpm-cert", + 6077: "iconstructsrv", + 6084: "reload-config", + 6085: "konspire2b", + 6086: "pdtp", + 6087: "ldss", + 6088: "doglms", + 6099: "raxa-mgmt", + 6100: "synchronet-db", + 6101: "synchronet-rtc", + 6102: "synchronet-upd", + 6103: "rets", + 6104: "dbdb", + 6105: "primaserver", + 6106: "mpsserver", + 6107: "etc-control", + 6108: "sercomm-scadmin", + 6109: "globecast-id", + 6110: "softcm", + 6111: "spc", + 6112: "dtspcd", + 6113: "dayliteserver", + 6114: "wrspice", + 6115: "xic", + 6116: "xtlserv", + 6117: "daylitetouch", + 6121: "spdy", + 6122: "bex-webadmin", + 6123: "backup-express", + 6124: "pnbs", + 6130: "damewaremobgtwy", + 6133: "nbt-wol", + 6140: "pulsonixnls", + 6141: "meta-corp", + 6142: "aspentec-lm", + 6143: "watershed-lm", + 6144: "statsci1-lm", + 6145: "statsci2-lm", + 6146: "lonewolf-lm", + 6147: "montage-lm", + 6148: "ricardo-lm", + 6149: "tal-pod", + 6159: "efb-aci", + 6160: "ecmp", + 6161: "patrol-ism", + 6162: "patrol-coll", + 6163: "pscribe", + 6200: "lm-x", + 6209: "qmtps", + 6222: "radmind", + 6241: "jeol-nsdtp-1", + 6242: "jeol-nsdtp-2", + 6243: "jeol-nsdtp-3", + 6244: "jeol-nsdtp-4", + 6251: "tl1-raw-ssl", + 6252: "tl1-ssh", + 6253: "crip", + 6267: "gld", + 6268: "grid", + 6269: "grid-alt", + 6300: "bmc-grx", + 6301: "bmc-ctd-ldap", + 6306: "ufmp", + 6315: "scup", + 6316: "abb-escp", + 6317: "nav-data-cmd", + 6320: "repsvc", + 6321: "emp-server1", + 6322: "emp-server2", + 6324: "hrd-ncs", + 6325: "dt-mgmtsvc", + 6326: "dt-vra", + 6343: "sflow", + 6344: "streletz", + 6346: "gnutella-svc", + 6347: "gnutella-rtr", + 6350: "adap", + 6355: "pmcs", + 6360: "metaedit-mu", + 6370: "metaedit-se", + 6379: "redis", + 6382: "metatude-mds", + 6389: "clariion-evr01", + 6390: "metaedit-ws", + 6417: "faxcomservice", + 6418: "syserverremote", + 6419: "svdrp", + 6420: "nim-vdrshell", + 6421: "nim-wan", + 6432: "pgbouncer", + 6442: "tarp", + 6443: "sun-sr-https", + 6444: "sge-qmaster", + 6445: "sge-execd", + 6446: "mysql-proxy", + 6455: "skip-cert-recv", + 6456: "skip-cert-send", + 6464: "ieee11073-20701", + 6471: "lvision-lm", + 6480: "sun-sr-http", + 6481: "servicetags", + 6482: "ldoms-mgmt", + 6483: "SunVTS-RMI", + 6484: "sun-sr-jms", + 6485: "sun-sr-iiop", + 6486: "sun-sr-iiops", + 6487: "sun-sr-iiop-aut", + 6488: "sun-sr-jmx", + 6489: "sun-sr-admin", + 6500: "boks", + 6501: "boks-servc", + 6502: "boks-servm", + 6503: "boks-clntd", + 6505: "badm-priv", + 6506: "badm-pub", + 6507: "bdir-priv", + 6508: "bdir-pub", + 6509: "mgcs-mfp-port", + 6510: "mcer-port", + 6513: "netconf-tls", + 6514: "syslog-tls", + 6515: "elipse-rec", + 6543: "lds-distrib", + 6544: "lds-dump", + 6547: "apc-6547", + 6548: "apc-6548", + 6549: "apc-6549", + 6550: "fg-sysupdate", + 6551: "sum", + 6558: "xdsxdm", + 6566: "sane-port", + 6568: "canit-store", + 6579: "affiliate", + 6580: "parsec-master", + 6581: "parsec-peer", + 6582: "parsec-game", + 6583: "joaJewelSuite", + 6600: "mshvlm", + 6601: "mstmg-sstp", + 6602: "wsscomfrmwk", + 6619: "odette-ftps", + 6620: "kftp-data", + 6621: "kftp", + 6622: "mcftp", + 6623: "ktelnet", + 6624: "datascaler-db", + 6625: "datascaler-ctl", + 6626: "wago-service", + 6627: "nexgen", + 6628: "afesc-mc", + 6629: "nexgen-aux", + 6632: "mxodbc-connect", + 6640: "ovsdb", + 6653: "openflow", + 6655: "pcs-sf-ui-man", + 6656: "emgmsg", + 6670: "vocaltec-gold", + 6671: "p4p-portal", + 6672: "vision-server", + 6673: "vision-elmd", + 6678: "vfbp", + 6679: "osaut", + 6687: "clever-ctrace", + 6688: "clever-tcpip", + 6689: "tsa", + 6690: "cleverdetect", + 6697: "ircs-u", + 6701: "kti-icad-srvr", + 6702: "e-design-net", + 6703: "e-design-web", + 6714: "ibprotocol", + 6715: "fibotrader-com", + 6716: "princity-agent", + 6767: "bmc-perf-agent", + 6768: "bmc-perf-mgrd", + 6769: "adi-gxp-srvprt", + 6770: "plysrv-http", + 6771: "plysrv-https", + 6777: "ntz-tracker", + 6778: "ntz-p2p-storage", + 6785: "dgpf-exchg", + 6786: "smc-jmx", + 6787: "smc-admin", + 6788: "smc-http", + 6789: "radg", + 6790: "hnmp", + 6791: "hnm", + 6801: "acnet", + 6817: "pentbox-sim", + 6831: "ambit-lm", + 6841: "netmo-default", + 6842: "netmo-http", + 6850: "iccrushmore", + 6868: "acctopus-cc", + 6888: "muse", + 6900: "rtimeviewer", + 6901: "jetstream", + 6935: "ethoscan", + 6936: "xsmsvc", + 6946: "bioserver", + 6951: "otlp", + 6961: "jmact3", + 6962: "jmevt2", + 6963: "swismgr1", + 6964: "swismgr2", + 6965: "swistrap", + 6966: "swispol", + 6969: "acmsoda", + 6970: "conductor", + 6997: "MobilitySrv", + 6998: "iatp-highpri", + 6999: "iatp-normalpri", + 7000: "afs3-fileserver", + 7001: "afs3-callback", + 7002: "afs3-prserver", + 7003: "afs3-vlserver", + 7004: "afs3-kaserver", + 7005: "afs3-volser", + 7006: "afs3-errors", + 7007: "afs3-bos", + 7008: "afs3-update", + 7009: "afs3-rmtsys", + 7010: "ups-onlinet", + 7011: "talon-disc", + 7012: "talon-engine", + 7013: "microtalon-dis", + 7014: "microtalon-com", + 7015: "talon-webserver", + 7016: "spg", + 7017: "grasp", + 7018: "fisa-svc", + 7019: "doceri-ctl", + 7020: "dpserve", + 7021: "dpserveadmin", + 7022: "ctdp", + 7023: "ct2nmcs", + 7024: "vmsvc", + 7025: "vmsvc-2", + 7030: "op-probe", + 7031: "iposplanet", + 7070: "arcp", + 7071: "iwg1", + 7073: "martalk", + 7080: "empowerid", + 7099: "lazy-ptop", + 7100: "font-service", + 7101: "elcn", + 7117: "rothaga", + 7121: "virprot-lm", + 7128: "scenidm", + 7129: "scenccs", + 7161: "cabsm-comm", + 7162: "caistoragemgr", + 7163: "cacsambroker", + 7164: "fsr", + 7165: "doc-server", + 7166: "aruba-server", + 7167: "casrmagent", + 7168: "cnckadserver", + 7169: "ccag-pib", + 7170: "nsrp", + 7171: "drm-production", + 7172: "metalbend", + 7173: "zsecure", + 7174: "clutild", + 7200: "fodms", + 7201: "dlip", + 7202: "pon-ictp", + 7215: "PS-Server", + 7216: "PS-Capture-Pro", + 7227: "ramp", + 7228: "citrixupp", + 7229: "citrixuppg", + 7236: "display", + 7237: "pads", + 7244: "frc-hicp", + 7262: "cnap", + 7272: "watchme-7272", + 7273: "oma-rlp", + 7274: "oma-rlp-s", + 7275: "oma-ulp", + 7276: "oma-ilp", + 7277: "oma-ilp-s", + 7278: "oma-dcdocbs", + 7279: "ctxlic", + 7280: "itactionserver1", + 7281: "itactionserver2", + 7282: "mzca-action", + 7283: "genstat", + 7365: "lcm-server", + 7391: "mindfilesys", + 7392: "mrssrendezvous", + 7393: "nfoldman", + 7394: "fse", + 7395: "winqedit", + 7397: "hexarc", + 7400: "rtps-discovery", + 7401: "rtps-dd-ut", + 7402: "rtps-dd-mt", + 7410: "ionixnetmon", + 7411: "daqstream", + 7421: "mtportmon", + 7426: "pmdmgr", + 7427: "oveadmgr", + 7428: "ovladmgr", + 7429: "opi-sock", + 7430: "xmpv7", + 7431: "pmd", + 7437: "faximum", + 7443: "oracleas-https", + 7471: "sttunnel", + 7473: "rise", + 7474: "neo4j", + 7478: "openit", + 7491: "telops-lmd", + 7500: "silhouette", + 7501: "ovbus", + 7508: "adcp", + 7509: "acplt", + 7510: "ovhpas", + 7511: "pafec-lm", + 7542: "saratoga", + 7543: "atul", + 7544: "nta-ds", + 7545: "nta-us", + 7546: "cfs", + 7547: "cwmp", + 7548: "tidp", + 7549: "nls-tl", + 7551: "controlone-con", + 7560: "sncp", + 7563: "cfw", + 7566: "vsi-omega", + 7569: "dell-eql-asm", + 7570: "aries-kfinder", + 7574: "coherence", + 7588: "sun-lm", + 7606: "mipi-debug", + 7624: "indi", + 7626: "simco", + 7627: "soap-http", + 7628: "zen-pawn", + 7629: "xdas", + 7630: "hawk", + 7631: "tesla-sys-msg", + 7633: "pmdfmgt", + 7648: "cuseeme", + 7672: "imqstomp", + 7673: "imqstomps", + 7674: "imqtunnels", + 7675: "imqtunnel", + 7676: "imqbrokerd", + 7677: "sun-user-https", + 7680: "pando-pub", + 7683: "dmt", + 7687: "bolt", + 7689: "collaber", + 7697: "klio", + 7700: "em7-secom", + 7707: "sync-em7", + 7708: "scinet", + 7720: "medimageportal", + 7724: "nsdeepfreezectl", + 7725: "nitrogen", + 7726: "freezexservice", + 7727: "trident-data", + 7728: "osvr", + 7734: "smip", + 7738: "aiagent", + 7741: "scriptview", + 7742: "msss", + 7743: "sstp-1", + 7744: "raqmon-pdu", + 7747: "prgp", + 7775: "inetfs", + 7777: "cbt", + 7778: "interwise", + 7779: "vstat", + 7781: "accu-lmgr", + 7786: "minivend", + 7787: "popup-reminders", + 7789: "office-tools", + 7794: "q3ade", + 7797: "pnet-conn", + 7798: "pnet-enc", + 7799: "altbsdp", + 7800: "asr", + 7801: "ssp-client", + 7810: "rbt-wanopt", + 7845: "apc-7845", + 7846: "apc-7846", + 7847: "csoauth", + 7869: "mobileanalyzer", + 7870: "rbt-smc", + 7871: "mdm", + 7878: "owms", + 7880: "pss", + 7887: "ubroker", + 7900: "mevent", + 7901: "tnos-sp", + 7902: "tnos-dp", + 7903: "tnos-dps", + 7913: "qo-secure", + 7932: "t2-drm", + 7933: "t2-brm", + 7962: "generalsync", + 7967: "supercell", + 7979: "micromuse-ncps", + 7980: "quest-vista", + 7981: "sossd-collect", + 7982: "sossd-agent", + 7997: "pushns", + 7999: "irdmi2", + 8000: "irdmi", + 8001: "vcom-tunnel", + 8002: "teradataordbms", + 8003: "mcreport", + 8005: "mxi", + 8006: "wpl-analytics", + 8007: "warppipe", + 8008: "http-alt", + 8019: "qbdb", + 8020: "intu-ec-svcdisc", + 8021: "intu-ec-client", + 8022: "oa-system", + 8025: "ca-audit-da", + 8026: "ca-audit-ds", + 8032: "pro-ed", + 8033: "mindprint", + 8034: "vantronix-mgmt", + 8040: "ampify", + 8041: "enguity-xccetp", + 8042: "fs-agent", + 8043: "fs-server", + 8044: "fs-mgmt", + 8051: "rocrail", + 8052: "senomix01", + 8053: "senomix02", + 8054: "senomix03", + 8055: "senomix04", + 8056: "senomix05", + 8057: "senomix06", + 8058: "senomix07", + 8059: "senomix08", + 8066: "toad-bi-appsrvr", + 8067: "infi-async", + 8070: "ucs-isc", + 8074: "gadugadu", + 8077: "mles", + 8080: "http-alt", + 8081: "sunproxyadmin", + 8082: "us-cli", + 8083: "us-srv", + 8086: "d-s-n", + 8087: "simplifymedia", + 8088: "radan-http", + 8090: "opsmessaging", + 8091: "jamlink", + 8097: "sac", + 8100: "xprint-server", + 8101: "ldoms-migr", + 8102: "kz-migr", + 8115: "mtl8000-matrix", + 8116: "cp-cluster", + 8117: "purityrpc", + 8118: "privoxy", + 8121: "apollo-data", + 8122: "apollo-admin", + 8128: "paycash-online", + 8129: "paycash-wbp", + 8130: "indigo-vrmi", + 8131: "indigo-vbcp", + 8132: "dbabble", + 8140: "puppet", + 8148: "isdd", + 8153: "quantastor", + 8160: "patrol", + 8161: "patrol-snmp", + 8162: "lpar2rrd", + 8181: "intermapper", + 8182: "vmware-fdm", + 8183: "proremote", + 8184: "itach", + 8190: "gcp-rphy", + 8191: "limnerpressure", + 8192: "spytechphone", + 8194: "blp1", + 8195: "blp2", + 8199: "vvr-data", + 8200: "trivnet1", + 8201: "trivnet2", + 8204: "lm-perfworks", + 8205: "lm-instmgr", + 8206: "lm-dta", + 8207: "lm-sserver", + 8208: "lm-webwatcher", + 8230: "rexecj", + 8243: "synapse-nhttps", + 8270: "robot-remote", + 8276: "pando-sec", + 8280: "synapse-nhttp", + 8282: "libelle", + 8292: "blp3", + 8293: "hiperscan-id", + 8294: "blp4", + 8300: "tmi", + 8301: "amberon", + 8313: "hub-open-net", + 8320: "tnp-discover", + 8321: "tnp", + 8322: "garmin-marine", + 8351: "server-find", + 8376: "cruise-enum", + 8377: "cruise-swroute", + 8378: "cruise-config", + 8379: "cruise-diags", + 8380: "cruise-update", + 8383: "m2mservices", + 8400: "cvd", + 8401: "sabarsd", + 8402: "abarsd", + 8403: "admind", + 8404: "svcloud", + 8405: "svbackup", + 8415: "dlpx-sp", + 8416: "espeech", + 8417: "espeech-rtp", + 8423: "aritts", + 8442: "cybro-a-bus", + 8443: "pcsync-https", + 8444: "pcsync-http", + 8445: "copy", + 8450: "npmp", + 8457: "nexentamv", + 8470: "cisco-avp", + 8471: "pim-port", + 8472: "otv", + 8473: "vp2p", + 8474: "noteshare", + 8500: "fmtp", + 8501: "cmtp-mgt", + 8502: "ftnmtp", + 8554: "rtsp-alt", + 8555: "d-fence", + 8567: "dof-tunnel", + 8600: "asterix", + 8610: "canon-mfnp", + 8611: "canon-bjnp1", + 8612: "canon-bjnp2", + 8613: "canon-bjnp3", + 8614: "canon-bjnp4", + 8615: "imink", + 8665: "monetra", + 8666: "monetra-admin", + 8675: "msi-cps-rm", + 8686: "sun-as-jmxrmi", + 8688: "openremote-ctrl", + 8699: "vnyx", + 8711: "nvc", + 8733: "ibus", + 8750: "dey-keyneg", + 8763: "mc-appserver", + 8764: "openqueue", + 8765: "ultraseek-http", + 8766: "amcs", + 8770: "dpap", + 8778: "uec", + 8786: "msgclnt", + 8787: "msgsrvr", + 8793: "acd-pm", + 8800: "sunwebadmin", + 8804: "truecm", + 8873: "dxspider", + 8880: "cddbp-alt", + 8881: "galaxy4d", + 8883: "secure-mqtt", + 8888: "ddi-tcp-1", + 8889: "ddi-tcp-2", + 8890: "ddi-tcp-3", + 8891: "ddi-tcp-4", + 8892: "ddi-tcp-5", + 8893: "ddi-tcp-6", + 8894: "ddi-tcp-7", + 8899: "ospf-lite", + 8900: "jmb-cds1", + 8901: "jmb-cds2", + 8910: "manyone-http", + 8911: "manyone-xml", + 8912: "wcbackup", + 8913: "dragonfly", + 8937: "twds", + 8953: "ub-dns-control", + 8954: "cumulus-admin", + 8980: "nod-provider", + 8989: "sunwebadmins", + 8990: "http-wmap", + 8991: "https-wmap", + 8997: "oracle-ms-ens", + 8998: "canto-roboflow", + 8999: "bctp", + 9000: "cslistener", + 9001: "etlservicemgr", + 9002: "dynamid", + 9005: "golem", + 9008: "ogs-server", + 9009: "pichat", + 9010: "sdr", + 9020: "tambora", + 9021: "panagolin-ident", + 9022: "paragent", + 9023: "swa-1", + 9024: "swa-2", + 9025: "swa-3", + 9026: "swa-4", + 9050: "versiera", + 9051: "fio-cmgmt", + 9060: "CardWeb-IO", + 9080: "glrpc", + 9083: "emc-pp-mgmtsvc", + 9084: "aurora", + 9085: "ibm-rsyscon", + 9086: "net2display", + 9087: "classic", + 9088: "sqlexec", + 9089: "sqlexec-ssl", + 9090: "websm", + 9091: "xmltec-xmlmail", + 9092: "XmlIpcRegSvc", + 9093: "copycat", + 9100: "hp-pdl-datastr", + 9101: "bacula-dir", + 9102: "bacula-fd", + 9103: "bacula-sd", + 9104: "peerwire", + 9105: "xadmin", + 9106: "astergate", + 9107: "astergatefax", + 9119: "mxit", + 9122: "grcmp", + 9123: "grcp", + 9131: "dddp", + 9160: "apani1", + 9161: "apani2", + 9162: "apani3", + 9163: "apani4", + 9164: "apani5", + 9191: "sun-as-jpda", + 9200: "wap-wsp", + 9201: "wap-wsp-wtp", + 9202: "wap-wsp-s", + 9203: "wap-wsp-wtp-s", + 9204: "wap-vcard", + 9205: "wap-vcal", + 9206: "wap-vcard-s", + 9207: "wap-vcal-s", + 9208: "rjcdb-vcards", + 9209: "almobile-system", + 9210: "oma-mlp", + 9211: "oma-mlp-s", + 9212: "serverviewdbms", + 9213: "serverstart", + 9214: "ipdcesgbs", + 9215: "insis", + 9216: "acme", + 9217: "fsc-port", + 9222: "teamcoherence", + 9255: "mon", + 9278: "pegasus", + 9279: "pegasus-ctl", + 9280: "pgps", + 9281: "swtp-port1", + 9282: "swtp-port2", + 9283: "callwaveiam", + 9284: "visd", + 9285: "n2h2server", + 9287: "cumulus", + 9292: "armtechdaemon", + 9293: "storview", + 9294: "armcenterhttp", + 9295: "armcenterhttps", + 9300: "vrace", + 9306: "sphinxql", + 9312: "sphinxapi", + 9318: "secure-ts", + 9321: "guibase", + 9343: "mpidcmgr", + 9344: "mphlpdmc", + 9345: "rancher", + 9346: "ctechlicensing", + 9374: "fjdmimgr", + 9380: "boxp", + 9387: "d2dconfig", + 9388: "d2ddatatrans", + 9389: "adws", + 9390: "otp", + 9396: "fjinvmgr", + 9397: "mpidcagt", + 9400: "sec-t4net-srv", + 9401: "sec-t4net-clt", + 9402: "sec-pc2fax-srv", + 9418: "git", + 9443: "tungsten-https", + 9444: "wso2esb-console", + 9445: "mindarray-ca", + 9450: "sntlkeyssrvr", + 9500: "ismserver", + 9535: "mngsuite", + 9536: "laes-bf", + 9555: "trispen-sra", + 9592: "ldgateway", + 9593: "cba8", + 9594: "msgsys", + 9595: "pds", + 9596: "mercury-disc", + 9597: "pd-admin", + 9598: "vscp", + 9599: "robix", + 9600: "micromuse-ncpw", + 9612: "streamcomm-ds", + 9614: "iadt-tls", + 9616: "erunbook-agent", + 9617: "erunbook-server", + 9618: "condor", + 9628: "odbcpathway", + 9629: "uniport", + 9630: "peoctlr", + 9631: "peocoll", + 9640: "pqsflows", + 9666: "zoomcp", + 9667: "xmms2", + 9668: "tec5-sdctp", + 9694: "client-wakeup", + 9695: "ccnx", + 9700: "board-roar", + 9747: "l5nas-parchan", + 9750: "board-voip", + 9753: "rasadv", + 9762: "tungsten-http", + 9800: "davsrc", + 9801: "sstp-2", + 9802: "davsrcs", + 9875: "sapv1", + 9876: "sd", + 9888: "cyborg-systems", + 9889: "gt-proxy", + 9898: "monkeycom", + 9900: "iua", + 9909: "domaintime", + 9911: "sype-transport", + 9925: "xybrid-cloud", + 9950: "apc-9950", + 9951: "apc-9951", + 9952: "apc-9952", + 9953: "acis", + 9954: "hinp", + 9955: "alljoyn-stm", + 9966: "odnsp", + 9978: "xybrid-rt", + 9979: "visweather", + 9981: "pumpkindb", + 9987: "dsm-scm-target", + 9988: "nsesrvr", + 9990: "osm-appsrvr", + 9991: "osm-oev", + 9992: "palace-1", + 9993: "palace-2", + 9994: "palace-3", + 9995: "palace-4", + 9996: "palace-5", + 9997: "palace-6", + 9998: "distinct32", + 9999: "distinct", + 10000: "ndmp", + 10001: "scp-config", + 10002: "documentum", + 10003: "documentum-s", + 10004: "emcrmirccd", + 10005: "emcrmird", + 10006: "netapp-sync", + 10007: "mvs-capacity", + 10008: "octopus", + 10009: "swdtp-sv", + 10010: "rxapi", + 10020: "abb-hw", + 10050: "zabbix-agent", + 10051: "zabbix-trapper", + 10055: "qptlmd", + 10080: "amanda", + 10081: "famdc", + 10100: "itap-ddtp", + 10101: "ezmeeting-2", + 10102: "ezproxy-2", + 10103: "ezrelay", + 10104: "swdtp", + 10107: "bctp-server", + 10110: "nmea-0183", + 10113: "netiq-endpoint", + 10114: "netiq-qcheck", + 10115: "netiq-endpt", + 10116: "netiq-voipa", + 10117: "iqrm", + 10125: "cimple", + 10128: "bmc-perf-sd", + 10129: "bmc-gms", + 10160: "qb-db-server", + 10161: "snmptls", + 10162: "snmptls-trap", + 10200: "trisoap", + 10201: "rsms", + 10252: "apollo-relay", + 10260: "axis-wimp-port", + 10261: "tile-ml", + 10288: "blocks", + 10321: "cosir", + 10540: "MOS-lower", + 10541: "MOS-upper", + 10542: "MOS-aux", + 10543: "MOS-soap", + 10544: "MOS-soap-opt", + 10548: "serverdocs", + 10631: "printopia", + 10800: "gap", + 10805: "lpdg", + 10809: "nbd", + 10860: "helix", + 10880: "bveapi", + 10933: "octopustentacle", + 10990: "rmiaux", + 11000: "irisa", + 11001: "metasys", + 11095: "weave", + 11103: "origo-sync", + 11104: "netapp-icmgmt", + 11105: "netapp-icdata", + 11106: "sgi-lk", + 11109: "sgi-dmfmgr", + 11110: "sgi-soap", + 11111: "vce", + 11112: "dicom", + 11161: "suncacao-snmp", + 11162: "suncacao-jmxmp", + 11163: "suncacao-rmi", + 11164: "suncacao-csa", + 11165: "suncacao-websvc", + 11172: "oemcacao-jmxmp", + 11173: "t5-straton", + 11174: "oemcacao-rmi", + 11175: "oemcacao-websvc", + 11201: "smsqp", + 11202: "dcsl-backup", + 11208: "wifree", + 11211: "memcache", + 11319: "imip", + 11320: "imip-channels", + 11321: "arena-server", + 11367: "atm-uhas", + 11371: "hkp", + 11489: "asgcypresstcps", + 11600: "tempest-port", + 11623: "emc-xsw-dconfig", + 11720: "h323callsigalt", + 11723: "emc-xsw-dcache", + 11751: "intrepid-ssl", + 11796: "lanschool", + 11876: "xoraya", + 11967: "sysinfo-sp", + 12000: "entextxid", + 12001: "entextnetwk", + 12002: "entexthigh", + 12003: "entextmed", + 12004: "entextlow", + 12005: "dbisamserver1", + 12006: "dbisamserver2", + 12007: "accuracer", + 12008: "accuracer-dbms", + 12010: "edbsrvr", + 12012: "vipera", + 12013: "vipera-ssl", + 12109: "rets-ssl", + 12121: "nupaper-ss", + 12168: "cawas", + 12172: "hivep", + 12300: "linogridengine", + 12302: "rads", + 12321: "warehouse-sss", + 12322: "warehouse", + 12345: "italk", + 12753: "tsaf", + 12865: "netperf", + 13160: "i-zipqd", + 13216: "bcslogc", + 13217: "rs-pias", + 13218: "emc-vcas-tcp", + 13223: "powwow-client", + 13224: "powwow-server", + 13400: "doip-data", + 13720: "bprd", + 13721: "bpdbm", + 13722: "bpjava-msvc", + 13724: "vnetd", + 13782: "bpcd", + 13783: "vopied", + 13785: "nbdb", + 13786: "nomdb", + 13818: "dsmcc-config", + 13819: "dsmcc-session", + 13820: "dsmcc-passthru", + 13821: "dsmcc-download", + 13822: "dsmcc-ccp", + 13823: "bmdss", + 13894: "ucontrol", + 13929: "dta-systems", + 13930: "medevolve", + 14000: "scotty-ft", + 14001: "sua", + 14033: "sage-best-com1", + 14034: "sage-best-com2", + 14141: "vcs-app", + 14142: "icpp", + 14143: "icpps", + 14145: "gcm-app", + 14149: "vrts-tdd", + 14150: "vcscmd", + 14154: "vad", + 14250: "cps", + 14414: "ca-web-update", + 14500: "xpra", + 14936: "hde-lcesrvr-1", + 14937: "hde-lcesrvr-2", + 15000: "hydap", + 15002: "onep-tls", + 15345: "xpilot", + 15363: "3link", + 15555: "cisco-snat", + 15660: "bex-xr", + 15740: "ptp", + 15999: "programmar", + 16000: "fmsas", + 16001: "fmsascon", + 16002: "gsms", + 16020: "jwpc", + 16021: "jwpc-bin", + 16161: "sun-sea-port", + 16162: "solaris-audit", + 16309: "etb4j", + 16310: "pduncs", + 16311: "pdefmns", + 16360: "netserialext1", + 16361: "netserialext2", + 16367: "netserialext3", + 16368: "netserialext4", + 16384: "connected", + 16385: "rdgs", + 16619: "xoms", + 16665: "axon-tunnel", + 16789: "cadsisvr", + 16900: "newbay-snc-mc", + 16950: "sgcip", + 16991: "intel-rci-mp", + 16992: "amt-soap-http", + 16993: "amt-soap-https", + 16994: "amt-redir-tcp", + 16995: "amt-redir-tls", + 17007: "isode-dua", + 17184: "vestasdlp", + 17185: "soundsvirtual", + 17219: "chipper", + 17220: "avtp", + 17221: "avdecc", + 17223: "isa100-gci", + 17225: "trdp-md", + 17234: "integrius-stp", + 17235: "ssh-mgmt", + 17500: "db-lsp", + 17555: "ailith", + 17729: "ea", + 17754: "zep", + 17755: "zigbee-ip", + 17756: "zigbee-ips", + 17777: "sw-orion", + 18000: "biimenu", + 18104: "radpdf", + 18136: "racf", + 18181: "opsec-cvp", + 18182: "opsec-ufp", + 18183: "opsec-sam", + 18184: "opsec-lea", + 18185: "opsec-omi", + 18186: "ohsc", + 18187: "opsec-ela", + 18241: "checkpoint-rtm", + 18242: "iclid", + 18243: "clusterxl", + 18262: "gv-pf", + 18463: "ac-cluster", + 18634: "rds-ib", + 18635: "rds-ip", + 18668: "vdmmesh", + 18769: "ique", + 18881: "infotos", + 18888: "apc-necmp", + 19000: "igrid", + 19007: "scintilla", + 19020: "j-link", + 19191: "opsec-uaa", + 19194: "ua-secureagent", + 19220: "cora", + 19283: "keysrvr", + 19315: "keyshadow", + 19398: "mtrgtrans", + 19410: "hp-sco", + 19411: "hp-sca", + 19412: "hp-sessmon", + 19539: "fxuptp", + 19540: "sxuptp", + 19541: "jcp", + 19998: "iec-104-sec", + 19999: "dnp-sec", + 20000: "dnp", + 20001: "microsan", + 20002: "commtact-http", + 20003: "commtact-https", + 20005: "openwebnet", + 20013: "ss-idi", + 20014: "opendeploy", + 20034: "nburn-id", + 20046: "tmophl7mts", + 20048: "mountd", + 20049: "nfsrdma", + 20057: "avesterra", + 20167: "tolfab", + 20202: "ipdtp-port", + 20222: "ipulse-ics", + 20480: "emwavemsg", + 20670: "track", + 20999: "athand-mmp", + 21000: "irtrans", + 21010: "notezilla-lan", + 21221: "aigairserver", + 21553: "rdm-tfs", + 21554: "dfserver", + 21590: "vofr-gateway", + 21800: "tvpm", + 21845: "webphone", + 21846: "netspeak-is", + 21847: "netspeak-cs", + 21848: "netspeak-acd", + 21849: "netspeak-cps", + 22000: "snapenetio", + 22001: "optocontrol", + 22002: "optohost002", + 22003: "optohost003", + 22004: "optohost004", + 22005: "optohost004", + 22125: "dcap", + 22128: "gsidcap", + 22222: "easyengine", + 22273: "wnn6", + 22305: "cis", + 22335: "shrewd-control", + 22343: "cis-secure", + 22347: "wibukey", + 22350: "codemeter", + 22351: "codemeter-cmwan", + 22537: "caldsoft-backup", + 22555: "vocaltec-wconf", + 22763: "talikaserver", + 22800: "aws-brf", + 22951: "brf-gw", + 23000: "inovaport1", + 23001: "inovaport2", + 23002: "inovaport3", + 23003: "inovaport4", + 23004: "inovaport5", + 23005: "inovaport6", + 23053: "gntp", + 23294: "5afe-dir", + 23333: "elxmgmt", + 23400: "novar-dbase", + 23401: "novar-alarm", + 23402: "novar-global", + 23456: "aequus", + 23457: "aequus-alt", + 23546: "areaguard-neo", + 24000: "med-ltp", + 24001: "med-fsp-rx", + 24002: "med-fsp-tx", + 24003: "med-supp", + 24004: "med-ovw", + 24005: "med-ci", + 24006: "med-net-svc", + 24242: "filesphere", + 24249: "vista-4gl", + 24321: "ild", + 24386: "intel-rci", + 24465: "tonidods", + 24554: "binkp", + 24577: "bilobit", + 24666: "sdtvwcam", + 24676: "canditv", + 24677: "flashfiler", + 24678: "proactivate", + 24680: "tcc-http", + 24754: "cslg", + 24922: "find", + 25000: "icl-twobase1", + 25001: "icl-twobase2", + 25002: "icl-twobase3", + 25003: "icl-twobase4", + 25004: "icl-twobase5", + 25005: "icl-twobase6", + 25006: "icl-twobase7", + 25007: "icl-twobase8", + 25008: "icl-twobase9", + 25009: "icl-twobase10", + 25576: "sauterdongle", + 25604: "idtp", + 25793: "vocaltec-hos", + 25900: "tasp-net", + 25901: "niobserver", + 25902: "nilinkanalyst", + 25903: "niprobe", + 26000: "quake", + 26133: "scscp", + 26208: "wnn6-ds", + 26257: "cockroach", + 26260: "ezproxy", + 26261: "ezmeeting", + 26262: "k3software-svr", + 26263: "k3software-cli", + 26486: "exoline-tcp", + 26487: "exoconfig", + 26489: "exonet", + 27345: "imagepump", + 27442: "jesmsjc", + 27504: "kopek-httphead", + 27782: "ars-vista", + 27876: "astrolink", + 27999: "tw-auth-key", + 28000: "nxlmd", + 28001: "pqsp", + 28200: "voxelstorm", + 28240: "siemensgsm", + 28589: "bosswave", + 29167: "otmp", + 29999: "bingbang", + 30000: "ndmps", + 30001: "pago-services1", + 30002: "pago-services2", + 30003: "amicon-fpsu-ra", + 30100: "rwp", + 30260: "kingdomsonline", + 30400: "gs-realtime", + 30999: "ovobs", + 31016: "ka-sddp", + 31020: "autotrac-acp", + 31400: "pace-licensed", + 31416: "xqosd", + 31457: "tetrinet", + 31620: "lm-mon", + 31685: "dsx-monitor", + 31765: "gamesmith-port", + 31948: "iceedcp-tx", + 31949: "iceedcp-rx", + 32034: "iracinghelper", + 32249: "t1distproc60", + 32400: "plex", + 32483: "apm-link", + 32635: "sec-ntb-clnt", + 32636: "DMExpress", + 32767: "filenet-powsrm", + 32768: "filenet-tms", + 32769: "filenet-rpc", + 32770: "filenet-nch", + 32771: "filenet-rmi", + 32772: "filenet-pa", + 32773: "filenet-cm", + 32774: "filenet-re", + 32775: "filenet-pch", + 32776: "filenet-peior", + 32777: "filenet-obrok", + 32801: "mlsn", + 32811: "retp", + 32896: "idmgratm", + 33060: "mysqlx", + 33123: "aurora-balaena", + 33331: "diamondport", + 33333: "dgi-serv", + 33334: "speedtrace", + 33434: "traceroute", + 33656: "snip-slave", + 34249: "turbonote-2", + 34378: "p-net-local", + 34379: "p-net-remote", + 34567: "dhanalakshmi", + 34962: "profinet-rt", + 34963: "profinet-rtm", + 34964: "profinet-cm", + 34980: "ethercat", + 35000: "heathview", + 35001: "rt-viewer", + 35002: "rt-sound", + 35003: "rt-devicemapper", + 35004: "rt-classmanager", + 35005: "rt-labtracker", + 35006: "rt-helper", + 35100: "axio-disc", + 35354: "kitim", + 35355: "altova-lm", + 35356: "guttersnex", + 35357: "openstack-id", + 36001: "allpeers", + 36524: "febooti-aw", + 36602: "observium-agent", + 36700: "mapx", + 36865: "kastenxpipe", + 37475: "neckar", + 37483: "gdrive-sync", + 37601: "eftp", + 37654: "unisys-eportal", + 38000: "ivs-database", + 38001: "ivs-insertion", + 38002: "cresco-control", + 38201: "galaxy7-data", + 38202: "fairview", + 38203: "agpolicy", + 38800: "sruth", + 38865: "secrmmsafecopya", + 39681: "turbonote-1", + 40000: "safetynetp", + 40404: "sptx", + 40841: "cscp", + 40842: "csccredir", + 40843: "csccfirewall", + 41111: "fs-qos", + 41121: "tentacle", + 41230: "z-wave-s", + 41794: "crestron-cip", + 41795: "crestron-ctp", + 41796: "crestron-cips", + 41797: "crestron-ctps", + 42508: "candp", + 42509: "candrp", + 42510: "caerpc", + 43000: "recvr-rc", + 43188: "reachout", + 43189: "ndm-agent-port", + 43190: "ip-provision", + 43191: "noit-transport", + 43210: "shaperai", + 43439: "eq3-update", + 43440: "ew-mgmt", + 43441: "ciscocsdb", + 44123: "z-wave-tunnel", + 44321: "pmcd", + 44322: "pmcdproxy", + 44323: "pmwebapi", + 44444: "cognex-dataman", + 44553: "rbr-debug", + 44818: "EtherNet-IP-2", + 44900: "m3da", + 45000: "asmp", + 45001: "asmps", + 45002: "rs-status", + 45045: "synctest", + 45054: "invision-ag", + 45514: "cloudcheck", + 45678: "eba", + 45824: "dai-shell", + 45825: "qdb2service", + 45966: "ssr-servermgr", + 46336: "inedo", + 46998: "spremotetablet", + 46999: "mediabox", + 47000: "mbus", + 47001: "winrm", + 47557: "dbbrowse", + 47624: "directplaysrvr", + 47806: "ap", + 47808: "bacnet", + 48000: "nimcontroller", + 48001: "nimspooler", + 48002: "nimhub", + 48003: "nimgtw", + 48004: "nimbusdb", + 48005: "nimbusdbctrl", + 48049: "3gpp-cbsp", + 48050: "weandsf", + 48128: "isnetserv", + 48129: "blp5", + 48556: "com-bardac-dw", + 48619: "iqobject", + 48653: "robotraconteur", + 49000: "matahari", + 49001: "nusrp", +} +var udpPortNames = map[UDPPort]string{ + 1: "tcpmux", + 2: "compressnet", + 3: "compressnet", + 5: "rje", + 7: "echo", + 9: "discard", + 11: "systat", + 13: "daytime", + 17: "qotd", + 18: "msp", + 19: "chargen", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 23: "telnet", + 25: "smtp", + 27: "nsw-fe", + 29: "msg-icp", + 31: "msg-auth", + 33: "dsp", + 37: "time", + 38: "rap", + 39: "rlp", + 41: "graphics", + 42: "name", + 43: "nicname", + 44: "mpm-flags", + 45: "mpm", + 46: "mpm-snd", + 48: "auditd", + 49: "tacacs", + 50: "re-mail-ck", + 52: "xns-time", + 53: "domain", + 54: "xns-ch", + 55: "isi-gl", + 56: "xns-auth", + 58: "xns-mail", + 62: "acas", + 63: "whoispp", + 64: "covia", + 65: "tacacs-ds", + 66: "sql-net", + 67: "bootps", + 68: "bootpc", + 69: "tftp", + 70: "gopher", + 71: "netrjs-1", + 72: "netrjs-2", + 73: "netrjs-3", + 74: "netrjs-4", + 76: "deos", + 78: "vettcp", + 79: "finger", + 80: "http", + 82: "xfer", + 83: "mit-ml-dev", + 84: "ctf", + 85: "mit-ml-dev", + 86: "mfcobol", + 88: "kerberos", + 89: "su-mit-tg", + 90: "dnsix", + 91: "mit-dov", + 92: "npp", + 93: "dcp", + 94: "objcall", + 95: "supdup", + 96: "dixie", + 97: "swift-rvf", + 98: "tacnews", + 99: "metagram", + 101: "hostname", + 102: "iso-tsap", + 103: "gppitnp", + 104: "acr-nema", + 105: "cso", + 106: "3com-tsmux", + 107: "rtelnet", + 108: "snagas", + 109: "pop2", + 110: "pop3", + 111: "sunrpc", + 112: "mcidas", + 113: "auth", + 115: "sftp", + 116: "ansanotify", + 117: "uucp-path", + 118: "sqlserv", + 119: "nntp", + 120: "cfdptkt", + 121: "erpc", + 122: "smakynet", + 123: "ntp", + 124: "ansatrader", + 125: "locus-map", + 126: "nxedit", + 127: "locus-con", + 128: "gss-xlicen", + 129: "pwdgen", + 130: "cisco-fna", + 131: "cisco-tna", + 132: "cisco-sys", + 133: "statsrv", + 134: "ingres-net", + 135: "epmap", + 136: "profile", + 137: "netbios-ns", + 138: "netbios-dgm", + 139: "netbios-ssn", + 140: "emfis-data", + 141: "emfis-cntl", + 142: "bl-idm", + 143: "imap", + 144: "uma", + 145: "uaac", + 146: "iso-tp0", + 147: "iso-ip", + 148: "jargon", + 149: "aed-512", + 150: "sql-net", + 151: "hems", + 152: "bftp", + 153: "sgmp", + 154: "netsc-prod", + 155: "netsc-dev", + 156: "sqlsrv", + 157: "knet-cmp", + 158: "pcmail-srv", + 159: "nss-routing", + 160: "sgmp-traps", + 161: "snmp", + 162: "snmptrap", + 163: "cmip-man", + 164: "cmip-agent", + 165: "xns-courier", + 166: "s-net", + 167: "namp", + 168: "rsvd", + 169: "send", + 170: "print-srv", + 171: "multiplex", + 172: "cl-1", + 173: "xyplex-mux", + 174: "mailq", + 175: "vmnet", + 176: "genrad-mux", + 177: "xdmcp", + 178: "nextstep", + 179: "bgp", + 180: "ris", + 181: "unify", + 182: "audit", + 183: "ocbinder", + 184: "ocserver", + 185: "remote-kis", + 186: "kis", + 187: "aci", + 188: "mumps", + 189: "qft", + 190: "gacp", + 191: "prospero", + 192: "osu-nms", + 193: "srmp", + 194: "irc", + 195: "dn6-nlm-aud", + 196: "dn6-smm-red", + 197: "dls", + 198: "dls-mon", + 199: "smux", + 200: "src", + 201: "at-rtmp", + 202: "at-nbp", + 203: "at-3", + 204: "at-echo", + 205: "at-5", + 206: "at-zis", + 207: "at-7", + 208: "at-8", + 209: "qmtp", + 210: "z39-50", + 211: "914c-g", + 212: "anet", + 213: "ipx", + 214: "vmpwscs", + 215: "softpc", + 216: "CAIlic", + 217: "dbase", + 218: "mpp", + 219: "uarps", + 220: "imap3", + 221: "fln-spx", + 222: "rsh-spx", + 223: "cdc", + 224: "masqdialer", + 242: "direct", + 243: "sur-meas", + 244: "inbusiness", + 245: "link", + 246: "dsp3270", + 247: "subntbcst-tftp", + 248: "bhfhs", + 256: "rap", + 257: "set", + 259: "esro-gen", + 260: "openport", + 261: "nsiiops", + 262: "arcisdms", + 263: "hdap", + 264: "bgmp", + 265: "x-bone-ctl", + 266: "sst", + 267: "td-service", + 268: "td-replica", + 269: "manet", + 270: "gist", + 280: "http-mgmt", + 281: "personal-link", + 282: "cableport-ax", + 283: "rescap", + 284: "corerjd", + 286: "fxp", + 287: "k-block", + 308: "novastorbakcup", + 309: "entrusttime", + 310: "bhmds", + 311: "asip-webadmin", + 312: "vslmp", + 313: "magenta-logic", + 314: "opalis-robot", + 315: "dpsi", + 316: "decauth", + 317: "zannet", + 318: "pkix-timestamp", + 319: "ptp-event", + 320: "ptp-general", + 321: "pip", + 322: "rtsps", + 333: "texar", + 344: "pdap", + 345: "pawserv", + 346: "zserv", + 347: "fatserv", + 348: "csi-sgwp", + 349: "mftp", + 350: "matip-type-a", + 351: "matip-type-b", + 352: "dtag-ste-sb", + 353: "ndsauth", + 354: "bh611", + 355: "datex-asn", + 356: "cloanto-net-1", + 357: "bhevent", + 358: "shrinkwrap", + 359: "nsrmp", + 360: "scoi2odialog", + 361: "semantix", + 362: "srssend", + 363: "rsvp-tunnel", + 364: "aurora-cmgr", + 365: "dtk", + 366: "odmr", + 367: "mortgageware", + 368: "qbikgdp", + 369: "rpc2portmap", + 370: "codaauth2", + 371: "clearcase", + 372: "ulistproc", + 373: "legent-1", + 374: "legent-2", + 375: "hassle", + 376: "nip", + 377: "tnETOS", + 378: "dsETOS", + 379: "is99c", + 380: "is99s", + 381: "hp-collector", + 382: "hp-managed-node", + 383: "hp-alarm-mgr", + 384: "arns", + 385: "ibm-app", + 386: "asa", + 387: "aurp", + 388: "unidata-ldm", + 389: "ldap", + 390: "uis", + 391: "synotics-relay", + 392: "synotics-broker", + 393: "meta5", + 394: "embl-ndt", + 395: "netcp", + 396: "netware-ip", + 397: "mptn", + 398: "kryptolan", + 399: "iso-tsap-c2", + 400: "osb-sd", + 401: "ups", + 402: "genie", + 403: "decap", + 404: "nced", + 405: "ncld", + 406: "imsp", + 407: "timbuktu", + 408: "prm-sm", + 409: "prm-nm", + 410: "decladebug", + 411: "rmt", + 412: "synoptics-trap", + 413: "smsp", + 414: "infoseek", + 415: "bnet", + 416: "silverplatter", + 417: "onmux", + 418: "hyper-g", + 419: "ariel1", + 420: "smpte", + 421: "ariel2", + 422: "ariel3", + 423: "opc-job-start", + 424: "opc-job-track", + 425: "icad-el", + 426: "smartsdp", + 427: "svrloc", + 428: "ocs-cmu", + 429: "ocs-amu", + 430: "utmpsd", + 431: "utmpcd", + 432: "iasd", + 433: "nnsp", + 434: "mobileip-agent", + 435: "mobilip-mn", + 436: "dna-cml", + 437: "comscm", + 438: "dsfgw", + 439: "dasp", + 440: "sgcp", + 441: "decvms-sysmgt", + 442: "cvc-hostd", + 443: "https", + 444: "snpp", + 445: "microsoft-ds", + 446: "ddm-rdb", + 447: "ddm-dfm", + 448: "ddm-ssl", + 449: "as-servermap", + 450: "tserver", + 451: "sfs-smp-net", + 452: "sfs-config", + 453: "creativeserver", + 454: "contentserver", + 455: "creativepartnr", + 456: "macon-udp", + 457: "scohelp", + 458: "appleqtc", + 459: "ampr-rcmd", + 460: "skronk", + 461: "datasurfsrv", + 462: "datasurfsrvsec", + 463: "alpes", + 464: "kpasswd", + 465: "igmpv3lite", + 466: "digital-vrc", + 467: "mylex-mapd", + 468: "photuris", + 469: "rcp", + 470: "scx-proxy", + 471: "mondex", + 472: "ljk-login", + 473: "hybrid-pop", + 474: "tn-tl-w2", + 475: "tcpnethaspsrv", + 476: "tn-tl-fd1", + 477: "ss7ns", + 478: "spsc", + 479: "iafserver", + 480: "iafdbase", + 481: "ph", + 482: "bgs-nsi", + 483: "ulpnet", + 484: "integra-sme", + 485: "powerburst", + 486: "avian", + 487: "saft", + 488: "gss-http", + 489: "nest-protocol", + 490: "micom-pfs", + 491: "go-login", + 492: "ticf-1", + 493: "ticf-2", + 494: "pov-ray", + 495: "intecourier", + 496: "pim-rp-disc", + 497: "retrospect", + 498: "siam", + 499: "iso-ill", + 500: "isakmp", + 501: "stmf", + 502: "mbap", + 503: "intrinsa", + 504: "citadel", + 505: "mailbox-lm", + 506: "ohimsrv", + 507: "crs", + 508: "xvttp", + 509: "snare", + 510: "fcp", + 511: "passgo", + 512: "comsat", + 513: "who", + 514: "syslog", + 515: "printer", + 516: "videotex", + 517: "talk", + 518: "ntalk", + 519: "utime", + 520: "router", + 521: "ripng", + 522: "ulp", + 523: "ibm-db2", + 524: "ncp", + 525: "timed", + 526: "tempo", + 527: "stx", + 528: "custix", + 529: "irc-serv", + 530: "courier", + 531: "conference", + 532: "netnews", + 533: "netwall", + 534: "windream", + 535: "iiop", + 536: "opalis-rdv", + 537: "nmsp", + 538: "gdomap", + 539: "apertus-ldp", + 540: "uucp", + 541: "uucp-rlogin", + 542: "commerce", + 543: "klogin", + 544: "kshell", + 545: "appleqtcsrvr", + 546: "dhcpv6-client", + 547: "dhcpv6-server", + 548: "afpovertcp", + 549: "idfp", + 550: "new-rwho", + 551: "cybercash", + 552: "devshr-nts", + 553: "pirp", + 554: "rtsp", + 555: "dsf", + 556: "remotefs", + 557: "openvms-sysipc", + 558: "sdnskmp", + 559: "teedtap", + 560: "rmonitor", + 561: "monitor", + 562: "chshell", + 563: "nntps", + 564: "9pfs", + 565: "whoami", + 566: "streettalk", + 567: "banyan-rpc", + 568: "ms-shuttle", + 569: "ms-rome", + 570: "meter", + 571: "meter", + 572: "sonar", + 573: "banyan-vip", + 574: "ftp-agent", + 575: "vemmi", + 576: "ipcd", + 577: "vnas", + 578: "ipdd", + 579: "decbsrv", + 580: "sntp-heartbeat", + 581: "bdp", + 582: "scc-security", + 583: "philips-vc", + 584: "keyserver", + 586: "password-chg", + 587: "submission", + 588: "cal", + 589: "eyelink", + 590: "tns-cml", + 591: "http-alt", + 592: "eudora-set", + 593: "http-rpc-epmap", + 594: "tpip", + 595: "cab-protocol", + 596: "smsd", + 597: "ptcnameservice", + 598: "sco-websrvrmg3", + 599: "acp", + 600: "ipcserver", + 601: "syslog-conn", + 602: "xmlrpc-beep", + 603: "idxp", + 604: "tunnel", + 605: "soap-beep", + 606: "urm", + 607: "nqs", + 608: "sift-uft", + 609: "npmp-trap", + 610: "npmp-local", + 611: "npmp-gui", + 612: "hmmp-ind", + 613: "hmmp-op", + 614: "sshell", + 615: "sco-inetmgr", + 616: "sco-sysmgr", + 617: "sco-dtmgr", + 618: "dei-icda", + 619: "compaq-evm", + 620: "sco-websrvrmgr", + 621: "escp-ip", + 622: "collaborator", + 623: "asf-rmcp", + 624: "cryptoadmin", + 625: "dec-dlm", + 626: "asia", + 627: "passgo-tivoli", + 628: "qmqp", + 629: "3com-amp3", + 630: "rda", + 631: "ipp", + 632: "bmpp", + 633: "servstat", + 634: "ginad", + 635: "rlzdbase", + 636: "ldaps", + 637: "lanserver", + 638: "mcns-sec", + 639: "msdp", + 640: "entrust-sps", + 641: "repcmd", + 642: "esro-emsdp", + 643: "sanity", + 644: "dwr", + 645: "pssc", + 646: "ldp", + 647: "dhcp-failover", + 648: "rrp", + 649: "cadview-3d", + 650: "obex", + 651: "ieee-mms", + 652: "hello-port", + 653: "repscmd", + 654: "aodv", + 655: "tinc", + 656: "spmp", + 657: "rmc", + 658: "tenfold", + 660: "mac-srvr-admin", + 661: "hap", + 662: "pftp", + 663: "purenoise", + 664: "asf-secure-rmcp", + 665: "sun-dr", + 666: "mdqs", + 667: "disclose", + 668: "mecomm", + 669: "meregister", + 670: "vacdsm-sws", + 671: "vacdsm-app", + 672: "vpps-qua", + 673: "cimplex", + 674: "acap", + 675: "dctp", + 676: "vpps-via", + 677: "vpp", + 678: "ggf-ncp", + 679: "mrm", + 680: "entrust-aaas", + 681: "entrust-aams", + 682: "xfr", + 683: "corba-iiop", + 684: "corba-iiop-ssl", + 685: "mdc-portmapper", + 686: "hcp-wismar", + 687: "asipregistry", + 688: "realm-rusd", + 689: "nmap", + 690: "vatp", + 691: "msexch-routing", + 692: "hyperwave-isp", + 693: "connendp", + 694: "ha-cluster", + 695: "ieee-mms-ssl", + 696: "rushd", + 697: "uuidgen", + 698: "olsr", + 699: "accessnetwork", + 700: "epp", + 701: "lmp", + 702: "iris-beep", + 704: "elcsd", + 705: "agentx", + 706: "silc", + 707: "borland-dsj", + 709: "entrust-kmsh", + 710: "entrust-ash", + 711: "cisco-tdp", + 712: "tbrpf", + 713: "iris-xpc", + 714: "iris-xpcs", + 715: "iris-lwz", + 716: "pana", + 729: "netviewdm1", + 730: "netviewdm2", + 731: "netviewdm3", + 741: "netgw", + 742: "netrcs", + 744: "flexlm", + 747: "fujitsu-dev", + 748: "ris-cm", + 749: "kerberos-adm", + 750: "loadav", + 751: "pump", + 752: "qrh", + 753: "rrh", + 754: "tell", + 758: "nlogin", + 759: "con", + 760: "ns", + 761: "rxe", + 762: "quotad", + 763: "cycleserv", + 764: "omserv", + 765: "webster", + 767: "phonebook", + 769: "vid", + 770: "cadlock", + 771: "rtip", + 772: "cycleserv2", + 773: "notify", + 774: "acmaint-dbd", + 775: "acmaint-transd", + 776: "wpages", + 777: "multiling-http", + 780: "wpgs", + 800: "mdbs-daemon", + 801: "device", + 802: "mbap-s", + 810: "fcp-udp", + 828: "itm-mcell-s", + 829: "pkix-3-ca-ra", + 830: "netconf-ssh", + 831: "netconf-beep", + 832: "netconfsoaphttp", + 833: "netconfsoapbeep", + 847: "dhcp-failover2", + 848: "gdoi", + 853: "domain-s", + 854: "dlep", + 860: "iscsi", + 861: "owamp-control", + 862: "twamp-control", + 873: "rsync", + 886: "iclcnet-locate", + 887: "iclcnet-svinfo", + 888: "accessbuilder", + 900: "omginitialrefs", + 901: "smpnameres", + 902: "ideafarm-door", + 903: "ideafarm-panic", + 910: "kink", + 911: "xact-backup", + 912: "apex-mesh", + 913: "apex-edge", + 989: "ftps-data", + 990: "ftps", + 991: "nas", + 992: "telnets", + 993: "imaps", + 995: "pop3s", + 996: "vsinet", + 997: "maitrd", + 998: "puparp", + 999: "applix", + 1000: "cadlock2", + 1010: "surf", + 1021: "exp1", + 1022: "exp2", + 1025: "blackjack", + 1026: "cap", + 1027: "6a44", + 1029: "solid-mux", + 1033: "netinfo-local", + 1034: "activesync", + 1035: "mxxrlogin", + 1036: "nsstp", + 1037: "ams", + 1038: "mtqp", + 1039: "sbl", + 1040: "netarx", + 1041: "danf-ak2", + 1042: "afrog", + 1043: "boinc-client", + 1044: "dcutility", + 1045: "fpitp", + 1046: "wfremotertm", + 1047: "neod1", + 1048: "neod2", + 1049: "td-postman", + 1050: "cma", + 1051: "optima-vnet", + 1052: "ddt", + 1053: "remote-as", + 1054: "brvread", + 1055: "ansyslmd", + 1056: "vfo", + 1057: "startron", + 1058: "nim", + 1059: "nimreg", + 1060: "polestar", + 1061: "kiosk", + 1062: "veracity", + 1063: "kyoceranetdev", + 1064: "jstel", + 1065: "syscomlan", + 1066: "fpo-fns", + 1067: "instl-boots", + 1068: "instl-bootc", + 1069: "cognex-insight", + 1070: "gmrupdateserv", + 1071: "bsquare-voip", + 1072: "cardax", + 1073: "bridgecontrol", + 1074: "warmspotMgmt", + 1075: "rdrmshc", + 1076: "dab-sti-c", + 1077: "imgames", + 1078: "avocent-proxy", + 1079: "asprovatalk", + 1080: "socks", + 1081: "pvuniwien", + 1082: "amt-esd-prot", + 1083: "ansoft-lm-1", + 1084: "ansoft-lm-2", + 1085: "webobjects", + 1086: "cplscrambler-lg", + 1087: "cplscrambler-in", + 1088: "cplscrambler-al", + 1089: "ff-annunc", + 1090: "ff-fms", + 1091: "ff-sm", + 1092: "obrpd", + 1093: "proofd", + 1094: "rootd", + 1095: "nicelink", + 1096: "cnrprotocol", + 1097: "sunclustermgr", + 1098: "rmiactivation", + 1099: "rmiregistry", + 1100: "mctp", + 1101: "pt2-discover", + 1102: "adobeserver-1", + 1103: "adobeserver-2", + 1104: "xrl", + 1105: "ftranhc", + 1106: "isoipsigport-1", + 1107: "isoipsigport-2", + 1108: "ratio-adp", + 1110: "nfsd-keepalive", + 1111: "lmsocialserver", + 1112: "icp", + 1113: "ltp-deepspace", + 1114: "mini-sql", + 1115: "ardus-trns", + 1116: "ardus-cntl", + 1117: "ardus-mtrns", + 1118: "sacred", + 1119: "bnetgame", + 1120: "bnetfile", + 1121: "rmpp", + 1122: "availant-mgr", + 1123: "murray", + 1124: "hpvmmcontrol", + 1125: "hpvmmagent", + 1126: "hpvmmdata", + 1127: "kwdb-commn", + 1128: "saphostctrl", + 1129: "saphostctrls", + 1130: "casp", + 1131: "caspssl", + 1132: "kvm-via-ip", + 1133: "dfn", + 1134: "aplx", + 1135: "omnivision", + 1136: "hhb-gateway", + 1137: "trim", + 1138: "encrypted-admin", + 1139: "evm", + 1140: "autonoc", + 1141: "mxomss", + 1142: "edtools", + 1143: "imyx", + 1144: "fuscript", + 1145: "x9-icue", + 1146: "audit-transfer", + 1147: "capioverlan", + 1148: "elfiq-repl", + 1149: "bvtsonar", + 1150: "blaze", + 1151: "unizensus", + 1152: "winpoplanmess", + 1153: "c1222-acse", + 1154: "resacommunity", + 1155: "nfa", + 1156: "iascontrol-oms", + 1157: "iascontrol", + 1158: "dbcontrol-oms", + 1159: "oracle-oms", + 1160: "olsv", + 1161: "health-polling", + 1162: "health-trap", + 1163: "sddp", + 1164: "qsm-proxy", + 1165: "qsm-gui", + 1166: "qsm-remote", + 1167: "cisco-ipsla", + 1168: "vchat", + 1169: "tripwire", + 1170: "atc-lm", + 1171: "atc-appserver", + 1172: "dnap", + 1173: "d-cinema-rrp", + 1174: "fnet-remote-ui", + 1175: "dossier", + 1176: "indigo-server", + 1177: "dkmessenger", + 1178: "sgi-storman", + 1179: "b2n", + 1180: "mc-client", + 1181: "3comnetman", + 1182: "accelenet-data", + 1183: "llsurfup-http", + 1184: "llsurfup-https", + 1185: "catchpole", + 1186: "mysql-cluster", + 1187: "alias", + 1188: "hp-webadmin", + 1189: "unet", + 1190: "commlinx-avl", + 1191: "gpfs", + 1192: "caids-sensor", + 1193: "fiveacross", + 1194: "openvpn", + 1195: "rsf-1", + 1196: "netmagic", + 1197: "carrius-rshell", + 1198: "cajo-discovery", + 1199: "dmidi", + 1200: "scol", + 1201: "nucleus-sand", + 1202: "caiccipc", + 1203: "ssslic-mgr", + 1204: "ssslog-mgr", + 1205: "accord-mgc", + 1206: "anthony-data", + 1207: "metasage", + 1208: "seagull-ais", + 1209: "ipcd3", + 1210: "eoss", + 1211: "groove-dpp", + 1212: "lupa", + 1213: "mpc-lifenet", + 1214: "kazaa", + 1215: "scanstat-1", + 1216: "etebac5", + 1217: "hpss-ndapi", + 1218: "aeroflight-ads", + 1219: "aeroflight-ret", + 1220: "qt-serveradmin", + 1221: "sweetware-apps", + 1222: "nerv", + 1223: "tgp", + 1224: "vpnz", + 1225: "slinkysearch", + 1226: "stgxfws", + 1227: "dns2go", + 1228: "florence", + 1229: "zented", + 1230: "periscope", + 1231: "menandmice-lpm", + 1232: "first-defense", + 1233: "univ-appserver", + 1234: "search-agent", + 1235: "mosaicsyssvc1", + 1236: "bvcontrol", + 1237: "tsdos390", + 1238: "hacl-qs", + 1239: "nmsd", + 1240: "instantia", + 1241: "nessus", + 1242: "nmasoverip", + 1243: "serialgateway", + 1244: "isbconference1", + 1245: "isbconference2", + 1246: "payrouter", + 1247: "visionpyramid", + 1248: "hermes", + 1249: "mesavistaco", + 1250: "swldy-sias", + 1251: "servergraph", + 1252: "bspne-pcc", + 1253: "q55-pcc", + 1254: "de-noc", + 1255: "de-cache-query", + 1256: "de-server", + 1257: "shockwave2", + 1258: "opennl", + 1259: "opennl-voice", + 1260: "ibm-ssd", + 1261: "mpshrsv", + 1262: "qnts-orb", + 1263: "dka", + 1264: "prat", + 1265: "dssiapi", + 1266: "dellpwrappks", + 1267: "epc", + 1268: "propel-msgsys", + 1269: "watilapp", + 1270: "opsmgr", + 1271: "excw", + 1272: "cspmlockmgr", + 1273: "emc-gateway", + 1274: "t1distproc", + 1275: "ivcollector", + 1277: "miva-mqs", + 1278: "dellwebadmin-1", + 1279: "dellwebadmin-2", + 1280: "pictrography", + 1281: "healthd", + 1282: "emperion", + 1283: "productinfo", + 1284: "iee-qfx", + 1285: "neoiface", + 1286: "netuitive", + 1287: "routematch", + 1288: "navbuddy", + 1289: "jwalkserver", + 1290: "winjaserver", + 1291: "seagulllms", + 1292: "dsdn", + 1293: "pkt-krb-ipsec", + 1294: "cmmdriver", + 1295: "ehtp", + 1296: "dproxy", + 1297: "sdproxy", + 1298: "lpcp", + 1299: "hp-sci", + 1300: "h323hostcallsc", + 1301: "ci3-software-1", + 1302: "ci3-software-2", + 1303: "sftsrv", + 1304: "boomerang", + 1305: "pe-mike", + 1306: "re-conn-proto", + 1307: "pacmand", + 1308: "odsi", + 1309: "jtag-server", + 1310: "husky", + 1311: "rxmon", + 1312: "sti-envision", + 1313: "bmc-patroldb", + 1314: "pdps", + 1315: "els", + 1316: "exbit-escp", + 1317: "vrts-ipcserver", + 1318: "krb5gatekeeper", + 1319: "amx-icsp", + 1320: "amx-axbnet", + 1321: "pip", + 1322: "novation", + 1323: "brcd", + 1324: "delta-mcp", + 1325: "dx-instrument", + 1326: "wimsic", + 1327: "ultrex", + 1328: "ewall", + 1329: "netdb-export", + 1330: "streetperfect", + 1331: "intersan", + 1332: "pcia-rxp-b", + 1333: "passwrd-policy", + 1334: "writesrv", + 1335: "digital-notary", + 1336: "ischat", + 1337: "menandmice-dns", + 1338: "wmc-log-svc", + 1339: "kjtsiteserver", + 1340: "naap", + 1341: "qubes", + 1342: "esbroker", + 1343: "re101", + 1344: "icap", + 1345: "vpjp", + 1346: "alta-ana-lm", + 1347: "bbn-mmc", + 1348: "bbn-mmx", + 1349: "sbook", + 1350: "editbench", + 1351: "equationbuilder", + 1352: "lotusnote", + 1353: "relief", + 1354: "XSIP-network", + 1355: "intuitive-edge", + 1356: "cuillamartin", + 1357: "pegboard", + 1358: "connlcli", + 1359: "ftsrv", + 1360: "mimer", + 1361: "linx", + 1362: "timeflies", + 1363: "ndm-requester", + 1364: "ndm-server", + 1365: "adapt-sna", + 1366: "netware-csp", + 1367: "dcs", + 1368: "screencast", + 1369: "gv-us", + 1370: "us-gv", + 1371: "fc-cli", + 1372: "fc-ser", + 1373: "chromagrafx", + 1374: "molly", + 1375: "bytex", + 1376: "ibm-pps", + 1377: "cichlid", + 1378: "elan", + 1379: "dbreporter", + 1380: "telesis-licman", + 1381: "apple-licman", + 1382: "udt-os", + 1383: "gwha", + 1384: "os-licman", + 1385: "atex-elmd", + 1386: "checksum", + 1387: "cadsi-lm", + 1388: "objective-dbc", + 1389: "iclpv-dm", + 1390: "iclpv-sc", + 1391: "iclpv-sas", + 1392: "iclpv-pm", + 1393: "iclpv-nls", + 1394: "iclpv-nlc", + 1395: "iclpv-wsm", + 1396: "dvl-activemail", + 1397: "audio-activmail", + 1398: "video-activmail", + 1399: "cadkey-licman", + 1400: "cadkey-tablet", + 1401: "goldleaf-licman", + 1402: "prm-sm-np", + 1403: "prm-nm-np", + 1404: "igi-lm", + 1405: "ibm-res", + 1406: "netlabs-lm", + 1408: "sophia-lm", + 1409: "here-lm", + 1410: "hiq", + 1411: "af", + 1412: "innosys", + 1413: "innosys-acl", + 1414: "ibm-mqseries", + 1415: "dbstar", + 1416: "novell-lu6-2", + 1417: "timbuktu-srv1", + 1418: "timbuktu-srv2", + 1419: "timbuktu-srv3", + 1420: "timbuktu-srv4", + 1421: "gandalf-lm", + 1422: "autodesk-lm", + 1423: "essbase", + 1424: "hybrid", + 1425: "zion-lm", + 1426: "sais", + 1427: "mloadd", + 1428: "informatik-lm", + 1429: "nms", + 1430: "tpdu", + 1431: "rgtp", + 1432: "blueberry-lm", + 1433: "ms-sql-s", + 1434: "ms-sql-m", + 1435: "ibm-cics", + 1436: "saism", + 1437: "tabula", + 1438: "eicon-server", + 1439: "eicon-x25", + 1440: "eicon-slp", + 1441: "cadis-1", + 1442: "cadis-2", + 1443: "ies-lm", + 1444: "marcam-lm", + 1445: "proxima-lm", + 1446: "ora-lm", + 1447: "apri-lm", + 1448: "oc-lm", + 1449: "peport", + 1450: "dwf", + 1451: "infoman", + 1452: "gtegsc-lm", + 1453: "genie-lm", + 1454: "interhdl-elmd", + 1455: "esl-lm", + 1456: "dca", + 1457: "valisys-lm", + 1458: "nrcabq-lm", + 1459: "proshare1", + 1460: "proshare2", + 1461: "ibm-wrless-lan", + 1462: "world-lm", + 1463: "nucleus", + 1464: "msl-lmd", + 1465: "pipes", + 1466: "oceansoft-lm", + 1467: "csdmbase", + 1468: "csdm", + 1469: "aal-lm", + 1470: "uaiact", + 1471: "csdmbase", + 1472: "csdm", + 1473: "openmath", + 1474: "telefinder", + 1475: "taligent-lm", + 1476: "clvm-cfg", + 1477: "ms-sna-server", + 1478: "ms-sna-base", + 1479: "dberegister", + 1480: "pacerforum", + 1481: "airs", + 1482: "miteksys-lm", + 1483: "afs", + 1484: "confluent", + 1485: "lansource", + 1486: "nms-topo-serv", + 1487: "localinfosrvr", + 1488: "docstor", + 1489: "dmdocbroker", + 1490: "insitu-conf", + 1492: "stone-design-1", + 1493: "netmap-lm", + 1494: "ica", + 1495: "cvc", + 1496: "liberty-lm", + 1497: "rfx-lm", + 1498: "sybase-sqlany", + 1499: "fhc", + 1500: "vlsi-lm", + 1501: "saiscm", + 1502: "shivadiscovery", + 1503: "imtc-mcs", + 1504: "evb-elm", + 1505: "funkproxy", + 1506: "utcd", + 1507: "symplex", + 1508: "diagmond", + 1509: "robcad-lm", + 1510: "mvx-lm", + 1511: "3l-l1", + 1512: "wins", + 1513: "fujitsu-dtc", + 1514: "fujitsu-dtcns", + 1515: "ifor-protocol", + 1516: "vpad", + 1517: "vpac", + 1518: "vpvd", + 1519: "vpvc", + 1520: "atm-zip-office", + 1521: "ncube-lm", + 1522: "ricardo-lm", + 1523: "cichild-lm", + 1524: "ingreslock", + 1525: "orasrv", + 1526: "pdap-np", + 1527: "tlisrv", + 1528: "ngr-t", + 1529: "coauthor", + 1530: "rap-service", + 1531: "rap-listen", + 1532: "miroconnect", + 1533: "virtual-places", + 1534: "micromuse-lm", + 1535: "ampr-info", + 1536: "ampr-inter", + 1537: "sdsc-lm", + 1538: "3ds-lm", + 1539: "intellistor-lm", + 1540: "rds", + 1541: "rds2", + 1542: "gridgen-elmd", + 1543: "simba-cs", + 1544: "aspeclmd", + 1545: "vistium-share", + 1546: "abbaccuray", + 1547: "laplink", + 1548: "axon-lm", + 1549: "shivasound", + 1550: "3m-image-lm", + 1551: "hecmtl-db", + 1552: "pciarray", + 1553: "sna-cs", + 1554: "caci-lm", + 1555: "livelan", + 1556: "veritas-pbx", + 1557: "arbortext-lm", + 1558: "xingmpeg", + 1559: "web2host", + 1560: "asci-val", + 1561: "facilityview", + 1562: "pconnectmgr", + 1563: "cadabra-lm", + 1564: "pay-per-view", + 1565: "winddlb", + 1566: "corelvideo", + 1567: "jlicelmd", + 1568: "tsspmap", + 1569: "ets", + 1570: "orbixd", + 1571: "rdb-dbs-disp", + 1572: "chip-lm", + 1573: "itscomm-ns", + 1574: "mvel-lm", + 1575: "oraclenames", + 1576: "moldflow-lm", + 1577: "hypercube-lm", + 1578: "jacobus-lm", + 1579: "ioc-sea-lm", + 1580: "tn-tl-r2", + 1581: "mil-2045-47001", + 1582: "msims", + 1583: "simbaexpress", + 1584: "tn-tl-fd2", + 1585: "intv", + 1586: "ibm-abtact", + 1587: "pra-elmd", + 1588: "triquest-lm", + 1589: "vqp", + 1590: "gemini-lm", + 1591: "ncpm-pm", + 1592: "commonspace", + 1593: "mainsoft-lm", + 1594: "sixtrak", + 1595: "radio", + 1596: "radio-bc", + 1597: "orbplus-iiop", + 1598: "picknfs", + 1599: "simbaservices", + 1600: "issd", + 1601: "aas", + 1602: "inspect", + 1603: "picodbc", + 1604: "icabrowser", + 1605: "slp", + 1606: "slm-api", + 1607: "stt", + 1608: "smart-lm", + 1609: "isysg-lm", + 1610: "taurus-wh", + 1611: "ill", + 1612: "netbill-trans", + 1613: "netbill-keyrep", + 1614: "netbill-cred", + 1615: "netbill-auth", + 1616: "netbill-prod", + 1617: "nimrod-agent", + 1618: "skytelnet", + 1619: "xs-openstorage", + 1620: "faxportwinport", + 1621: "softdataphone", + 1622: "ontime", + 1623: "jaleosnd", + 1624: "udp-sr-port", + 1625: "svs-omagent", + 1626: "shockwave", + 1627: "t128-gateway", + 1628: "lontalk-norm", + 1629: "lontalk-urgnt", + 1630: "oraclenet8cman", + 1631: "visitview", + 1632: "pammratc", + 1633: "pammrpc", + 1634: "loaprobe", + 1635: "edb-server1", + 1636: "isdc", + 1637: "islc", + 1638: "ismc", + 1639: "cert-initiator", + 1640: "cert-responder", + 1641: "invision", + 1642: "isis-am", + 1643: "isis-ambc", + 1644: "saiseh", + 1645: "sightline", + 1646: "sa-msg-port", + 1647: "rsap", + 1648: "concurrent-lm", + 1649: "kermit", + 1650: "nkd", + 1651: "shiva-confsrvr", + 1652: "xnmp", + 1653: "alphatech-lm", + 1654: "stargatealerts", + 1655: "dec-mbadmin", + 1656: "dec-mbadmin-h", + 1657: "fujitsu-mmpdc", + 1658: "sixnetudr", + 1659: "sg-lm", + 1660: "skip-mc-gikreq", + 1661: "netview-aix-1", + 1662: "netview-aix-2", + 1663: "netview-aix-3", + 1664: "netview-aix-4", + 1665: "netview-aix-5", + 1666: "netview-aix-6", + 1667: "netview-aix-7", + 1668: "netview-aix-8", + 1669: "netview-aix-9", + 1670: "netview-aix-10", + 1671: "netview-aix-11", + 1672: "netview-aix-12", + 1673: "proshare-mc-1", + 1674: "proshare-mc-2", + 1675: "pdp", + 1676: "netcomm2", + 1677: "groupwise", + 1678: "prolink", + 1679: "darcorp-lm", + 1680: "microcom-sbp", + 1681: "sd-elmd", + 1682: "lanyon-lantern", + 1683: "ncpm-hip", + 1684: "snaresecure", + 1685: "n2nremote", + 1686: "cvmon", + 1687: "nsjtp-ctrl", + 1688: "nsjtp-data", + 1689: "firefox", + 1690: "ng-umds", + 1691: "empire-empuma", + 1692: "sstsys-lm", + 1693: "rrirtr", + 1694: "rrimwm", + 1695: "rrilwm", + 1696: "rrifmm", + 1697: "rrisat", + 1698: "rsvp-encap-1", + 1699: "rsvp-encap-2", + 1700: "mps-raft", + 1701: "l2f", + 1702: "deskshare", + 1703: "hb-engine", + 1704: "bcs-broker", + 1705: "slingshot", + 1706: "jetform", + 1707: "vdmplay", + 1708: "gat-lmd", + 1709: "centra", + 1710: "impera", + 1711: "pptconference", + 1712: "registrar", + 1713: "conferencetalk", + 1714: "sesi-lm", + 1715: "houdini-lm", + 1716: "xmsg", + 1717: "fj-hdnet", + 1718: "h323gatedisc", + 1719: "h323gatestat", + 1720: "h323hostcall", + 1721: "caicci", + 1722: "hks-lm", + 1723: "pptp", + 1724: "csbphonemaster", + 1725: "iden-ralp", + 1726: "iberiagames", + 1727: "winddx", + 1728: "telindus", + 1729: "citynl", + 1730: "roketz", + 1731: "msiccp", + 1732: "proxim", + 1733: "siipat", + 1734: "cambertx-lm", + 1735: "privatechat", + 1736: "street-stream", + 1737: "ultimad", + 1738: "gamegen1", + 1739: "webaccess", + 1740: "encore", + 1741: "cisco-net-mgmt", + 1742: "3Com-nsd", + 1743: "cinegrfx-lm", + 1744: "ncpm-ft", + 1745: "remote-winsock", + 1746: "ftrapid-1", + 1747: "ftrapid-2", + 1748: "oracle-em1", + 1749: "aspen-services", + 1750: "sslp", + 1751: "swiftnet", + 1752: "lofr-lm", + 1754: "oracle-em2", + 1755: "ms-streaming", + 1756: "capfast-lmd", + 1757: "cnhrp", + 1758: "tftp-mcast", + 1759: "spss-lm", + 1760: "www-ldap-gw", + 1761: "cft-0", + 1762: "cft-1", + 1763: "cft-2", + 1764: "cft-3", + 1765: "cft-4", + 1766: "cft-5", + 1767: "cft-6", + 1768: "cft-7", + 1769: "bmc-net-adm", + 1770: "bmc-net-svc", + 1771: "vaultbase", + 1772: "essweb-gw", + 1773: "kmscontrol", + 1774: "global-dtserv", + 1776: "femis", + 1777: "powerguardian", + 1778: "prodigy-intrnet", + 1779: "pharmasoft", + 1780: "dpkeyserv", + 1781: "answersoft-lm", + 1782: "hp-hcip", + 1784: "finle-lm", + 1785: "windlm", + 1786: "funk-logger", + 1787: "funk-license", + 1788: "psmond", + 1789: "hello", + 1790: "nmsp", + 1791: "ea1", + 1792: "ibm-dt-2", + 1793: "rsc-robot", + 1794: "cera-bcm", + 1795: "dpi-proxy", + 1796: "vocaltec-admin", + 1797: "uma", + 1798: "etp", + 1799: "netrisk", + 1800: "ansys-lm", + 1801: "msmq", + 1802: "concomp1", + 1803: "hp-hcip-gwy", + 1804: "enl", + 1805: "enl-name", + 1806: "musiconline", + 1807: "fhsp", + 1808: "oracle-vp2", + 1809: "oracle-vp1", + 1810: "jerand-lm", + 1811: "scientia-sdb", + 1812: "radius", + 1813: "radius-acct", + 1814: "tdp-suite", + 1815: "mmpft", + 1816: "harp", + 1817: "rkb-oscs", + 1818: "etftp", + 1819: "plato-lm", + 1820: "mcagent", + 1821: "donnyworld", + 1822: "es-elmd", + 1823: "unisys-lm", + 1824: "metrics-pas", + 1825: "direcpc-video", + 1826: "ardt", + 1827: "asi", + 1828: "itm-mcell-u", + 1829: "optika-emedia", + 1830: "net8-cman", + 1831: "myrtle", + 1832: "tht-treasure", + 1833: "udpradio", + 1834: "ardusuni", + 1835: "ardusmul", + 1836: "ste-smsc", + 1837: "csoft1", + 1838: "talnet", + 1839: "netopia-vo1", + 1840: "netopia-vo2", + 1841: "netopia-vo3", + 1842: "netopia-vo4", + 1843: "netopia-vo5", + 1844: "direcpc-dll", + 1845: "altalink", + 1846: "tunstall-pnc", + 1847: "slp-notify", + 1848: "fjdocdist", + 1849: "alpha-sms", + 1850: "gsi", + 1851: "ctcd", + 1852: "virtual-time", + 1853: "vids-avtp", + 1854: "buddy-draw", + 1855: "fiorano-rtrsvc", + 1856: "fiorano-msgsvc", + 1857: "datacaptor", + 1858: "privateark", + 1859: "gammafetchsvr", + 1860: "sunscalar-svc", + 1861: "lecroy-vicp", + 1862: "mysql-cm-agent", + 1863: "msnp", + 1864: "paradym-31port", + 1865: "entp", + 1866: "swrmi", + 1867: "udrive", + 1868: "viziblebrowser", + 1869: "transact", + 1870: "sunscalar-dns", + 1871: "canocentral0", + 1872: "canocentral1", + 1873: "fjmpjps", + 1874: "fjswapsnp", + 1875: "westell-stats", + 1876: "ewcappsrv", + 1877: "hp-webqosdb", + 1878: "drmsmc", + 1879: "nettgain-nms", + 1880: "vsat-control", + 1881: "ibm-mqseries2", + 1882: "ecsqdmn", + 1883: "mqtt", + 1884: "idmaps", + 1885: "vrtstrapserver", + 1886: "leoip", + 1887: "filex-lport", + 1888: "ncconfig", + 1889: "unify-adapter", + 1890: "wilkenlistener", + 1891: "childkey-notif", + 1892: "childkey-ctrl", + 1893: "elad", + 1894: "o2server-port", + 1896: "b-novative-ls", + 1897: "metaagent", + 1898: "cymtec-port", + 1899: "mc2studios", + 1900: "ssdp", + 1901: "fjicl-tep-a", + 1902: "fjicl-tep-b", + 1903: "linkname", + 1904: "fjicl-tep-c", + 1905: "sugp", + 1906: "tpmd", + 1907: "intrastar", + 1908: "dawn", + 1909: "global-wlink", + 1910: "ultrabac", + 1911: "mtp", + 1912: "rhp-iibp", + 1913: "armadp", + 1914: "elm-momentum", + 1915: "facelink", + 1916: "persona", + 1917: "noagent", + 1918: "can-nds", + 1919: "can-dch", + 1920: "can-ferret", + 1921: "noadmin", + 1922: "tapestry", + 1923: "spice", + 1924: "xiip", + 1925: "discovery-port", + 1926: "egs", + 1927: "videte-cipc", + 1928: "emsd-port", + 1929: "bandwiz-system", + 1930: "driveappserver", + 1931: "amdsched", + 1932: "ctt-broker", + 1933: "xmapi", + 1934: "xaapi", + 1935: "macromedia-fcs", + 1936: "jetcmeserver", + 1937: "jwserver", + 1938: "jwclient", + 1939: "jvserver", + 1940: "jvclient", + 1941: "dic-aida", + 1942: "res", + 1943: "beeyond-media", + 1944: "close-combat", + 1945: "dialogic-elmd", + 1946: "tekpls", + 1947: "sentinelsrm", + 1948: "eye2eye", + 1949: "ismaeasdaqlive", + 1950: "ismaeasdaqtest", + 1951: "bcs-lmserver", + 1952: "mpnjsc", + 1953: "rapidbase", + 1954: "abr-api", + 1955: "abr-secure", + 1956: "vrtl-vmf-ds", + 1957: "unix-status", + 1958: "dxadmind", + 1959: "simp-all", + 1960: "nasmanager", + 1961: "bts-appserver", + 1962: "biap-mp", + 1963: "webmachine", + 1964: "solid-e-engine", + 1965: "tivoli-npm", + 1966: "slush", + 1967: "sns-quote", + 1968: "lipsinc", + 1969: "lipsinc1", + 1970: "netop-rc", + 1971: "netop-school", + 1972: "intersys-cache", + 1973: "dlsrap", + 1974: "drp", + 1975: "tcoflashagent", + 1976: "tcoregagent", + 1977: "tcoaddressbook", + 1978: "unisql", + 1979: "unisql-java", + 1980: "pearldoc-xact", + 1981: "p2pq", + 1982: "estamp", + 1983: "lhtp", + 1984: "bb", + 1985: "hsrp", + 1986: "licensedaemon", + 1987: "tr-rsrb-p1", + 1988: "tr-rsrb-p2", + 1989: "tr-rsrb-p3", + 1990: "stun-p1", + 1991: "stun-p2", + 1992: "stun-p3", + 1993: "snmp-tcp-port", + 1994: "stun-port", + 1995: "perf-port", + 1996: "tr-rsrb-port", + 1997: "gdp-port", + 1998: "x25-svc-port", + 1999: "tcp-id-port", + 2000: "cisco-sccp", + 2001: "wizard", + 2002: "globe", + 2003: "brutus", + 2004: "emce", + 2005: "oracle", + 2006: "raid-cd", + 2007: "raid-am", + 2008: "terminaldb", + 2009: "whosockami", + 2010: "pipe-server", + 2011: "servserv", + 2012: "raid-ac", + 2013: "raid-cd", + 2014: "raid-sf", + 2015: "raid-cs", + 2016: "bootserver", + 2017: "bootclient", + 2018: "rellpack", + 2019: "about", + 2020: "xinupageserver", + 2021: "xinuexpansion1", + 2022: "xinuexpansion2", + 2023: "xinuexpansion3", + 2024: "xinuexpansion4", + 2025: "xribs", + 2026: "scrabble", + 2027: "shadowserver", + 2028: "submitserver", + 2029: "hsrpv6", + 2030: "device2", + 2031: "mobrien-chat", + 2032: "blackboard", + 2033: "glogger", + 2034: "scoremgr", + 2035: "imsldoc", + 2036: "e-dpnet", + 2037: "applus", + 2038: "objectmanager", + 2039: "prizma", + 2040: "lam", + 2041: "interbase", + 2042: "isis", + 2043: "isis-bcast", + 2044: "rimsl", + 2045: "cdfunc", + 2046: "sdfunc", + 2047: "dls", + 2048: "dls-monitor", + 2049: "shilp", + 2050: "av-emb-config", + 2051: "epnsdp", + 2052: "clearvisn", + 2053: "lot105-ds-upd", + 2054: "weblogin", + 2055: "iop", + 2056: "omnisky", + 2057: "rich-cp", + 2058: "newwavesearch", + 2059: "bmc-messaging", + 2060: "teleniumdaemon", + 2061: "netmount", + 2062: "icg-swp", + 2063: "icg-bridge", + 2064: "icg-iprelay", + 2065: "dlsrpn", + 2066: "aura", + 2067: "dlswpn", + 2068: "avauthsrvprtcl", + 2069: "event-port", + 2070: "ah-esp-encap", + 2071: "acp-port", + 2072: "msync", + 2073: "gxs-data-port", + 2074: "vrtl-vmf-sa", + 2075: "newlixengine", + 2076: "newlixconfig", + 2077: "tsrmagt", + 2078: "tpcsrvr", + 2079: "idware-router", + 2080: "autodesk-nlm", + 2081: "kme-trap-port", + 2082: "infowave", + 2083: "radsec", + 2084: "sunclustergeo", + 2085: "ada-cip", + 2086: "gnunet", + 2087: "eli", + 2088: "ip-blf", + 2089: "sep", + 2090: "lrp", + 2091: "prp", + 2092: "descent3", + 2093: "nbx-cc", + 2094: "nbx-au", + 2095: "nbx-ser", + 2096: "nbx-dir", + 2097: "jetformpreview", + 2098: "dialog-port", + 2099: "h2250-annex-g", + 2100: "amiganetfs", + 2101: "rtcm-sc104", + 2102: "zephyr-srv", + 2103: "zephyr-clt", + 2104: "zephyr-hm", + 2105: "minipay", + 2106: "mzap", + 2107: "bintec-admin", + 2108: "comcam", + 2109: "ergolight", + 2110: "umsp", + 2111: "dsatp", + 2112: "idonix-metanet", + 2113: "hsl-storm", + 2114: "newheights", + 2115: "kdm", + 2116: "ccowcmr", + 2117: "mentaclient", + 2118: "mentaserver", + 2119: "gsigatekeeper", + 2120: "qencp", + 2121: "scientia-ssdb", + 2122: "caupc-remote", + 2123: "gtp-control", + 2124: "elatelink", + 2125: "lockstep", + 2126: "pktcable-cops", + 2127: "index-pc-wb", + 2128: "net-steward", + 2129: "cs-live", + 2130: "xds", + 2131: "avantageb2b", + 2132: "solera-epmap", + 2133: "zymed-zpp", + 2134: "avenue", + 2135: "gris", + 2136: "appworxsrv", + 2137: "connect", + 2138: "unbind-cluster", + 2139: "ias-auth", + 2140: "ias-reg", + 2141: "ias-admind", + 2142: "tdmoip", + 2143: "lv-jc", + 2144: "lv-ffx", + 2145: "lv-pici", + 2146: "lv-not", + 2147: "lv-auth", + 2148: "veritas-ucl", + 2149: "acptsys", + 2150: "dynamic3d", + 2151: "docent", + 2152: "gtp-user", + 2153: "ctlptc", + 2154: "stdptc", + 2155: "brdptc", + 2156: "trp", + 2157: "xnds", + 2158: "touchnetplus", + 2159: "gdbremote", + 2160: "apc-2160", + 2161: "apc-2161", + 2162: "navisphere", + 2163: "navisphere-sec", + 2164: "ddns-v3", + 2165: "x-bone-api", + 2166: "iwserver", + 2167: "raw-serial", + 2168: "easy-soft-mux", + 2169: "brain", + 2170: "eyetv", + 2171: "msfw-storage", + 2172: "msfw-s-storage", + 2173: "msfw-replica", + 2174: "msfw-array", + 2175: "airsync", + 2176: "rapi", + 2177: "qwave", + 2178: "bitspeer", + 2179: "vmrdp", + 2180: "mc-gt-srv", + 2181: "eforward", + 2182: "cgn-stat", + 2183: "cgn-config", + 2184: "nvd", + 2185: "onbase-dds", + 2186: "gtaua", + 2187: "ssmd", + 2190: "tivoconnect", + 2191: "tvbus", + 2192: "asdis", + 2193: "drwcs", + 2197: "mnp-exchange", + 2198: "onehome-remote", + 2199: "onehome-help", + 2200: "ici", + 2201: "ats", + 2202: "imtc-map", + 2203: "b2-runtime", + 2204: "b2-license", + 2205: "jps", + 2206: "hpocbus", + 2207: "hpssd", + 2208: "hpiod", + 2209: "rimf-ps", + 2210: "noaaport", + 2211: "emwin", + 2212: "leecoposserver", + 2213: "kali", + 2214: "rpi", + 2215: "ipcore", + 2216: "vtu-comms", + 2217: "gotodevice", + 2218: "bounzza", + 2219: "netiq-ncap", + 2220: "netiq", + 2221: "ethernet-ip-s", + 2222: "EtherNet-IP-1", + 2223: "rockwell-csp2", + 2224: "efi-mg", + 2226: "di-drm", + 2227: "di-msg", + 2228: "ehome-ms", + 2229: "datalens", + 2230: "queueadm", + 2231: "wimaxasncp", + 2232: "ivs-video", + 2233: "infocrypt", + 2234: "directplay", + 2235: "sercomm-wlink", + 2236: "nani", + 2237: "optech-port1-lm", + 2238: "aviva-sna", + 2239: "imagequery", + 2240: "recipe", + 2241: "ivsd", + 2242: "foliocorp", + 2243: "magicom", + 2244: "nmsserver", + 2245: "hao", + 2246: "pc-mta-addrmap", + 2247: "antidotemgrsvr", + 2248: "ums", + 2249: "rfmp", + 2250: "remote-collab", + 2251: "dif-port", + 2252: "njenet-ssl", + 2253: "dtv-chan-req", + 2254: "seispoc", + 2255: "vrtp", + 2256: "pcc-mfp", + 2257: "simple-tx-rx", + 2258: "rcts", + 2260: "apc-2260", + 2261: "comotionmaster", + 2262: "comotionback", + 2263: "ecwcfg", + 2264: "apx500api-1", + 2265: "apx500api-2", + 2266: "mfserver", + 2267: "ontobroker", + 2268: "amt", + 2269: "mikey", + 2270: "starschool", + 2271: "mmcals", + 2272: "mmcal", + 2273: "mysql-im", + 2274: "pcttunnell", + 2275: "ibridge-data", + 2276: "ibridge-mgmt", + 2277: "bluectrlproxy", + 2278: "s3db", + 2279: "xmquery", + 2280: "lnvpoller", + 2281: "lnvconsole", + 2282: "lnvalarm", + 2283: "lnvstatus", + 2284: "lnvmaps", + 2285: "lnvmailmon", + 2286: "nas-metering", + 2287: "dna", + 2288: "netml", + 2289: "dict-lookup", + 2290: "sonus-logging", + 2291: "eapsp", + 2292: "mib-streaming", + 2293: "npdbgmngr", + 2294: "konshus-lm", + 2295: "advant-lm", + 2296: "theta-lm", + 2297: "d2k-datamover1", + 2298: "d2k-datamover2", + 2299: "pc-telecommute", + 2300: "cvmmon", + 2301: "cpq-wbem", + 2302: "binderysupport", + 2303: "proxy-gateway", + 2304: "attachmate-uts", + 2305: "mt-scaleserver", + 2306: "tappi-boxnet", + 2307: "pehelp", + 2308: "sdhelp", + 2309: "sdserver", + 2310: "sdclient", + 2311: "messageservice", + 2312: "wanscaler", + 2313: "iapp", + 2314: "cr-websystems", + 2315: "precise-sft", + 2316: "sent-lm", + 2317: "attachmate-g32", + 2318: "cadencecontrol", + 2319: "infolibria", + 2320: "siebel-ns", + 2321: "rdlap", + 2322: "ofsd", + 2323: "3d-nfsd", + 2324: "cosmocall", + 2325: "ansysli", + 2326: "idcp", + 2327: "xingcsm", + 2328: "netrix-sftm", + 2329: "nvd", + 2330: "tscchat", + 2331: "agentview", + 2332: "rcc-host", + 2333: "snapp", + 2334: "ace-client", + 2335: "ace-proxy", + 2336: "appleugcontrol", + 2337: "ideesrv", + 2338: "norton-lambert", + 2339: "3com-webview", + 2340: "wrs-registry", + 2341: "xiostatus", + 2342: "manage-exec", + 2343: "nati-logos", + 2344: "fcmsys", + 2345: "dbm", + 2346: "redstorm-join", + 2347: "redstorm-find", + 2348: "redstorm-info", + 2349: "redstorm-diag", + 2350: "psbserver", + 2351: "psrserver", + 2352: "pslserver", + 2353: "pspserver", + 2354: "psprserver", + 2355: "psdbserver", + 2356: "gxtelmd", + 2357: "unihub-server", + 2358: "futrix", + 2359: "flukeserver", + 2360: "nexstorindltd", + 2361: "tl1", + 2362: "digiman", + 2363: "mediacntrlnfsd", + 2364: "oi-2000", + 2365: "dbref", + 2366: "qip-login", + 2367: "service-ctrl", + 2368: "opentable", + 2370: "l3-hbmon", + 2372: "lanmessenger", + 2381: "compaq-https", + 2382: "ms-olap3", + 2383: "ms-olap4", + 2384: "sd-capacity", + 2385: "sd-data", + 2386: "virtualtape", + 2387: "vsamredirector", + 2388: "mynahautostart", + 2389: "ovsessionmgr", + 2390: "rsmtp", + 2391: "3com-net-mgmt", + 2392: "tacticalauth", + 2393: "ms-olap1", + 2394: "ms-olap2", + 2395: "lan900-remote", + 2396: "wusage", + 2397: "ncl", + 2398: "orbiter", + 2399: "fmpro-fdal", + 2400: "opequus-server", + 2401: "cvspserver", + 2402: "taskmaster2000", + 2403: "taskmaster2000", + 2404: "iec-104", + 2405: "trc-netpoll", + 2406: "jediserver", + 2407: "orion", + 2409: "sns-protocol", + 2410: "vrts-registry", + 2411: "netwave-ap-mgmt", + 2412: "cdn", + 2413: "orion-rmi-reg", + 2414: "beeyond", + 2415: "codima-rtp", + 2416: "rmtserver", + 2417: "composit-server", + 2418: "cas", + 2419: "attachmate-s2s", + 2420: "dslremote-mgmt", + 2421: "g-talk", + 2422: "crmsbits", + 2423: "rnrp", + 2424: "kofax-svr", + 2425: "fjitsuappmgr", + 2426: "vcmp", + 2427: "mgcp-gateway", + 2428: "ott", + 2429: "ft-role", + 2430: "venus", + 2431: "venus-se", + 2432: "codasrv", + 2433: "codasrv-se", + 2434: "pxc-epmap", + 2435: "optilogic", + 2436: "topx", + 2437: "unicontrol", + 2438: "msp", + 2439: "sybasedbsynch", + 2440: "spearway", + 2441: "pvsw-inet", + 2442: "netangel", + 2443: "powerclientcsf", + 2444: "btpp2sectrans", + 2445: "dtn1", + 2446: "bues-service", + 2447: "ovwdb", + 2448: "hpppssvr", + 2449: "ratl", + 2450: "netadmin", + 2451: "netchat", + 2452: "snifferclient", + 2453: "madge-ltd", + 2454: "indx-dds", + 2455: "wago-io-system", + 2456: "altav-remmgt", + 2457: "rapido-ip", + 2458: "griffin", + 2459: "community", + 2460: "ms-theater", + 2461: "qadmifoper", + 2462: "qadmifevent", + 2463: "lsi-raid-mgmt", + 2464: "direcpc-si", + 2465: "lbm", + 2466: "lbf", + 2467: "high-criteria", + 2468: "qip-msgd", + 2469: "mti-tcs-comm", + 2470: "taskman-port", + 2471: "seaodbc", + 2472: "c3", + 2473: "aker-cdp", + 2474: "vitalanalysis", + 2475: "ace-server", + 2476: "ace-svr-prop", + 2477: "ssm-cvs", + 2478: "ssm-cssps", + 2479: "ssm-els", + 2480: "powerexchange", + 2481: "giop", + 2482: "giop-ssl", + 2483: "ttc", + 2484: "ttc-ssl", + 2485: "netobjects1", + 2486: "netobjects2", + 2487: "pns", + 2488: "moy-corp", + 2489: "tsilb", + 2490: "qip-qdhcp", + 2491: "conclave-cpp", + 2492: "groove", + 2493: "talarian-mqs", + 2494: "bmc-ar", + 2495: "fast-rem-serv", + 2496: "dirgis", + 2497: "quaddb", + 2498: "odn-castraq", + 2499: "unicontrol", + 2500: "rtsserv", + 2501: "rtsclient", + 2502: "kentrox-prot", + 2503: "nms-dpnss", + 2504: "wlbs", + 2505: "ppcontrol", + 2506: "jbroker", + 2507: "spock", + 2508: "jdatastore", + 2509: "fjmpss", + 2510: "fjappmgrbulk", + 2511: "metastorm", + 2512: "citrixima", + 2513: "citrixadmin", + 2514: "facsys-ntp", + 2515: "facsys-router", + 2516: "maincontrol", + 2517: "call-sig-trans", + 2518: "willy", + 2519: "globmsgsvc", + 2520: "pvsw", + 2521: "adaptecmgr", + 2522: "windb", + 2523: "qke-llc-v3", + 2524: "optiwave-lm", + 2525: "ms-v-worlds", + 2526: "ema-sent-lm", + 2527: "iqserver", + 2528: "ncr-ccl", + 2529: "utsftp", + 2530: "vrcommerce", + 2531: "ito-e-gui", + 2532: "ovtopmd", + 2533: "snifferserver", + 2534: "combox-web-acc", + 2535: "madcap", + 2536: "btpp2audctr1", + 2537: "upgrade", + 2538: "vnwk-prapi", + 2539: "vsiadmin", + 2540: "lonworks", + 2541: "lonworks2", + 2542: "udrawgraph", + 2543: "reftek", + 2544: "novell-zen", + 2545: "sis-emt", + 2546: "vytalvaultbrtp", + 2547: "vytalvaultvsmp", + 2548: "vytalvaultpipe", + 2549: "ipass", + 2550: "ads", + 2551: "isg-uda-server", + 2552: "call-logging", + 2553: "efidiningport", + 2554: "vcnet-link-v10", + 2555: "compaq-wcp", + 2556: "nicetec-nmsvc", + 2557: "nicetec-mgmt", + 2558: "pclemultimedia", + 2559: "lstp", + 2560: "labrat", + 2561: "mosaixcc", + 2562: "delibo", + 2563: "cti-redwood", + 2564: "hp-3000-telnet", + 2565: "coord-svr", + 2566: "pcs-pcw", + 2567: "clp", + 2568: "spamtrap", + 2569: "sonuscallsig", + 2570: "hs-port", + 2571: "cecsvc", + 2572: "ibp", + 2573: "trustestablish", + 2574: "blockade-bpsp", + 2575: "hl7", + 2576: "tclprodebugger", + 2577: "scipticslsrvr", + 2578: "rvs-isdn-dcp", + 2579: "mpfoncl", + 2580: "tributary", + 2581: "argis-te", + 2582: "argis-ds", + 2583: "mon", + 2584: "cyaserv", + 2585: "netx-server", + 2586: "netx-agent", + 2587: "masc", + 2588: "privilege", + 2589: "quartus-tcl", + 2590: "idotdist", + 2591: "maytagshuffle", + 2592: "netrek", + 2593: "mns-mail", + 2594: "dts", + 2595: "worldfusion1", + 2596: "worldfusion2", + 2597: "homesteadglory", + 2598: "citriximaclient", + 2599: "snapd", + 2600: "hpstgmgr", + 2601: "discp-client", + 2602: "discp-server", + 2603: "servicemeter", + 2604: "nsc-ccs", + 2605: "nsc-posa", + 2606: "netmon", + 2607: "connection", + 2608: "wag-service", + 2609: "system-monitor", + 2610: "versa-tek", + 2611: "lionhead", + 2612: "qpasa-agent", + 2613: "smntubootstrap", + 2614: "neveroffline", + 2615: "firepower", + 2616: "appswitch-emp", + 2617: "cmadmin", + 2618: "priority-e-com", + 2619: "bruce", + 2620: "lpsrecommender", + 2621: "miles-apart", + 2622: "metricadbc", + 2623: "lmdp", + 2624: "aria", + 2625: "blwnkl-port", + 2626: "gbjd816", + 2627: "moshebeeri", + 2628: "dict", + 2629: "sitaraserver", + 2630: "sitaramgmt", + 2631: "sitaradir", + 2632: "irdg-post", + 2633: "interintelli", + 2634: "pk-electronics", + 2635: "backburner", + 2636: "solve", + 2637: "imdocsvc", + 2638: "sybaseanywhere", + 2639: "aminet", + 2640: "ami-control", + 2641: "hdl-srv", + 2642: "tragic", + 2643: "gte-samp", + 2644: "travsoft-ipx-t", + 2645: "novell-ipx-cmd", + 2646: "and-lm", + 2647: "syncserver", + 2648: "upsnotifyprot", + 2649: "vpsipport", + 2650: "eristwoguns", + 2651: "ebinsite", + 2652: "interpathpanel", + 2653: "sonus", + 2654: "corel-vncadmin", + 2655: "unglue", + 2656: "kana", + 2657: "sns-dispatcher", + 2658: "sns-admin", + 2659: "sns-query", + 2660: "gcmonitor", + 2661: "olhost", + 2662: "bintec-capi", + 2663: "bintec-tapi", + 2664: "patrol-mq-gm", + 2665: "patrol-mq-nm", + 2666: "extensis", + 2667: "alarm-clock-s", + 2668: "alarm-clock-c", + 2669: "toad", + 2670: "tve-announce", + 2671: "newlixreg", + 2672: "nhserver", + 2673: "firstcall42", + 2674: "ewnn", + 2675: "ttc-etap", + 2676: "simslink", + 2677: "gadgetgate1way", + 2678: "gadgetgate2way", + 2679: "syncserverssl", + 2680: "pxc-sapxom", + 2681: "mpnjsomb", + 2683: "ncdloadbalance", + 2684: "mpnjsosv", + 2685: "mpnjsocl", + 2686: "mpnjsomg", + 2687: "pq-lic-mgmt", + 2688: "md-cg-http", + 2689: "fastlynx", + 2690: "hp-nnm-data", + 2691: "itinternet", + 2692: "admins-lms", + 2694: "pwrsevent", + 2695: "vspread", + 2696: "unifyadmin", + 2697: "oce-snmp-trap", + 2698: "mck-ivpip", + 2699: "csoft-plusclnt", + 2700: "tqdata", + 2701: "sms-rcinfo", + 2702: "sms-xfer", + 2703: "sms-chat", + 2704: "sms-remctrl", + 2705: "sds-admin", + 2706: "ncdmirroring", + 2707: "emcsymapiport", + 2708: "banyan-net", + 2709: "supermon", + 2710: "sso-service", + 2711: "sso-control", + 2712: "aocp", + 2713: "raventbs", + 2714: "raventdm", + 2715: "hpstgmgr2", + 2716: "inova-ip-disco", + 2717: "pn-requester", + 2718: "pn-requester2", + 2719: "scan-change", + 2720: "wkars", + 2721: "smart-diagnose", + 2722: "proactivesrvr", + 2723: "watchdog-nt", + 2724: "qotps", + 2725: "msolap-ptp2", + 2726: "tams", + 2727: "mgcp-callagent", + 2728: "sqdr", + 2729: "tcim-control", + 2730: "nec-raidplus", + 2731: "fyre-messanger", + 2732: "g5m", + 2733: "signet-ctf", + 2734: "ccs-software", + 2735: "netiq-mc", + 2736: "radwiz-nms-srv", + 2737: "srp-feedback", + 2738: "ndl-tcp-ois-gw", + 2739: "tn-timing", + 2740: "alarm", + 2741: "tsb", + 2742: "tsb2", + 2743: "murx", + 2744: "honyaku", + 2745: "urbisnet", + 2746: "cpudpencap", + 2747: "fjippol-swrly", + 2748: "fjippol-polsvr", + 2749: "fjippol-cnsl", + 2750: "fjippol-port1", + 2751: "fjippol-port2", + 2752: "rsisysaccess", + 2753: "de-spot", + 2754: "apollo-cc", + 2755: "expresspay", + 2756: "simplement-tie", + 2757: "cnrp", + 2758: "apollo-status", + 2759: "apollo-gms", + 2760: "sabams", + 2761: "dicom-iscl", + 2762: "dicom-tls", + 2763: "desktop-dna", + 2764: "data-insurance", + 2765: "qip-audup", + 2766: "compaq-scp", + 2767: "uadtc", + 2768: "uacs", + 2769: "exce", + 2770: "veronica", + 2771: "vergencecm", + 2772: "auris", + 2773: "rbakcup1", + 2774: "rbakcup2", + 2775: "smpp", + 2776: "ridgeway1", + 2777: "ridgeway2", + 2778: "gwen-sonya", + 2779: "lbc-sync", + 2780: "lbc-control", + 2781: "whosells", + 2782: "everydayrc", + 2783: "aises", + 2784: "www-dev", + 2785: "aic-np", + 2786: "aic-oncrpc", + 2787: "piccolo", + 2788: "fryeserv", + 2789: "media-agent", + 2790: "plgproxy", + 2791: "mtport-regist", + 2792: "f5-globalsite", + 2793: "initlsmsad", + 2795: "livestats", + 2796: "ac-tech", + 2797: "esp-encap", + 2798: "tmesis-upshot", + 2799: "icon-discover", + 2800: "acc-raid", + 2801: "igcp", + 2802: "veritas-udp1", + 2803: "btprjctrl", + 2804: "dvr-esm", + 2805: "wta-wsp-s", + 2806: "cspuni", + 2807: "cspmulti", + 2808: "j-lan-p", + 2809: "corbaloc", + 2810: "netsteward", + 2811: "gsiftp", + 2812: "atmtcp", + 2813: "llm-pass", + 2814: "llm-csv", + 2815: "lbc-measure", + 2816: "lbc-watchdog", + 2817: "nmsigport", + 2818: "rmlnk", + 2819: "fc-faultnotify", + 2820: "univision", + 2821: "vrts-at-port", + 2822: "ka0wuc", + 2823: "cqg-netlan", + 2824: "cqg-netlan-1", + 2826: "slc-systemlog", + 2827: "slc-ctrlrloops", + 2828: "itm-lm", + 2829: "silkp1", + 2830: "silkp2", + 2831: "silkp3", + 2832: "silkp4", + 2833: "glishd", + 2834: "evtp", + 2835: "evtp-data", + 2836: "catalyst", + 2837: "repliweb", + 2838: "starbot", + 2839: "nmsigport", + 2840: "l3-exprt", + 2841: "l3-ranger", + 2842: "l3-hawk", + 2843: "pdnet", + 2844: "bpcp-poll", + 2845: "bpcp-trap", + 2846: "aimpp-hello", + 2847: "aimpp-port-req", + 2848: "amt-blc-port", + 2849: "fxp", + 2850: "metaconsole", + 2851: "webemshttp", + 2852: "bears-01", + 2853: "ispipes", + 2854: "infomover", + 2856: "cesdinv", + 2857: "simctlp", + 2858: "ecnp", + 2859: "activememory", + 2860: "dialpad-voice1", + 2861: "dialpad-voice2", + 2862: "ttg-protocol", + 2863: "sonardata", + 2864: "astromed-main", + 2865: "pit-vpn", + 2866: "iwlistener", + 2867: "esps-portal", + 2868: "npep-messaging", + 2869: "icslap", + 2870: "daishi", + 2871: "msi-selectplay", + 2872: "radix", + 2874: "dxmessagebase1", + 2875: "dxmessagebase2", + 2876: "sps-tunnel", + 2877: "bluelance", + 2878: "aap", + 2879: "ucentric-ds", + 2880: "synapse", + 2881: "ndsp", + 2882: "ndtp", + 2883: "ndnp", + 2884: "flashmsg", + 2885: "topflow", + 2886: "responselogic", + 2887: "aironetddp", + 2888: "spcsdlobby", + 2889: "rsom", + 2890: "cspclmulti", + 2891: "cinegrfx-elmd", + 2892: "snifferdata", + 2893: "vseconnector", + 2894: "abacus-remote", + 2895: "natuslink", + 2896: "ecovisiong6-1", + 2897: "citrix-rtmp", + 2898: "appliance-cfg", + 2899: "powergemplus", + 2900: "quicksuite", + 2901: "allstorcns", + 2902: "netaspi", + 2903: "suitcase", + 2904: "m2ua", + 2906: "caller9", + 2907: "webmethods-b2b", + 2908: "mao", + 2909: "funk-dialout", + 2910: "tdaccess", + 2911: "blockade", + 2912: "epicon", + 2913: "boosterware", + 2914: "gamelobby", + 2915: "tksocket", + 2916: "elvin-server", + 2917: "elvin-client", + 2918: "kastenchasepad", + 2919: "roboer", + 2920: "roboeda", + 2921: "cesdcdman", + 2922: "cesdcdtrn", + 2923: "wta-wsp-wtp-s", + 2924: "precise-vip", + 2926: "mobile-file-dl", + 2927: "unimobilectrl", + 2928: "redstone-cpss", + 2929: "amx-webadmin", + 2930: "amx-weblinx", + 2931: "circle-x", + 2932: "incp", + 2933: "4-tieropmgw", + 2934: "4-tieropmcli", + 2935: "qtp", + 2936: "otpatch", + 2937: "pnaconsult-lm", + 2938: "sm-pas-1", + 2939: "sm-pas-2", + 2940: "sm-pas-3", + 2941: "sm-pas-4", + 2942: "sm-pas-5", + 2943: "ttnrepository", + 2944: "megaco-h248", + 2945: "h248-binary", + 2946: "fjsvmpor", + 2947: "gpsd", + 2948: "wap-push", + 2949: "wap-pushsecure", + 2950: "esip", + 2951: "ottp", + 2952: "mpfwsas", + 2953: "ovalarmsrv", + 2954: "ovalarmsrv-cmd", + 2955: "csnotify", + 2956: "ovrimosdbman", + 2957: "jmact5", + 2958: "jmact6", + 2959: "rmopagt", + 2960: "dfoxserver", + 2961: "boldsoft-lm", + 2962: "iph-policy-cli", + 2963: "iph-policy-adm", + 2964: "bullant-srap", + 2965: "bullant-rap", + 2966: "idp-infotrieve", + 2967: "ssc-agent", + 2968: "enpp", + 2969: "essp", + 2970: "index-net", + 2971: "netclip", + 2972: "pmsm-webrctl", + 2973: "svnetworks", + 2974: "signal", + 2975: "fjmpcm", + 2976: "cns-srv-port", + 2977: "ttc-etap-ns", + 2978: "ttc-etap-ds", + 2979: "h263-video", + 2980: "wimd", + 2981: "mylxamport", + 2982: "iwb-whiteboard", + 2983: "netplan", + 2984: "hpidsadmin", + 2985: "hpidsagent", + 2986: "stonefalls", + 2987: "identify", + 2988: "hippad", + 2989: "zarkov", + 2990: "boscap", + 2991: "wkstn-mon", + 2992: "avenyo", + 2993: "veritas-vis1", + 2994: "veritas-vis2", + 2995: "idrs", + 2996: "vsixml", + 2997: "rebol", + 2998: "realsecure", + 2999: "remoteware-un", + 3000: "hbci", + 3002: "exlm-agent", + 3003: "cgms", + 3004: "csoftragent", + 3005: "geniuslm", + 3006: "ii-admin", + 3007: "lotusmtap", + 3008: "midnight-tech", + 3009: "pxc-ntfy", + 3010: "ping-pong", + 3011: "trusted-web", + 3012: "twsdss", + 3013: "gilatskysurfer", + 3014: "broker-service", + 3015: "nati-dstp", + 3016: "notify-srvr", + 3017: "event-listener", + 3018: "srvc-registry", + 3019: "resource-mgr", + 3020: "cifs", + 3021: "agriserver", + 3022: "csregagent", + 3023: "magicnotes", + 3024: "nds-sso", + 3025: "arepa-raft", + 3026: "agri-gateway", + 3027: "LiebDevMgmt-C", + 3028: "LiebDevMgmt-DM", + 3029: "LiebDevMgmt-A", + 3030: "arepa-cas", + 3031: "eppc", + 3032: "redwood-chat", + 3033: "pdb", + 3034: "osmosis-aeea", + 3035: "fjsv-gssagt", + 3036: "hagel-dump", + 3037: "hp-san-mgmt", + 3038: "santak-ups", + 3039: "cogitate", + 3040: "tomato-springs", + 3041: "di-traceware", + 3042: "journee", + 3043: "brp", + 3044: "epp", + 3045: "responsenet", + 3046: "di-ase", + 3047: "hlserver", + 3048: "pctrader", + 3049: "nsws", + 3050: "gds-db", + 3051: "galaxy-server", + 3052: "apc-3052", + 3053: "dsom-server", + 3054: "amt-cnf-prot", + 3055: "policyserver", + 3056: "cdl-server", + 3057: "goahead-fldup", + 3058: "videobeans", + 3059: "qsoft", + 3060: "interserver", + 3061: "cautcpd", + 3062: "ncacn-ip-tcp", + 3063: "ncadg-ip-udp", + 3064: "rprt", + 3065: "slinterbase", + 3066: "netattachsdmp", + 3067: "fjhpjp", + 3068: "ls3bcast", + 3069: "ls3", + 3070: "mgxswitch", + 3072: "csd-monitor", + 3073: "vcrp", + 3074: "xbox", + 3075: "orbix-locator", + 3076: "orbix-config", + 3077: "orbix-loc-ssl", + 3078: "orbix-cfg-ssl", + 3079: "lv-frontpanel", + 3080: "stm-pproc", + 3081: "tl1-lv", + 3082: "tl1-raw", + 3083: "tl1-telnet", + 3084: "itm-mccs", + 3085: "pcihreq", + 3086: "jdl-dbkitchen", + 3087: "asoki-sma", + 3088: "xdtp", + 3089: "ptk-alink", + 3090: "stss", + 3091: "1ci-smcs", + 3093: "rapidmq-center", + 3094: "rapidmq-reg", + 3095: "panasas", + 3096: "ndl-aps", + 3098: "umm-port", + 3099: "chmd", + 3100: "opcon-xps", + 3101: "hp-pxpib", + 3102: "slslavemon", + 3103: "autocuesmi", + 3104: "autocuetime", + 3105: "cardbox", + 3106: "cardbox-http", + 3107: "business", + 3108: "geolocate", + 3109: "personnel", + 3110: "sim-control", + 3111: "wsynch", + 3112: "ksysguard", + 3113: "cs-auth-svr", + 3114: "ccmad", + 3115: "mctet-master", + 3116: "mctet-gateway", + 3117: "mctet-jserv", + 3118: "pkagent", + 3119: "d2000kernel", + 3120: "d2000webserver", + 3122: "vtr-emulator", + 3123: "edix", + 3124: "beacon-port", + 3125: "a13-an", + 3127: "ctx-bridge", + 3128: "ndl-aas", + 3129: "netport-id", + 3130: "icpv2", + 3131: "netbookmark", + 3132: "ms-rule-engine", + 3133: "prism-deploy", + 3134: "ecp", + 3135: "peerbook-port", + 3136: "grubd", + 3137: "rtnt-1", + 3138: "rtnt-2", + 3139: "incognitorv", + 3140: "ariliamulti", + 3141: "vmodem", + 3142: "rdc-wh-eos", + 3143: "seaview", + 3144: "tarantella", + 3145: "csi-lfap", + 3146: "bears-02", + 3147: "rfio", + 3148: "nm-game-admin", + 3149: "nm-game-server", + 3150: "nm-asses-admin", + 3151: "nm-assessor", + 3152: "feitianrockey", + 3153: "s8-client-port", + 3154: "ccmrmi", + 3155: "jpegmpeg", + 3156: "indura", + 3157: "e3consultants", + 3158: "stvp", + 3159: "navegaweb-port", + 3160: "tip-app-server", + 3161: "doc1lm", + 3162: "sflm", + 3163: "res-sap", + 3164: "imprs", + 3165: "newgenpay", + 3166: "sossecollector", + 3167: "nowcontact", + 3168: "poweronnud", + 3169: "serverview-as", + 3170: "serverview-asn", + 3171: "serverview-gf", + 3172: "serverview-rm", + 3173: "serverview-icc", + 3174: "armi-server", + 3175: "t1-e1-over-ip", + 3176: "ars-master", + 3177: "phonex-port", + 3178: "radclientport", + 3179: "h2gf-w-2m", + 3180: "mc-brk-srv", + 3181: "bmcpatrolagent", + 3182: "bmcpatrolrnvu", + 3183: "cops-tls", + 3184: "apogeex-port", + 3185: "smpppd", + 3186: "iiw-port", + 3187: "odi-port", + 3188: "brcm-comm-port", + 3189: "pcle-infex", + 3190: "csvr-proxy", + 3191: "csvr-sslproxy", + 3192: "firemonrcc", + 3193: "spandataport", + 3194: "magbind", + 3195: "ncu-1", + 3196: "ncu-2", + 3197: "embrace-dp-s", + 3198: "embrace-dp-c", + 3199: "dmod-workspace", + 3200: "tick-port", + 3201: "cpq-tasksmart", + 3202: "intraintra", + 3203: "netwatcher-mon", + 3204: "netwatcher-db", + 3205: "isns", + 3206: "ironmail", + 3207: "vx-auth-port", + 3208: "pfu-prcallback", + 3209: "netwkpathengine", + 3210: "flamenco-proxy", + 3211: "avsecuremgmt", + 3212: "surveyinst", + 3213: "neon24x7", + 3214: "jmq-daemon-1", + 3215: "jmq-daemon-2", + 3216: "ferrari-foam", + 3217: "unite", + 3218: "smartpackets", + 3219: "wms-messenger", + 3220: "xnm-ssl", + 3221: "xnm-clear-text", + 3222: "glbp", + 3223: "digivote", + 3224: "aes-discovery", + 3225: "fcip-port", + 3226: "isi-irp", + 3227: "dwnmshttp", + 3228: "dwmsgserver", + 3229: "global-cd-port", + 3230: "sftdst-port", + 3231: "vidigo", + 3232: "mdtp", + 3233: "whisker", + 3234: "alchemy", + 3235: "mdap-port", + 3236: "apparenet-ts", + 3237: "apparenet-tps", + 3238: "apparenet-as", + 3239: "apparenet-ui", + 3240: "triomotion", + 3241: "sysorb", + 3242: "sdp-id-port", + 3243: "timelot", + 3244: "onesaf", + 3245: "vieo-fe", + 3246: "dvt-system", + 3247: "dvt-data", + 3248: "procos-lm", + 3249: "ssp", + 3250: "hicp", + 3251: "sysscanner", + 3252: "dhe", + 3253: "pda-data", + 3254: "pda-sys", + 3255: "semaphore", + 3256: "cpqrpm-agent", + 3257: "cpqrpm-server", + 3258: "ivecon-port", + 3259: "epncdp2", + 3260: "iscsi-target", + 3261: "winshadow", + 3262: "necp", + 3263: "ecolor-imager", + 3264: "ccmail", + 3265: "altav-tunnel", + 3266: "ns-cfg-server", + 3267: "ibm-dial-out", + 3268: "msft-gc", + 3269: "msft-gc-ssl", + 3270: "verismart", + 3271: "csoft-prev", + 3272: "user-manager", + 3273: "sxmp", + 3274: "ordinox-server", + 3275: "samd", + 3276: "maxim-asics", + 3277: "awg-proxy", + 3278: "lkcmserver", + 3279: "admind", + 3280: "vs-server", + 3281: "sysopt", + 3282: "datusorb", + 3283: "Apple Remote Desktop (Net Assistant)", + 3284: "4talk", + 3285: "plato", + 3286: "e-net", + 3287: "directvdata", + 3288: "cops", + 3289: "enpc", + 3290: "caps-lm", + 3291: "sah-lm", + 3292: "cart-o-rama", + 3293: "fg-fps", + 3294: "fg-gip", + 3295: "dyniplookup", + 3296: "rib-slm", + 3297: "cytel-lm", + 3298: "deskview", + 3299: "pdrncs", + 3302: "mcs-fastmail", + 3303: "opsession-clnt", + 3304: "opsession-srvr", + 3305: "odette-ftp", + 3306: "mysql", + 3307: "opsession-prxy", + 3308: "tns-server", + 3309: "tns-adv", + 3310: "dyna-access", + 3311: "mcns-tel-ret", + 3312: "appman-server", + 3313: "uorb", + 3314: "uohost", + 3315: "cdid", + 3316: "aicc-cmi", + 3317: "vsaiport", + 3318: "ssrip", + 3319: "sdt-lmd", + 3320: "officelink2000", + 3321: "vnsstr", + 3326: "sftu", + 3327: "bbars", + 3328: "egptlm", + 3329: "hp-device-disc", + 3330: "mcs-calypsoicf", + 3331: "mcs-messaging", + 3332: "mcs-mailsvr", + 3333: "dec-notes", + 3334: "directv-web", + 3335: "directv-soft", + 3336: "directv-tick", + 3337: "directv-catlg", + 3338: "anet-b", + 3339: "anet-l", + 3340: "anet-m", + 3341: "anet-h", + 3342: "webtie", + 3343: "ms-cluster-net", + 3344: "bnt-manager", + 3345: "influence", + 3346: "trnsprntproxy", + 3347: "phoenix-rpc", + 3348: "pangolin-laser", + 3349: "chevinservices", + 3350: "findviatv", + 3351: "btrieve", + 3352: "ssql", + 3353: "fatpipe", + 3354: "suitjd", + 3355: "ordinox-dbase", + 3356: "upnotifyps", + 3357: "adtech-test", + 3358: "mpsysrmsvr", + 3359: "wg-netforce", + 3360: "kv-server", + 3361: "kv-agent", + 3362: "dj-ilm", + 3363: "nati-vi-server", + 3364: "creativeserver", + 3365: "contentserver", + 3366: "creativepartnr", + 3372: "tip2", + 3373: "lavenir-lm", + 3374: "cluster-disc", + 3375: "vsnm-agent", + 3376: "cdbroker", + 3377: "cogsys-lm", + 3378: "wsicopy", + 3379: "socorfs", + 3380: "sns-channels", + 3381: "geneous", + 3382: "fujitsu-neat", + 3383: "esp-lm", + 3384: "hp-clic", + 3385: "qnxnetman", + 3386: "gprs-sig", + 3387: "backroomnet", + 3388: "cbserver", + 3389: "ms-wbt-server", + 3390: "dsc", + 3391: "savant", + 3392: "efi-lm", + 3393: "d2k-tapestry1", + 3394: "d2k-tapestry2", + 3395: "dyna-lm", + 3396: "printer-agent", + 3397: "cloanto-lm", + 3398: "mercantile", + 3399: "csms", + 3400: "csms2", + 3401: "filecast", + 3402: "fxaengine-net", + 3405: "nokia-ann-ch1", + 3406: "nokia-ann-ch2", + 3407: "ldap-admin", + 3408: "BESApi", + 3409: "networklens", + 3410: "networklenss", + 3411: "biolink-auth", + 3412: "xmlblaster", + 3413: "svnet", + 3414: "wip-port", + 3415: "bcinameservice", + 3416: "commandport", + 3417: "csvr", + 3418: "rnmap", + 3419: "softaudit", + 3420: "ifcp-port", + 3421: "bmap", + 3422: "rusb-sys-port", + 3423: "xtrm", + 3424: "xtrms", + 3425: "agps-port", + 3426: "arkivio", + 3427: "websphere-snmp", + 3428: "twcss", + 3429: "gcsp", + 3430: "ssdispatch", + 3431: "ndl-als", + 3432: "osdcp", + 3433: "opnet-smp", + 3434: "opencm", + 3435: "pacom", + 3436: "gc-config", + 3437: "autocueds", + 3438: "spiral-admin", + 3439: "hri-port", + 3440: "ans-console", + 3441: "connect-client", + 3442: "connect-server", + 3443: "ov-nnm-websrv", + 3444: "denali-server", + 3445: "monp", + 3446: "3comfaxrpc", + 3447: "directnet", + 3448: "dnc-port", + 3449: "hotu-chat", + 3450: "castorproxy", + 3451: "asam", + 3452: "sabp-signal", + 3453: "pscupd", + 3454: "mira", + 3455: "prsvp", + 3456: "vat", + 3457: "vat-control", + 3458: "d3winosfi", + 3459: "integral", + 3460: "edm-manager", + 3461: "edm-stager", + 3462: "edm-std-notify", + 3463: "edm-adm-notify", + 3464: "edm-mgr-sync", + 3465: "edm-mgr-cntrl", + 3466: "workflow", + 3467: "rcst", + 3468: "ttcmremotectrl", + 3469: "pluribus", + 3470: "jt400", + 3471: "jt400-ssl", + 3472: "jaugsremotec-1", + 3473: "jaugsremotec-2", + 3474: "ttntspauto", + 3475: "genisar-port", + 3476: "nppmp", + 3477: "ecomm", + 3478: "stun", + 3479: "twrpc", + 3480: "plethora", + 3481: "cleanerliverc", + 3482: "vulture", + 3483: "slim-devices", + 3484: "gbs-stp", + 3485: "celatalk", + 3486: "ifsf-hb-port", + 3487: "ltcudp", + 3488: "fs-rh-srv", + 3489: "dtp-dia", + 3490: "colubris", + 3491: "swr-port", + 3492: "tvdumtray-port", + 3493: "nut", + 3494: "ibm3494", + 3495: "seclayer-tcp", + 3496: "seclayer-tls", + 3497: "ipether232port", + 3498: "dashpas-port", + 3499: "sccip-media", + 3500: "rtmp-port", + 3501: "isoft-p2p", + 3502: "avinstalldisc", + 3503: "lsp-ping", + 3504: "ironstorm", + 3505: "ccmcomm", + 3506: "apc-3506", + 3507: "nesh-broker", + 3508: "interactionweb", + 3509: "vt-ssl", + 3510: "xss-port", + 3511: "webmail-2", + 3512: "aztec", + 3513: "arcpd", + 3514: "must-p2p", + 3515: "must-backplane", + 3516: "smartcard-port", + 3517: "802-11-iapp", + 3518: "artifact-msg", + 3519: "galileo", + 3520: "galileolog", + 3521: "mc3ss", + 3522: "nssocketport", + 3523: "odeumservlink", + 3524: "ecmport", + 3525: "eisport", + 3526: "starquiz-port", + 3527: "beserver-msg-q", + 3528: "jboss-iiop", + 3529: "jboss-iiop-ssl", + 3530: "gf", + 3531: "joltid", + 3532: "raven-rmp", + 3533: "raven-rdp", + 3534: "urld-port", + 3535: "ms-la", + 3536: "snac", + 3537: "ni-visa-remote", + 3538: "ibm-diradm", + 3539: "ibm-diradm-ssl", + 3540: "pnrp-port", + 3541: "voispeed-port", + 3542: "hacl-monitor", + 3543: "qftest-lookup", + 3544: "teredo", + 3545: "camac", + 3547: "symantec-sim", + 3548: "interworld", + 3549: "tellumat-nms", + 3550: "ssmpp", + 3551: "apcupsd", + 3552: "taserver", + 3553: "rbr-discovery", + 3554: "questnotify", + 3555: "razor", + 3556: "sky-transport", + 3557: "personalos-001", + 3558: "mcp-port", + 3559: "cctv-port", + 3560: "iniserve-port", + 3561: "bmc-onekey", + 3562: "sdbproxy", + 3563: "watcomdebug", + 3564: "esimport", + 3567: "dof-eps", + 3568: "dof-tunnel-sec", + 3569: "mbg-ctrl", + 3570: "mccwebsvr-port", + 3571: "megardsvr-port", + 3572: "megaregsvrport", + 3573: "tag-ups-1", + 3574: "dmaf-caster", + 3575: "ccm-port", + 3576: "cmc-port", + 3577: "config-port", + 3578: "data-port", + 3579: "ttat3lb", + 3580: "nati-svrloc", + 3581: "kfxaclicensing", + 3582: "press", + 3583: "canex-watch", + 3584: "u-dbap", + 3585: "emprise-lls", + 3586: "emprise-lsc", + 3587: "p2pgroup", + 3588: "sentinel", + 3589: "isomair", + 3590: "wv-csp-sms", + 3591: "gtrack-server", + 3592: "gtrack-ne", + 3593: "bpmd", + 3594: "mediaspace", + 3595: "shareapp", + 3596: "iw-mmogame", + 3597: "a14", + 3598: "a15", + 3599: "quasar-server", + 3600: "trap-daemon", + 3601: "visinet-gui", + 3602: "infiniswitchcl", + 3603: "int-rcv-cntrl", + 3604: "bmc-jmx-port", + 3605: "comcam-io", + 3606: "splitlock", + 3607: "precise-i3", + 3608: "trendchip-dcp", + 3609: "cpdi-pidas-cm", + 3610: "echonet", + 3611: "six-degrees", + 3612: "hp-dataprotect", + 3613: "alaris-disc", + 3614: "sigma-port", + 3615: "start-network", + 3616: "cd3o-protocol", + 3617: "sharp-server", + 3618: "aairnet-1", + 3619: "aairnet-2", + 3620: "ep-pcp", + 3621: "ep-nsp", + 3622: "ff-lr-port", + 3623: "haipe-discover", + 3624: "dist-upgrade", + 3625: "volley", + 3626: "bvcdaemon-port", + 3627: "jamserverport", + 3628: "ept-machine", + 3629: "escvpnet", + 3630: "cs-remote-db", + 3631: "cs-services", + 3632: "distcc", + 3633: "wacp", + 3634: "hlibmgr", + 3635: "sdo", + 3636: "servistaitsm", + 3637: "scservp", + 3638: "ehp-backup", + 3639: "xap-ha", + 3640: "netplay-port1", + 3641: "netplay-port2", + 3642: "juxml-port", + 3643: "audiojuggler", + 3644: "ssowatch", + 3645: "cyc", + 3646: "xss-srv-port", + 3647: "splitlock-gw", + 3648: "fjcp", + 3649: "nmmp", + 3650: "prismiq-plugin", + 3651: "xrpc-registry", + 3652: "vxcrnbuport", + 3653: "tsp", + 3654: "vaprtm", + 3655: "abatemgr", + 3656: "abatjss", + 3657: "immedianet-bcn", + 3658: "ps-ams", + 3659: "apple-sasl", + 3660: "can-nds-ssl", + 3661: "can-ferret-ssl", + 3662: "pserver", + 3663: "dtp", + 3664: "ups-engine", + 3665: "ent-engine", + 3666: "eserver-pap", + 3667: "infoexch", + 3668: "dell-rm-port", + 3669: "casanswmgmt", + 3670: "smile", + 3671: "efcp", + 3672: "lispworks-orb", + 3673: "mediavault-gui", + 3674: "wininstall-ipc", + 3675: "calltrax", + 3676: "va-pacbase", + 3677: "roverlog", + 3678: "ipr-dglt", + 3679: "Escale (Newton Dock)", + 3680: "npds-tracker", + 3681: "bts-x73", + 3682: "cas-mapi", + 3683: "bmc-ea", + 3684: "faxstfx-port", + 3685: "dsx-agent", + 3686: "tnmpv2", + 3687: "simple-push", + 3688: "simple-push-s", + 3689: "daap", + 3690: "svn", + 3691: "magaya-network", + 3692: "intelsync", + 3695: "bmc-data-coll", + 3696: "telnetcpcd", + 3697: "nw-license", + 3698: "sagectlpanel", + 3699: "kpn-icw", + 3700: "lrs-paging", + 3701: "netcelera", + 3702: "ws-discovery", + 3703: "adobeserver-3", + 3704: "adobeserver-4", + 3705: "adobeserver-5", + 3706: "rt-event", + 3707: "rt-event-s", + 3708: "sun-as-iiops", + 3709: "ca-idms", + 3710: "portgate-auth", + 3711: "edb-server2", + 3712: "sentinel-ent", + 3713: "tftps", + 3714: "delos-dms", + 3715: "anoto-rendezv", + 3716: "wv-csp-sms-cir", + 3717: "wv-csp-udp-cir", + 3718: "opus-services", + 3719: "itelserverport", + 3720: "ufastro-instr", + 3721: "xsync", + 3722: "xserveraid", + 3723: "sychrond", + 3724: "blizwow", + 3725: "na-er-tip", + 3726: "array-manager", + 3727: "e-mdu", + 3728: "e-woa", + 3729: "fksp-audit", + 3730: "client-ctrl", + 3731: "smap", + 3732: "m-wnn", + 3733: "multip-msg", + 3734: "synel-data", + 3735: "pwdis", + 3736: "rs-rmi", + 3738: "versatalk", + 3739: "launchbird-lm", + 3740: "heartbeat", + 3741: "wysdma", + 3742: "cst-port", + 3743: "ipcs-command", + 3744: "sasg", + 3745: "gw-call-port", + 3746: "linktest", + 3747: "linktest-s", + 3748: "webdata", + 3749: "cimtrak", + 3750: "cbos-ip-port", + 3751: "gprs-cube", + 3752: "vipremoteagent", + 3753: "nattyserver", + 3754: "timestenbroker", + 3755: "sas-remote-hlp", + 3756: "canon-capt", + 3757: "grf-port", + 3758: "apw-registry", + 3759: "exapt-lmgr", + 3760: "adtempusclient", + 3761: "gsakmp", + 3762: "gbs-smp", + 3763: "xo-wave", + 3764: "mni-prot-rout", + 3765: "rtraceroute", + 3767: "listmgr-port", + 3768: "rblcheckd", + 3769: "haipe-otnk", + 3770: "cindycollab", + 3771: "paging-port", + 3772: "ctp", + 3773: "ctdhercules", + 3774: "zicom", + 3775: "ispmmgr", + 3776: "dvcprov-port", + 3777: "jibe-eb", + 3778: "c-h-it-port", + 3779: "cognima", + 3780: "nnp", + 3781: "abcvoice-port", + 3782: "iso-tp0s", + 3783: "bim-pem", + 3784: "bfd-control", + 3785: "bfd-echo", + 3786: "upstriggervsw", + 3787: "fintrx", + 3788: "isrp-port", + 3789: "remotedeploy", + 3790: "quickbooksrds", + 3791: "tvnetworkvideo", + 3792: "sitewatch", + 3793: "dcsoftware", + 3794: "jaus", + 3795: "myblast", + 3796: "spw-dialer", + 3797: "idps", + 3798: "minilock", + 3799: "radius-dynauth", + 3800: "pwgpsi", + 3801: "ibm-mgr", + 3802: "vhd", + 3803: "soniqsync", + 3804: "iqnet-port", + 3805: "tcpdataserver", + 3806: "wsmlb", + 3807: "spugna", + 3808: "sun-as-iiops-ca", + 3809: "apocd", + 3810: "wlanauth", + 3811: "amp", + 3812: "neto-wol-server", + 3813: "rap-ip", + 3814: "neto-dcs", + 3815: "lansurveyorxml", + 3816: "sunlps-http", + 3817: "tapeware", + 3818: "crinis-hb", + 3819: "epl-slp", + 3820: "scp", + 3821: "pmcp", + 3822: "acp-discovery", + 3823: "acp-conduit", + 3824: "acp-policy", + 3825: "ffserver", + 3826: "warmux", + 3827: "netmpi", + 3828: "neteh", + 3829: "neteh-ext", + 3830: "cernsysmgmtagt", + 3831: "dvapps", + 3832: "xxnetserver", + 3833: "aipn-auth", + 3834: "spectardata", + 3835: "spectardb", + 3836: "markem-dcp", + 3837: "mkm-discovery", + 3838: "sos", + 3839: "amx-rms", + 3840: "flirtmitmir", + 3842: "nhci", + 3843: "quest-agent", + 3844: "rnm", + 3845: "v-one-spp", + 3846: "an-pcp", + 3847: "msfw-control", + 3848: "item", + 3849: "spw-dnspreload", + 3850: "qtms-bootstrap", + 3851: "spectraport", + 3852: "sse-app-config", + 3853: "sscan", + 3854: "stryker-com", + 3855: "opentrac", + 3856: "informer", + 3857: "trap-port", + 3858: "trap-port-mom", + 3859: "nav-port", + 3860: "sasp", + 3861: "winshadow-hd", + 3862: "giga-pocket", + 3863: "asap-udp", + 3865: "xpl", + 3866: "dzdaemon", + 3867: "dzoglserver", + 3869: "ovsam-mgmt", + 3870: "ovsam-d-agent", + 3871: "avocent-adsap", + 3872: "oem-agent", + 3873: "fagordnc", + 3874: "sixxsconfig", + 3875: "pnbscada", + 3876: "dl-agent", + 3877: "xmpcr-interface", + 3878: "fotogcad", + 3879: "appss-lm", + 3880: "igrs", + 3881: "idac", + 3882: "msdts1", + 3883: "vrpn", + 3884: "softrack-meter", + 3885: "topflow-ssl", + 3886: "nei-management", + 3887: "ciphire-data", + 3888: "ciphire-serv", + 3889: "dandv-tester", + 3890: "ndsconnect", + 3891: "rtc-pm-port", + 3892: "pcc-image-port", + 3893: "cgi-starapi", + 3894: "syam-agent", + 3895: "syam-smc", + 3896: "sdo-tls", + 3897: "sdo-ssh", + 3898: "senip", + 3899: "itv-control", + 3900: "udt-os", + 3901: "nimsh", + 3902: "nimaux", + 3903: "charsetmgr", + 3904: "omnilink-port", + 3905: "mupdate", + 3906: "topovista-data", + 3907: "imoguia-port", + 3908: "hppronetman", + 3909: "surfcontrolcpa", + 3910: "prnrequest", + 3911: "prnstatus", + 3912: "gbmt-stars", + 3913: "listcrt-port", + 3914: "listcrt-port-2", + 3915: "agcat", + 3916: "wysdmc", + 3917: "aftmux", + 3918: "pktcablemmcops", + 3919: "hyperip", + 3920: "exasoftport1", + 3921: "herodotus-net", + 3922: "sor-update", + 3923: "symb-sb-port", + 3924: "mpl-gprs-port", + 3925: "zmp", + 3926: "winport", + 3927: "natdataservice", + 3928: "netboot-pxe", + 3929: "smauth-port", + 3930: "syam-webserver", + 3931: "msr-plugin-port", + 3932: "dyn-site", + 3933: "plbserve-port", + 3934: "sunfm-port", + 3935: "sdp-portmapper", + 3936: "mailprox", + 3937: "dvbservdsc", + 3938: "dbcontrol-agent", + 3939: "aamp", + 3940: "xecp-node", + 3941: "homeportal-web", + 3942: "srdp", + 3943: "tig", + 3944: "sops", + 3945: "emcads", + 3946: "backupedge", + 3947: "ccp", + 3948: "apdap", + 3949: "drip", + 3950: "namemunge", + 3951: "pwgippfax", + 3952: "i3-sessionmgr", + 3953: "xmlink-connect", + 3954: "adrep", + 3955: "p2pcommunity", + 3956: "gvcp", + 3957: "mqe-broker", + 3958: "mqe-agent", + 3959: "treehopper", + 3960: "bess", + 3961: "proaxess", + 3962: "sbi-agent", + 3963: "thrp", + 3964: "sasggprs", + 3965: "ati-ip-to-ncpe", + 3966: "bflckmgr", + 3967: "ppsms", + 3968: "ianywhere-dbns", + 3969: "landmarks", + 3970: "lanrevagent", + 3971: "lanrevserver", + 3972: "iconp", + 3973: "progistics", + 3974: "citysearch", + 3975: "airshot", + 3976: "opswagent", + 3977: "opswmanager", + 3978: "secure-cfg-svr", + 3979: "smwan", + 3980: "acms", + 3981: "starfish", + 3982: "eis", + 3983: "eisp", + 3984: "mapper-nodemgr", + 3985: "mapper-mapethd", + 3986: "mapper-ws-ethd", + 3987: "centerline", + 3988: "dcs-config", + 3989: "bv-queryengine", + 3990: "bv-is", + 3991: "bv-smcsrv", + 3992: "bv-ds", + 3993: "bv-agent", + 3995: "iss-mgmt-ssl", + 3996: "abcsoftware", + 3997: "agentsease-db", + 3998: "dnx", + 3999: "nvcnet", + 4000: "terabase", + 4001: "newoak", + 4002: "pxc-spvr-ft", + 4003: "pxc-splr-ft", + 4004: "pxc-roid", + 4005: "pxc-pin", + 4006: "pxc-spvr", + 4007: "pxc-splr", + 4008: "netcheque", + 4009: "chimera-hwm", + 4010: "samsung-unidex", + 4011: "altserviceboot", + 4012: "pda-gate", + 4013: "acl-manager", + 4014: "taiclock", + 4015: "talarian-mcast1", + 4016: "talarian-mcast2", + 4017: "talarian-mcast3", + 4018: "talarian-mcast4", + 4019: "talarian-mcast5", + 4020: "trap", + 4021: "nexus-portal", + 4022: "dnox", + 4023: "esnm-zoning", + 4024: "tnp1-port", + 4025: "partimage", + 4026: "as-debug", + 4027: "bxp", + 4028: "dtserver-port", + 4029: "ip-qsig", + 4030: "jdmn-port", + 4031: "suucp", + 4032: "vrts-auth-port", + 4033: "sanavigator", + 4034: "ubxd", + 4035: "wap-push-http", + 4036: "wap-push-https", + 4037: "ravehd", + 4038: "fazzt-ptp", + 4039: "fazzt-admin", + 4040: "yo-main", + 4041: "houston", + 4042: "ldxp", + 4043: "nirp", + 4044: "ltp", + 4045: "npp", + 4046: "acp-proto", + 4047: "ctp-state", + 4049: "wafs", + 4050: "cisco-wafs", + 4051: "cppdp", + 4052: "interact", + 4053: "ccu-comm-1", + 4054: "ccu-comm-2", + 4055: "ccu-comm-3", + 4056: "lms", + 4057: "wfm", + 4058: "kingfisher", + 4059: "dlms-cosem", + 4060: "dsmeter-iatc", + 4061: "ice-location", + 4062: "ice-slocation", + 4063: "ice-router", + 4064: "ice-srouter", + 4065: "avanti-cdp", + 4066: "pmas", + 4067: "idp", + 4068: "ipfltbcst", + 4069: "minger", + 4070: "tripe", + 4071: "aibkup", + 4072: "zieto-sock", + 4073: "iRAPP", + 4074: "cequint-cityid", + 4075: "perimlan", + 4076: "seraph", + 4077: "ascomalarm", + 4079: "santools", + 4080: "lorica-in", + 4081: "lorica-in-sec", + 4082: "lorica-out", + 4083: "lorica-out-sec", + 4084: "fortisphere-vm", + 4086: "ftsync", + 4089: "opencore", + 4090: "omasgport", + 4091: "ewinstaller", + 4092: "ewdgs", + 4093: "pvxpluscs", + 4094: "sysrqd", + 4095: "xtgui", + 4096: "bre", + 4097: "patrolview", + 4098: "drmsfsd", + 4099: "dpcp", + 4100: "igo-incognito", + 4101: "brlp-0", + 4102: "brlp-1", + 4103: "brlp-2", + 4104: "brlp-3", + 4105: "shofar", + 4106: "synchronite", + 4107: "j-ac", + 4108: "accel", + 4109: "izm", + 4110: "g2tag", + 4111: "xgrid", + 4112: "apple-vpns-rp", + 4113: "aipn-reg", + 4114: "jomamqmonitor", + 4115: "cds", + 4116: "smartcard-tls", + 4117: "hillrserv", + 4118: "netscript", + 4119: "assuria-slm", + 4121: "e-builder", + 4122: "fprams", + 4123: "z-wave", + 4124: "tigv2", + 4125: "opsview-envoy", + 4126: "ddrepl", + 4127: "unikeypro", + 4128: "nufw", + 4129: "nuauth", + 4130: "fronet", + 4131: "stars", + 4132: "nuts-dem", + 4133: "nuts-bootp", + 4134: "nifty-hmi", + 4135: "cl-db-attach", + 4136: "cl-db-request", + 4137: "cl-db-remote", + 4138: "nettest", + 4139: "thrtx", + 4140: "cedros-fds", + 4141: "oirtgsvc", + 4142: "oidocsvc", + 4143: "oidsr", + 4145: "vvr-control", + 4146: "tgcconnect", + 4147: "vrxpservman", + 4148: "hhb-handheld", + 4149: "agslb", + 4150: "PowerAlert-nsa", + 4151: "menandmice-noh", + 4152: "idig-mux", + 4153: "mbl-battd", + 4154: "atlinks", + 4155: "bzr", + 4156: "stat-results", + 4157: "stat-scanner", + 4158: "stat-cc", + 4159: "nss", + 4160: "jini-discovery", + 4161: "omscontact", + 4162: "omstopology", + 4163: "silverpeakpeer", + 4164: "silverpeakcomm", + 4165: "altcp", + 4166: "joost", + 4167: "ddgn", + 4168: "pslicser", + 4169: "iadt-disc", + 4172: "pcoip", + 4173: "mma-discovery", + 4174: "sm-disc", + 4177: "wello", + 4178: "storman", + 4179: "MaxumSP", + 4180: "httpx", + 4181: "macbak", + 4182: "pcptcpservice", + 4183: "cyborgnet", + 4184: "universe-suite", + 4185: "wcpp", + 4188: "vatata", + 4191: "dsmipv6", + 4192: "azeti-bd", + 4197: "hctl", + 4199: "eims-admin", + 4300: "corelccam", + 4301: "d-data", + 4302: "d-data-control", + 4303: "srcp", + 4304: "owserver", + 4305: "batman", + 4306: "pinghgl", + 4307: "trueconf", + 4308: "compx-lockview", + 4309: "dserver", + 4310: "mirrtex", + 4320: "fdt-rcatp", + 4321: "rwhois", + 4322: "trim-event", + 4323: "trim-ice", + 4325: "geognosisman", + 4326: "geognosis", + 4327: "jaxer-web", + 4328: "jaxer-manager", + 4333: "ahsp", + 4340: "gaia", + 4341: "lisp-data", + 4342: "lisp-control", + 4343: "unicall", + 4344: "vinainstall", + 4345: "m4-network-as", + 4346: "elanlm", + 4347: "lansurveyor", + 4348: "itose", + 4349: "fsportmap", + 4350: "net-device", + 4351: "plcy-net-svcs", + 4352: "pjlink", + 4353: "f5-iquery", + 4354: "qsnet-trans", + 4355: "qsnet-workst", + 4356: "qsnet-assist", + 4357: "qsnet-cond", + 4358: "qsnet-nucl", + 4359: "omabcastltkm", + 4361: "nacnl", + 4362: "afore-vdp-disc", + 4366: "shadowstream", + 4368: "wxbrief", + 4369: "epmd", + 4370: "elpro-tunnel", + 4371: "l2c-disc", + 4372: "l2c-data", + 4373: "remctl", + 4375: "tolteces", + 4376: "bip", + 4377: "cp-spxsvr", + 4378: "cp-spxdpy", + 4379: "ctdb", + 4389: "xandros-cms", + 4390: "wiegand", + 4394: "apwi-disc", + 4395: "omnivisionesx", + 4400: "ds-srv", + 4401: "ds-srvr", + 4402: "ds-clnt", + 4403: "ds-user", + 4404: "ds-admin", + 4405: "ds-mail", + 4406: "ds-slp", + 4412: "smallchat", + 4413: "avi-nms-disc", + 4416: "pjj-player-disc", + 4418: "axysbridge", + 4420: "nvm-express", + 4425: "netrockey6", + 4426: "beacon-port-2", + 4430: "rsqlserver", + 4432: "l-acoustics", + 4441: "netblox", + 4442: "saris", + 4443: "pharos", + 4444: "krb524", + 4445: "upnotifyp", + 4446: "n1-fwp", + 4447: "n1-rmgmt", + 4448: "asc-slmd", + 4449: "privatewire", + 4450: "camp", + 4451: "ctisystemmsg", + 4452: "ctiprogramload", + 4453: "nssalertmgr", + 4454: "nssagentmgr", + 4455: "prchat-user", + 4456: "prchat-server", + 4457: "prRegister", + 4458: "mcp", + 4484: "hpssmgmt", + 4486: "icms", + 4488: "awacs-ice", + 4500: "ipsec-nat-t", + 4534: "armagetronad", + 4535: "ehs", + 4536: "ehs-ssl", + 4537: "wssauthsvc", + 4538: "swx-gate", + 4545: "worldscores", + 4546: "sf-lm", + 4547: "lanner-lm", + 4548: "synchromesh", + 4549: "aegate", + 4550: "gds-adppiw-db", + 4551: "ieee-mih", + 4552: "menandmice-mon", + 4554: "msfrs", + 4555: "rsip", + 4556: "dtn-bundle", + 4557: "mtcevrunqss", + 4558: "mtcevrunqman", + 4559: "hylafax", + 4566: "kwtc", + 4567: "tram", + 4568: "bmc-reporting", + 4569: "iax", + 4591: "l3t-at-an", + 4592: "hrpd-ith-at-an", + 4593: "ipt-anri-anri", + 4594: "ias-session", + 4595: "ias-paging", + 4596: "ias-neighbor", + 4597: "a21-an-1xbs", + 4598: "a16-an-an", + 4599: "a17-an-an", + 4600: "piranha1", + 4601: "piranha2", + 4621: "ventoso", + 4658: "playsta2-app", + 4659: "playsta2-lob", + 4660: "smaclmgr", + 4661: "kar2ouche", + 4662: "oms", + 4663: "noteit", + 4664: "ems", + 4665: "contclientms", + 4666: "eportcomm", + 4667: "mmacomm", + 4668: "mmaeds", + 4669: "eportcommdata", + 4670: "light", + 4671: "acter", + 4672: "rfa", + 4673: "cxws", + 4674: "appiq-mgmt", + 4675: "dhct-status", + 4676: "dhct-alerts", + 4677: "bcs", + 4678: "traversal", + 4679: "mgesupervision", + 4680: "mgemanagement", + 4681: "parliant", + 4682: "finisar", + 4683: "spike", + 4684: "rfid-rp1", + 4685: "autopac", + 4686: "msp-os", + 4687: "nst", + 4688: "mobile-p2p", + 4689: "altovacentral", + 4690: "prelude", + 4691: "mtn", + 4692: "conspiracy", + 4700: "netxms-agent", + 4701: "netxms-mgmt", + 4702: "netxms-sync", + 4711: "trinity-dist", + 4725: "truckstar", + 4726: "a26-fap-fgw", + 4727: "fcis-disc", + 4728: "capmux", + 4729: "gsmtap", + 4730: "gearman", + 4732: "ohmtrigger", + 4737: "ipdr-sp", + 4738: "solera-lpn", + 4739: "ipfix", + 4740: "ipfixs", + 4741: "lumimgrd", + 4742: "sicct-sdp", + 4743: "openhpid", + 4744: "ifsp", + 4745: "fmp", + 4746: "intelliadm-disc", + 4747: "buschtrommel", + 4749: "profilemac", + 4750: "ssad", + 4751: "spocp", + 4752: "snap", + 4753: "simon-disc", + 4754: "gre-in-udp", + 4755: "gre-udp-dtls", + 4784: "bfd-multi-ctl", + 4785: "cncp", + 4789: "vxlan", + 4790: "vxlan-gpe", + 4791: "roce", + 4800: "iims", + 4801: "iwec", + 4802: "ilss", + 4803: "notateit-disc", + 4804: "aja-ntv4-disc", + 4827: "htcp", + 4837: "varadero-0", + 4838: "varadero-1", + 4839: "varadero-2", + 4840: "opcua-udp", + 4841: "quosa", + 4842: "gw-asv", + 4843: "opcua-tls", + 4844: "gw-log", + 4845: "wcr-remlib", + 4846: "contamac-icm", + 4847: "wfc", + 4848: "appserv-http", + 4849: "appserv-https", + 4850: "sun-as-nodeagt", + 4851: "derby-repli", + 4867: "unify-debug", + 4868: "phrelay", + 4869: "phrelaydbg", + 4870: "cc-tracking", + 4871: "wired", + 4876: "tritium-can", + 4877: "lmcs", + 4878: "inst-discovery", + 4881: "socp-t", + 4882: "socp-c", + 4884: "hivestor", + 4885: "abbs", + 4894: "lyskom", + 4899: "radmin-port", + 4900: "hfcs", + 4914: "bones", + 4936: "an-signaling", + 4937: "atsc-mh-ssc", + 4940: "eq-office-4940", + 4941: "eq-office-4941", + 4942: "eq-office-4942", + 4949: "munin", + 4950: "sybasesrvmon", + 4951: "pwgwims", + 4952: "sagxtsds", + 4969: "ccss-qmm", + 4970: "ccss-qsm", + 4980: "ctxs-vpp", + 4986: "mrip", + 4987: "smar-se-port1", + 4988: "smar-se-port2", + 4989: "parallel", + 4990: "busycal", + 4991: "vrt", + 4999: "hfcs-manager", + 5000: "commplex-main", + 5001: "commplex-link", + 5002: "rfe", + 5003: "fmpro-internal", + 5004: "avt-profile-1", + 5005: "avt-profile-2", + 5006: "wsm-server", + 5007: "wsm-server-ssl", + 5008: "synapsis-edge", + 5009: "winfs", + 5010: "telelpathstart", + 5011: "telelpathattack", + 5012: "nsp", + 5013: "fmpro-v6", + 5014: "onpsocket", + 5020: "zenginkyo-1", + 5021: "zenginkyo-2", + 5022: "mice", + 5023: "htuilsrv", + 5024: "scpi-telnet", + 5025: "scpi-raw", + 5026: "strexec-d", + 5027: "strexec-s", + 5029: "infobright", + 5030: "surfpass", + 5031: "dmp", + 5042: "asnaacceler8db", + 5043: "swxadmin", + 5044: "lxi-evntsvc", + 5046: "vpm-udp", + 5047: "iscape", + 5049: "ivocalize", + 5050: "mmcc", + 5051: "ita-agent", + 5052: "ita-manager", + 5053: "rlm-disc", + 5055: "unot", + 5056: "intecom-ps1", + 5057: "intecom-ps2", + 5058: "locus-disc", + 5059: "sds", + 5060: "sip", + 5061: "sips", + 5062: "na-localise", + 5064: "ca-1", + 5065: "ca-2", + 5066: "stanag-5066", + 5067: "authentx", + 5069: "i-net-2000-npr", + 5070: "vtsas", + 5071: "powerschool", + 5072: "ayiya", + 5073: "tag-pm", + 5074: "alesquery", + 5078: "pixelpusher", + 5079: "cp-spxrpts", + 5080: "onscreen", + 5081: "sdl-ets", + 5082: "qcp", + 5083: "qfp", + 5084: "llrp", + 5085: "encrypted-llrp", + 5092: "magpie", + 5093: "sentinel-lm", + 5094: "hart-ip", + 5099: "sentlm-srv2srv", + 5100: "socalia", + 5101: "talarian-udp", + 5102: "oms-nonsecure", + 5104: "tinymessage", + 5105: "hughes-ap", + 5111: "taep-as-svc", + 5112: "pm-cmdsvr", + 5116: "emb-proj-cmd", + 5120: "barracuda-bbs", + 5133: "nbt-pc", + 5136: "minotaur-sa", + 5137: "ctsd", + 5145: "rmonitor-secure", + 5150: "atmp", + 5151: "esri-sde", + 5152: "sde-discovery", + 5154: "bzflag", + 5155: "asctrl-agent", + 5164: "vpa-disc", + 5165: "ife-icorp", + 5166: "winpcs", + 5167: "scte104", + 5168: "scte30", + 5190: "aol", + 5191: "aol-1", + 5192: "aol-2", + 5193: "aol-3", + 5200: "targus-getdata", + 5201: "targus-getdata1", + 5202: "targus-getdata2", + 5203: "targus-getdata3", + 5223: "hpvirtgrp", + 5224: "hpvirtctrl", + 5225: "hp-server", + 5226: "hp-status", + 5227: "perfd", + 5234: "eenet", + 5235: "galaxy-network", + 5236: "padl2sim", + 5237: "mnet-discovery", + 5245: "downtools-disc", + 5246: "capwap-control", + 5247: "capwap-data", + 5248: "caacws", + 5249: "caaclang2", + 5250: "soagateway", + 5251: "caevms", + 5252: "movaz-ssc", + 5264: "3com-njack-1", + 5265: "3com-njack-2", + 5270: "cartographerxmp", + 5271: "cuelink-disc", + 5272: "pk", + 5282: "transmit-port", + 5298: "presence", + 5299: "nlg-data", + 5300: "hacl-hb", + 5301: "hacl-gs", + 5302: "hacl-cfg", + 5303: "hacl-probe", + 5304: "hacl-local", + 5305: "hacl-test", + 5306: "sun-mc-grp", + 5307: "sco-aip", + 5308: "cfengine", + 5309: "jprinter", + 5310: "outlaws", + 5312: "permabit-cs", + 5313: "rrdp", + 5314: "opalis-rbt-ipc", + 5315: "hacl-poll", + 5343: "kfserver", + 5344: "xkotodrcp", + 5349: "stuns", + 5350: "pcp-multicast", + 5351: "pcp", + 5352: "dns-llq", + 5353: "mdns", + 5354: "mdnsresponder", + 5355: "llmnr", + 5356: "ms-smlbiz", + 5357: "wsdapi", + 5358: "wsdapi-s", + 5359: "ms-alerter", + 5360: "ms-sideshow", + 5361: "ms-s-sideshow", + 5362: "serverwsd2", + 5363: "net-projection", + 5364: "kdnet", + 5397: "stresstester", + 5398: "elektron-admin", + 5399: "securitychase", + 5400: "excerpt", + 5401: "excerpts", + 5402: "mftp", + 5403: "hpoms-ci-lstn", + 5404: "hpoms-dps-lstn", + 5405: "netsupport", + 5406: "systemics-sox", + 5407: "foresyte-clear", + 5408: "foresyte-sec", + 5409: "salient-dtasrv", + 5410: "salient-usrmgr", + 5411: "actnet", + 5412: "continuus", + 5413: "wwiotalk", + 5414: "statusd", + 5415: "ns-server", + 5416: "sns-gateway", + 5417: "sns-agent", + 5418: "mcntp", + 5419: "dj-ice", + 5420: "cylink-c", + 5421: "netsupport2", + 5422: "salient-mux", + 5423: "virtualuser", + 5424: "beyond-remote", + 5425: "br-channel", + 5426: "devbasic", + 5427: "sco-peer-tta", + 5428: "telaconsole", + 5429: "base", + 5430: "radec-corp", + 5431: "park-agent", + 5432: "postgresql", + 5433: "pyrrho", + 5434: "sgi-arrayd", + 5435: "sceanics", + 5436: "pmip6-cntl", + 5437: "pmip6-data", + 5443: "spss", + 5450: "tiepie-disc", + 5453: "surebox", + 5454: "apc-5454", + 5455: "apc-5455", + 5456: "apc-5456", + 5461: "silkmeter", + 5462: "ttl-publisher", + 5463: "ttlpriceproxy", + 5464: "quailnet", + 5465: "netops-broker", + 5474: "apsolab-rpc", + 5500: "fcp-addr-srvr1", + 5501: "fcp-addr-srvr2", + 5502: "fcp-srvr-inst1", + 5503: "fcp-srvr-inst2", + 5504: "fcp-cics-gw1", + 5505: "checkoutdb", + 5506: "amc", + 5553: "sgi-eventmond", + 5554: "sgi-esphttp", + 5555: "personal-agent", + 5556: "freeciv", + 5567: "dof-dps-mc-sec", + 5568: "sdt", + 5569: "rdmnet-device", + 5573: "sdmmp", + 5580: "tmosms0", + 5581: "tmosms1", + 5582: "fac-restore", + 5583: "tmo-icon-sync", + 5584: "bis-web", + 5585: "bis-sync", + 5597: "ininmessaging", + 5598: "mctfeed", + 5599: "esinstall", + 5600: "esmmanager", + 5601: "esmagent", + 5602: "a1-msc", + 5603: "a1-bs", + 5604: "a3-sdunode", + 5605: "a4-sdunode", + 5627: "ninaf", + 5628: "htrust", + 5629: "symantec-sfdb", + 5630: "precise-comm", + 5631: "pcanywheredata", + 5632: "pcanywherestat", + 5633: "beorl", + 5634: "xprtld", + 5670: "zre-disc", + 5671: "amqps", + 5672: "amqp", + 5673: "jms", + 5674: "hyperscsi-port", + 5675: "v5ua", + 5676: "raadmin", + 5677: "questdb2-lnchr", + 5678: "rrac", + 5679: "dccm", + 5680: "auriga-router", + 5681: "ncxcp", + 5682: "brightcore", + 5683: "coap", + 5684: "coaps", + 5687: "gog-multiplayer", + 5688: "ggz", + 5689: "qmvideo", + 5713: "proshareaudio", + 5714: "prosharevideo", + 5715: "prosharedata", + 5716: "prosharerequest", + 5717: "prosharenotify", + 5718: "dpm", + 5719: "dpm-agent", + 5720: "ms-licensing", + 5721: "dtpt", + 5722: "msdfsr", + 5723: "omhs", + 5724: "omsdk", + 5728: "io-dist-group", + 5729: "openmail", + 5730: "unieng", + 5741: "ida-discover1", + 5742: "ida-discover2", + 5743: "watchdoc-pod", + 5744: "watchdoc", + 5745: "fcopy-server", + 5746: "fcopys-server", + 5747: "tunatic", + 5748: "tunalyzer", + 5750: "rscd", + 5755: "openmailg", + 5757: "x500ms", + 5766: "openmailns", + 5767: "s-openmail", + 5768: "openmailpxy", + 5769: "spramsca", + 5770: "spramsd", + 5771: "netagent", + 5777: "dali-port", + 5781: "3par-evts", + 5782: "3par-mgmt", + 5783: "3par-mgmt-ssl", + 5784: "ibar", + 5785: "3par-rcopy", + 5786: "cisco-redu", + 5787: "waascluster", + 5793: "xtreamx", + 5794: "spdp", + 5813: "icmpd", + 5814: "spt-automation", + 5859: "wherehoo", + 5863: "ppsuitemsg", + 5900: "rfb", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 5963: "indy", + 5968: "mppolicy-v5", + 5969: "mppolicy-mgr", + 5984: "couchdb", + 5985: "wsman", + 5986: "wsmans", + 5987: "wbem-rmi", + 5988: "wbem-http", + 5989: "wbem-https", + 5990: "wbem-exp-https", + 5991: "nuxsl", + 5992: "consul-insight", + 5999: "cvsup", + 6064: "ndl-ahp-svc", + 6065: "winpharaoh", + 6066: "ewctsp", + 6069: "trip", + 6070: "messageasap", + 6071: "ssdtp", + 6072: "diagnose-proc", + 6073: "directplay8", + 6074: "max", + 6080: "gue", + 6081: "geneve", + 6082: "p25cai", + 6083: "miami-bcast", + 6085: "konspire2b", + 6086: "pdtp", + 6087: "ldss", + 6088: "doglms-notify", + 6100: "synchronet-db", + 6101: "synchronet-rtc", + 6102: "synchronet-upd", + 6103: "rets", + 6104: "dbdb", + 6105: "primaserver", + 6106: "mpsserver", + 6107: "etc-control", + 6108: "sercomm-scadmin", + 6109: "globecast-id", + 6110: "softcm", + 6111: "spc", + 6112: "dtspcd", + 6118: "tipc", + 6122: "bex-webadmin", + 6123: "backup-express", + 6124: "pnbs", + 6133: "nbt-wol", + 6140: "pulsonixnls", + 6141: "meta-corp", + 6142: "aspentec-lm", + 6143: "watershed-lm", + 6144: "statsci1-lm", + 6145: "statsci2-lm", + 6146: "lonewolf-lm", + 6147: "montage-lm", + 6148: "ricardo-lm", + 6149: "tal-pod", + 6160: "ecmp-data", + 6161: "patrol-ism", + 6162: "patrol-coll", + 6163: "pscribe", + 6200: "lm-x", + 6201: "thermo-calc", + 6209: "qmtps", + 6222: "radmind", + 6241: "jeol-nsddp-1", + 6242: "jeol-nsddp-2", + 6243: "jeol-nsddp-3", + 6244: "jeol-nsddp-4", + 6251: "tl1-raw-ssl", + 6252: "tl1-ssh", + 6253: "crip", + 6268: "grid", + 6269: "grid-alt", + 6300: "bmc-grx", + 6301: "bmc-ctd-ldap", + 6306: "ufmp", + 6315: "scup-disc", + 6316: "abb-escp", + 6317: "nav-data", + 6320: "repsvc", + 6321: "emp-server1", + 6322: "emp-server2", + 6324: "hrd-ns-disc", + 6343: "sflow", + 6346: "gnutella-svc", + 6347: "gnutella-rtr", + 6350: "adap", + 6355: "pmcs", + 6360: "metaedit-mu", + 6363: "ndn", + 6370: "metaedit-se", + 6382: "metatude-mds", + 6389: "clariion-evr01", + 6390: "metaedit-ws", + 6417: "faxcomservice", + 6419: "svdrp-disc", + 6420: "nim-vdrshell", + 6421: "nim-wan", + 6443: "sun-sr-https", + 6444: "sge-qmaster", + 6445: "sge-execd", + 6446: "mysql-proxy", + 6455: "skip-cert-recv", + 6456: "skip-cert-send", + 6464: "ieee11073-20701", + 6471: "lvision-lm", + 6480: "sun-sr-http", + 6481: "servicetags", + 6482: "ldoms-mgmt", + 6483: "SunVTS-RMI", + 6484: "sun-sr-jms", + 6485: "sun-sr-iiop", + 6486: "sun-sr-iiops", + 6487: "sun-sr-iiop-aut", + 6488: "sun-sr-jmx", + 6489: "sun-sr-admin", + 6500: "boks", + 6501: "boks-servc", + 6502: "boks-servm", + 6503: "boks-clntd", + 6505: "badm-priv", + 6506: "badm-pub", + 6507: "bdir-priv", + 6508: "bdir-pub", + 6509: "mgcs-mfp-port", + 6510: "mcer-port", + 6511: "dccp-udp", + 6514: "syslog-tls", + 6515: "elipse-rec", + 6543: "lds-distrib", + 6544: "lds-dump", + 6547: "apc-6547", + 6548: "apc-6548", + 6549: "apc-6549", + 6550: "fg-sysupdate", + 6551: "sum", + 6558: "xdsxdm", + 6566: "sane-port", + 6568: "rp-reputation", + 6579: "affiliate", + 6580: "parsec-master", + 6581: "parsec-peer", + 6582: "parsec-game", + 6583: "joaJewelSuite", + 6619: "odette-ftps", + 6620: "kftp-data", + 6621: "kftp", + 6622: "mcftp", + 6623: "ktelnet", + 6626: "wago-service", + 6627: "nexgen", + 6628: "afesc-mc", + 6629: "nexgen-aux", + 6633: "cisco-vpath-tun", + 6634: "mpls-pm", + 6635: "mpls-udp", + 6636: "mpls-udp-dtls", + 6653: "openflow", + 6657: "palcom-disc", + 6670: "vocaltec-gold", + 6671: "p4p-portal", + 6672: "vision-server", + 6673: "vision-elmd", + 6678: "vfbp-disc", + 6679: "osaut", + 6689: "tsa", + 6696: "babel", + 6701: "kti-icad-srvr", + 6702: "e-design-net", + 6703: "e-design-web", + 6714: "ibprotocol", + 6715: "fibotrader-com", + 6767: "bmc-perf-agent", + 6768: "bmc-perf-mgrd", + 6769: "adi-gxp-srvprt", + 6770: "plysrv-http", + 6771: "plysrv-https", + 6784: "bfd-lag", + 6785: "dgpf-exchg", + 6786: "smc-jmx", + 6787: "smc-admin", + 6788: "smc-http", + 6790: "hnmp", + 6791: "hnm", + 6801: "acnet", + 6831: "ambit-lm", + 6841: "netmo-default", + 6842: "netmo-http", + 6850: "iccrushmore", + 6868: "acctopus-st", + 6888: "muse", + 6935: "ethoscan", + 6936: "xsmsvc", + 6946: "bioserver", + 6951: "otlp", + 6961: "jmact3", + 6962: "jmevt2", + 6963: "swismgr1", + 6964: "swismgr2", + 6965: "swistrap", + 6966: "swispol", + 6969: "acmsoda", + 6997: "MobilitySrv", + 6998: "iatp-highpri", + 6999: "iatp-normalpri", + 7000: "afs3-fileserver", + 7001: "afs3-callback", + 7002: "afs3-prserver", + 7003: "afs3-vlserver", + 7004: "afs3-kaserver", + 7005: "afs3-volser", + 7006: "afs3-errors", + 7007: "afs3-bos", + 7008: "afs3-update", + 7009: "afs3-rmtsys", + 7010: "ups-onlinet", + 7011: "talon-disc", + 7012: "talon-engine", + 7013: "microtalon-dis", + 7014: "microtalon-com", + 7015: "talon-webserver", + 7016: "spg", + 7017: "grasp", + 7019: "doceri-view", + 7020: "dpserve", + 7021: "dpserveadmin", + 7022: "ctdp", + 7023: "ct2nmcs", + 7024: "vmsvc", + 7025: "vmsvc-2", + 7030: "op-probe", + 7040: "quest-disc", + 7070: "arcp", + 7071: "iwg1", + 7080: "empowerid", + 7088: "zixi-transport", + 7095: "jdp-disc", + 7099: "lazy-ptop", + 7100: "font-service", + 7101: "elcn", + 7107: "aes-x170", + 7121: "virprot-lm", + 7128: "scenidm", + 7129: "scenccs", + 7161: "cabsm-comm", + 7162: "caistoragemgr", + 7163: "cacsambroker", + 7164: "fsr", + 7165: "doc-server", + 7166: "aruba-server", + 7169: "ccag-pib", + 7170: "nsrp", + 7171: "drm-production", + 7174: "clutild", + 7181: "janus-disc", + 7200: "fodms", + 7201: "dlip", + 7227: "ramp", + 7235: "aspcoordination", + 7244: "frc-hicp-disc", + 7262: "cnap", + 7272: "watchme-7272", + 7273: "oma-rlp", + 7274: "oma-rlp-s", + 7275: "oma-ulp", + 7276: "oma-ilp", + 7277: "oma-ilp-s", + 7278: "oma-dcdocbs", + 7279: "ctxlic", + 7280: "itactionserver1", + 7281: "itactionserver2", + 7282: "mzca-alert", + 7365: "lcm-server", + 7391: "mindfilesys", + 7392: "mrssrendezvous", + 7393: "nfoldman", + 7394: "fse", + 7395: "winqedit", + 7397: "hexarc", + 7400: "rtps-discovery", + 7401: "rtps-dd-ut", + 7402: "rtps-dd-mt", + 7410: "ionixnetmon", + 7411: "daqstream", + 7421: "mtportmon", + 7426: "pmdmgr", + 7427: "oveadmgr", + 7428: "ovladmgr", + 7429: "opi-sock", + 7430: "xmpv7", + 7431: "pmd", + 7437: "faximum", + 7443: "oracleas-https", + 7473: "rise", + 7491: "telops-lmd", + 7500: "silhouette", + 7501: "ovbus", + 7510: "ovhpas", + 7511: "pafec-lm", + 7542: "saratoga", + 7543: "atul", + 7544: "nta-ds", + 7545: "nta-us", + 7546: "cfs", + 7547: "cwmp", + 7548: "tidp", + 7549: "nls-tl", + 7550: "cloudsignaling", + 7560: "sncp", + 7566: "vsi-omega", + 7570: "aries-kfinder", + 7574: "coherence-disc", + 7588: "sun-lm", + 7606: "mipi-debug", + 7624: "indi", + 7627: "soap-http", + 7628: "zen-pawn", + 7629: "xdas", + 7633: "pmdfmgt", + 7648: "cuseeme", + 7674: "imqtunnels", + 7675: "imqtunnel", + 7676: "imqbrokerd", + 7677: "sun-user-https", + 7680: "pando-pub", + 7689: "collaber", + 7697: "klio", + 7707: "sync-em7", + 7708: "scinet", + 7720: "medimageportal", + 7724: "nsdeepfreezectl", + 7725: "nitrogen", + 7726: "freezexservice", + 7727: "trident-data", + 7728: "osvr", + 7734: "smip", + 7738: "aiagent", + 7741: "scriptview", + 7743: "sstp-1", + 7744: "raqmon-pdu", + 7747: "prgp", + 7777: "cbt", + 7778: "interwise", + 7779: "vstat", + 7781: "accu-lmgr", + 7784: "s-bfd", + 7786: "minivend", + 7787: "popup-reminders", + 7789: "office-tools", + 7794: "q3ade", + 7797: "pnet-conn", + 7798: "pnet-enc", + 7799: "altbsdp", + 7800: "asr", + 7801: "ssp-client", + 7802: "vns-tp", + 7810: "rbt-wanopt", + 7845: "apc-7845", + 7846: "apc-7846", + 7872: "mipv6tls", + 7880: "pss", + 7887: "ubroker", + 7900: "mevent", + 7901: "tnos-sp", + 7902: "tnos-dp", + 7903: "tnos-dps", + 7913: "qo-secure", + 7932: "t2-drm", + 7933: "t2-brm", + 7962: "generalsync", + 7967: "supercell", + 7979: "micromuse-ncps", + 7980: "quest-vista", + 7982: "sossd-disc", + 7998: "usicontentpush", + 7999: "irdmi2", + 8000: "irdmi", + 8001: "vcom-tunnel", + 8002: "teradataordbms", + 8003: "mcreport", + 8005: "mxi", + 8006: "wpl-disc", + 8007: "warppipe", + 8008: "http-alt", + 8019: "qbdb", + 8020: "intu-ec-svcdisc", + 8021: "intu-ec-client", + 8022: "oa-system", + 8025: "ca-audit-da", + 8026: "ca-audit-ds", + 8032: "pro-ed", + 8033: "mindprint", + 8034: "vantronix-mgmt", + 8040: "ampify", + 8041: "enguity-xccetp", + 8052: "senomix01", + 8053: "senomix02", + 8054: "senomix03", + 8055: "senomix04", + 8056: "senomix05", + 8057: "senomix06", + 8058: "senomix07", + 8059: "senomix08", + 8060: "aero", + 8074: "gadugadu", + 8080: "http-alt", + 8081: "sunproxyadmin", + 8082: "us-cli", + 8083: "us-srv", + 8086: "d-s-n", + 8087: "simplifymedia", + 8088: "radan-http", + 8097: "sac", + 8100: "xprint-server", + 8115: "mtl8000-matrix", + 8116: "cp-cluster", + 8118: "privoxy", + 8121: "apollo-data", + 8122: "apollo-admin", + 8128: "paycash-online", + 8129: "paycash-wbp", + 8130: "indigo-vrmi", + 8131: "indigo-vbcp", + 8132: "dbabble", + 8148: "isdd", + 8149: "eor-game", + 8160: "patrol", + 8161: "patrol-snmp", + 8182: "vmware-fdm", + 8184: "itach", + 8192: "spytechphone", + 8194: "blp1", + 8195: "blp2", + 8199: "vvr-data", + 8200: "trivnet1", + 8201: "trivnet2", + 8202: "aesop", + 8204: "lm-perfworks", + 8205: "lm-instmgr", + 8206: "lm-dta", + 8207: "lm-sserver", + 8208: "lm-webwatcher", + 8230: "rexecj", + 8231: "hncp-udp-port", + 8232: "hncp-dtls-port", + 8243: "synapse-nhttps", + 8276: "pando-sec", + 8280: "synapse-nhttp", + 8282: "libelle-disc", + 8292: "blp3", + 8294: "blp4", + 8300: "tmi", + 8301: "amberon", + 8320: "tnp-discover", + 8321: "tnp", + 8322: "garmin-marine", + 8351: "server-find", + 8376: "cruise-enum", + 8377: "cruise-swroute", + 8378: "cruise-config", + 8379: "cruise-diags", + 8380: "cruise-update", + 8383: "m2mservices", + 8384: "marathontp", + 8400: "cvd", + 8401: "sabarsd", + 8402: "abarsd", + 8403: "admind", + 8416: "espeech", + 8417: "espeech-rtp", + 8442: "cybro-a-bus", + 8443: "pcsync-https", + 8444: "pcsync-http", + 8445: "copy-disc", + 8450: "npmp", + 8472: "otv", + 8473: "vp2p", + 8474: "noteshare", + 8500: "fmtp", + 8501: "cmtp-av", + 8503: "lsp-self-ping", + 8554: "rtsp-alt", + 8555: "d-fence", + 8567: "dof-tunnel", + 8600: "asterix", + 8609: "canon-cpp-disc", + 8610: "canon-mfnp", + 8611: "canon-bjnp1", + 8612: "canon-bjnp2", + 8613: "canon-bjnp3", + 8614: "canon-bjnp4", + 8675: "msi-cps-rm-disc", + 8686: "sun-as-jmxrmi", + 8732: "dtp-net", + 8733: "ibus", + 8763: "mc-appserver", + 8764: "openqueue", + 8765: "ultraseek-http", + 8766: "amcs", + 8770: "dpap", + 8786: "msgclnt", + 8787: "msgsrvr", + 8793: "acd-pm", + 8800: "sunwebadmin", + 8804: "truecm", + 8805: "pfcp", + 8808: "ssports-bcast", + 8873: "dxspider", + 8880: "cddbp-alt", + 8883: "secure-mqtt", + 8888: "ddi-udp-1", + 8889: "ddi-udp-2", + 8890: "ddi-udp-3", + 8891: "ddi-udp-4", + 8892: "ddi-udp-5", + 8893: "ddi-udp-6", + 8894: "ddi-udp-7", + 8899: "ospf-lite", + 8900: "jmb-cds1", + 8901: "jmb-cds2", + 8910: "manyone-http", + 8911: "manyone-xml", + 8912: "wcbackup", + 8913: "dragonfly", + 8954: "cumulus-admin", + 8980: "nod-provider", + 8981: "nod-client", + 8989: "sunwebadmins", + 8990: "http-wmap", + 8991: "https-wmap", + 8999: "bctp", + 9000: "cslistener", + 9001: "etlservicemgr", + 9002: "dynamid", + 9007: "ogs-client", + 9009: "pichat", + 9020: "tambora", + 9021: "panagolin-ident", + 9022: "paragent", + 9023: "swa-1", + 9024: "swa-2", + 9025: "swa-3", + 9026: "swa-4", + 9060: "CardWeb-RT", + 9080: "glrpc", + 9084: "aurora", + 9085: "ibm-rsyscon", + 9086: "net2display", + 9087: "classic", + 9088: "sqlexec", + 9089: "sqlexec-ssl", + 9090: "websm", + 9091: "xmltec-xmlmail", + 9092: "XmlIpcRegSvc", + 9100: "hp-pdl-datastr", + 9101: "bacula-dir", + 9102: "bacula-fd", + 9103: "bacula-sd", + 9104: "peerwire", + 9105: "xadmin", + 9106: "astergate-disc", + 9119: "mxit", + 9131: "dddp", + 9160: "apani1", + 9161: "apani2", + 9162: "apani3", + 9163: "apani4", + 9164: "apani5", + 9191: "sun-as-jpda", + 9200: "wap-wsp", + 9201: "wap-wsp-wtp", + 9202: "wap-wsp-s", + 9203: "wap-wsp-wtp-s", + 9204: "wap-vcard", + 9205: "wap-vcal", + 9206: "wap-vcard-s", + 9207: "wap-vcal-s", + 9208: "rjcdb-vcards", + 9209: "almobile-system", + 9210: "oma-mlp", + 9211: "oma-mlp-s", + 9212: "serverviewdbms", + 9213: "serverstart", + 9214: "ipdcesgbs", + 9215: "insis", + 9216: "acme", + 9217: "fsc-port", + 9222: "teamcoherence", + 9255: "mon", + 9277: "traingpsdata", + 9278: "pegasus", + 9279: "pegasus-ctl", + 9280: "pgps", + 9281: "swtp-port1", + 9282: "swtp-port2", + 9283: "callwaveiam", + 9284: "visd", + 9285: "n2h2server", + 9286: "n2receive", + 9287: "cumulus", + 9292: "armtechdaemon", + 9293: "storview", + 9294: "armcenterhttp", + 9295: "armcenterhttps", + 9300: "vrace", + 9318: "secure-ts", + 9321: "guibase", + 9343: "mpidcmgr", + 9344: "mphlpdmc", + 9346: "ctechlicensing", + 9374: "fjdmimgr", + 9380: "boxp", + 9396: "fjinvmgr", + 9397: "mpidcagt", + 9400: "sec-t4net-srv", + 9401: "sec-t4net-clt", + 9402: "sec-pc2fax-srv", + 9418: "git", + 9443: "tungsten-https", + 9444: "wso2esb-console", + 9450: "sntlkeyssrvr", + 9500: "ismserver", + 9522: "sma-spw", + 9535: "mngsuite", + 9536: "laes-bf", + 9555: "trispen-sra", + 9592: "ldgateway", + 9593: "cba8", + 9594: "msgsys", + 9595: "pds", + 9596: "mercury-disc", + 9597: "pd-admin", + 9598: "vscp", + 9599: "robix", + 9600: "micromuse-ncpw", + 9612: "streamcomm-ds", + 9618: "condor", + 9628: "odbcpathway", + 9629: "uniport", + 9632: "mc-comm", + 9667: "xmms2", + 9668: "tec5-sdctp", + 9694: "client-wakeup", + 9695: "ccnx", + 9700: "board-roar", + 9747: "l5nas-parchan", + 9750: "board-voip", + 9753: "rasadv", + 9762: "tungsten-http", + 9800: "davsrc", + 9801: "sstp-2", + 9802: "davsrcs", + 9875: "sapv1", + 9878: "kca-service", + 9888: "cyborg-systems", + 9889: "gt-proxy", + 9898: "monkeycom", + 9899: "sctp-tunneling", + 9900: "iua", + 9901: "enrp", + 9903: "multicast-ping", + 9909: "domaintime", + 9911: "sype-transport", + 9950: "apc-9950", + 9951: "apc-9951", + 9952: "apc-9952", + 9953: "acis", + 9955: "alljoyn-mcm", + 9956: "alljoyn", + 9966: "odnsp", + 9987: "dsm-scm-target", + 9990: "osm-appsrvr", + 9991: "osm-oev", + 9992: "palace-1", + 9993: "palace-2", + 9994: "palace-3", + 9995: "palace-4", + 9996: "palace-5", + 9997: "palace-6", + 9998: "distinct32", + 9999: "distinct", + 10000: "ndmp", + 10001: "scp-config", + 10002: "documentum", + 10003: "documentum-s", + 10007: "mvs-capacity", + 10008: "octopus", + 10009: "swdtp-sv", + 10050: "zabbix-agent", + 10051: "zabbix-trapper", + 10080: "amanda", + 10081: "famdc", + 10100: "itap-ddtp", + 10101: "ezmeeting-2", + 10102: "ezproxy-2", + 10103: "ezrelay", + 10104: "swdtp", + 10107: "bctp-server", + 10110: "nmea-0183", + 10111: "nmea-onenet", + 10113: "netiq-endpoint", + 10114: "netiq-qcheck", + 10115: "netiq-endpt", + 10116: "netiq-voipa", + 10117: "iqrm", + 10128: "bmc-perf-sd", + 10160: "qb-db-server", + 10161: "snmpdtls", + 10162: "snmpdtls-trap", + 10200: "trisoap", + 10201: "rscs", + 10252: "apollo-relay", + 10253: "eapol-relay", + 10260: "axis-wimp-port", + 10288: "blocks", + 10439: "bngsync", + 10500: "hip-nat-t", + 10540: "MOS-lower", + 10541: "MOS-upper", + 10542: "MOS-aux", + 10543: "MOS-soap", + 10544: "MOS-soap-opt", + 10800: "gap", + 10805: "lpdg", + 10810: "nmc-disc", + 10860: "helix", + 10880: "bveapi", + 10990: "rmiaux", + 11000: "irisa", + 11001: "metasys", + 10023: "cefd-vmp", + 11095: "weave", + 11106: "sgi-lk", + 11108: "myq-termlink", + 11111: "vce", + 11112: "dicom", + 11161: "suncacao-snmp", + 11162: "suncacao-jmxmp", + 11163: "suncacao-rmi", + 11164: "suncacao-csa", + 11165: "suncacao-websvc", + 11171: "snss", + 11201: "smsqp", + 11208: "wifree", + 11211: "memcache", + 11319: "imip", + 11320: "imip-channels", + 11321: "arena-server", + 11367: "atm-uhas", + 11371: "hkp", + 11430: "lsdp", + 11600: "tempest-port", + 11720: "h323callsigalt", + 11723: "emc-xsw-dcache", + 11751: "intrepid-ssl", + 11796: "lanschool-mpt", + 11876: "xoraya", + 11877: "x2e-disc", + 11967: "sysinfo-sp", + 12000: "entextxid", + 12001: "entextnetwk", + 12002: "entexthigh", + 12003: "entextmed", + 12004: "entextlow", + 12005: "dbisamserver1", + 12006: "dbisamserver2", + 12007: "accuracer", + 12008: "accuracer-dbms", + 12009: "ghvpn", + 12012: "vipera", + 12013: "vipera-ssl", + 12109: "rets-ssl", + 12121: "nupaper-ss", + 12168: "cawas", + 12172: "hivep", + 12300: "linogridengine", + 12321: "warehouse-sss", + 12322: "warehouse", + 12345: "italk", + 12753: "tsaf", + 13160: "i-zipqd", + 13216: "bcslogc", + 13217: "rs-pias", + 13218: "emc-vcas-udp", + 13223: "powwow-client", + 13224: "powwow-server", + 13400: "doip-disc", + 13720: "bprd", + 13721: "bpdbm", + 13722: "bpjava-msvc", + 13724: "vnetd", + 13782: "bpcd", + 13783: "vopied", + 13785: "nbdb", + 13786: "nomdb", + 13818: "dsmcc-config", + 13819: "dsmcc-session", + 13820: "dsmcc-passthru", + 13821: "dsmcc-download", + 13822: "dsmcc-ccp", + 13894: "ucontrol", + 13929: "dta-systems", + 14000: "scotty-ft", + 14001: "sua", + 14002: "scotty-disc", + 14033: "sage-best-com1", + 14034: "sage-best-com2", + 14141: "vcs-app", + 14142: "icpp", + 14145: "gcm-app", + 14149: "vrts-tdd", + 14154: "vad", + 14250: "cps", + 14414: "ca-web-update", + 14936: "hde-lcesrvr-1", + 14937: "hde-lcesrvr-2", + 15000: "hydap", + 15118: "v2g-secc", + 15345: "xpilot", + 15363: "3link", + 15555: "cisco-snat", + 15660: "bex-xr", + 15740: "ptp", + 15998: "2ping", + 16003: "alfin", + 16161: "sun-sea-port", + 16309: "etb4j", + 16310: "pduncs", + 16311: "pdefmns", + 16360: "netserialext1", + 16361: "netserialext2", + 16367: "netserialext3", + 16368: "netserialext4", + 16384: "connected", + 16666: "vtp", + 16900: "newbay-snc-mc", + 16950: "sgcip", + 16991: "intel-rci-mp", + 16992: "amt-soap-http", + 16993: "amt-soap-https", + 16994: "amt-redir-tcp", + 16995: "amt-redir-tls", + 17007: "isode-dua", + 17185: "soundsvirtual", + 17219: "chipper", + 17220: "avtp", + 17221: "avdecc", + 17222: "cpsp", + 17224: "trdp-pd", + 17225: "trdp-md", + 17234: "integrius-stp", + 17235: "ssh-mgmt", + 17500: "db-lsp-disc", + 17729: "ea", + 17754: "zep", + 17755: "zigbee-ip", + 17756: "zigbee-ips", + 18000: "biimenu", + 18181: "opsec-cvp", + 18182: "opsec-ufp", + 18183: "opsec-sam", + 18184: "opsec-lea", + 18185: "opsec-omi", + 18186: "ohsc", + 18187: "opsec-ela", + 18241: "checkpoint-rtm", + 18262: "gv-pf", + 18463: "ac-cluster", + 18634: "rds-ib", + 18635: "rds-ip", + 18668: "vdmmesh-disc", + 18769: "ique", + 18881: "infotos", + 18888: "apc-necmp", + 19000: "igrid", + 19007: "scintilla", + 19191: "opsec-uaa", + 19194: "ua-secureagent", + 19220: "cora-disc", + 19283: "keysrvr", + 19315: "keyshadow", + 19398: "mtrgtrans", + 19410: "hp-sco", + 19411: "hp-sca", + 19412: "hp-sessmon", + 19539: "fxuptp", + 19540: "sxuptp", + 19541: "jcp", + 19788: "mle", + 19999: "dnp-sec", + 20000: "dnp", + 20001: "microsan", + 20002: "commtact-http", + 20003: "commtact-https", + 20005: "openwebnet", + 20012: "ss-idi-disc", + 20014: "opendeploy", + 20034: "nburn-id", + 20046: "tmophl7mts", + 20048: "mountd", + 20049: "nfsrdma", + 20167: "tolfab", + 20202: "ipdtp-port", + 20222: "ipulse-ics", + 20480: "emwavemsg", + 20670: "track", + 20999: "athand-mmp", + 21000: "irtrans", + 21554: "dfserver", + 21590: "vofr-gateway", + 21800: "tvpm", + 21845: "webphone", + 21846: "netspeak-is", + 21847: "netspeak-cs", + 21848: "netspeak-acd", + 21849: "netspeak-cps", + 22000: "snapenetio", + 22001: "optocontrol", + 22002: "optohost002", + 22003: "optohost003", + 22004: "optohost004", + 22005: "optohost004", + 22273: "wnn6", + 22305: "cis", + 22335: "shrewd-stream", + 22343: "cis-secure", + 22347: "wibukey", + 22350: "codemeter", + 22555: "vocaltec-phone", + 22763: "talikaserver", + 22800: "aws-brf", + 22951: "brf-gw", + 23000: "inovaport1", + 23001: "inovaport2", + 23002: "inovaport3", + 23003: "inovaport4", + 23004: "inovaport5", + 23005: "inovaport6", + 23272: "s102", + 23294: "5afe-disc", + 23333: "elxmgmt", + 23400: "novar-dbase", + 23401: "novar-alarm", + 23402: "novar-global", + 24000: "med-ltp", + 24001: "med-fsp-rx", + 24002: "med-fsp-tx", + 24003: "med-supp", + 24004: "med-ovw", + 24005: "med-ci", + 24006: "med-net-svc", + 24242: "filesphere", + 24249: "vista-4gl", + 24321: "ild", + 24322: "hid", + 24386: "intel-rci", + 24465: "tonidods", + 24554: "binkp", + 24577: "bilobit-update", + 24676: "canditv", + 24677: "flashfiler", + 24678: "proactivate", + 24680: "tcc-http", + 24850: "assoc-disc", + 24922: "find", + 25000: "icl-twobase1", + 25001: "icl-twobase2", + 25002: "icl-twobase3", + 25003: "icl-twobase4", + 25004: "icl-twobase5", + 25005: "icl-twobase6", + 25006: "icl-twobase7", + 25007: "icl-twobase8", + 25008: "icl-twobase9", + 25009: "icl-twobase10", + 25793: "vocaltec-hos", + 25900: "tasp-net", + 25901: "niobserver", + 25902: "nilinkanalyst", + 25903: "niprobe", + 25954: "bf-game", + 25955: "bf-master", + 26000: "quake", + 26133: "scscp", + 26208: "wnn6-ds", + 26260: "ezproxy", + 26261: "ezmeeting", + 26262: "k3software-svr", + 26263: "k3software-cli", + 26486: "exoline-udp", + 26487: "exoconfig", + 26489: "exonet", + 27345: "imagepump", + 27442: "jesmsjc", + 27504: "kopek-httphead", + 27782: "ars-vista", + 27999: "tw-auth-key", + 28000: "nxlmd", + 28119: "a27-ran-ran", + 28200: "voxelstorm", + 28240: "siemensgsm", + 29167: "otmp", + 30001: "pago-services1", + 30002: "pago-services2", + 30003: "amicon-fpsu-ra", + 30004: "amicon-fpsu-s", + 30260: "kingdomsonline", + 30832: "samsung-disc", + 30999: "ovobs", + 31016: "ka-kdp", + 31029: "yawn", + 31416: "xqosd", + 31457: "tetrinet", + 31620: "lm-mon", + 31765: "gamesmith-port", + 31948: "iceedcp-tx", + 31949: "iceedcp-rx", + 32034: "iracinghelper", + 32249: "t1distproc60", + 32483: "apm-link", + 32635: "sec-ntb-clnt", + 32636: "DMExpress", + 32767: "filenet-powsrm", + 32768: "filenet-tms", + 32769: "filenet-rpc", + 32770: "filenet-nch", + 32771: "filenet-rmi", + 32772: "filenet-pa", + 32773: "filenet-cm", + 32774: "filenet-re", + 32775: "filenet-pch", + 32776: "filenet-peior", + 32777: "filenet-obrok", + 32801: "mlsn", + 32896: "idmgratm", + 33123: "aurora-balaena", + 33331: "diamondport", + 33334: "speedtrace-disc", + 33434: "traceroute", + 33656: "snip-slave", + 34249: "turbonote-2", + 34378: "p-net-local", + 34379: "p-net-remote", + 34567: "edi_service", + 34962: "profinet-rt", + 34963: "profinet-rtm", + 34964: "profinet-cm", + 34980: "ethercat", + 35001: "rt-viewer", + 35004: "rt-classmanager", + 35100: "axio-disc", + 35355: "altova-lm-disc", + 36001: "allpeers", + 36411: "wlcp", + 36865: "kastenxpipe", + 37475: "neckar", + 37654: "unisys-eportal", + 38002: "crescoctrl-disc", + 38201: "galaxy7-data", + 38202: "fairview", + 38203: "agpolicy", + 39681: "turbonote-1", + 40000: "safetynetp", + 40023: "k-patentssensor", + 40841: "cscp", + 40842: "csccredir", + 40843: "csccfirewall", + 40853: "ortec-disc", + 41111: "fs-qos", + 41230: "z-wave-s", + 41794: "crestron-cip", + 41795: "crestron-ctp", + 42508: "candp", + 42509: "candrp", + 42510: "caerpc", + 43000: "recvr-rc-disc", + 43188: "reachout", + 43189: "ndm-agent-port", + 43190: "ip-provision", + 43210: "shaperai-disc", + 43439: "eq3-config", + 43440: "ew-disc-cmd", + 43441: "ciscocsdb", + 44321: "pmcd", + 44322: "pmcdproxy", + 44544: "domiq", + 44553: "rbr-debug", + 44600: "asihpi", + 44818: "EtherNet-IP-2", + 44900: "m3da-disc", + 45000: "asmp-mon", + 45054: "invision-ag", + 45514: "cloudcheck-ping", + 45678: "eba", + 45825: "qdb2service", + 45966: "ssr-servermgr", + 46999: "mediabox", + 47000: "mbus", + 47100: "jvl-mactalk", + 47557: "dbbrowse", + 47624: "directplaysrvr", + 47806: "ap", + 47808: "bacnet", + 47809: "presonus-ucnet", + 48000: "nimcontroller", + 48001: "nimspooler", + 48002: "nimhub", + 48003: "nimgtw", + 48128: "isnetserv", + 48129: "blp5", + 48556: "com-bardac-dw", + 48619: "iqobject", + 48653: "robotraconteur", + 49001: "nusdp-disc", +} +var sctpPortNames = map[SCTPPort]string{ + 9: "discard", + 20: "ftp-data", + 21: "ftp", + 22: "ssh", + 80: "http", + 179: "bgp", + 443: "https", + 1021: "exp1", + 1022: "exp2", + 1167: "cisco-ipsla", + 1720: "h323hostcall", + 2049: "nfs", + 2225: "rcip-itu", + 2904: "m2ua", + 2905: "m3ua", + 2944: "megaco-h248", + 2945: "h248-binary", + 3097: "itu-bicc-stc", + 3565: "m2pa", + 3863: "asap-sctp", + 3864: "asap-sctp-tls", + 3868: "diameter", + 4333: "ahsp", + 4502: "a25-fap-fgw", + 4711: "trinity-dist", + 4739: "ipfix", + 4740: "ipfixs", + 5060: "sip", + 5061: "sips", + 5090: "car", + 5091: "cxtp", + 5215: "noteza", + 5445: "smbdirect", + 5672: "amqp", + 5675: "v5ua", + 5868: "diameters", + 5910: "cm", + 5911: "cpdlc", + 5912: "fis", + 5913: "ads-c", + 6704: "frc-hp", + 6705: "frc-mp", + 6706: "frc-lp", + 6970: "conductor-mpx", + 7626: "simco", + 7701: "nfapi", + 7728: "osvr", + 8471: "pim-port", + 9082: "lcs-ap", + 9084: "aurora", + 9900: "iua", + 9901: "enrp-sctp", + 9902: "enrp-sctp-tls", + 11997: "wmereceiving", + 11998: "wmedistribution", + 11999: "wmereporting", + 14001: "sua", + 20049: "nfsrdma", + 25471: "rna", + 29118: "sgsap", + 29168: "sbcap", + 29169: "iuhsctpassoc", + 30100: "rwp", + 36412: "s1-control", + 36422: "x2-control", + 36423: "slmap", + 36424: "nq-ap", + 36443: "m2ap", + 36444: "m3ap", + 36462: "xw-control", + 38412: "ng-control", + 38422: "xn-control", + 38472: "f1-control", +} diff --git a/vendor/github.com/google/gopacket/layers/icmp4.go b/vendor/github.com/google/gopacket/layers/icmp4.go new file mode 100644 index 00000000..bd3f03f0 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp4.go @@ -0,0 +1,267 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "reflect" + + "github.com/google/gopacket" +) + +const ( + ICMPv4TypeEchoReply = 0 + ICMPv4TypeDestinationUnreachable = 3 + ICMPv4TypeSourceQuench = 4 + ICMPv4TypeRedirect = 5 + ICMPv4TypeEchoRequest = 8 + ICMPv4TypeRouterAdvertisement = 9 + ICMPv4TypeRouterSolicitation = 10 + ICMPv4TypeTimeExceeded = 11 + ICMPv4TypeParameterProblem = 12 + ICMPv4TypeTimestampRequest = 13 + ICMPv4TypeTimestampReply = 14 + ICMPv4TypeInfoRequest = 15 + ICMPv4TypeInfoReply = 16 + ICMPv4TypeAddressMaskRequest = 17 + ICMPv4TypeAddressMaskReply = 18 +) + +const ( + // DestinationUnreachable + ICMPv4CodeNet = 0 + ICMPv4CodeHost = 1 + ICMPv4CodeProtocol = 2 + ICMPv4CodePort = 3 + ICMPv4CodeFragmentationNeeded = 4 + ICMPv4CodeSourceRoutingFailed = 5 + ICMPv4CodeNetUnknown = 6 + ICMPv4CodeHostUnknown = 7 + ICMPv4CodeSourceIsolated = 8 + ICMPv4CodeNetAdminProhibited = 9 + ICMPv4CodeHostAdminProhibited = 10 + ICMPv4CodeNetTOS = 11 + ICMPv4CodeHostTOS = 12 + ICMPv4CodeCommAdminProhibited = 13 + ICMPv4CodeHostPrecedence = 14 + ICMPv4CodePrecedenceCutoff = 15 + + // TimeExceeded + ICMPv4CodeTTLExceeded = 0 + ICMPv4CodeFragmentReassemblyTimeExceeded = 1 + + // ParameterProblem + ICMPv4CodePointerIndicatesError = 0 + ICMPv4CodeMissingOption = 1 + ICMPv4CodeBadLength = 2 + + // Redirect + // ICMPv4CodeNet = same as for DestinationUnreachable + // ICMPv4CodeHost = same as for DestinationUnreachable + ICMPv4CodeTOSNet = 2 + ICMPv4CodeTOSHost = 3 +) + +type icmpv4TypeCodeInfoStruct struct { + typeStr string + codeStr *map[uint8]string +} + +var ( + icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{ + ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{ + "DestinationUnreachable", &map[uint8]string{ + ICMPv4CodeNet: "Net", + ICMPv4CodeHost: "Host", + ICMPv4CodeProtocol: "Protocol", + ICMPv4CodePort: "Port", + ICMPv4CodeFragmentationNeeded: "FragmentationNeeded", + ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed", + ICMPv4CodeNetUnknown: "NetUnknown", + ICMPv4CodeHostUnknown: "HostUnknown", + ICMPv4CodeSourceIsolated: "SourceIsolated", + ICMPv4CodeNetAdminProhibited: "NetAdminProhibited", + ICMPv4CodeHostAdminProhibited: "HostAdminProhibited", + ICMPv4CodeNetTOS: "NetTOS", + ICMPv4CodeHostTOS: "HostTOS", + ICMPv4CodeCommAdminProhibited: "CommAdminProhibited", + ICMPv4CodeHostPrecedence: "HostPrecedence", + ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff", + }, + }, + ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{ + "TimeExceeded", &map[uint8]string{ + ICMPv4CodeTTLExceeded: "TTLExceeded", + ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded", + }, + }, + ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{ + "ParameterProblem", &map[uint8]string{ + ICMPv4CodePointerIndicatesError: "PointerIndicatesError", + ICMPv4CodeMissingOption: "MissingOption", + ICMPv4CodeBadLength: "BadLength", + }, + }, + ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{ + "SourceQuench", nil, + }, + ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{ + "Redirect", &map[uint8]string{ + ICMPv4CodeNet: "Net", + ICMPv4CodeHost: "Host", + ICMPv4CodeTOSNet: "TOS+Net", + ICMPv4CodeTOSHost: "TOS+Host", + }, + }, + ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{ + "EchoRequest", nil, + }, + ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{ + "EchoReply", nil, + }, + ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{ + "TimestampRequest", nil, + }, + ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{ + "TimestampReply", nil, + }, + ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{ + "InfoRequest", nil, + }, + ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{ + "InfoReply", nil, + }, + ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{ + "RouterSolicitation", nil, + }, + ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{ + "RouterAdvertisement", nil, + }, + ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{ + "AddressMaskRequest", nil, + }, + ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{ + "AddressMaskReply", nil, + }, + } +) + +type ICMPv4TypeCode uint16 + +// Type returns the ICMPv4 type field. +func (a ICMPv4TypeCode) Type() uint8 { + return uint8(a >> 8) +} + +// Code returns the ICMPv4 code field. +func (a ICMPv4TypeCode) Code() uint8 { + return uint8(a) +} + +func (a ICMPv4TypeCode) String() string { + t, c := a.Type(), a.Code() + strInfo, ok := icmpv4TypeCodeInfo[t] + if !ok { + // Unknown ICMPv4 type field + return fmt.Sprintf("%d(%d)", t, c) + } + typeStr := strInfo.typeStr + if strInfo.codeStr == nil && c == 0 { + // The ICMPv4 type does not make use of the code field + return fmt.Sprintf("%s", strInfo.typeStr) + } + if strInfo.codeStr == nil && c != 0 { + // The ICMPv4 type does not make use of the code field, but it is present anyway + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + codeStr, ok := (*strInfo.codeStr)[c] + if !ok { + // We don't know this ICMPv4 code; print the numerical value + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + return fmt.Sprintf("%s(%s)", typeStr, codeStr) +} + +func (a ICMPv4TypeCode) GoString() string { + t := reflect.TypeOf(a) + return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code()) +} + +// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer. +func (a ICMPv4TypeCode) SerializeTo(bytes []byte) { + binary.BigEndian.PutUint16(bytes, uint16(a)) +} + +// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode +// gopacket type from the ICMPv4 type and code values. +func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode { + return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code})) +} + +// ICMPv4 is the layer for IPv4 ICMP packet data. +type ICMPv4 struct { + BaseLayer + TypeCode ICMPv4TypeCode + Checksum uint16 + Id uint16 + Seq uint16 +} + +// LayerType returns LayerTypeICMPv4. +func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet") + } + i.TypeCode = CreateICMPv4TypeCode(data[0], data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.Id = binary.BigEndian.Uint16(data[4:6]) + i.Seq = binary.BigEndian.Uint16(data[6:8]) + i.BaseLayer = BaseLayer{data[:8], data[8:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + i.TypeCode.SerializeTo(bytes) + binary.BigEndian.PutUint16(bytes[4:], i.Id) + binary.BigEndian.PutUint16(bytes[6:], i.Seq) + if opts.ComputeChecksums { + bytes[2] = 0 + bytes[3] = 0 + i.Checksum = tcpipChecksum(b.Bytes(), 0) + } + binary.BigEndian.PutUint16(bytes[2:], i.Checksum) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv4) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv4 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv4) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv4{} + return decodingLayerDecoder(i, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/icmp6.go b/vendor/github.com/google/gopacket/layers/icmp6.go new file mode 100644 index 00000000..09afd11a --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp6.go @@ -0,0 +1,266 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "reflect" + + "github.com/google/gopacket" +) + +const ( + // The following are from RFC 4443 + ICMPv6TypeDestinationUnreachable = 1 + ICMPv6TypePacketTooBig = 2 + ICMPv6TypeTimeExceeded = 3 + ICMPv6TypeParameterProblem = 4 + ICMPv6TypeEchoRequest = 128 + ICMPv6TypeEchoReply = 129 + + // The following are from RFC 4861 + ICMPv6TypeRouterSolicitation = 133 + ICMPv6TypeRouterAdvertisement = 134 + ICMPv6TypeNeighborSolicitation = 135 + ICMPv6TypeNeighborAdvertisement = 136 + ICMPv6TypeRedirect = 137 + + // The following are from RFC 2710 + ICMPv6TypeMLDv1MulticastListenerQueryMessage = 130 + ICMPv6TypeMLDv1MulticastListenerReportMessage = 131 + ICMPv6TypeMLDv1MulticastListenerDoneMessage = 132 + + // The following are from RFC 3810 + ICMPv6TypeMLDv2MulticastListenerReportMessageV2 = 143 +) + +const ( + // DestinationUnreachable + ICMPv6CodeNoRouteToDst = 0 + ICMPv6CodeAdminProhibited = 1 + ICMPv6CodeBeyondScopeOfSrc = 2 + ICMPv6CodeAddressUnreachable = 3 + ICMPv6CodePortUnreachable = 4 + ICMPv6CodeSrcAddressFailedPolicy = 5 + ICMPv6CodeRejectRouteToDst = 6 + + // TimeExceeded + ICMPv6CodeHopLimitExceeded = 0 + ICMPv6CodeFragmentReassemblyTimeExceeded = 1 + + // ParameterProblem + ICMPv6CodeErroneousHeaderField = 0 + ICMPv6CodeUnrecognizedNextHeader = 1 + ICMPv6CodeUnrecognizedIPv6Option = 2 +) + +type icmpv6TypeCodeInfoStruct struct { + typeStr string + codeStr *map[uint8]string +} + +var ( + icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{ + ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{ + "DestinationUnreachable", &map[uint8]string{ + ICMPv6CodeNoRouteToDst: "NoRouteToDst", + ICMPv6CodeAdminProhibited: "AdminProhibited", + ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc", + ICMPv6CodeAddressUnreachable: "AddressUnreachable", + ICMPv6CodePortUnreachable: "PortUnreachable", + ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy", + ICMPv6CodeRejectRouteToDst: "RejectRouteToDst", + }, + }, + ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{ + "PacketTooBig", nil, + }, + ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{ + "TimeExceeded", &map[uint8]string{ + ICMPv6CodeHopLimitExceeded: "HopLimitExceeded", + ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded", + }, + }, + ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{ + "ParameterProblem", &map[uint8]string{ + ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField", + ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader", + ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option", + }, + }, + ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{ + "EchoRequest", nil, + }, + ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{ + "EchoReply", nil, + }, + ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{ + "RouterSolicitation", nil, + }, + ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{ + "RouterAdvertisement", nil, + }, + ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{ + "NeighborSolicitation", nil, + }, + ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{ + "NeighborAdvertisement", nil, + }, + ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{ + "Redirect", nil, + }, + } +) + +type ICMPv6TypeCode uint16 + +// Type returns the ICMPv6 type field. +func (a ICMPv6TypeCode) Type() uint8 { + return uint8(a >> 8) +} + +// Code returns the ICMPv6 code field. +func (a ICMPv6TypeCode) Code() uint8 { + return uint8(a) +} + +func (a ICMPv6TypeCode) String() string { + t, c := a.Type(), a.Code() + strInfo, ok := icmpv6TypeCodeInfo[t] + if !ok { + // Unknown ICMPv6 type field + return fmt.Sprintf("%d(%d)", t, c) + } + typeStr := strInfo.typeStr + if strInfo.codeStr == nil && c == 0 { + // The ICMPv6 type does not make use of the code field + return fmt.Sprintf("%s", strInfo.typeStr) + } + if strInfo.codeStr == nil && c != 0 { + // The ICMPv6 type does not make use of the code field, but it is present anyway + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + codeStr, ok := (*strInfo.codeStr)[c] + if !ok { + // We don't know this ICMPv6 code; print the numerical value + return fmt.Sprintf("%s(Code: %d)", typeStr, c) + } + return fmt.Sprintf("%s(%s)", typeStr, codeStr) +} + +func (a ICMPv6TypeCode) GoString() string { + t := reflect.TypeOf(a) + return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code()) +} + +// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer. +func (a ICMPv6TypeCode) SerializeTo(bytes []byte) { + binary.BigEndian.PutUint16(bytes, uint16(a)) +} + +// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode +// gopacket type from the ICMPv6 type and code values. +func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode { + return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code})) +} + +// ICMPv6 is the layer for IPv6 ICMP packet data +type ICMPv6 struct { + BaseLayer + TypeCode ICMPv6TypeCode + Checksum uint16 + // TypeBytes is deprecated and always nil. See the different ICMPv6 message types + // instead (e.g. ICMPv6TypeRouterSolicitation). + TypeBytes []byte + tcpipchecksum +} + +// LayerType returns LayerTypeICMPv6. +func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 } + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return errors.New("ICMP layer less then 4 bytes for ICMPv6 packet") + } + i.TypeCode = CreateICMPv6TypeCode(data[0], data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.BaseLayer = BaseLayer{data[:4], data[4:]} + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + i.TypeCode.SerializeTo(bytes) + + if opts.ComputeChecksums { + bytes[2] = 0 + bytes[3] = 0 + csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6) + if err != nil { + return err + } + i.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[2:], i.Checksum) + + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6 +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6) NextLayerType() gopacket.LayerType { + switch i.TypeCode.Type() { + case ICMPv6TypeEchoRequest: + return LayerTypeICMPv6Echo + case ICMPv6TypeEchoReply: + return LayerTypeICMPv6Echo + case ICMPv6TypeRouterSolicitation: + return LayerTypeICMPv6RouterSolicitation + case ICMPv6TypeRouterAdvertisement: + return LayerTypeICMPv6RouterAdvertisement + case ICMPv6TypeNeighborSolicitation: + return LayerTypeICMPv6NeighborSolicitation + case ICMPv6TypeNeighborAdvertisement: + return LayerTypeICMPv6NeighborAdvertisement + case ICMPv6TypeRedirect: + return LayerTypeICMPv6Redirect + case ICMPv6TypeMLDv1MulticastListenerQueryMessage: // Same Code for MLDv1 Query and MLDv2 Query + if len(i.Payload) > 20 { // Only payload size differs + return LayerTypeMLDv2MulticastListenerQuery + } else { + return LayerTypeMLDv1MulticastListenerQuery + } + case ICMPv6TypeMLDv1MulticastListenerDoneMessage: + return LayerTypeMLDv1MulticastListenerDone + case ICMPv6TypeMLDv1MulticastListenerReportMessage: + return LayerTypeMLDv1MulticastListenerReport + case ICMPv6TypeMLDv2MulticastListenerReportMessageV2: + return LayerTypeMLDv2MulticastListenerReport + } + + return gopacket.LayerTypePayload +} + +func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6{} + return decodingLayerDecoder(i, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/icmp6msg.go b/vendor/github.com/google/gopacket/layers/icmp6msg.go new file mode 100644 index 00000000..d9268db0 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/icmp6msg.go @@ -0,0 +1,578 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "net" + "time" + + "github.com/google/gopacket" +) + +// Based on RFC 4861 + +// ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option. +type ICMPv6Opt uint8 + +const ( + _ ICMPv6Opt = iota + + // ICMPv6OptSourceAddress contains the link-layer address of the sender of + // the packet. It is used in the Neighbor Solicitation, Router + // Solicitation, and Router Advertisement packets. Must be ignored for other + // Neighbor discovery messages. + ICMPv6OptSourceAddress + + // ICMPv6OptTargetAddress contains the link-layer address of the target. It + // is used in Neighbor Advertisement and Redirect packets. Must be ignored + // for other Neighbor discovery messages. + ICMPv6OptTargetAddress + + // ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes + // for Address Autoconfiguration. The Prefix Information option appears in + // Router Advertisement packets and MUST be silently ignored for other + // messages. + ICMPv6OptPrefixInfo + + // ICMPv6OptRedirectedHeader is used in Redirect messages and contains all + // or part of the packet that is being redirected. + ICMPv6OptRedirectedHeader + + // ICMPv6OptMTU is used in Router Advertisement messages to ensure that all + // nodes on a link use the same MTU value in those cases where the link MTU + // is not well known. This option MUST be silently ignored for other + // Neighbor Discovery messages. + ICMPv6OptMTU +) + +// ICMPv6Echo represents the structure of a ping. +type ICMPv6Echo struct { + BaseLayer + Identifier uint16 + SeqNumber uint16 +} + +// ICMPv6RouterSolicitation is sent by hosts to find routers. +type ICMPv6RouterSolicitation struct { + BaseLayer + Options ICMPv6Options +} + +// ICMPv6RouterAdvertisement is sent by routers in response to Solicitation. +type ICMPv6RouterAdvertisement struct { + BaseLayer + HopLimit uint8 + Flags uint8 + RouterLifetime uint16 + ReachableTime uint32 + RetransTimer uint32 + Options ICMPv6Options +} + +// ICMPv6NeighborSolicitation is sent to request the link-layer address of a +// target node. +type ICMPv6NeighborSolicitation struct { + BaseLayer + TargetAddress net.IP + Options ICMPv6Options +} + +// ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation. +type ICMPv6NeighborAdvertisement struct { + BaseLayer + Flags uint8 + TargetAddress net.IP + Options ICMPv6Options +} + +// ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node +// on the path to a destination. +type ICMPv6Redirect struct { + BaseLayer + TargetAddress net.IP + DestinationAddress net.IP + Options ICMPv6Options +} + +// ICMPv6Option contains the type and data for a single option. +type ICMPv6Option struct { + Type ICMPv6Opt + Data []byte +} + +// ICMPv6Options is a slice of ICMPv6Option. +type ICMPv6Options []ICMPv6Option + +func (i ICMPv6Opt) String() string { + switch i { + case ICMPv6OptSourceAddress: + return "SourceAddress" + case ICMPv6OptTargetAddress: + return "TargetAddress" + case ICMPv6OptPrefixInfo: + return "PrefixInfo" + case ICMPv6OptRedirectedHeader: + return "RedirectedHeader" + case ICMPv6OptMTU: + return "MTU" + default: + return fmt.Sprintf("Unknown(%d)", i) + } +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6Echo +} + +// LayerType returns LayerTypeICMPv6Echo. +func (i *ICMPv6Echo) LayerType() gopacket.LayerType { + return LayerTypeICMPv6Echo +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6Echo) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6Echo) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return errors.New("ICMP layer less then 4 bytes for ICMPv6 Echo") + } + i.Identifier = binary.BigEndian.Uint16(data[0:2]) + i.SeqNumber = binary.BigEndian.Uint16(data[2:4]) + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6Echo) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(4) + if err != nil { + return err + } + + binary.BigEndian.PutUint16(buf, i.Identifier) + binary.BigEndian.PutUint16(buf[2:], i.SeqNumber) + return nil +} + +// LayerType returns LayerTypeICMPv6. +func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType { + return LayerTypeICMPv6RouterSolicitation +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + // first 4 bytes are reserved followed by options + if len(data) < 4 { + df.SetTruncated() + return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation") + } + + // truncate old options + i.Options = i.Options[:0] + + return i.Options.DecodeFromBytes(data[4:], df) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := i.Options.SerializeTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(4) + if err != nil { + return err + } + + copy(buf, lotsOfZeros[:4]) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6RouterSolicitation) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6RouterSolicitation +} + +// LayerType returns LayerTypeICMPv6RouterAdvertisement. +func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType { + return LayerTypeICMPv6RouterAdvertisement +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + df.SetTruncated() + return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement") + } + + i.HopLimit = uint8(data[0]) + // M, O bit followed by 6 reserved bits + i.Flags = uint8(data[1]) + i.RouterLifetime = binary.BigEndian.Uint16(data[2:4]) + i.ReachableTime = binary.BigEndian.Uint32(data[4:8]) + i.RetransTimer = binary.BigEndian.Uint32(data[8:12]) + i.BaseLayer = BaseLayer{data, nil} // assume no payload + + // truncate old options + i.Options = i.Options[:0] + + return i.Options.DecodeFromBytes(data[12:], df) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := i.Options.SerializeTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(12) + if err != nil { + return err + } + + buf[0] = byte(i.HopLimit) + buf[1] = byte(i.Flags) + binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime) + binary.BigEndian.PutUint32(buf[4:], i.ReachableTime) + binary.BigEndian.PutUint32(buf[8:], i.RetransTimer) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6RouterAdvertisement) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6RouterAdvertisement +} + +// ManagedAddressConfig is true when addresses are available via DHCPv6. If +// set, the OtherConfig flag is redundant. +func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool { + return i.Flags&0x80 != 0 +} + +// OtherConfig is true when there is other configuration information available +// via DHCPv6. For example, DNS-related information. +func (i *ICMPv6RouterAdvertisement) OtherConfig() bool { + return i.Flags&0x40 != 0 +} + +// LayerType returns LayerTypeICMPv6NeighborSolicitation. +func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType { + return LayerTypeICMPv6NeighborSolicitation +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 20 { + df.SetTruncated() + return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation") + } + + i.TargetAddress = net.IP(data[4:20]) + i.BaseLayer = BaseLayer{data, nil} // assume no payload + + // truncate old options + i.Options = i.Options[:0] + + return i.Options.DecodeFromBytes(data[20:], df) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := i.Options.SerializeTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(20) + if err != nil { + return err + } + + copy(buf, lotsOfZeros[:4]) + copy(buf[4:], i.TargetAddress) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6NeighborSolicitation) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6NeighborSolicitation +} + +// LayerType returns LayerTypeICMPv6NeighborAdvertisement. +func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType { + return LayerTypeICMPv6NeighborAdvertisement +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 20 { + df.SetTruncated() + return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement") + } + + i.Flags = uint8(data[0]) + i.TargetAddress = net.IP(data[4:20]) + i.BaseLayer = BaseLayer{data, nil} // assume no payload + + // truncate old options + i.Options = i.Options[:0] + + return i.Options.DecodeFromBytes(data[20:], df) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := i.Options.SerializeTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(20) + if err != nil { + return err + } + + buf[0] = byte(i.Flags) + copy(buf[1:], lotsOfZeros[:3]) + copy(buf[4:], i.TargetAddress) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6NeighborAdvertisement) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6NeighborAdvertisement +} + +// Router indicates whether the sender is a router or not. +func (i *ICMPv6NeighborAdvertisement) Router() bool { + return i.Flags&0x80 != 0 +} + +// Solicited indicates whether the advertisement was solicited or not. +func (i *ICMPv6NeighborAdvertisement) Solicited() bool { + return i.Flags&0x40 != 0 +} + +// Override indicates whether the advertisement should Override an existing +// cache entry. +func (i *ICMPv6NeighborAdvertisement) Override() bool { + return i.Flags&0x20 != 0 +} + +// LayerType returns LayerTypeICMPv6Redirect. +func (i *ICMPv6Redirect) LayerType() gopacket.LayerType { + return LayerTypeICMPv6Redirect +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 36 { + df.SetTruncated() + return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect") + } + + i.TargetAddress = net.IP(data[4:20]) + i.DestinationAddress = net.IP(data[20:36]) + i.BaseLayer = BaseLayer{data, nil} // assume no payload + + // truncate old options + i.Options = i.Options[:0] + + return i.Options.DecodeFromBytes(data[36:], df) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := i.Options.SerializeTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(36) + if err != nil { + return err + } + + copy(buf, lotsOfZeros[:4]) + copy(buf[4:], i.TargetAddress) + copy(buf[20:], i.DestinationAddress) + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *ICMPv6Redirect) CanDecode() gopacket.LayerClass { + return LayerTypeICMPv6Redirect +} + +func (i ICMPv6Option) String() string { + hd := hex.EncodeToString(i.Data) + if len(hd) > 0 { + hd = " 0x" + hd + } + + switch i.Type { + case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress: + return fmt.Sprintf("ICMPv6Option(%s:%v)", + i.Type, + net.HardwareAddr(i.Data)) + case ICMPv6OptPrefixInfo: + if len(i.Data) == 30 { + prefixLen := uint8(i.Data[0]) + onLink := (i.Data[1]&0x80 != 0) + autonomous := (i.Data[1]&0x40 != 0) + validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second + preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second + + prefix := net.IP(i.Data[14:]) + + return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)", + i.Type, + prefix, prefixLen, + onLink, autonomous, + validLifetime, preferredLifetime) + } + case ICMPv6OptRedirectedHeader: + // could invoke IP decoder on data... probably best not to + break + case ICMPv6OptMTU: + if len(i.Data) == 6 { + return fmt.Sprintf("ICMPv6Option(%s:%v)", + i.Type, + binary.BigEndian.Uint32(i.Data[2:])) + } + + } + return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd) +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + for len(data) > 0 { + if len(data) < 2 { + df.SetTruncated() + return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option") + } + + // unit is 8 octets, convert to bytes + length := int(data[1]) * 8 + + if length == 0 { + df.SetTruncated() + return errors.New("ICMPv6 message option with length 0") + } + + if len(data) < length { + df.SetTruncated() + return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length) + } + + o := ICMPv6Option{ + Type: ICMPv6Opt(data[0]), + Data: data[2:length], + } + + // chop off option we just consumed + data = data[length:] + + *i = append(*i, o) + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + for _, opt := range []ICMPv6Option(*i) { + length := len(opt.Data) + 2 + buf, err := b.PrependBytes(length) + if err != nil { + return err + } + + buf[0] = byte(opt.Type) + buf[1] = byte(length / 8) + copy(buf[2:], opt.Data) + } + + return nil +} + +func decodeICMPv6Echo(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6Echo{} + return decodingLayerDecoder(i, data, p) +} + +func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6RouterSolicitation{} + return decodingLayerDecoder(i, data, p) +} + +func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6RouterAdvertisement{} + return decodingLayerDecoder(i, data, p) +} + +func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6NeighborSolicitation{} + return decodingLayerDecoder(i, data, p) +} + +func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6NeighborAdvertisement{} + return decodingLayerDecoder(i, data, p) +} + +func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error { + i := &ICMPv6Redirect{} + return decodingLayerDecoder(i, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/igmp.go b/vendor/github.com/google/gopacket/layers/igmp.go new file mode 100644 index 00000000..d0084153 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/igmp.go @@ -0,0 +1,355 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "net" + "time" + + "github.com/google/gopacket" +) + +type IGMPType uint8 + +const ( + IGMPMembershipQuery IGMPType = 0x11 // General or group specific query + IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report + IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report + IGMPLeaveGroup IGMPType = 0x17 // Leave Group + IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report +) + +// String conversions for IGMP message types +func (i IGMPType) String() string { + switch i { + case IGMPMembershipQuery: + return "IGMP Membership Query" + case IGMPMembershipReportV1: + return "IGMPv1 Membership Report" + case IGMPMembershipReportV2: + return "IGMPv2 Membership Report" + case IGMPMembershipReportV3: + return "IGMPv3 Membership Report" + case IGMPLeaveGroup: + return "Leave Group" + default: + return "" + } +} + +type IGMPv3GroupRecordType uint8 + +const ( + IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x + IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x + IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x + IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x + IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x + IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x +) + +func (i IGMPv3GroupRecordType) String() string { + switch i { + case IGMPIsIn: + return "MODE_IS_INCLUDE" + case IGMPIsEx: + return "MODE_IS_EXCLUDE" + case IGMPToIn: + return "CHANGE_TO_INCLUDE_MODE" + case IGMPToEx: + return "CHANGE_TO_EXCLUDE_MODE" + case IGMPAllow: + return "ALLOW_NEW_SOURCES" + case IGMPBlock: + return "BLOCK_OLD_SOURCES" + default: + return "" + } +} + +// IGMP represents an IGMPv3 message. +type IGMP struct { + BaseLayer + Type IGMPType + MaxResponseTime time.Duration + Checksum uint16 + GroupAddress net.IP + SupressRouterProcessing bool + RobustnessValue uint8 + IntervalTime time.Duration + SourceAddresses []net.IP + NumberOfGroupRecords uint16 + NumberOfSources uint16 + GroupRecords []IGMPv3GroupRecord + Version uint8 // IGMP protocol version +} + +// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet. +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Max Resp Time | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type IGMPv1or2 struct { + BaseLayer + Type IGMPType // IGMP message type + MaxResponseTime time.Duration // meaningful only in Membership Query messages + Checksum uint16 // 16-bit checksum of entire ip payload + GroupAddress net.IP // either 0 or an IP multicast address + Version uint8 +} + +// decodeResponse dissects IGMPv1 or IGMPv2 packet. +func (i *IGMPv1or2) decodeResponse(data []byte) error { + if len(data) < 8 { + return errors.New("IGMP packet too small") + } + + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.GroupAddress = net.IP(data[4:8]) + + return nil +} + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x22 | Reserved | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reserved | Number of Group Records (M) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [1] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [2] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Group Record [M] . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Record Type | Aux Data Len | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Multicast Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . Auxiliary Data . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message. +type IGMPv3GroupRecord struct { + Type IGMPv3GroupRecordType + AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec. + NumberOfSources uint16 + MulticastAddress net.IP + SourceAddresses []net.IP + AuxData uint32 // NOT USED +} + +func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error { + if len(data) < 8 { + return errors.New("IGMPv3 Membership Report too small #1") + } + + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8]) + + recordOffset := 8 + for j := 0; j < int(i.NumberOfGroupRecords); j++ { + if len(data) < recordOffset+8 { + return errors.New("IGMPv3 Membership Report too small #2") + } + + var gr IGMPv3GroupRecord + gr.Type = IGMPv3GroupRecordType(data[recordOffset]) + gr.AuxDataLen = data[recordOffset+1] + gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4]) + gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8]) + + if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 { + return errors.New("IGMPv3 Membership Report too small #3") + } + + // append source address records. + for i := 0; i < int(gr.NumberOfSources); i++ { + sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4]) + gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr) + } + + i.GroupRecords = append(i.GroupRecords, gr) + recordOffset += 8 + 4*int(gr.NumberOfSources) + } + return nil +} + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x11 | Max Resp Code | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Resv |S| QRV | QQIC | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- . -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11 +func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error { + if len(data) < 12 { + return errors.New("IGMPv3 Membership Query too small #1") + } + + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.SupressRouterProcessing = data[8]&0x8 != 0 + i.GroupAddress = net.IP(data[4:8]) + i.RobustnessValue = data[8] & 0x7 + i.IntervalTime = igmpTimeDecode(data[9]) + i.NumberOfSources = binary.BigEndian.Uint16(data[10:12]) + + if len(data) < 12+int(i.NumberOfSources)*4 { + return errors.New("IGMPv3 Membership Query too small #2") + } + + for j := 0; j < int(i.NumberOfSources); j++ { + i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4])) + } + + return nil +} + +// igmpTimeDecode decodes the duration created by the given byte, using the +// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1. +func igmpTimeDecode(t uint8) time.Duration { + if t&0x80 == 0 { + return time.Millisecond * 100 * time.Duration(t) + } + mant := (t & 0x70) >> 4 + exp := t & 0x0F + return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3)) +} + +// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats. +func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP } +func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP } + +func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + return errors.New("IGMP Packet too small") + } + + i.Type = IGMPType(data[0]) + i.MaxResponseTime = igmpTimeDecode(data[1]) + i.Checksum = binary.BigEndian.Uint16(data[2:4]) + i.GroupAddress = net.IP(data[4:8]) + + return nil +} + +func (i *IGMPv1or2) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +func (i *IGMPv1or2) CanDecode() gopacket.LayerClass { + return LayerTypeIGMP +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 1 { + return errors.New("IGMP packet is too small") + } + + // common IGMP header values between versions 1..3 of IGMP specification.. + i.Type = IGMPType(data[0]) + + switch i.Type { + case IGMPMembershipQuery: + i.decodeIGMPv3MembershipQuery(data) + case IGMPMembershipReportV3: + i.decodeIGMPv3MembershipReport(data) + default: + return errors.New("unsupported IGMP type") + } + + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (i *IGMP) CanDecode() gopacket.LayerClass { + return LayerTypeIGMP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (i *IGMP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the +// IGMP type are performed against byte[0], logic then iniitalizes and +// passes the appropriate struct (IGMP or IGMPv1or2) to +// decodingLayerDecoder. +func decodeIGMP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 1 { + return errors.New("IGMP packet is too small") + } + + // byte 0 contains IGMP message type. + switch IGMPType(data[0]) { + case IGMPMembershipQuery: + // IGMPv3 Membership Query payload is >= 12 + if len(data) >= 12 { + i := &IGMP{Version: 3} + return decodingLayerDecoder(i, data, p) + } else if len(data) == 8 { + i := &IGMPv1or2{} + if data[1] == 0x00 { + i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0 + } else { + i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0 + } + + return decodingLayerDecoder(i, data, p) + } + case IGMPMembershipReportV3: + i := &IGMP{Version: 3} + return decodingLayerDecoder(i, data, p) + case IGMPMembershipReportV1: + i := &IGMPv1or2{Version: 1} + return decodingLayerDecoder(i, data, p) + case IGMPLeaveGroup, IGMPMembershipReportV2: + // leave group and Query Report v2 used in IGMPv2 only. + i := &IGMPv1or2{Version: 2} + return decodingLayerDecoder(i, data, p) + default: + } + + return errors.New("Unable to determine IGMP type.") +} diff --git a/vendor/github.com/google/gopacket/layers/ip4.go b/vendor/github.com/google/gopacket/layers/ip4.go new file mode 100644 index 00000000..2b3c0c6b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip4.go @@ -0,0 +1,325 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + "strings" + + "github.com/google/gopacket" +) + +type IPv4Flag uint8 + +const ( + IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;) + IPv4DontFragment IPv4Flag = 1 << 1 + IPv4MoreFragments IPv4Flag = 1 << 0 +) + +func (f IPv4Flag) String() string { + var s []string + if f&IPv4EvilBit != 0 { + s = append(s, "Evil") + } + if f&IPv4DontFragment != 0 { + s = append(s, "DF") + } + if f&IPv4MoreFragments != 0 { + s = append(s, "MF") + } + return strings.Join(s, "|") +} + +// IPv4 is the header of an IP packet. +type IPv4 struct { + BaseLayer + Version uint8 + IHL uint8 + TOS uint8 + Length uint16 + Id uint16 + Flags IPv4Flag + FragOffset uint16 + TTL uint8 + Protocol IPProtocol + Checksum uint16 + SrcIP net.IP + DstIP net.IP + Options []IPv4Option + Padding []byte +} + +// LayerType returns LayerTypeIPv4 +func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 } +func (i *IPv4) NetworkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP) +} + +type IPv4Option struct { + OptionType uint8 + OptionLength uint8 + OptionData []byte +} + +func (i IPv4Option) String() string { + return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData) +} + +// for the current ipv4 options, return the number of bytes (including +// padding that the options used) +func (ip *IPv4) getIPv4OptionSize() uint8 { + optionSize := uint8(0) + for _, opt := range ip.Options { + switch opt.OptionType { + case 0: + // this is the end of option lists + optionSize++ + case 1: + // this is the padding + optionSize++ + default: + optionSize += opt.OptionLength + + } + } + // make sure the options are aligned to 32 bit boundary + if (optionSize % 4) != 0 { + optionSize += 4 - (optionSize % 4) + } + return optionSize +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + optionLength := ip.getIPv4OptionSize() + bytes, err := b.PrependBytes(20 + int(optionLength)) + if err != nil { + return err + } + if opts.FixLengths { + ip.IHL = 5 + (optionLength / 4) + ip.Length = uint16(len(b.Bytes())) + } + bytes[0] = (ip.Version << 4) | ip.IHL + bytes[1] = ip.TOS + binary.BigEndian.PutUint16(bytes[2:], ip.Length) + binary.BigEndian.PutUint16(bytes[4:], ip.Id) + binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags()) + bytes[8] = ip.TTL + bytes[9] = byte(ip.Protocol) + if err := ip.AddressTo4(); err != nil { + return err + } + copy(bytes[12:16], ip.SrcIP) + copy(bytes[16:20], ip.DstIP) + + curLocation := 20 + // Now, we will encode the options + for _, opt := range ip.Options { + switch opt.OptionType { + case 0: + // this is the end of option lists + bytes[curLocation] = 0 + curLocation++ + case 1: + // this is the padding + bytes[curLocation] = 1 + curLocation++ + default: + bytes[curLocation] = opt.OptionType + bytes[curLocation+1] = opt.OptionLength + + // sanity checking to protect us from buffer overrun + if len(opt.OptionData) > int(opt.OptionLength-2) { + return errors.New("option length is smaller than length of option data") + } + copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData) + curLocation += int(opt.OptionLength) + } + } + + if opts.ComputeChecksums { + ip.Checksum = checksum(bytes) + } + binary.BigEndian.PutUint16(bytes[10:], ip.Checksum) + return nil +} + +func checksum(bytes []byte) uint16 { + // Clear checksum bytes + bytes[10] = 0 + bytes[11] = 0 + + // Compute checksum + var csum uint32 + for i := 0; i < len(bytes); i += 2 { + csum += uint32(bytes[i]) << 8 + csum += uint32(bytes[i+1]) + } + for { + // Break when sum is less or equals to 0xFFFF + if csum <= 65535 { + break + } + // Add carry to the sum + csum = (csum >> 16) + uint32(uint16(csum)) + } + // Flip all the bits + return ^uint16(csum) +} + +func (ip *IPv4) flagsfrags() (ff uint16) { + ff |= uint16(ip.Flags) << 13 + ff |= ip.FragOffset + return +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 20 { + df.SetTruncated() + return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data)) + } + flagsfrags := binary.BigEndian.Uint16(data[6:8]) + + ip.Version = uint8(data[0]) >> 4 + ip.IHL = uint8(data[0]) & 0x0F + ip.TOS = data[1] + ip.Length = binary.BigEndian.Uint16(data[2:4]) + ip.Id = binary.BigEndian.Uint16(data[4:6]) + ip.Flags = IPv4Flag(flagsfrags >> 13) + ip.FragOffset = flagsfrags & 0x1FFF + ip.TTL = data[8] + ip.Protocol = IPProtocol(data[9]) + ip.Checksum = binary.BigEndian.Uint16(data[10:12]) + ip.SrcIP = data[12:16] + ip.DstIP = data[16:20] + ip.Options = ip.Options[:0] + ip.Padding = nil + // Set up an initial guess for contents/payload... we'll reset these soon. + ip.BaseLayer = BaseLayer{Contents: data} + + // This code is added for the following enviroment: + // * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver ) + if ip.Length == 0 { + // If using TSO(TCP Segmentation Offload), length is zero. + // The actual packet length is the length of data. + ip.Length = uint16(len(data)) + } + + if ip.Length < 20 { + return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length) + } else if ip.IHL < 5 { + return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL) + } else if int(ip.IHL*4) > int(ip.Length) { + return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length) + } + if cmp := len(data) - int(ip.Length); cmp > 0 { + data = data[:ip.Length] + } else if cmp < 0 { + df.SetTruncated() + if int(ip.IHL)*4 > len(data) { + return errors.New("Not all IP header bytes available") + } + } + ip.Contents = data[:ip.IHL*4] + ip.Payload = data[ip.IHL*4:] + // From here on, data contains the header options. + data = data[20 : ip.IHL*4] + // Pull out IP options + for len(data) > 0 { + if ip.Options == nil { + // Pre-allocate to avoid growing the slice too much. + ip.Options = make([]IPv4Option, 0, 4) + } + opt := IPv4Option{OptionType: data[0]} + switch opt.OptionType { + case 0: // End of options + opt.OptionLength = 1 + ip.Options = append(ip.Options, opt) + ip.Padding = data[1:] + return nil + case 1: // 1 byte padding + opt.OptionLength = 1 + data = data[1:] + ip.Options = append(ip.Options, opt) + default: + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data)) + } + opt.OptionLength = data[1] + if len(data) < int(opt.OptionLength) { + df.SetTruncated() + return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength) + } + if opt.OptionLength <= 2 { + return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength) + } + opt.OptionData = data[2:opt.OptionLength] + data = data[opt.OptionLength:] + ip.Options = append(ip.Options, opt) + } + } + return nil +} + +func (i *IPv4) CanDecode() gopacket.LayerClass { + return LayerTypeIPv4 +} + +func (i *IPv4) NextLayerType() gopacket.LayerType { + if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 { + return gopacket.LayerTypeFragment + } + return i.Protocol.LayerType() +} + +func decodeIPv4(data []byte, p gopacket.PacketBuilder) error { + ip := &IPv4{} + err := ip.DecodeFromBytes(data, p) + p.AddLayer(ip) + p.SetNetworkLayer(ip) + if err != nil { + return err + } + return p.NextDecoder(ip.NextLayerType()) +} + +func checkIPv4Address(addr net.IP) (net.IP, error) { + if c := addr.To4(); c != nil { + return c, nil + } + if len(addr) == net.IPv6len { + return nil, errors.New("address is IPv6") + } + return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len) +} + +func (ip *IPv4) AddressTo4() error { + var src, dst net.IP + + if addr, err := checkIPv4Address(ip.SrcIP); err != nil { + return fmt.Errorf("Invalid source IPv4 address (%s)", err) + } else { + src = addr + } + if addr, err := checkIPv4Address(ip.DstIP); err != nil { + return fmt.Errorf("Invalid destination IPv4 address (%s)", err) + } else { + dst = addr + } + ip.SrcIP = src + ip.DstIP = dst + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ip6.go b/vendor/github.com/google/gopacket/layers/ip6.go new file mode 100644 index 00000000..87b9d33d --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ip6.go @@ -0,0 +1,722 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +const ( + // IPv6HopByHopOptionJumbogram code as defined in RFC 2675 + IPv6HopByHopOptionJumbogram = 0xC2 +) + +const ( + ipv6MaxPayloadLength = 65535 +) + +// IPv6 is the layer for the IPv6 header. +type IPv6 struct { + // http://www.networksorcery.com/enp/protocol/ipv6.htm + BaseLayer + Version uint8 + TrafficClass uint8 + FlowLabel uint32 + Length uint16 + NextHeader IPProtocol + HopLimit uint8 + SrcIP net.IP + DstIP net.IP + HopByHop *IPv6HopByHop + // hbh will be pointed to by HopByHop if that layer exists. + hbh IPv6HopByHop +} + +// LayerType returns LayerTypeIPv6 +func (ipv6 *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 } + +// NetworkFlow returns this new Flow (EndpointIPv6, SrcIP, DstIP) +func (ipv6 *IPv6) NetworkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointIPv6, ipv6.SrcIP, ipv6.DstIP) +} + +// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found +func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) { + var tlv *IPv6HopByHopOption + + for _, t := range hopopts.Options { + if t.OptionType == IPv6HopByHopOptionJumbogram { + tlv = t + break + } + } + if tlv == nil { + // Not found + return 0, false, nil + } + if len(tlv.OptionData) != 4 { + return 0, false, errors.New("Jumbo length TLV data must have length 4") + } + l := binary.BigEndian.Uint32(tlv.OptionData) + if l <= ipv6MaxPayloadLength { + return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1) + } + // Found + return l, true, nil +} + +// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist +// (if necessary add hop-by-hop header) +func addIPv6JumboOption(ip6 *IPv6) { + var tlv *IPv6HopByHopOption + + if ip6.HopByHop == nil { + // Add IPv6 HopByHop + ip6.HopByHop = &IPv6HopByHop{} + ip6.HopByHop.NextHeader = ip6.NextHeader + ip6.HopByHop.HeaderLength = 0 + ip6.NextHeader = IPProtocolIPv6HopByHop + } + for _, t := range ip6.HopByHop.Options { + if t.OptionType == IPv6HopByHopOptionJumbogram { + tlv = t + break + } + } + if tlv == nil { + // Add Jumbo TLV + tlv = &IPv6HopByHopOption{} + ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv) + } + tlv.SetJumboLength(0) +} + +// Set jumbo length in serialized IPv6 payload (starting with HopByHop header) +func setIPv6PayloadJumboLength(hbh []byte) error { + pLen := len(hbh) + if pLen < 8 { + //HopByHop is minimum 8 bytes + return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen) + } + hbhLen := int((hbh[1] + 1) * 8) + if hbhLen > pLen { + return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen) + } + offset := 2 //start with options + for offset < hbhLen { + opt := hbh[offset] + if opt == 0 { + //Pad1 + offset++ + continue + } + optLen := int(hbh[offset+1]) + if opt == IPv6HopByHopOptionJumbogram { + if optLen == 4 { + binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen)) + return nil + } + return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen) + } + offset += 2 + optLen + } + return errors.New("Jumbo TLV not found") +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (ipv6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var jumbo bool + var err error + + payload := b.Bytes() + pLen := len(payload) + if pLen > ipv6MaxPayloadLength { + jumbo = true + if opts.FixLengths { + // We need to set the length later because the hop-by-hop header may + // not exist or else need padding, so pLen may yet change + addIPv6JumboOption(ipv6) + } else if ipv6.HopByHop == nil { + return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen) + } else { + _, ok, err := getIPv6HopByHopJumboLength(ipv6.HopByHop) + if err != nil { + return err + } + if !ok { + return errors.New("Missing jumbo length hop-by-hop option") + } + } + } + + hbhAlreadySerialized := false + if ipv6.HopByHop != nil { + for _, l := range b.Layers() { + if l == LayerTypeIPv6HopByHop { + hbhAlreadySerialized = true + break + } + } + } + if ipv6.HopByHop != nil && !hbhAlreadySerialized { + if ipv6.NextHeader != IPProtocolIPv6HopByHop { + // Just fix it instead of throwing an error + ipv6.NextHeader = IPProtocolIPv6HopByHop + } + err = ipv6.HopByHop.SerializeTo(b, opts) + if err != nil { + return err + } + payload = b.Bytes() + pLen = len(payload) + if opts.FixLengths && jumbo { + err := setIPv6PayloadJumboLength(payload) + if err != nil { + return err + } + } + } + + if !jumbo && pLen > ipv6MaxPayloadLength { + return errors.New("Cannot fit payload into IPv6 header") + } + bytes, err := b.PrependBytes(40) + if err != nil { + return err + } + bytes[0] = (ipv6.Version << 4) | (ipv6.TrafficClass >> 4) + bytes[1] = (ipv6.TrafficClass << 4) | uint8(ipv6.FlowLabel>>16) + binary.BigEndian.PutUint16(bytes[2:], uint16(ipv6.FlowLabel)) + if opts.FixLengths { + if jumbo { + ipv6.Length = 0 + } else { + ipv6.Length = uint16(pLen) + } + } + binary.BigEndian.PutUint16(bytes[4:], ipv6.Length) + bytes[6] = byte(ipv6.NextHeader) + bytes[7] = byte(ipv6.HopLimit) + if err := ipv6.AddressTo16(); err != nil { + return err + } + copy(bytes[8:], ipv6.SrcIP) + copy(bytes[24:], ipv6.DstIP) + return nil +} + +// DecodeFromBytes implementation according to gopacket.DecodingLayer +func (ipv6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 40 { + df.SetTruncated() + return fmt.Errorf("Invalid ip6 header. Length %d less than 40", len(data)) + } + ipv6.Version = uint8(data[0]) >> 4 + ipv6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF) + ipv6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF + ipv6.Length = binary.BigEndian.Uint16(data[4:6]) + ipv6.NextHeader = IPProtocol(data[6]) + ipv6.HopLimit = data[7] + ipv6.SrcIP = data[8:24] + ipv6.DstIP = data[24:40] + ipv6.HopByHop = nil + ipv6.BaseLayer = BaseLayer{data[:40], data[40:]} + + // We treat a HopByHop IPv6 option as part of the IPv6 packet, since its + // options are crucial for understanding what's actually happening per packet. + if ipv6.NextHeader == IPProtocolIPv6HopByHop { + err := ipv6.hbh.DecodeFromBytes(ipv6.Payload, df) + if err != nil { + return err + } + ipv6.HopByHop = &ipv6.hbh + pEnd, jumbo, err := getIPv6HopByHopJumboLength(ipv6.HopByHop) + if err != nil { + return err + } + if jumbo && ipv6.Length == 0 { + pEnd := int(pEnd) + if pEnd > len(ipv6.Payload) { + df.SetTruncated() + pEnd = len(ipv6.Payload) + } + ipv6.Payload = ipv6.Payload[:pEnd] + return nil + } else if jumbo && ipv6.Length != 0 { + return errors.New("IPv6 has jumbo length and IPv6 length is not 0") + } else if !jumbo && ipv6.Length == 0 { + return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option") + } else { + ipv6.Payload = ipv6.Payload[ipv6.hbh.ActualLength:] + } + } + + if ipv6.Length == 0 { + return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ipv6.NextHeader) + } + + pEnd := int(ipv6.Length) + if pEnd > len(ipv6.Payload) { + df.SetTruncated() + pEnd = len(ipv6.Payload) + } + ipv6.Payload = ipv6.Payload[:pEnd] + + return nil +} + +// CanDecode implementation according to gopacket.DecodingLayer +func (ipv6 *IPv6) CanDecode() gopacket.LayerClass { + return LayerTypeIPv6 +} + +// NextLayerType implementation according to gopacket.DecodingLayer +func (ipv6 *IPv6) NextLayerType() gopacket.LayerType { + if ipv6.HopByHop != nil { + return ipv6.HopByHop.NextHeader.LayerType() + } + return ipv6.NextHeader.LayerType() +} + +func decodeIPv6(data []byte, p gopacket.PacketBuilder) error { + ip6 := &IPv6{} + err := ip6.DecodeFromBytes(data, p) + p.AddLayer(ip6) + p.SetNetworkLayer(ip6) + if ip6.HopByHop != nil { + p.AddLayer(ip6.HopByHop) + } + if err != nil { + return err + } + return p.NextDecoder(ip6.NextLayerType()) +} + +type ipv6HeaderTLVOption struct { + OptionType, OptionLength uint8 + ActualLength int + OptionData []byte + OptionAlignment [2]uint8 // Xn+Y = [2]uint8{X, Y} +} + +func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int { + if fixLengths { + h.OptionLength = uint8(len(h.OptionData)) + } + length := int(h.OptionLength) + 2 + if !dryrun { + data[0] = h.OptionType + data[1] = h.OptionLength + copy(data[2:], h.OptionData) + } + return length +} + +func decodeIPv6HeaderTLVOption(data []byte, df gopacket.DecodeFeedback) (h *ipv6HeaderTLVOption, _ error) { + if len(data) < 2 { + df.SetTruncated() + return nil, errors.New("IPv6 header option too small") + } + h = &ipv6HeaderTLVOption{} + if data[0] == 0 { + h.ActualLength = 1 + return + } + h.OptionType = data[0] + h.OptionLength = data[1] + h.ActualLength = int(h.OptionLength) + 2 + if len(data) < h.ActualLength { + df.SetTruncated() + return nil, errors.New("IPv6 header TLV option too small") + } + h.OptionData = data[2:h.ActualLength] + return +} + +func serializeTLVOptionPadding(data []byte, padLength int) { + if padLength <= 0 { + return + } + if padLength == 1 { + data[0] = 0x0 + return + } + tlvLength := uint8(padLength) - 2 + data[0] = 0x1 + data[1] = tlvLength + if tlvLength != 0 { + for k := range data[2:] { + data[k+2] = 0x0 + } + } + return +} + +// If buf is 'nil' do a serialize dry run +func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int { + var l int + + dryrun := buf == nil + length := 2 + for _, opt := range options { + if fixLengths { + x := int(opt.OptionAlignment[0]) + y := int(opt.OptionAlignment[1]) + if x != 0 { + n := length / x + offset := x*n + y + if offset < length { + offset += x + } + if length != offset { + pad := offset - length + if !dryrun { + serializeTLVOptionPadding(buf[length-2:], pad) + } + length += pad + } + } + } + if dryrun { + l = opt.serializeTo(nil, fixLengths, true) + } else { + l = opt.serializeTo(buf[length-2:], fixLengths, false) + } + length += l + } + if fixLengths { + pad := length % 8 + if pad != 0 { + if !dryrun { + serializeTLVOptionPadding(buf[length-2:], pad) + } + length += pad + } + } + return length - 2 +} + +type ipv6ExtensionBase struct { + BaseLayer + NextHeader IPProtocol + HeaderLength uint8 + ActualLength int +} + +func decodeIPv6ExtensionBase(data []byte, df gopacket.DecodeFeedback) (i ipv6ExtensionBase, returnedErr error) { + if len(data) < 2 { + df.SetTruncated() + return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than 2", len(data)) + } + i.NextHeader = IPProtocol(data[0]) + i.HeaderLength = data[1] + i.ActualLength = int(i.HeaderLength)*8 + 8 + if len(data) < i.ActualLength { + return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than specified length %d", len(data), i.ActualLength) + } + i.Contents = data[:i.ActualLength] + i.Payload = data[i.ActualLength:] + return +} + +// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6 +// extensions. You can use it with a DecodingLayerParser to handle IPv6 stacks +// which may or may not have extensions. +type IPv6ExtensionSkipper struct { + NextHeader IPProtocol + BaseLayer +} + +// DecodeFromBytes implementation according to gopacket.DecodingLayer +func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + extension, err := decodeIPv6ExtensionBase(data, df) + if err != nil { + return err + } + i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]} + i.NextHeader = extension.NextHeader + return nil +} + +// CanDecode implementation according to gopacket.DecodingLayer +func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass { + return LayerClassIPv6Extension +} + +// NextLayerType implementation according to gopacket.DecodingLayer +func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType { + return i.NextHeader.LayerType() +} + +// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension. +type IPv6HopByHopOption ipv6HeaderTLVOption + +// IPv6HopByHop is the IPv6 hop-by-hop extension. +type IPv6HopByHop struct { + ipv6ExtensionBase + Options []*IPv6HopByHopOption +} + +// LayerType returns LayerTypeIPv6HopByHop. +func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop } + +// SerializeTo implementation according to gopacket.SerializableLayer +func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var bytes []byte + var err error + + o := make([]*ipv6HeaderTLVOption, 0, len(i.Options)) + for _, v := range i.Options { + o = append(o, (*ipv6HeaderTLVOption)(v)) + } + + l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths) + bytes, err = b.PrependBytes(l) + if err != nil { + return err + } + serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths) + + length := len(bytes) + 2 + if length%8 != 0 { + return errors.New("IPv6HopByHop actual length must be multiple of 8") + } + bytes, err = b.PrependBytes(2) + if err != nil { + return err + } + bytes[0] = uint8(i.NextHeader) + if opts.FixLengths { + i.HeaderLength = uint8((length / 8) - 1) + } + bytes[1] = uint8(i.HeaderLength) + return nil +} + +// DecodeFromBytes implementation according to gopacket.DecodingLayer +func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + var err error + i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df) + if err != nil { + return err + } + i.Options = i.Options[:0] + offset := 2 + for offset < i.ActualLength { + opt, err := decodeIPv6HeaderTLVOption(data[offset:], df) + if err != nil { + return err + } + i.Options = append(i.Options, (*IPv6HopByHopOption)(opt)) + offset += opt.ActualLength + } + return nil +} + +func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6HopByHop{} + err := i.DecodeFromBytes(data, p) + p.AddLayer(i) + if err != nil { + return err + } + return p.NextDecoder(i.NextHeader) +} + +// SetJumboLength adds the IPv6HopByHopOptionJumbogram with the given length +func (o *IPv6HopByHopOption) SetJumboLength(len uint32) { + o.OptionType = IPv6HopByHopOptionJumbogram + o.OptionLength = 4 + o.ActualLength = 6 + if o.OptionData == nil { + o.OptionData = make([]byte, 4) + } + binary.BigEndian.PutUint32(o.OptionData, len) + o.OptionAlignment = [2]uint8{4, 2} +} + +// IPv6Routing is the IPv6 routing extension. +type IPv6Routing struct { + ipv6ExtensionBase + RoutingType uint8 + SegmentsLeft uint8 + // This segment is supposed to be zero according to RFC2460, the second set of + // 4 bytes in the extension. + Reserved []byte + // SourceRoutingIPs is the set of IPv6 addresses requested for source routing, + // set only if RoutingType == 0. + SourceRoutingIPs []net.IP +} + +// LayerType returns LayerTypeIPv6Routing. +func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing } + +func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error { + base, err := decodeIPv6ExtensionBase(data, p) + if err != nil { + return err + } + i := &IPv6Routing{ + ipv6ExtensionBase: base, + RoutingType: data[2], + SegmentsLeft: data[3], + Reserved: data[4:8], + } + switch i.RoutingType { + case 0: // Source routing + if (i.ActualLength-8)%16 != 0 { + return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength) + } + for d := i.Contents[8:]; len(d) >= 16; d = d[16:] { + i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16])) + } + default: + return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType) + } + p.AddLayer(i) + return p.NextDecoder(i.NextHeader) +} + +// IPv6Fragment is the IPv6 fragment header, used for packet +// fragmentation/defragmentation. +type IPv6Fragment struct { + BaseLayer + NextHeader IPProtocol + // Reserved1 is bits [8-16), from least to most significant, 0-indexed + Reserved1 uint8 + FragmentOffset uint16 + // Reserved2 is bits [29-31), from least to most significant, 0-indexed + Reserved2 uint8 + MoreFragments bool + Identification uint32 +} + +// LayerType returns LayerTypeIPv6Fragment. +func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment } + +func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 8 { + p.SetTruncated() + return fmt.Errorf("Invalid ip6-fragment header. Length %d less than 8", len(data)) + } + i := &IPv6Fragment{ + BaseLayer: BaseLayer{data[:8], data[8:]}, + NextHeader: IPProtocol(data[0]), + Reserved1: data[1], + FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3, + Reserved2: data[3] & 0x6 >> 1, + MoreFragments: data[3]&0x1 != 0, + Identification: binary.BigEndian.Uint32(data[4:8]), + } + p.AddLayer(i) + return p.NextDecoder(gopacket.DecodeFragment) +} + +// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension. +type IPv6DestinationOption ipv6HeaderTLVOption + +// IPv6Destination is the IPv6 destination options header. +type IPv6Destination struct { + ipv6ExtensionBase + Options []*IPv6DestinationOption +} + +// LayerType returns LayerTypeIPv6Destination. +func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination } + +// DecodeFromBytes implementation according to gopacket.DecodingLayer +func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + var err error + i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df) + if err != nil { + return err + } + offset := 2 + for offset < i.ActualLength { + opt, err := decodeIPv6HeaderTLVOption(data[offset:], df) + if err != nil { + return err + } + i.Options = append(i.Options, (*IPv6DestinationOption)(opt)) + offset += opt.ActualLength + } + return nil +} + +func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error { + i := &IPv6Destination{} + err := i.DecodeFromBytes(data, p) + p.AddLayer(i) + if err != nil { + return err + } + return p.NextDecoder(i.NextHeader) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var bytes []byte + var err error + + o := make([]*ipv6HeaderTLVOption, 0, len(i.Options)) + for _, v := range i.Options { + o = append(o, (*ipv6HeaderTLVOption)(v)) + } + + l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths) + bytes, err = b.PrependBytes(l) + if err != nil { + return err + } + serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths) + + length := len(bytes) + 2 + if length%8 != 0 { + return errors.New("IPv6Destination actual length must be multiple of 8") + } + bytes, err = b.PrependBytes(2) + if err != nil { + return err + } + bytes[0] = uint8(i.NextHeader) + if opts.FixLengths { + i.HeaderLength = uint8((length / 8) - 1) + } + bytes[1] = uint8(i.HeaderLength) + return nil +} + +func checkIPv6Address(addr net.IP) error { + if len(addr) == net.IPv6len { + return nil + } + if len(addr) == net.IPv4len { + return errors.New("address is IPv4") + } + return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len) +} + +// AddressTo16 ensures IPv6.SrcIP and IPv6.DstIP are actually IPv6 addresses (i.e. 16 byte addresses) +func (ipv6 *IPv6) AddressTo16() error { + if err := checkIPv6Address(ipv6.SrcIP); err != nil { + return fmt.Errorf("Invalid source IPv6 address (%s)", err) + } + if err := checkIPv6Address(ipv6.DstIP); err != nil { + return fmt.Errorf("Invalid destination IPv6 address (%s)", err) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ipsec.go b/vendor/github.com/google/gopacket/layers/ipsec.go new file mode 100644 index 00000000..12f31caf --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ipsec.go @@ -0,0 +1,77 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +// IPSecAH is the authentication header for IPv4/6 defined in +// http://tools.ietf.org/html/rfc2402 +type IPSecAH struct { + // While the auth header can be used for both IPv4 and v6, its format is that of + // an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase + // to build it. + ipv6ExtensionBase + Reserved uint16 + SPI, Seq uint32 + AuthenticationData []byte +} + +// LayerType returns LayerTypeIPSecAH. +func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH } + +func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 12 { + p.SetTruncated() + return errors.New("IPSec AH packet less than 12 bytes") + } + i := &IPSecAH{ + ipv6ExtensionBase: ipv6ExtensionBase{ + NextHeader: IPProtocol(data[0]), + HeaderLength: data[1], + }, + Reserved: binary.BigEndian.Uint16(data[2:4]), + SPI: binary.BigEndian.Uint32(data[4:8]), + Seq: binary.BigEndian.Uint32(data[8:12]), + } + i.ActualLength = (int(i.HeaderLength) + 2) * 4 + if len(data) < i.ActualLength { + p.SetTruncated() + return errors.New("Truncated AH packet < ActualLength") + } + i.AuthenticationData = data[12:i.ActualLength] + i.Contents = data[:i.ActualLength] + i.Payload = data[i.ActualLength:] + p.AddLayer(i) + return p.NextDecoder(i.NextHeader) +} + +// IPSecESP is the encapsulating security payload defined in +// http://tools.ietf.org/html/rfc2406 +type IPSecESP struct { + BaseLayer + SPI, Seq uint32 + // Encrypted contains the encrypted set of bytes sent in an ESP + Encrypted []byte +} + +// LayerType returns LayerTypeIPSecESP. +func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP } + +func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error { + i := &IPSecESP{ + BaseLayer: BaseLayer{data, nil}, + SPI: binary.BigEndian.Uint32(data[:4]), + Seq: binary.BigEndian.Uint32(data[4:8]), + Encrypted: data[8:], + } + p.AddLayer(i) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/layertypes.go b/vendor/github.com/google/gopacket/layers/layertypes.go new file mode 100644 index 00000000..69d25ae8 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/layertypes.go @@ -0,0 +1,223 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +var ( + LayerTypeARP = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)}) + LayerTypeCiscoDiscovery = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)}) + LayerTypeEthernetCTP = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)}) + LayerTypeEthernetCTPForwardData = gopacket.RegisterLayerType(13, gopacket.LayerTypeMetadata{Name: "EthernetCTPForwardData", Decoder: nil}) + LayerTypeEthernetCTPReply = gopacket.RegisterLayerType(14, gopacket.LayerTypeMetadata{Name: "EthernetCTPReply", Decoder: nil}) + LayerTypeDot1Q = gopacket.RegisterLayerType(15, gopacket.LayerTypeMetadata{Name: "Dot1Q", Decoder: gopacket.DecodeFunc(decodeDot1Q)}) + LayerTypeEtherIP = gopacket.RegisterLayerType(16, gopacket.LayerTypeMetadata{Name: "EtherIP", Decoder: gopacket.DecodeFunc(decodeEtherIP)}) + LayerTypeEthernet = gopacket.RegisterLayerType(17, gopacket.LayerTypeMetadata{Name: "Ethernet", Decoder: gopacket.DecodeFunc(decodeEthernet)}) + LayerTypeGRE = gopacket.RegisterLayerType(18, gopacket.LayerTypeMetadata{Name: "GRE", Decoder: gopacket.DecodeFunc(decodeGRE)}) + LayerTypeICMPv4 = gopacket.RegisterLayerType(19, gopacket.LayerTypeMetadata{Name: "ICMPv4", Decoder: gopacket.DecodeFunc(decodeICMPv4)}) + LayerTypeIPv4 = gopacket.RegisterLayerType(20, gopacket.LayerTypeMetadata{Name: "IPv4", Decoder: gopacket.DecodeFunc(decodeIPv4)}) + LayerTypeIPv6 = gopacket.RegisterLayerType(21, gopacket.LayerTypeMetadata{Name: "IPv6", Decoder: gopacket.DecodeFunc(decodeIPv6)}) + LayerTypeLLC = gopacket.RegisterLayerType(22, gopacket.LayerTypeMetadata{Name: "LLC", Decoder: gopacket.DecodeFunc(decodeLLC)}) + LayerTypeSNAP = gopacket.RegisterLayerType(23, gopacket.LayerTypeMetadata{Name: "SNAP", Decoder: gopacket.DecodeFunc(decodeSNAP)}) + LayerTypeMPLS = gopacket.RegisterLayerType(24, gopacket.LayerTypeMetadata{Name: "MPLS", Decoder: gopacket.DecodeFunc(decodeMPLS)}) + LayerTypePPP = gopacket.RegisterLayerType(25, gopacket.LayerTypeMetadata{Name: "PPP", Decoder: gopacket.DecodeFunc(decodePPP)}) + LayerTypePPPoE = gopacket.RegisterLayerType(26, gopacket.LayerTypeMetadata{Name: "PPPoE", Decoder: gopacket.DecodeFunc(decodePPPoE)}) + LayerTypeRUDP = gopacket.RegisterLayerType(27, gopacket.LayerTypeMetadata{Name: "RUDP", Decoder: gopacket.DecodeFunc(decodeRUDP)}) + LayerTypeSCTP = gopacket.RegisterLayerType(28, gopacket.LayerTypeMetadata{Name: "SCTP", Decoder: gopacket.DecodeFunc(decodeSCTP)}) + LayerTypeSCTPUnknownChunkType = gopacket.RegisterLayerType(29, gopacket.LayerTypeMetadata{Name: "SCTPUnknownChunkType", Decoder: nil}) + LayerTypeSCTPData = gopacket.RegisterLayerType(30, gopacket.LayerTypeMetadata{Name: "SCTPData", Decoder: nil}) + LayerTypeSCTPInit = gopacket.RegisterLayerType(31, gopacket.LayerTypeMetadata{Name: "SCTPInit", Decoder: nil}) + LayerTypeSCTPSack = gopacket.RegisterLayerType(32, gopacket.LayerTypeMetadata{Name: "SCTPSack", Decoder: nil}) + LayerTypeSCTPHeartbeat = gopacket.RegisterLayerType(33, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeat", Decoder: nil}) + LayerTypeSCTPError = gopacket.RegisterLayerType(34, gopacket.LayerTypeMetadata{Name: "SCTPError", Decoder: nil}) + LayerTypeSCTPShutdown = gopacket.RegisterLayerType(35, gopacket.LayerTypeMetadata{Name: "SCTPShutdown", Decoder: nil}) + LayerTypeSCTPShutdownAck = gopacket.RegisterLayerType(36, gopacket.LayerTypeMetadata{Name: "SCTPShutdownAck", Decoder: nil}) + LayerTypeSCTPCookieEcho = gopacket.RegisterLayerType(37, gopacket.LayerTypeMetadata{Name: "SCTPCookieEcho", Decoder: nil}) + LayerTypeSCTPEmptyLayer = gopacket.RegisterLayerType(38, gopacket.LayerTypeMetadata{Name: "SCTPEmptyLayer", Decoder: nil}) + LayerTypeSCTPInitAck = gopacket.RegisterLayerType(39, gopacket.LayerTypeMetadata{Name: "SCTPInitAck", Decoder: nil}) + LayerTypeSCTPHeartbeatAck = gopacket.RegisterLayerType(40, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeatAck", Decoder: nil}) + LayerTypeSCTPAbort = gopacket.RegisterLayerType(41, gopacket.LayerTypeMetadata{Name: "SCTPAbort", Decoder: nil}) + LayerTypeSCTPShutdownComplete = gopacket.RegisterLayerType(42, gopacket.LayerTypeMetadata{Name: "SCTPShutdownComplete", Decoder: nil}) + LayerTypeSCTPCookieAck = gopacket.RegisterLayerType(43, gopacket.LayerTypeMetadata{Name: "SCTPCookieAck", Decoder: nil}) + LayerTypeTCP = gopacket.RegisterLayerType(44, gopacket.LayerTypeMetadata{Name: "TCP", Decoder: gopacket.DecodeFunc(decodeTCP)}) + LayerTypeUDP = gopacket.RegisterLayerType(45, gopacket.LayerTypeMetadata{Name: "UDP", Decoder: gopacket.DecodeFunc(decodeUDP)}) + LayerTypeIPv6HopByHop = gopacket.RegisterLayerType(46, gopacket.LayerTypeMetadata{Name: "IPv6HopByHop", Decoder: gopacket.DecodeFunc(decodeIPv6HopByHop)}) + LayerTypeIPv6Routing = gopacket.RegisterLayerType(47, gopacket.LayerTypeMetadata{Name: "IPv6Routing", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)}) + LayerTypeIPv6Fragment = gopacket.RegisterLayerType(48, gopacket.LayerTypeMetadata{Name: "IPv6Fragment", Decoder: gopacket.DecodeFunc(decodeIPv6Fragment)}) + LayerTypeIPv6Destination = gopacket.RegisterLayerType(49, gopacket.LayerTypeMetadata{Name: "IPv6Destination", Decoder: gopacket.DecodeFunc(decodeIPv6Destination)}) + LayerTypeIPSecAH = gopacket.RegisterLayerType(50, gopacket.LayerTypeMetadata{Name: "IPSecAH", Decoder: gopacket.DecodeFunc(decodeIPSecAH)}) + LayerTypeIPSecESP = gopacket.RegisterLayerType(51, gopacket.LayerTypeMetadata{Name: "IPSecESP", Decoder: gopacket.DecodeFunc(decodeIPSecESP)}) + LayerTypeUDPLite = gopacket.RegisterLayerType(52, gopacket.LayerTypeMetadata{Name: "UDPLite", Decoder: gopacket.DecodeFunc(decodeUDPLite)}) + LayerTypeFDDI = gopacket.RegisterLayerType(53, gopacket.LayerTypeMetadata{Name: "FDDI", Decoder: gopacket.DecodeFunc(decodeFDDI)}) + LayerTypeLoopback = gopacket.RegisterLayerType(54, gopacket.LayerTypeMetadata{Name: "Loopback", Decoder: gopacket.DecodeFunc(decodeLoopback)}) + LayerTypeEAP = gopacket.RegisterLayerType(55, gopacket.LayerTypeMetadata{Name: "EAP", Decoder: gopacket.DecodeFunc(decodeEAP)}) + LayerTypeEAPOL = gopacket.RegisterLayerType(56, gopacket.LayerTypeMetadata{Name: "EAPOL", Decoder: gopacket.DecodeFunc(decodeEAPOL)}) + LayerTypeICMPv6 = gopacket.RegisterLayerType(57, gopacket.LayerTypeMetadata{Name: "ICMPv6", Decoder: gopacket.DecodeFunc(decodeICMPv6)}) + LayerTypeLinkLayerDiscovery = gopacket.RegisterLayerType(58, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscovery", Decoder: gopacket.DecodeFunc(decodeLinkLayerDiscovery)}) + LayerTypeCiscoDiscoveryInfo = gopacket.RegisterLayerType(59, gopacket.LayerTypeMetadata{Name: "CiscoDiscoveryInfo", Decoder: gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)}) + LayerTypeLinkLayerDiscoveryInfo = gopacket.RegisterLayerType(60, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscoveryInfo", Decoder: nil}) + LayerTypeNortelDiscovery = gopacket.RegisterLayerType(61, gopacket.LayerTypeMetadata{Name: "NortelDiscovery", Decoder: gopacket.DecodeFunc(decodeNortelDiscovery)}) + LayerTypeIGMP = gopacket.RegisterLayerType(62, gopacket.LayerTypeMetadata{Name: "IGMP", Decoder: gopacket.DecodeFunc(decodeIGMP)}) + LayerTypePFLog = gopacket.RegisterLayerType(63, gopacket.LayerTypeMetadata{Name: "PFLog", Decoder: gopacket.DecodeFunc(decodePFLog)}) + LayerTypeRadioTap = gopacket.RegisterLayerType(64, gopacket.LayerTypeMetadata{Name: "RadioTap", Decoder: gopacket.DecodeFunc(decodeRadioTap)}) + LayerTypeDot11 = gopacket.RegisterLayerType(65, gopacket.LayerTypeMetadata{Name: "Dot11", Decoder: gopacket.DecodeFunc(decodeDot11)}) + LayerTypeDot11Ctrl = gopacket.RegisterLayerType(66, gopacket.LayerTypeMetadata{Name: "Dot11Ctrl", Decoder: gopacket.DecodeFunc(decodeDot11Ctrl)}) + LayerTypeDot11Data = gopacket.RegisterLayerType(67, gopacket.LayerTypeMetadata{Name: "Dot11Data", Decoder: gopacket.DecodeFunc(decodeDot11Data)}) + LayerTypeDot11DataCFAck = gopacket.RegisterLayerType(68, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)}) + LayerTypeDot11DataCFPoll = gopacket.RegisterLayerType(69, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)}) + LayerTypeDot11DataCFAckPoll = gopacket.RegisterLayerType(70, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)}) + LayerTypeDot11DataNull = gopacket.RegisterLayerType(71, gopacket.LayerTypeMetadata{Name: "Dot11DataNull", Decoder: gopacket.DecodeFunc(decodeDot11DataNull)}) + LayerTypeDot11DataCFAckNoData = gopacket.RegisterLayerType(72, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)}) + LayerTypeDot11DataCFPollNoData = gopacket.RegisterLayerType(73, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)}) + LayerTypeDot11DataCFAckPollNoData = gopacket.RegisterLayerType(74, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)}) + LayerTypeDot11DataQOSData = gopacket.RegisterLayerType(75, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSData", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSData)}) + LayerTypeDot11DataQOSDataCFAck = gopacket.RegisterLayerType(76, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck)}) + LayerTypeDot11DataQOSDataCFPoll = gopacket.RegisterLayerType(77, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll)}) + LayerTypeDot11DataQOSDataCFAckPoll = gopacket.RegisterLayerType(78, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll)}) + LayerTypeDot11DataQOSNull = gopacket.RegisterLayerType(79, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSNull", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSNull)}) + LayerTypeDot11DataQOSCFPollNoData = gopacket.RegisterLayerType(80, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData)}) + LayerTypeDot11DataQOSCFAckPollNoData = gopacket.RegisterLayerType(81, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData)}) + LayerTypeDot11InformationElement = gopacket.RegisterLayerType(82, gopacket.LayerTypeMetadata{Name: "Dot11InformationElement", Decoder: gopacket.DecodeFunc(decodeDot11InformationElement)}) + LayerTypeDot11CtrlCTS = gopacket.RegisterLayerType(83, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCTS)}) + LayerTypeDot11CtrlRTS = gopacket.RegisterLayerType(84, gopacket.LayerTypeMetadata{Name: "Dot11CtrlRTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlRTS)}) + LayerTypeDot11CtrlBlockAckReq = gopacket.RegisterLayerType(85, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAckReq", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq)}) + LayerTypeDot11CtrlBlockAck = gopacket.RegisterLayerType(86, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAck)}) + LayerTypeDot11CtrlPowersavePoll = gopacket.RegisterLayerType(87, gopacket.LayerTypeMetadata{Name: "Dot11CtrlPowersavePoll", Decoder: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll)}) + LayerTypeDot11CtrlAck = gopacket.RegisterLayerType(88, gopacket.LayerTypeMetadata{Name: "Dot11CtrlAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlAck)}) + LayerTypeDot11CtrlCFEnd = gopacket.RegisterLayerType(89, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEnd", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEnd)}) + LayerTypeDot11CtrlCFEndAck = gopacket.RegisterLayerType(90, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEndAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck)}) + LayerTypeDot11MgmtAssociationReq = gopacket.RegisterLayerType(91, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq)}) + LayerTypeDot11MgmtAssociationResp = gopacket.RegisterLayerType(92, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp)}) + LayerTypeDot11MgmtReassociationReq = gopacket.RegisterLayerType(93, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq)}) + LayerTypeDot11MgmtReassociationResp = gopacket.RegisterLayerType(94, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp)}) + LayerTypeDot11MgmtProbeReq = gopacket.RegisterLayerType(95, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeReq)}) + LayerTypeDot11MgmtProbeResp = gopacket.RegisterLayerType(96, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeResp)}) + LayerTypeDot11MgmtMeasurementPilot = gopacket.RegisterLayerType(97, gopacket.LayerTypeMetadata{Name: "Dot11MgmtMeasurementPilot", Decoder: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot)}) + LayerTypeDot11MgmtBeacon = gopacket.RegisterLayerType(98, gopacket.LayerTypeMetadata{Name: "Dot11MgmtBeacon", Decoder: gopacket.DecodeFunc(decodeDot11MgmtBeacon)}) + LayerTypeDot11MgmtATIM = gopacket.RegisterLayerType(99, gopacket.LayerTypeMetadata{Name: "Dot11MgmtATIM", Decoder: gopacket.DecodeFunc(decodeDot11MgmtATIM)}) + LayerTypeDot11MgmtDisassociation = gopacket.RegisterLayerType(100, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDisassociation", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDisassociation)}) + LayerTypeDot11MgmtAuthentication = gopacket.RegisterLayerType(101, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAuthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAuthentication)}) + LayerTypeDot11MgmtDeauthentication = gopacket.RegisterLayerType(102, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDeauthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication)}) + LayerTypeDot11MgmtAction = gopacket.RegisterLayerType(103, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAction", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAction)}) + LayerTypeDot11MgmtActionNoAck = gopacket.RegisterLayerType(104, gopacket.LayerTypeMetadata{Name: "Dot11MgmtActionNoAck", Decoder: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck)}) + LayerTypeDot11MgmtArubaWLAN = gopacket.RegisterLayerType(105, gopacket.LayerTypeMetadata{Name: "Dot11MgmtArubaWLAN", Decoder: gopacket.DecodeFunc(decodeDot11MgmtArubaWLAN)}) + LayerTypeDot11WEP = gopacket.RegisterLayerType(106, gopacket.LayerTypeMetadata{Name: "Dot11WEP", Decoder: gopacket.DecodeFunc(decodeDot11WEP)}) + LayerTypeDNS = gopacket.RegisterLayerType(107, gopacket.LayerTypeMetadata{Name: "DNS", Decoder: gopacket.DecodeFunc(decodeDNS)}) + LayerTypeUSB = gopacket.RegisterLayerType(108, gopacket.LayerTypeMetadata{Name: "USB", Decoder: gopacket.DecodeFunc(decodeUSB)}) + LayerTypeUSBRequestBlockSetup = gopacket.RegisterLayerType(109, gopacket.LayerTypeMetadata{Name: "USBRequestBlockSetup", Decoder: gopacket.DecodeFunc(decodeUSBRequestBlockSetup)}) + LayerTypeUSBControl = gopacket.RegisterLayerType(110, gopacket.LayerTypeMetadata{Name: "USBControl", Decoder: gopacket.DecodeFunc(decodeUSBControl)}) + LayerTypeUSBInterrupt = gopacket.RegisterLayerType(111, gopacket.LayerTypeMetadata{Name: "USBInterrupt", Decoder: gopacket.DecodeFunc(decodeUSBInterrupt)}) + LayerTypeUSBBulk = gopacket.RegisterLayerType(112, gopacket.LayerTypeMetadata{Name: "USBBulk", Decoder: gopacket.DecodeFunc(decodeUSBBulk)}) + LayerTypeLinuxSLL = gopacket.RegisterLayerType(113, gopacket.LayerTypeMetadata{Name: "Linux SLL", Decoder: gopacket.DecodeFunc(decodeLinuxSLL)}) + LayerTypeSFlow = gopacket.RegisterLayerType(114, gopacket.LayerTypeMetadata{Name: "SFlow", Decoder: gopacket.DecodeFunc(decodeSFlow)}) + LayerTypePrismHeader = gopacket.RegisterLayerType(115, gopacket.LayerTypeMetadata{Name: "Prism monitor mode header", Decoder: gopacket.DecodeFunc(decodePrismHeader)}) + LayerTypeVXLAN = gopacket.RegisterLayerType(116, gopacket.LayerTypeMetadata{Name: "VXLAN", Decoder: gopacket.DecodeFunc(decodeVXLAN)}) + LayerTypeNTP = gopacket.RegisterLayerType(117, gopacket.LayerTypeMetadata{Name: "NTP", Decoder: gopacket.DecodeFunc(decodeNTP)}) + LayerTypeDHCPv4 = gopacket.RegisterLayerType(118, gopacket.LayerTypeMetadata{Name: "DHCPv4", Decoder: gopacket.DecodeFunc(decodeDHCPv4)}) + LayerTypeVRRP = gopacket.RegisterLayerType(119, gopacket.LayerTypeMetadata{Name: "VRRP", Decoder: gopacket.DecodeFunc(decodeVRRP)}) + LayerTypeGeneve = gopacket.RegisterLayerType(120, gopacket.LayerTypeMetadata{Name: "Geneve", Decoder: gopacket.DecodeFunc(decodeGeneve)}) + LayerTypeSTP = gopacket.RegisterLayerType(121, gopacket.LayerTypeMetadata{Name: "STP", Decoder: gopacket.DecodeFunc(decodeSTP)}) + LayerTypeBFD = gopacket.RegisterLayerType(122, gopacket.LayerTypeMetadata{Name: "BFD", Decoder: gopacket.DecodeFunc(decodeBFD)}) + LayerTypeOSPF = gopacket.RegisterLayerType(123, gopacket.LayerTypeMetadata{Name: "OSPF", Decoder: gopacket.DecodeFunc(decodeOSPF)}) + LayerTypeICMPv6RouterSolicitation = gopacket.RegisterLayerType(124, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterSolicitation)}) + LayerTypeICMPv6RouterAdvertisement = gopacket.RegisterLayerType(125, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterAdvertisement)}) + LayerTypeICMPv6NeighborSolicitation = gopacket.RegisterLayerType(126, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborSolicitation)}) + LayerTypeICMPv6NeighborAdvertisement = gopacket.RegisterLayerType(127, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborAdvertisement)}) + LayerTypeICMPv6Redirect = gopacket.RegisterLayerType(128, gopacket.LayerTypeMetadata{Name: "ICMPv6Redirect", Decoder: gopacket.DecodeFunc(decodeICMPv6Redirect)}) + LayerTypeGTPv1U = gopacket.RegisterLayerType(129, gopacket.LayerTypeMetadata{Name: "GTPv1U", Decoder: gopacket.DecodeFunc(decodeGTPv1u)}) + LayerTypeEAPOLKey = gopacket.RegisterLayerType(130, gopacket.LayerTypeMetadata{Name: "EAPOLKey", Decoder: gopacket.DecodeFunc(decodeEAPOLKey)}) + LayerTypeLCM = gopacket.RegisterLayerType(131, gopacket.LayerTypeMetadata{Name: "LCM", Decoder: gopacket.DecodeFunc(decodeLCM)}) + LayerTypeICMPv6Echo = gopacket.RegisterLayerType(132, gopacket.LayerTypeMetadata{Name: "ICMPv6Echo", Decoder: gopacket.DecodeFunc(decodeICMPv6Echo)}) + LayerTypeSIP = gopacket.RegisterLayerType(133, gopacket.LayerTypeMetadata{Name: "SIP", Decoder: gopacket.DecodeFunc(decodeSIP)}) + LayerTypeDHCPv6 = gopacket.RegisterLayerType(134, gopacket.LayerTypeMetadata{Name: "DHCPv6", Decoder: gopacket.DecodeFunc(decodeDHCPv6)}) + LayerTypeMLDv1MulticastListenerReport = gopacket.RegisterLayerType(135, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerReport)}) + LayerTypeMLDv1MulticastListenerDone = gopacket.RegisterLayerType(136, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerDone", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerDone)}) + LayerTypeMLDv1MulticastListenerQuery = gopacket.RegisterLayerType(137, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerQuery)}) + LayerTypeMLDv2MulticastListenerReport = gopacket.RegisterLayerType(138, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerReport)}) + LayerTypeMLDv2MulticastListenerQuery = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)}) + LayerTypeTLS = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)}) + LayerTypeModbusTCP = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)}) + LayerTypeRMCP = gopacket.RegisterLayerType(142, gopacket.LayerTypeMetadata{Name: "RMCP", Decoder: gopacket.DecodeFunc(decodeRMCP)}) + LayerTypeASF = gopacket.RegisterLayerType(143, gopacket.LayerTypeMetadata{Name: "ASF", Decoder: gopacket.DecodeFunc(decodeASF)}) + LayerTypeASFPresencePong = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)}) + LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)}) + LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)}) +) + +var ( + // LayerClassIPNetwork contains TCP/IP network layer types. + LayerClassIPNetwork = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPv4, + LayerTypeIPv6, + }) + // LayerClassIPTransport contains TCP/IP transport layer types. + LayerClassIPTransport = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeTCP, + LayerTypeUDP, + LayerTypeSCTP, + }) + // LayerClassIPControl contains TCP/IP control protocols. + LayerClassIPControl = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeICMPv4, + LayerTypeICMPv6, + }) + // LayerClassSCTPChunk contains SCTP chunk types (not the top-level SCTP + // layer). + LayerClassSCTPChunk = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeSCTPUnknownChunkType, + LayerTypeSCTPData, + LayerTypeSCTPInit, + LayerTypeSCTPSack, + LayerTypeSCTPHeartbeat, + LayerTypeSCTPError, + LayerTypeSCTPShutdown, + LayerTypeSCTPShutdownAck, + LayerTypeSCTPCookieEcho, + LayerTypeSCTPEmptyLayer, + LayerTypeSCTPInitAck, + LayerTypeSCTPHeartbeatAck, + LayerTypeSCTPAbort, + LayerTypeSCTPShutdownComplete, + LayerTypeSCTPCookieAck, + }) + // LayerClassIPv6Extension contains IPv6 extension headers. + LayerClassIPv6Extension = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPv6HopByHop, + LayerTypeIPv6Routing, + LayerTypeIPv6Fragment, + LayerTypeIPv6Destination, + }) + LayerClassIPSec = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeIPSecAH, + LayerTypeIPSecESP, + }) + // LayerClassICMPv6NDP contains ICMPv6 neighbor discovery protocol + // messages. + LayerClassICMPv6NDP = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeICMPv6RouterSolicitation, + LayerTypeICMPv6RouterAdvertisement, + LayerTypeICMPv6NeighborSolicitation, + LayerTypeICMPv6NeighborAdvertisement, + LayerTypeICMPv6Redirect, + }) + // LayerClassMLDv1 contains multicast listener discovery protocol + LayerClassMLDv1 = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeMLDv1MulticastListenerQuery, + LayerTypeMLDv1MulticastListenerReport, + LayerTypeMLDv1MulticastListenerDone, + }) + // LayerClassMLDv2 contains multicast listener discovery protocol v2 + LayerClassMLDv2 = gopacket.NewLayerClass([]gopacket.LayerType{ + LayerTypeMLDv1MulticastListenerReport, + LayerTypeMLDv1MulticastListenerDone, + LayerTypeMLDv2MulticastListenerReport, + LayerTypeMLDv1MulticastListenerQuery, + LayerTypeMLDv2MulticastListenerQuery, + }) +) diff --git a/vendor/github.com/google/gopacket/layers/lcm.go b/vendor/github.com/google/gopacket/layers/lcm.go new file mode 100644 index 00000000..58a4b828 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/lcm.go @@ -0,0 +1,218 @@ +// Copyright 2018 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +const ( + // LCMShortHeaderMagic is the LCM small message header magic number + LCMShortHeaderMagic uint32 = 0x4c433032 + // LCMFragmentedHeaderMagic is the LCM fragmented message header magic number + LCMFragmentedHeaderMagic uint32 = 0x4c433033 +) + +// LCM (Lightweight Communications and Marshalling) is a set of libraries and +// tools for message passing and data marshalling, targeted at real-time systems +// where high-bandwidth and low latency are critical. It provides a +// publish/subscribe message passing model and automatic +// marshalling/unmarshalling code generation with bindings for applications in a +// variety of programming languages. +// +// References +// https://lcm-proj.github.io/ +// https://github.com/lcm-proj/lcm +type LCM struct { + // Common (short & fragmented header) fields + Magic uint32 + SequenceNumber uint32 + // Fragmented header only fields + PayloadSize uint32 + FragmentOffset uint32 + FragmentNumber uint16 + TotalFragments uint16 + // Common field + ChannelName string + // Gopacket helper fields + Fragmented bool + fingerprint LCMFingerprint + contents []byte + payload []byte +} + +// LCMFingerprint is the type of a LCM fingerprint. +type LCMFingerprint uint64 + +var ( + // lcmLayerTypes contains a map of all LCM fingerprints that we support and + // their LayerType + lcmLayerTypes = map[LCMFingerprint]gopacket.LayerType{} + layerTypeIndex = 1001 +) + +// RegisterLCMLayerType allows users to register decoders for the underlying +// LCM payload. This is done based on the fingerprint that every LCM message +// contains and which identifies it uniquely. If num is not the zero value it +// will be used when registering with RegisterLayerType towards gopacket, +// otherwise an incremental value starting from 1001 will be used. +func RegisterLCMLayerType(num int, name string, fingerprint LCMFingerprint, + decoder gopacket.Decoder) gopacket.LayerType { + metadata := gopacket.LayerTypeMetadata{Name: name, Decoder: decoder} + + if num == 0 { + num = layerTypeIndex + layerTypeIndex++ + } + + lcmLayerTypes[fingerprint] = gopacket.RegisterLayerType(num, metadata) + + return lcmLayerTypes[fingerprint] +} + +// SupportedLCMFingerprints returns a slice of all LCM fingerprints that has +// been registered so far. +func SupportedLCMFingerprints() []LCMFingerprint { + fingerprints := make([]LCMFingerprint, 0, len(lcmLayerTypes)) + for fp := range lcmLayerTypes { + fingerprints = append(fingerprints, fp) + } + return fingerprints +} + +// GetLCMLayerType returns the underlying LCM message's LayerType. +// This LayerType has to be registered by using RegisterLCMLayerType. +func GetLCMLayerType(fingerprint LCMFingerprint) gopacket.LayerType { + layerType, ok := lcmLayerTypes[fingerprint] + if !ok { + return gopacket.LayerTypePayload + } + + return layerType +} + +func decodeLCM(data []byte, p gopacket.PacketBuilder) error { + lcm := &LCM{} + + err := lcm.DecodeFromBytes(data, p) + if err != nil { + return err + } + + p.AddLayer(lcm) + p.SetApplicationLayer(lcm) + + return p.NextDecoder(lcm.NextLayerType()) +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("LCM < 8 bytes") + } + offset := 0 + + lcm.Magic = binary.BigEndian.Uint32(data[offset:4]) + offset += 4 + + if lcm.Magic != LCMShortHeaderMagic && lcm.Magic != LCMFragmentedHeaderMagic { + return fmt.Errorf("Received LCM header magic %v does not match know "+ + "LCM magic numbers. Dropping packet.", lcm.Magic) + } + + lcm.SequenceNumber = binary.BigEndian.Uint32(data[offset:8]) + offset += 4 + + if lcm.Magic == LCMFragmentedHeaderMagic { + lcm.Fragmented = true + + lcm.PayloadSize = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + + lcm.FragmentOffset = binary.BigEndian.Uint32(data[offset : offset+4]) + offset += 4 + + lcm.FragmentNumber = binary.BigEndian.Uint16(data[offset : offset+2]) + offset += 2 + + lcm.TotalFragments = binary.BigEndian.Uint16(data[offset : offset+2]) + offset += 2 + } else { + lcm.Fragmented = false + } + + if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) { + buffer := make([]byte, 0) + for _, b := range data[offset:] { + offset++ + + if b == 0 { + break + } + + buffer = append(buffer, b) + } + + lcm.ChannelName = string(buffer) + } + + lcm.fingerprint = LCMFingerprint( + binary.BigEndian.Uint64(data[offset : offset+8])) + + lcm.contents = data[:offset] + lcm.payload = data[offset:] + + return nil +} + +// CanDecode returns a set of layers that LCM objects can decode. +// As LCM objects can only decode the LCM layer, we just return that layer. +func (lcm LCM) CanDecode() gopacket.LayerClass { + return LayerTypeLCM +} + +// NextLayerType specifies the LCM payload layer type following this header. +// As LCM packets are serialized structs with uniq fingerprints for each uniq +// combination of data types, lookup of correct layer type is based on that +// fingerprint. +func (lcm LCM) NextLayerType() gopacket.LayerType { + if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) { + return GetLCMLayerType(lcm.fingerprint) + } + + return gopacket.LayerTypeFragment +} + +// LayerType returns LayerTypeLCM +func (lcm LCM) LayerType() gopacket.LayerType { + return LayerTypeLCM +} + +// LayerContents returns the contents of the LCM header. +func (lcm LCM) LayerContents() []byte { + return lcm.contents +} + +// LayerPayload returns the payload following this LCM header. +func (lcm LCM) LayerPayload() []byte { + return lcm.payload +} + +// Payload returns the payload following this LCM header. +func (lcm LCM) Payload() []byte { + return lcm.LayerPayload() +} + +// Fingerprint returns the LCM fingerprint of the underlying message. +func (lcm LCM) Fingerprint() LCMFingerprint { + return lcm.fingerprint +} diff --git a/vendor/github.com/google/gopacket/layers/linux_sll.go b/vendor/github.com/google/gopacket/layers/linux_sll.go new file mode 100644 index 00000000..85a4f8bd --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/linux_sll.go @@ -0,0 +1,98 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +type LinuxSLLPacketType uint16 + +const ( + LinuxSLLPacketTypeHost LinuxSLLPacketType = 0 // To us + LinuxSLLPacketTypeBroadcast LinuxSLLPacketType = 1 // To all + LinuxSLLPacketTypeMulticast LinuxSLLPacketType = 2 // To group + LinuxSLLPacketTypeOtherhost LinuxSLLPacketType = 3 // To someone else + LinuxSLLPacketTypeOutgoing LinuxSLLPacketType = 4 // Outgoing of any type + // These ones are invisible by user level + LinuxSLLPacketTypeLoopback LinuxSLLPacketType = 5 // MC/BRD frame looped back + LinuxSLLPacketTypeFastroute LinuxSLLPacketType = 6 // Fastrouted frame +) + +func (l LinuxSLLPacketType) String() string { + switch l { + case LinuxSLLPacketTypeHost: + return "host" + case LinuxSLLPacketTypeBroadcast: + return "broadcast" + case LinuxSLLPacketTypeMulticast: + return "multicast" + case LinuxSLLPacketTypeOtherhost: + return "otherhost" + case LinuxSLLPacketTypeOutgoing: + return "outgoing" + case LinuxSLLPacketTypeLoopback: + return "loopback" + case LinuxSLLPacketTypeFastroute: + return "fastroute" + } + return fmt.Sprintf("Unknown(%d)", int(l)) +} + +type LinuxSLL struct { + BaseLayer + PacketType LinuxSLLPacketType + AddrLen uint16 + Addr net.HardwareAddr + EthernetType EthernetType + AddrType uint16 +} + +// LayerType returns LayerTypeLinuxSLL. +func (sll *LinuxSLL) LayerType() gopacket.LayerType { return LayerTypeLinuxSLL } + +func (sll *LinuxSLL) CanDecode() gopacket.LayerClass { + return LayerTypeLinuxSLL +} + +func (sll *LinuxSLL) LinkFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointMAC, sll.Addr, nil) +} + +func (sll *LinuxSLL) NextLayerType() gopacket.LayerType { + return sll.EthernetType.LayerType() +} + +func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 16 { + return errors.New("Linux SLL packet too small") + } + sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2])) + sll.AddrType = binary.BigEndian.Uint16(data[2:4]) + sll.AddrLen = binary.BigEndian.Uint16(data[4:6]) + + sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6]) + sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16])) + sll.BaseLayer = BaseLayer{data[:16], data[16:]} + + return nil +} + +func decodeLinuxSLL(data []byte, p gopacket.PacketBuilder) error { + sll := &LinuxSLL{} + if err := sll.DecodeFromBytes(data, p); err != nil { + return err + } + p.AddLayer(sll) + p.SetLinkLayer(sll) + return p.NextDecoder(sll.EthernetType) +} diff --git a/vendor/github.com/google/gopacket/layers/llc.go b/vendor/github.com/google/gopacket/layers/llc.go new file mode 100644 index 00000000..cad68036 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/llc.go @@ -0,0 +1,193 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// LLC is the layer used for 802.2 Logical Link Control headers. +// See http://standards.ieee.org/getieee802/download/802.2-1998.pdf +type LLC struct { + BaseLayer + DSAP uint8 + IG bool // true means group, false means individual + SSAP uint8 + CR bool // true means response, false means command + Control uint16 +} + +// LayerType returns gopacket.LayerTypeLLC. +func (l *LLC) LayerType() gopacket.LayerType { return LayerTypeLLC } + +// DecodeFromBytes decodes the given bytes into this layer. +func (l *LLC) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 3 { + return errors.New("LLC header too small") + } + l.DSAP = data[0] & 0xFE + l.IG = data[0]&0x1 != 0 + l.SSAP = data[1] & 0xFE + l.CR = data[1]&0x1 != 0 + l.Control = uint16(data[2]) + + if l.Control&0x1 == 0 || l.Control&0x3 == 0x1 { + if len(data) < 4 { + return errors.New("LLC header too small") + } + l.Control = l.Control<<8 | uint16(data[3]) + l.Contents = data[:4] + l.Payload = data[4:] + } else { + l.Contents = data[:3] + l.Payload = data[3:] + } + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (l *LLC) CanDecode() gopacket.LayerClass { + return LayerTypeLLC +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (l *LLC) NextLayerType() gopacket.LayerType { + switch { + case l.DSAP == 0xAA && l.SSAP == 0xAA: + return LayerTypeSNAP + case l.DSAP == 0x42 && l.SSAP == 0x42: + return LayerTypeSTP + } + return gopacket.LayerTypeZero // Not implemented +} + +// SNAP is used inside LLC. See +// http://standards.ieee.org/getieee802/download/802-2001.pdf. +// From http://en.wikipedia.org/wiki/Subnetwork_Access_Protocol: +// "[T]he Subnetwork Access Protocol (SNAP) is a mechanism for multiplexing, +// on networks using IEEE 802.2 LLC, more protocols than can be distinguished +// by the 8-bit 802.2 Service Access Point (SAP) fields." +type SNAP struct { + BaseLayer + OrganizationalCode []byte + Type EthernetType +} + +// LayerType returns gopacket.LayerTypeSNAP. +func (s *SNAP) LayerType() gopacket.LayerType { return LayerTypeSNAP } + +// DecodeFromBytes decodes the given bytes into this layer. +func (s *SNAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 5 { + return errors.New("SNAP header too small") + } + s.OrganizationalCode = data[:3] + s.Type = EthernetType(binary.BigEndian.Uint16(data[3:5])) + s.BaseLayer = BaseLayer{data[:5], data[5:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (s *SNAP) CanDecode() gopacket.LayerClass { + return LayerTypeSNAP +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (s *SNAP) NextLayerType() gopacket.LayerType { + // See BUG(gconnel) in decodeSNAP + return s.Type.LayerType() +} + +func decodeLLC(data []byte, p gopacket.PacketBuilder) error { + l := &LLC{} + err := l.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(l) + return p.NextDecoder(l.NextLayerType()) +} + +func decodeSNAP(data []byte, p gopacket.PacketBuilder) error { + s := &SNAP{} + err := s.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(s) + // BUG(gconnell): When decoding SNAP, we treat the SNAP type as an Ethernet + // type. This may not actually be an ethernet type in all cases, + // depending on the organizational code. Right now, we don't check. + return p.NextDecoder(s.Type) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (l *LLC) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var igFlag, crFlag byte + var length int + + if l.Control&0xFF00 != 0 { + length = 4 + } else { + length = 3 + } + + if l.DSAP&0x1 != 0 { + return errors.New("DSAP value invalid, should not include IG flag bit") + } + + if l.SSAP&0x1 != 0 { + return errors.New("SSAP value invalid, should not include CR flag bit") + } + + if buf, err := b.PrependBytes(length); err != nil { + return err + } else { + igFlag = 0 + if l.IG { + igFlag = 0x1 + } + + crFlag = 0 + if l.CR { + crFlag = 0x1 + } + + buf[0] = l.DSAP + igFlag + buf[1] = l.SSAP + crFlag + + if length == 4 { + buf[2] = uint8(l.Control >> 8) + buf[3] = uint8(l.Control) + } else { + buf[2] = uint8(l.Control) + } + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (s *SNAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if buf, err := b.PrependBytes(5); err != nil { + return err + } else { + buf[0] = s.OrganizationalCode[0] + buf[1] = s.OrganizationalCode[1] + buf[2] = s.OrganizationalCode[2] + binary.BigEndian.PutUint16(buf[3:5], uint16(s.Type)) + } + + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/lldp.go b/vendor/github.com/google/gopacket/layers/lldp.go new file mode 100644 index 00000000..16a5bbad --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/lldp.go @@ -0,0 +1,1603 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// LLDPTLVType is the type of each TLV value in a LinkLayerDiscovery packet. +type LLDPTLVType byte + +const ( + LLDPTLVEnd LLDPTLVType = 0 + LLDPTLVChassisID LLDPTLVType = 1 + LLDPTLVPortID LLDPTLVType = 2 + LLDPTLVTTL LLDPTLVType = 3 + LLDPTLVPortDescription LLDPTLVType = 4 + LLDPTLVSysName LLDPTLVType = 5 + LLDPTLVSysDescription LLDPTLVType = 6 + LLDPTLVSysCapabilities LLDPTLVType = 7 + LLDPTLVMgmtAddress LLDPTLVType = 8 + LLDPTLVOrgSpecific LLDPTLVType = 127 +) + +// LinkLayerDiscoveryValue is a TLV value inside a LinkLayerDiscovery packet layer. +type LinkLayerDiscoveryValue struct { + Type LLDPTLVType + Length uint16 + Value []byte +} + +func (c *LinkLayerDiscoveryValue) len() int { + return 0 +} + +// LLDPChassisIDSubType specifies the value type for a single LLDPChassisID.ID +type LLDPChassisIDSubType byte + +// LLDP Chassis Types +const ( + LLDPChassisIDSubTypeReserved LLDPChassisIDSubType = 0 + LLDPChassisIDSubTypeChassisComp LLDPChassisIDSubType = 1 + LLDPChassisIDSubtypeIfaceAlias LLDPChassisIDSubType = 2 + LLDPChassisIDSubTypePortComp LLDPChassisIDSubType = 3 + LLDPChassisIDSubTypeMACAddr LLDPChassisIDSubType = 4 + LLDPChassisIDSubTypeNetworkAddr LLDPChassisIDSubType = 5 + LLDPChassisIDSubtypeIfaceName LLDPChassisIDSubType = 6 + LLDPChassisIDSubTypeLocal LLDPChassisIDSubType = 7 +) + +type LLDPChassisID struct { + Subtype LLDPChassisIDSubType + ID []byte +} + +func (c *LLDPChassisID) serialize() []byte { + + var buf = make([]byte, c.serializedLen()) + idLen := uint16(LLDPTLVChassisID)<<9 | uint16(len(c.ID)+1) //id should take 7 bits, length should take 9 bits, +1 for subtype + binary.BigEndian.PutUint16(buf[0:2], idLen) + buf[2] = byte(c.Subtype) + copy(buf[3:], c.ID) + return buf +} + +func (c *LLDPChassisID) serializedLen() int { + return len(c.ID) + 3 // +2 for id and length, +1 for subtype +} + +// LLDPPortIDSubType specifies the value type for a single LLDPPortID.ID +type LLDPPortIDSubType byte + +// LLDP PortID types +const ( + LLDPPortIDSubtypeReserved LLDPPortIDSubType = 0 + LLDPPortIDSubtypeIfaceAlias LLDPPortIDSubType = 1 + LLDPPortIDSubtypePortComp LLDPPortIDSubType = 2 + LLDPPortIDSubtypeMACAddr LLDPPortIDSubType = 3 + LLDPPortIDSubtypeNetworkAddr LLDPPortIDSubType = 4 + LLDPPortIDSubtypeIfaceName LLDPPortIDSubType = 5 + LLDPPortIDSubtypeAgentCircuitID LLDPPortIDSubType = 6 + LLDPPortIDSubtypeLocal LLDPPortIDSubType = 7 +) + +type LLDPPortID struct { + Subtype LLDPPortIDSubType + ID []byte +} + +func (c *LLDPPortID) serialize() []byte { + + var buf = make([]byte, c.serializedLen()) + idLen := uint16(LLDPTLVPortID)<<9 | uint16(len(c.ID)+1) //id should take 7 bits, length should take 9 bits, +1 for subtype + binary.BigEndian.PutUint16(buf[0:2], idLen) + buf[2] = byte(c.Subtype) + copy(buf[3:], c.ID) + return buf +} + +func (c *LLDPPortID) serializedLen() int { + return len(c.ID) + 3 // +2 for id and length, +1 for subtype +} + +// LinkLayerDiscovery is a packet layer containing the LinkLayer Discovery Protocol. +// See http:http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf +// ChassisID, PortID and TTL are mandatory TLV's. Other values can be decoded +// with DecodeValues() +type LinkLayerDiscovery struct { + BaseLayer + ChassisID LLDPChassisID + PortID LLDPPortID + TTL uint16 + Values []LinkLayerDiscoveryValue +} + +type IEEEOUI uint32 + +// http://standards.ieee.org/develop/regauth/oui/oui.txt +const ( + IEEEOUI8021 IEEEOUI = 0x0080c2 + IEEEOUI8023 IEEEOUI = 0x00120f + IEEEOUI80211 IEEEOUI = 0x000fac + IEEEOUI8021Qbg IEEEOUI = 0x0013BF + IEEEOUICisco2 IEEEOUI = 0x000142 + IEEEOUIMedia IEEEOUI = 0x0012bb // TR-41 + IEEEOUIProfinet IEEEOUI = 0x000ecf + IEEEOUIDCBX IEEEOUI = 0x001b21 +) + +// LLDPOrgSpecificTLV is an Organisation-specific TLV +type LLDPOrgSpecificTLV struct { + OUI IEEEOUI + SubType uint8 + Info []byte +} + +// LLDPCapabilities Types +const ( + LLDPCapsOther uint16 = 1 << 0 + LLDPCapsRepeater uint16 = 1 << 1 + LLDPCapsBridge uint16 = 1 << 2 + LLDPCapsWLANAP uint16 = 1 << 3 + LLDPCapsRouter uint16 = 1 << 4 + LLDPCapsPhone uint16 = 1 << 5 + LLDPCapsDocSis uint16 = 1 << 6 + LLDPCapsStationOnly uint16 = 1 << 7 + LLDPCapsCVLAN uint16 = 1 << 8 + LLDPCapsSVLAN uint16 = 1 << 9 + LLDPCapsTmpr uint16 = 1 << 10 +) + +// LLDPCapabilities represents the capabilities of a device +type LLDPCapabilities struct { + Other bool + Repeater bool + Bridge bool + WLANAP bool + Router bool + Phone bool + DocSis bool + StationOnly bool + CVLAN bool + SVLAN bool + TMPR bool +} + +type LLDPSysCapabilities struct { + SystemCap LLDPCapabilities + EnabledCap LLDPCapabilities +} + +type IANAAddressFamily byte + +// LLDP Management Address Subtypes +// http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml +const ( + IANAAddressFamilyReserved IANAAddressFamily = 0 + IANAAddressFamilyIPV4 IANAAddressFamily = 1 + IANAAddressFamilyIPV6 IANAAddressFamily = 2 + IANAAddressFamilyNSAP IANAAddressFamily = 3 + IANAAddressFamilyHDLC IANAAddressFamily = 4 + IANAAddressFamilyBBN1822 IANAAddressFamily = 5 + IANAAddressFamily802 IANAAddressFamily = 6 + IANAAddressFamilyE163 IANAAddressFamily = 7 + IANAAddressFamilyE164 IANAAddressFamily = 8 + IANAAddressFamilyF69 IANAAddressFamily = 9 + IANAAddressFamilyX121 IANAAddressFamily = 10 + IANAAddressFamilyIPX IANAAddressFamily = 11 + IANAAddressFamilyAtalk IANAAddressFamily = 12 + IANAAddressFamilyDecnet IANAAddressFamily = 13 + IANAAddressFamilyBanyan IANAAddressFamily = 14 + IANAAddressFamilyE164NSAP IANAAddressFamily = 15 + IANAAddressFamilyDNS IANAAddressFamily = 16 + IANAAddressFamilyDistname IANAAddressFamily = 17 + IANAAddressFamilyASNumber IANAAddressFamily = 18 + IANAAddressFamilyXTPIPV4 IANAAddressFamily = 19 + IANAAddressFamilyXTPIPV6 IANAAddressFamily = 20 + IANAAddressFamilyXTP IANAAddressFamily = 21 + IANAAddressFamilyFcWWPN IANAAddressFamily = 22 + IANAAddressFamilyFcWWNN IANAAddressFamily = 23 + IANAAddressFamilyGWID IANAAddressFamily = 24 + IANAAddressFamilyL2VPN IANAAddressFamily = 25 +) + +type LLDPInterfaceSubtype byte + +// LLDP Interface Subtypes +const ( + LLDPInterfaceSubtypeUnknown LLDPInterfaceSubtype = 1 + LLDPInterfaceSubtypeifIndex LLDPInterfaceSubtype = 2 + LLDPInterfaceSubtypeSysPort LLDPInterfaceSubtype = 3 +) + +type LLDPMgmtAddress struct { + Subtype IANAAddressFamily + Address []byte + InterfaceSubtype LLDPInterfaceSubtype + InterfaceNumber uint32 + OID string +} + +// LinkLayerDiscoveryInfo represents the decoded details for a set of LinkLayerDiscoveryValues +// Organisation-specific TLV's can be decoded using the various Decode() methods +type LinkLayerDiscoveryInfo struct { + BaseLayer + PortDescription string + SysName string + SysDescription string + SysCapabilities LLDPSysCapabilities + MgmtAddress LLDPMgmtAddress + OrgTLVs []LLDPOrgSpecificTLV // Private TLVs + Unknown []LinkLayerDiscoveryValue // undecoded TLVs +} + +/// IEEE 802.1 TLV Subtypes +const ( + LLDP8021SubtypePortVLANID uint8 = 1 + LLDP8021SubtypeProtocolVLANID uint8 = 2 + LLDP8021SubtypeVLANName uint8 = 3 + LLDP8021SubtypeProtocolIdentity uint8 = 4 + LLDP8021SubtypeVDIUsageDigest uint8 = 5 + LLDP8021SubtypeManagementVID uint8 = 6 + LLDP8021SubtypeLinkAggregation uint8 = 7 +) + +// VLAN Port Protocol ID options +const ( + LLDPProtocolVLANIDCapability byte = 1 << 1 + LLDPProtocolVLANIDStatus byte = 1 << 2 +) + +type PortProtocolVLANID struct { + Supported bool + Enabled bool + ID uint16 +} + +type VLANName struct { + ID uint16 + Name string +} + +type ProtocolIdentity []byte + +// LACP options +const ( + LLDPAggregationCapability byte = 1 << 0 + LLDPAggregationStatus byte = 1 << 1 +) + +// IEEE 802 Link Aggregation parameters +type LLDPLinkAggregation struct { + Supported bool + Enabled bool + PortID uint32 +} + +// LLDPInfo8021 represents the information carried in 802.1 Org-specific TLVs +type LLDPInfo8021 struct { + PVID uint16 + PPVIDs []PortProtocolVLANID + VLANNames []VLANName + ProtocolIdentities []ProtocolIdentity + VIDUsageDigest uint32 + ManagementVID uint16 + LinkAggregation LLDPLinkAggregation +} + +// IEEE 802.3 TLV Subtypes +const ( + LLDP8023SubtypeMACPHY uint8 = 1 + LLDP8023SubtypeMDIPower uint8 = 2 + LLDP8023SubtypeLinkAggregation uint8 = 3 + LLDP8023SubtypeMTU uint8 = 4 +) + +// MACPHY options +const ( + LLDPMACPHYCapability byte = 1 << 0 + LLDPMACPHYStatus byte = 1 << 1 +) + +// From IANA-MAU-MIB (introduced by RFC 4836) - dot3MauType +const ( + LLDPMAUTypeUnknown uint16 = 0 + LLDPMAUTypeAUI uint16 = 1 + LLDPMAUType10Base5 uint16 = 2 + LLDPMAUTypeFOIRL uint16 = 3 + LLDPMAUType10Base2 uint16 = 4 + LLDPMAUType10BaseT uint16 = 5 + LLDPMAUType10BaseFP uint16 = 6 + LLDPMAUType10BaseFB uint16 = 7 + LLDPMAUType10BaseFL uint16 = 8 + LLDPMAUType10BROAD36 uint16 = 9 + LLDPMAUType10BaseT_HD uint16 = 10 + LLDPMAUType10BaseT_FD uint16 = 11 + LLDPMAUType10BaseFL_HD uint16 = 12 + LLDPMAUType10BaseFL_FD uint16 = 13 + LLDPMAUType100BaseT4 uint16 = 14 + LLDPMAUType100BaseTX_HD uint16 = 15 + LLDPMAUType100BaseTX_FD uint16 = 16 + LLDPMAUType100BaseFX_HD uint16 = 17 + LLDPMAUType100BaseFX_FD uint16 = 18 + LLDPMAUType100BaseT2_HD uint16 = 19 + LLDPMAUType100BaseT2_FD uint16 = 20 + LLDPMAUType1000BaseX_HD uint16 = 21 + LLDPMAUType1000BaseX_FD uint16 = 22 + LLDPMAUType1000BaseLX_HD uint16 = 23 + LLDPMAUType1000BaseLX_FD uint16 = 24 + LLDPMAUType1000BaseSX_HD uint16 = 25 + LLDPMAUType1000BaseSX_FD uint16 = 26 + LLDPMAUType1000BaseCX_HD uint16 = 27 + LLDPMAUType1000BaseCX_FD uint16 = 28 + LLDPMAUType1000BaseT_HD uint16 = 29 + LLDPMAUType1000BaseT_FD uint16 = 30 + LLDPMAUType10GBaseX uint16 = 31 + LLDPMAUType10GBaseLX4 uint16 = 32 + LLDPMAUType10GBaseR uint16 = 33 + LLDPMAUType10GBaseER uint16 = 34 + LLDPMAUType10GBaseLR uint16 = 35 + LLDPMAUType10GBaseSR uint16 = 36 + LLDPMAUType10GBaseW uint16 = 37 + LLDPMAUType10GBaseEW uint16 = 38 + LLDPMAUType10GBaseLW uint16 = 39 + LLDPMAUType10GBaseSW uint16 = 40 + LLDPMAUType10GBaseCX4 uint16 = 41 + LLDPMAUType2BaseTL uint16 = 42 + LLDPMAUType10PASS_TS uint16 = 43 + LLDPMAUType100BaseBX10D uint16 = 44 + LLDPMAUType100BaseBX10U uint16 = 45 + LLDPMAUType100BaseLX10 uint16 = 46 + LLDPMAUType1000BaseBX10D uint16 = 47 + LLDPMAUType1000BaseBX10U uint16 = 48 + LLDPMAUType1000BaseLX10 uint16 = 49 + LLDPMAUType1000BasePX10D uint16 = 50 + LLDPMAUType1000BasePX10U uint16 = 51 + LLDPMAUType1000BasePX20D uint16 = 52 + LLDPMAUType1000BasePX20U uint16 = 53 + LLDPMAUType10GBaseT uint16 = 54 + LLDPMAUType10GBaseLRM uint16 = 55 + LLDPMAUType1000BaseKX uint16 = 56 + LLDPMAUType10GBaseKX4 uint16 = 57 + LLDPMAUType10GBaseKR uint16 = 58 + LLDPMAUType10_1GBasePRX_D1 uint16 = 59 + LLDPMAUType10_1GBasePRX_D2 uint16 = 60 + LLDPMAUType10_1GBasePRX_D3 uint16 = 61 + LLDPMAUType10_1GBasePRX_U1 uint16 = 62 + LLDPMAUType10_1GBasePRX_U2 uint16 = 63 + LLDPMAUType10_1GBasePRX_U3 uint16 = 64 + LLDPMAUType10GBasePR_D1 uint16 = 65 + LLDPMAUType10GBasePR_D2 uint16 = 66 + LLDPMAUType10GBasePR_D3 uint16 = 67 + LLDPMAUType10GBasePR_U1 uint16 = 68 + LLDPMAUType10GBasePR_U3 uint16 = 69 +) + +// From RFC 3636 - ifMauAutoNegCapAdvertisedBits +const ( + LLDPMAUPMDOther uint16 = 1 << 15 + LLDPMAUPMD10BaseT uint16 = 1 << 14 + LLDPMAUPMD10BaseT_FD uint16 = 1 << 13 + LLDPMAUPMD100BaseT4 uint16 = 1 << 12 + LLDPMAUPMD100BaseTX uint16 = 1 << 11 + LLDPMAUPMD100BaseTX_FD uint16 = 1 << 10 + LLDPMAUPMD100BaseT2 uint16 = 1 << 9 + LLDPMAUPMD100BaseT2_FD uint16 = 1 << 8 + LLDPMAUPMDFDXPAUSE uint16 = 1 << 7 + LLDPMAUPMDFDXAPAUSE uint16 = 1 << 6 + LLDPMAUPMDFDXSPAUSE uint16 = 1 << 5 + LLDPMAUPMDFDXBPAUSE uint16 = 1 << 4 + LLDPMAUPMD1000BaseX uint16 = 1 << 3 + LLDPMAUPMD1000BaseX_FD uint16 = 1 << 2 + LLDPMAUPMD1000BaseT uint16 = 1 << 1 + LLDPMAUPMD1000BaseT_FD uint16 = 1 << 0 +) + +// Inverted ifMauAutoNegCapAdvertisedBits if required +// (Some manufacturers misinterpreted the spec - +// see https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1455) +const ( + LLDPMAUPMDOtherInv uint16 = 1 << 0 + LLDPMAUPMD10BaseTInv uint16 = 1 << 1 + LLDPMAUPMD10BaseT_FDInv uint16 = 1 << 2 + LLDPMAUPMD100BaseT4Inv uint16 = 1 << 3 + LLDPMAUPMD100BaseTXInv uint16 = 1 << 4 + LLDPMAUPMD100BaseTX_FDInv uint16 = 1 << 5 + LLDPMAUPMD100BaseT2Inv uint16 = 1 << 6 + LLDPMAUPMD100BaseT2_FDInv uint16 = 1 << 7 + LLDPMAUPMDFDXPAUSEInv uint16 = 1 << 8 + LLDPMAUPMDFDXAPAUSEInv uint16 = 1 << 9 + LLDPMAUPMDFDXSPAUSEInv uint16 = 1 << 10 + LLDPMAUPMDFDXBPAUSEInv uint16 = 1 << 11 + LLDPMAUPMD1000BaseXInv uint16 = 1 << 12 + LLDPMAUPMD1000BaseX_FDInv uint16 = 1 << 13 + LLDPMAUPMD1000BaseTInv uint16 = 1 << 14 + LLDPMAUPMD1000BaseT_FDInv uint16 = 1 << 15 +) + +type LLDPMACPHYConfigStatus struct { + AutoNegSupported bool + AutoNegEnabled bool + AutoNegCapability uint16 + MAUType uint16 +} + +// MDI Power options +const ( + LLDPMDIPowerPortClass byte = 1 << 0 + LLDPMDIPowerCapability byte = 1 << 1 + LLDPMDIPowerStatus byte = 1 << 2 + LLDPMDIPowerPairsAbility byte = 1 << 3 +) + +type LLDPPowerType byte + +type LLDPPowerSource byte + +type LLDPPowerPriority byte + +const ( + LLDPPowerPriorityUnknown LLDPPowerPriority = 0 + LLDPPowerPriorityMedium LLDPPowerPriority = 1 + LLDPPowerPriorityHigh LLDPPowerPriority = 2 + LLDPPowerPriorityLow LLDPPowerPriority = 3 +) + +type LLDPPowerViaMDI8023 struct { + PortClassPSE bool // false = PD + PSESupported bool + PSEEnabled bool + PSEPairsAbility bool + PSEPowerPair uint8 + PSEClass uint8 + Type LLDPPowerType + Source LLDPPowerSource + Priority LLDPPowerPriority + Requested uint16 // 1-510 Watts + Allocated uint16 // 1-510 Watts +} + +// LLDPInfo8023 represents the information carried in 802.3 Org-specific TLVs +type LLDPInfo8023 struct { + MACPHYConfigStatus LLDPMACPHYConfigStatus + PowerViaMDI LLDPPowerViaMDI8023 + LinkAggregation LLDPLinkAggregation + MTU uint16 +} + +// IEEE 802.1Qbg TLV Subtypes +const ( + LLDP8021QbgEVB uint8 = 0 + LLDP8021QbgCDCP uint8 = 1 + LLDP8021QbgVDP uint8 = 2 + LLDP8021QbgEVB22 uint8 = 13 +) + +// LLDPEVBCapabilities Types +const ( + LLDPEVBCapsSTD uint16 = 1 << 7 + LLDPEVBCapsRR uint16 = 1 << 6 + LLDPEVBCapsRTE uint16 = 1 << 2 + LLDPEVBCapsECP uint16 = 1 << 1 + LLDPEVBCapsVDP uint16 = 1 << 0 +) + +// LLDPEVBCapabilities represents the EVB capabilities of a device +type LLDPEVBCapabilities struct { + StandardBridging bool + ReflectiveRelay bool + RetransmissionTimerExponent bool + EdgeControlProtocol bool + VSIDiscoveryProtocol bool +} + +type LLDPEVBSettings struct { + Supported LLDPEVBCapabilities + Enabled LLDPEVBCapabilities + SupportedVSIs uint16 + ConfiguredVSIs uint16 + RTEExponent uint8 +} + +// LLDPInfo8021Qbg represents the information carried in 802.1Qbg Org-specific TLVs +type LLDPInfo8021Qbg struct { + EVBSettings LLDPEVBSettings +} + +type LLDPMediaSubtype uint8 + +// Media TLV Subtypes +const ( + LLDPMediaTypeCapabilities LLDPMediaSubtype = 1 + LLDPMediaTypeNetwork LLDPMediaSubtype = 2 + LLDPMediaTypeLocation LLDPMediaSubtype = 3 + LLDPMediaTypePower LLDPMediaSubtype = 4 + LLDPMediaTypeHardware LLDPMediaSubtype = 5 + LLDPMediaTypeFirmware LLDPMediaSubtype = 6 + LLDPMediaTypeSoftware LLDPMediaSubtype = 7 + LLDPMediaTypeSerial LLDPMediaSubtype = 8 + LLDPMediaTypeManufacturer LLDPMediaSubtype = 9 + LLDPMediaTypeModel LLDPMediaSubtype = 10 + LLDPMediaTypeAssetID LLDPMediaSubtype = 11 +) + +type LLDPMediaClass uint8 + +// Media Class Values +const ( + LLDPMediaClassUndefined LLDPMediaClass = 0 + LLDPMediaClassEndpointI LLDPMediaClass = 1 + LLDPMediaClassEndpointII LLDPMediaClass = 2 + LLDPMediaClassEndpointIII LLDPMediaClass = 3 + LLDPMediaClassNetwork LLDPMediaClass = 4 +) + +// LLDPMediaCapabilities Types +const ( + LLDPMediaCapsLLDP uint16 = 1 << 0 + LLDPMediaCapsNetwork uint16 = 1 << 1 + LLDPMediaCapsLocation uint16 = 1 << 2 + LLDPMediaCapsPowerPSE uint16 = 1 << 3 + LLDPMediaCapsPowerPD uint16 = 1 << 4 + LLDPMediaCapsInventory uint16 = 1 << 5 +) + +// LLDPMediaCapabilities represents the LLDP Media capabilities of a device +type LLDPMediaCapabilities struct { + Capabilities bool + NetworkPolicy bool + Location bool + PowerPSE bool + PowerPD bool + Inventory bool + Class LLDPMediaClass +} + +type LLDPApplicationType uint8 + +const ( + LLDPAppTypeReserved LLDPApplicationType = 0 + LLDPAppTypeVoice LLDPApplicationType = 1 + LLDPappTypeVoiceSignaling LLDPApplicationType = 2 + LLDPappTypeGuestVoice LLDPApplicationType = 3 + LLDPappTypeGuestVoiceSignaling LLDPApplicationType = 4 + LLDPappTypeSoftphoneVoice LLDPApplicationType = 5 + LLDPappTypeVideoConferencing LLDPApplicationType = 6 + LLDPappTypeStreamingVideo LLDPApplicationType = 7 + LLDPappTypeVideoSignaling LLDPApplicationType = 8 +) + +type LLDPNetworkPolicy struct { + ApplicationType LLDPApplicationType + Defined bool + Tagged bool + VLANId uint16 + L2Priority uint16 + DSCPValue uint8 +} + +type LLDPLocationFormat uint8 + +const ( + LLDPLocationFormatInvalid LLDPLocationFormat = 0 + LLDPLocationFormatCoordinate LLDPLocationFormat = 1 + LLDPLocationFormatAddress LLDPLocationFormat = 2 + LLDPLocationFormatECS LLDPLocationFormat = 3 +) + +type LLDPLocationAddressWhat uint8 + +const ( + LLDPLocationAddressWhatDHCP LLDPLocationAddressWhat = 0 + LLDPLocationAddressWhatNetwork LLDPLocationAddressWhat = 1 + LLDPLocationAddressWhatClient LLDPLocationAddressWhat = 2 +) + +type LLDPLocationAddressType uint8 + +const ( + LLDPLocationAddressTypeLanguage LLDPLocationAddressType = 0 + LLDPLocationAddressTypeNational LLDPLocationAddressType = 1 + LLDPLocationAddressTypeCounty LLDPLocationAddressType = 2 + LLDPLocationAddressTypeCity LLDPLocationAddressType = 3 + LLDPLocationAddressTypeCityDivision LLDPLocationAddressType = 4 + LLDPLocationAddressTypeNeighborhood LLDPLocationAddressType = 5 + LLDPLocationAddressTypeStreet LLDPLocationAddressType = 6 + LLDPLocationAddressTypeLeadingStreet LLDPLocationAddressType = 16 + LLDPLocationAddressTypeTrailingStreet LLDPLocationAddressType = 17 + LLDPLocationAddressTypeStreetSuffix LLDPLocationAddressType = 18 + LLDPLocationAddressTypeHouseNum LLDPLocationAddressType = 19 + LLDPLocationAddressTypeHouseSuffix LLDPLocationAddressType = 20 + LLDPLocationAddressTypeLandmark LLDPLocationAddressType = 21 + LLDPLocationAddressTypeAdditional LLDPLocationAddressType = 22 + LLDPLocationAddressTypeName LLDPLocationAddressType = 23 + LLDPLocationAddressTypePostal LLDPLocationAddressType = 24 + LLDPLocationAddressTypeBuilding LLDPLocationAddressType = 25 + LLDPLocationAddressTypeUnit LLDPLocationAddressType = 26 + LLDPLocationAddressTypeFloor LLDPLocationAddressType = 27 + LLDPLocationAddressTypeRoom LLDPLocationAddressType = 28 + LLDPLocationAddressTypePlace LLDPLocationAddressType = 29 + LLDPLocationAddressTypeScript LLDPLocationAddressType = 128 +) + +type LLDPLocationCoordinate struct { + LatitudeResolution uint8 + Latitude uint64 + LongitudeResolution uint8 + Longitude uint64 + AltitudeType uint8 + AltitudeResolution uint16 + Altitude uint32 + Datum uint8 +} + +type LLDPLocationAddressLine struct { + Type LLDPLocationAddressType + Value string +} + +type LLDPLocationAddress struct { + What LLDPLocationAddressWhat + CountryCode string + AddressLines []LLDPLocationAddressLine +} + +type LLDPLocationECS struct { + ELIN string +} + +// LLDP represents a physical location. +// Only one of the embedded types will contain values, depending on Format. +type LLDPLocation struct { + Format LLDPLocationFormat + Coordinate LLDPLocationCoordinate + Address LLDPLocationAddress + ECS LLDPLocationECS +} + +type LLDPPowerViaMDI struct { + Type LLDPPowerType + Source LLDPPowerSource + Priority LLDPPowerPriority + Value uint16 +} + +// LLDPInfoMedia represents the information carried in TR-41 Org-specific TLVs +type LLDPInfoMedia struct { + MediaCapabilities LLDPMediaCapabilities + NetworkPolicy LLDPNetworkPolicy + Location LLDPLocation + PowerViaMDI LLDPPowerViaMDI + HardwareRevision string + FirmwareRevision string + SoftwareRevision string + SerialNumber string + Manufacturer string + Model string + AssetID string +} + +type LLDPCisco2Subtype uint8 + +// Cisco2 TLV Subtypes +const ( + LLDPCisco2PowerViaMDI LLDPCisco2Subtype = 1 +) + +const ( + LLDPCiscoPSESupport uint8 = 1 << 0 + LLDPCiscoArchShared uint8 = 1 << 1 + LLDPCiscoPDSparePair uint8 = 1 << 2 + LLDPCiscoPSESparePair uint8 = 1 << 3 +) + +// LLDPInfoCisco2 represents the information carried in Cisco Org-specific TLVs +type LLDPInfoCisco2 struct { + PSEFourWirePoESupported bool + PDSparePairArchitectureShared bool + PDRequestSparePairPoEOn bool + PSESparePairPoEOn bool +} + +// Profinet Subtypes +type LLDPProfinetSubtype uint8 + +const ( + LLDPProfinetPNIODelay LLDPProfinetSubtype = 1 + LLDPProfinetPNIOPortStatus LLDPProfinetSubtype = 2 + LLDPProfinetPNIOMRPPortStatus LLDPProfinetSubtype = 4 + LLDPProfinetPNIOChassisMAC LLDPProfinetSubtype = 5 + LLDPProfinetPNIOPTCPStatus LLDPProfinetSubtype = 6 +) + +type LLDPPNIODelay struct { + RXLocal uint32 + RXRemote uint32 + TXLocal uint32 + TXRemote uint32 + CableLocal uint32 +} + +type LLDPPNIOPortStatus struct { + Class2 uint16 + Class3 uint16 +} + +type LLDPPNIOMRPPortStatus struct { + UUID []byte + Status uint16 +} + +type LLDPPNIOPTCPStatus struct { + MasterAddress []byte + SubdomainUUID []byte + IRDataUUID []byte + PeriodValid bool + PeriodLength uint32 + RedPeriodValid bool + RedPeriodBegin uint32 + OrangePeriodValid bool + OrangePeriodBegin uint32 + GreenPeriodValid bool + GreenPeriodBegin uint32 +} + +// LLDPInfoProfinet represents the information carried in Profinet Org-specific TLVs +type LLDPInfoProfinet struct { + PNIODelay LLDPPNIODelay + PNIOPortStatus LLDPPNIOPortStatus + PNIOMRPPortStatus LLDPPNIOMRPPortStatus + ChassisMAC []byte + PNIOPTCPStatus LLDPPNIOPTCPStatus +} + +// LayerType returns gopacket.LayerTypeLinkLayerDiscovery. +func (c *LinkLayerDiscovery) LayerType() gopacket.LayerType { + return LayerTypeLinkLayerDiscovery +} + +// SerializeTo serializes LLDP packet to bytes and writes on SerializeBuffer. +func (c *LinkLayerDiscovery) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + chassIDLen := c.ChassisID.serializedLen() + portIDLen := c.PortID.serializedLen() + vb, err := b.AppendBytes(chassIDLen + portIDLen + 4) // +4 for TTL + if err != nil { + return err + } + copy(vb[:chassIDLen], c.ChassisID.serialize()) + copy(vb[chassIDLen:], c.PortID.serialize()) + ttlIDLen := uint16(LLDPTLVTTL)<<9 | uint16(2) + binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen:], ttlIDLen) + binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen+2:], c.TTL) + + for _, v := range c.Values { + vb, err := b.AppendBytes(int(v.Length) + 2) // +2 for TLV type and length; 1 byte for subtype is included in v.Value + if err != nil { + return err + } + idLen := ((uint16(v.Type) << 9) | v.Length) + binary.BigEndian.PutUint16(vb[0:2], idLen) + copy(vb[2:], v.Value) + } + + vb, err = b.AppendBytes(2) // End Tlv, 2 bytes + if err != nil { + return err + } + binary.BigEndian.PutUint16(vb[len(vb)-2:], uint16(0)) //End tlv, 2 bytes, all zero + return nil + +} + +func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error { + var vals []LinkLayerDiscoveryValue + vData := data[0:] + for len(vData) > 0 { + if len(vData) < 2 { + p.SetTruncated() + return errors.New("LLDP vdata < 2 bytes") + } + nbit := vData[0] & 0x01 + t := LLDPTLVType(vData[0] >> 1) + val := LinkLayerDiscoveryValue{Type: t, Length: uint16(nbit)<<8 + uint16(vData[1])} + if val.Length > 0 { + if len(vData) < int(val.Length+2) { + p.SetTruncated() + return fmt.Errorf("LLDP VData < %d bytes", val.Length+2) + } + val.Value = vData[2 : val.Length+2] + } + vals = append(vals, val) + if t == LLDPTLVEnd { + break + } + if len(vData) < int(2+val.Length) { + return errors.New("Malformed LinkLayerDiscovery Header") + } + vData = vData[2+val.Length:] + } + if len(vals) < 4 { + return errors.New("Missing mandatory LinkLayerDiscovery TLV") + } + c := &LinkLayerDiscovery{} + gotEnd := false + for _, v := range vals { + switch v.Type { + case LLDPTLVEnd: + gotEnd = true + case LLDPTLVChassisID: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery ChassisID TLV") + } + c.ChassisID.Subtype = LLDPChassisIDSubType(v.Value[0]) + c.ChassisID.ID = v.Value[1:] + case LLDPTLVPortID: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery PortID TLV") + } + c.PortID.Subtype = LLDPPortIDSubType(v.Value[0]) + c.PortID.ID = v.Value[1:] + case LLDPTLVTTL: + if len(v.Value) < 2 { + return errors.New("Malformed LinkLayerDiscovery TTL TLV") + } + c.TTL = binary.BigEndian.Uint16(v.Value[0:2]) + default: + c.Values = append(c.Values, v) + } + } + if c.ChassisID.Subtype == 0 || c.PortID.Subtype == 0 || !gotEnd { + return errors.New("Missing mandatory LinkLayerDiscovery TLV") + } + c.Contents = data + p.AddLayer(c) + + info := &LinkLayerDiscoveryInfo{} + p.AddLayer(info) + for _, v := range c.Values { + switch v.Type { + case LLDPTLVPortDescription: + info.PortDescription = string(v.Value) + case LLDPTLVSysName: + info.SysName = string(v.Value) + case LLDPTLVSysDescription: + info.SysDescription = string(v.Value) + case LLDPTLVSysCapabilities: + if err := checkLLDPTLVLen(v, 4); err != nil { + return err + } + info.SysCapabilities.SystemCap = getCapabilities(binary.BigEndian.Uint16(v.Value[0:2])) + info.SysCapabilities.EnabledCap = getCapabilities(binary.BigEndian.Uint16(v.Value[2:4])) + case LLDPTLVMgmtAddress: + if err := checkLLDPTLVLen(v, 9); err != nil { + return err + } + mlen := v.Value[0] + if err := checkLLDPTLVLen(v, int(mlen+7)); err != nil { + return err + } + info.MgmtAddress.Subtype = IANAAddressFamily(v.Value[1]) + info.MgmtAddress.Address = v.Value[2 : mlen+1] + info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1]) + info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6]) + olen := v.Value[mlen+6] + if err := checkLLDPTLVLen(v, int(mlen+7+olen)); err != nil { + return err + } + info.MgmtAddress.OID = string(v.Value[mlen+7 : mlen+7+olen]) + case LLDPTLVOrgSpecific: + if err := checkLLDPTLVLen(v, 4); err != nil { + return err + } + info.OrgTLVs = append(info.OrgTLVs, LLDPOrgSpecificTLV{IEEEOUI(binary.BigEndian.Uint32(append([]byte{byte(0)}, v.Value[0:3]...))), uint8(v.Value[3]), v.Value[4:]}) + } + } + return nil +} + +func (l *LinkLayerDiscoveryInfo) Decode8021() (info LLDPInfo8021, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8021 { + continue + } + switch o.SubType { + case LLDP8021SubtypePortVLANID: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.PVID = binary.BigEndian.Uint16(o.Info[0:2]) + case LLDP8021SubtypeProtocolVLANID: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + sup := (o.Info[0]&LLDPProtocolVLANIDCapability > 0) + en := (o.Info[0]&LLDPProtocolVLANIDStatus > 0) + id := binary.BigEndian.Uint16(o.Info[1:3]) + info.PPVIDs = append(info.PPVIDs, PortProtocolVLANID{sup, en, id}) + case LLDP8021SubtypeVLANName: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + id := binary.BigEndian.Uint16(o.Info[0:2]) + info.VLANNames = append(info.VLANNames, VLANName{id, string(o.Info[3:])}) + case LLDP8021SubtypeProtocolIdentity: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + l := int(o.Info[0]) + if l > 0 { + info.ProtocolIdentities = append(info.ProtocolIdentities, o.Info[1:1+l]) + } + case LLDP8021SubtypeVDIUsageDigest: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.VIDUsageDigest = binary.BigEndian.Uint32(o.Info[0:4]) + case LLDP8021SubtypeManagementVID: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.ManagementVID = binary.BigEndian.Uint16(o.Info[0:2]) + case LLDP8021SubtypeLinkAggregation: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPAggregationCapability > 0) + en := (o.Info[0]&LLDPAggregationStatus > 0) + info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])} + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) Decode8023() (info LLDPInfo8023, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8023 { + continue + } + switch o.SubType { + case LLDP8023SubtypeMACPHY: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPMACPHYCapability > 0) + en := (o.Info[0]&LLDPMACPHYStatus > 0) + ca := binary.BigEndian.Uint16(o.Info[1:3]) + mau := binary.BigEndian.Uint16(o.Info[3:5]) + info.MACPHYConfigStatus = LLDPMACPHYConfigStatus{sup, en, ca, mau} + case LLDP8023SubtypeMDIPower: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + info.PowerViaMDI.PortClassPSE = (o.Info[0]&LLDPMDIPowerPortClass > 0) + info.PowerViaMDI.PSESupported = (o.Info[0]&LLDPMDIPowerCapability > 0) + info.PowerViaMDI.PSEEnabled = (o.Info[0]&LLDPMDIPowerStatus > 0) + info.PowerViaMDI.PSEPairsAbility = (o.Info[0]&LLDPMDIPowerPairsAbility > 0) + info.PowerViaMDI.PSEPowerPair = uint8(o.Info[1]) + info.PowerViaMDI.PSEClass = uint8(o.Info[2]) + if len(o.Info) >= 7 { + info.PowerViaMDI.Type = LLDPPowerType((o.Info[3] & 0xc0) >> 6) + info.PowerViaMDI.Source = LLDPPowerSource((o.Info[3] & 0x30) >> 4) + if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 { + info.PowerViaMDI.Source += 128 // For Stringify purposes + } + info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[3] & 0x0f) + info.PowerViaMDI.Requested = binary.BigEndian.Uint16(o.Info[4:6]) + info.PowerViaMDI.Allocated = binary.BigEndian.Uint16(o.Info[6:8]) + } + case LLDP8023SubtypeLinkAggregation: + if err = checkLLDPOrgSpecificLen(o, 5); err != nil { + return + } + sup := (o.Info[0]&LLDPAggregationCapability > 0) + en := (o.Info[0]&LLDPAggregationStatus > 0) + info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])} + case LLDP8023SubtypeMTU: + if err = checkLLDPOrgSpecificLen(o, 2); err != nil { + return + } + info.MTU = binary.BigEndian.Uint16(o.Info[0:2]) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) Decode8021Qbg() (info LLDPInfo8021Qbg, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUI8021Qbg { + continue + } + switch o.SubType { + case LLDP8021QbgEVB: + if err = checkLLDPOrgSpecificLen(o, 9); err != nil { + return + } + info.EVBSettings.Supported = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[0:2])) + info.EVBSettings.Enabled = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[2:4])) + info.EVBSettings.SupportedVSIs = binary.BigEndian.Uint16(o.Info[4:6]) + info.EVBSettings.ConfiguredVSIs = binary.BigEndian.Uint16(o.Info[6:8]) + info.EVBSettings.RTEExponent = uint8(o.Info[8]) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeMedia() (info LLDPInfoMedia, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUIMedia { + continue + } + switch LLDPMediaSubtype(o.SubType) { + case LLDPMediaTypeCapabilities: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + b := binary.BigEndian.Uint16(o.Info[0:2]) + info.MediaCapabilities.Capabilities = (b & LLDPMediaCapsLLDP) > 0 + info.MediaCapabilities.NetworkPolicy = (b & LLDPMediaCapsNetwork) > 0 + info.MediaCapabilities.Location = (b & LLDPMediaCapsLocation) > 0 + info.MediaCapabilities.PowerPSE = (b & LLDPMediaCapsPowerPSE) > 0 + info.MediaCapabilities.PowerPD = (b & LLDPMediaCapsPowerPD) > 0 + info.MediaCapabilities.Inventory = (b & LLDPMediaCapsInventory) > 0 + info.MediaCapabilities.Class = LLDPMediaClass(o.Info[2]) + case LLDPMediaTypeNetwork: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.NetworkPolicy.ApplicationType = LLDPApplicationType(o.Info[0]) + b := binary.BigEndian.Uint16(o.Info[1:3]) + info.NetworkPolicy.Defined = (b & 0x8000) == 0 + info.NetworkPolicy.Tagged = (b & 0x4000) > 0 + info.NetworkPolicy.VLANId = (b & 0x1ffe) >> 1 + b = binary.BigEndian.Uint16(o.Info[2:4]) + info.NetworkPolicy.L2Priority = (b & 0x01c0) >> 6 + info.NetworkPolicy.DSCPValue = uint8(o.Info[3] & 0x3f) + case LLDPMediaTypeLocation: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + info.Location.Format = LLDPLocationFormat(o.Info[0]) + o.Info = o.Info[1:] + switch info.Location.Format { + case LLDPLocationFormatCoordinate: + if err = checkLLDPOrgSpecificLen(o, 16); err != nil { + return + } + info.Location.Coordinate.LatitudeResolution = uint8(o.Info[0]&0xfc) >> 2 + b := binary.BigEndian.Uint64(o.Info[0:8]) + info.Location.Coordinate.Latitude = (b & 0x03ffffffff000000) >> 24 + info.Location.Coordinate.LongitudeResolution = uint8(o.Info[5]&0xfc) >> 2 + b = binary.BigEndian.Uint64(o.Info[5:13]) + info.Location.Coordinate.Longitude = (b & 0x03ffffffff000000) >> 24 + info.Location.Coordinate.AltitudeType = uint8((o.Info[10] & 0x30) >> 4) + b1 := binary.BigEndian.Uint16(o.Info[10:12]) + info.Location.Coordinate.AltitudeResolution = (b1 & 0xfc0) >> 6 + b2 := binary.BigEndian.Uint32(o.Info[11:15]) + info.Location.Coordinate.Altitude = b2 & 0x3fffffff + info.Location.Coordinate.Datum = uint8(o.Info[15]) + case LLDPLocationFormatAddress: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + //ll := uint8(o.Info[0]) + info.Location.Address.What = LLDPLocationAddressWhat(o.Info[1]) + info.Location.Address.CountryCode = string(o.Info[2:4]) + data := o.Info[4:] + for len(data) > 1 { + aType := LLDPLocationAddressType(data[0]) + aLen := int(data[1]) + if len(data) >= aLen+2 { + info.Location.Address.AddressLines = append(info.Location.Address.AddressLines, LLDPLocationAddressLine{aType, string(data[2 : aLen+2])}) + data = data[aLen+2:] + } else { + break + } + } + case LLDPLocationFormatECS: + info.Location.ECS.ELIN = string(o.Info) + } + case LLDPMediaTypePower: + if err = checkLLDPOrgSpecificLen(o, 3); err != nil { + return + } + info.PowerViaMDI.Type = LLDPPowerType((o.Info[0] & 0xc0) >> 6) + info.PowerViaMDI.Source = LLDPPowerSource((o.Info[0] & 0x30) >> 4) + if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 { + info.PowerViaMDI.Source += 128 // For Stringify purposes + } + info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[0] & 0x0f) + info.PowerViaMDI.Value = binary.BigEndian.Uint16(o.Info[1:3]) * 100 // 0 to 102.3 w, 0.1W increments + case LLDPMediaTypeHardware: + info.HardwareRevision = string(o.Info) + case LLDPMediaTypeFirmware: + info.FirmwareRevision = string(o.Info) + case LLDPMediaTypeSoftware: + info.SoftwareRevision = string(o.Info) + case LLDPMediaTypeSerial: + info.SerialNumber = string(o.Info) + case LLDPMediaTypeManufacturer: + info.Manufacturer = string(o.Info) + case LLDPMediaTypeModel: + info.Model = string(o.Info) + case LLDPMediaTypeAssetID: + info.AssetID = string(o.Info) + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeCisco2() (info LLDPInfoCisco2, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUICisco2 { + continue + } + switch LLDPCisco2Subtype(o.SubType) { + case LLDPCisco2PowerViaMDI: + if err = checkLLDPOrgSpecificLen(o, 1); err != nil { + return + } + info.PSEFourWirePoESupported = (o.Info[0] & LLDPCiscoPSESupport) > 0 + info.PDSparePairArchitectureShared = (o.Info[0] & LLDPCiscoArchShared) > 0 + info.PDRequestSparePairPoEOn = (o.Info[0] & LLDPCiscoPDSparePair) > 0 + info.PSESparePairPoEOn = (o.Info[0] & LLDPCiscoPSESparePair) > 0 + } + } + return +} + +func (l *LinkLayerDiscoveryInfo) DecodeProfinet() (info LLDPInfoProfinet, err error) { + for _, o := range l.OrgTLVs { + if o.OUI != IEEEOUIProfinet { + continue + } + switch LLDPProfinetSubtype(o.SubType) { + case LLDPProfinetPNIODelay: + if err = checkLLDPOrgSpecificLen(o, 20); err != nil { + return + } + info.PNIODelay.RXLocal = binary.BigEndian.Uint32(o.Info[0:4]) + info.PNIODelay.RXRemote = binary.BigEndian.Uint32(o.Info[4:8]) + info.PNIODelay.TXLocal = binary.BigEndian.Uint32(o.Info[8:12]) + info.PNIODelay.TXRemote = binary.BigEndian.Uint32(o.Info[12:16]) + info.PNIODelay.CableLocal = binary.BigEndian.Uint32(o.Info[16:20]) + case LLDPProfinetPNIOPortStatus: + if err = checkLLDPOrgSpecificLen(o, 4); err != nil { + return + } + info.PNIOPortStatus.Class2 = binary.BigEndian.Uint16(o.Info[0:2]) + info.PNIOPortStatus.Class3 = binary.BigEndian.Uint16(o.Info[2:4]) + case LLDPProfinetPNIOMRPPortStatus: + if err = checkLLDPOrgSpecificLen(o, 18); err != nil { + return + } + info.PNIOMRPPortStatus.UUID = o.Info[0:16] + info.PNIOMRPPortStatus.Status = binary.BigEndian.Uint16(o.Info[16:18]) + case LLDPProfinetPNIOChassisMAC: + if err = checkLLDPOrgSpecificLen(o, 6); err != nil { + return + } + info.ChassisMAC = o.Info[0:6] + case LLDPProfinetPNIOPTCPStatus: + if err = checkLLDPOrgSpecificLen(o, 54); err != nil { + return + } + info.PNIOPTCPStatus.MasterAddress = o.Info[0:6] + info.PNIOPTCPStatus.SubdomainUUID = o.Info[6:22] + info.PNIOPTCPStatus.IRDataUUID = o.Info[22:38] + b := binary.BigEndian.Uint32(o.Info[38:42]) + info.PNIOPTCPStatus.PeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.PeriodLength = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[42:46]) + info.PNIOPTCPStatus.RedPeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.RedPeriodBegin = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[46:50]) + info.PNIOPTCPStatus.OrangePeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.OrangePeriodBegin = b & 0x7fffffff + b = binary.BigEndian.Uint32(o.Info[50:54]) + info.PNIOPTCPStatus.GreenPeriodValid = (b & 0x80000000) > 0 + info.PNIOPTCPStatus.GreenPeriodBegin = b & 0x7fffffff + } + } + return +} + +// LayerType returns gopacket.LayerTypeLinkLayerDiscoveryInfo. +func (c *LinkLayerDiscoveryInfo) LayerType() gopacket.LayerType { + return LayerTypeLinkLayerDiscoveryInfo +} + +func getCapabilities(v uint16) (c LLDPCapabilities) { + c.Other = (v&LLDPCapsOther > 0) + c.Repeater = (v&LLDPCapsRepeater > 0) + c.Bridge = (v&LLDPCapsBridge > 0) + c.WLANAP = (v&LLDPCapsWLANAP > 0) + c.Router = (v&LLDPCapsRouter > 0) + c.Phone = (v&LLDPCapsPhone > 0) + c.DocSis = (v&LLDPCapsDocSis > 0) + c.StationOnly = (v&LLDPCapsStationOnly > 0) + c.CVLAN = (v&LLDPCapsCVLAN > 0) + c.SVLAN = (v&LLDPCapsSVLAN > 0) + c.TMPR = (v&LLDPCapsTmpr > 0) + return +} + +func getEVBCapabilities(v uint16) (c LLDPEVBCapabilities) { + c.StandardBridging = (v & LLDPEVBCapsSTD) > 0 + c.StandardBridging = (v & LLDPEVBCapsSTD) > 0 + c.ReflectiveRelay = (v & LLDPEVBCapsRR) > 0 + c.RetransmissionTimerExponent = (v & LLDPEVBCapsRTE) > 0 + c.EdgeControlProtocol = (v & LLDPEVBCapsECP) > 0 + c.VSIDiscoveryProtocol = (v & LLDPEVBCapsVDP) > 0 + return +} + +func (t LLDPTLVType) String() (s string) { + switch t { + case LLDPTLVEnd: + s = "TLV End" + case LLDPTLVChassisID: + s = "Chassis ID" + case LLDPTLVPortID: + s = "Port ID" + case LLDPTLVTTL: + s = "TTL" + case LLDPTLVPortDescription: + s = "Port Description" + case LLDPTLVSysName: + s = "System Name" + case LLDPTLVSysDescription: + s = "System Description" + case LLDPTLVSysCapabilities: + s = "System Capabilities" + case LLDPTLVMgmtAddress: + s = "Management Address" + case LLDPTLVOrgSpecific: + s = "Organisation Specific" + default: + s = "Unknown" + } + return +} + +func (t LLDPChassisIDSubType) String() (s string) { + switch t { + case LLDPChassisIDSubTypeReserved: + s = "Reserved" + case LLDPChassisIDSubTypeChassisComp: + s = "Chassis Component" + case LLDPChassisIDSubtypeIfaceAlias: + s = "Interface Alias" + case LLDPChassisIDSubTypePortComp: + s = "Port Component" + case LLDPChassisIDSubTypeMACAddr: + s = "MAC Address" + case LLDPChassisIDSubTypeNetworkAddr: + s = "Network Address" + case LLDPChassisIDSubtypeIfaceName: + s = "Interface Name" + case LLDPChassisIDSubTypeLocal: + s = "Local" + default: + s = "Unknown" + } + return +} + +func (t LLDPPortIDSubType) String() (s string) { + switch t { + case LLDPPortIDSubtypeReserved: + s = "Reserved" + case LLDPPortIDSubtypeIfaceAlias: + s = "Interface Alias" + case LLDPPortIDSubtypePortComp: + s = "Port Component" + case LLDPPortIDSubtypeMACAddr: + s = "MAC Address" + case LLDPPortIDSubtypeNetworkAddr: + s = "Network Address" + case LLDPPortIDSubtypeIfaceName: + s = "Interface Name" + case LLDPPortIDSubtypeAgentCircuitID: + s = "Agent Circuit ID" + case LLDPPortIDSubtypeLocal: + s = "Local" + default: + s = "Unknown" + } + return +} + +func (t IANAAddressFamily) String() (s string) { + switch t { + case IANAAddressFamilyReserved: + s = "Reserved" + case IANAAddressFamilyIPV4: + s = "IPv4" + case IANAAddressFamilyIPV6: + s = "IPv6" + case IANAAddressFamilyNSAP: + s = "NSAP" + case IANAAddressFamilyHDLC: + s = "HDLC" + case IANAAddressFamilyBBN1822: + s = "BBN 1822" + case IANAAddressFamily802: + s = "802 media plus Ethernet 'canonical format'" + case IANAAddressFamilyE163: + s = "E.163" + case IANAAddressFamilyE164: + s = "E.164 (SMDS, Frame Relay, ATM)" + case IANAAddressFamilyF69: + s = "F.69 (Telex)" + case IANAAddressFamilyX121: + s = "X.121, X.25, Frame Relay" + case IANAAddressFamilyIPX: + s = "IPX" + case IANAAddressFamilyAtalk: + s = "Appletalk" + case IANAAddressFamilyDecnet: + s = "Decnet IV" + case IANAAddressFamilyBanyan: + s = "Banyan Vines" + case IANAAddressFamilyE164NSAP: + s = "E.164 with NSAP format subaddress" + case IANAAddressFamilyDNS: + s = "DNS" + case IANAAddressFamilyDistname: + s = "Distinguished Name" + case IANAAddressFamilyASNumber: + s = "AS Number" + case IANAAddressFamilyXTPIPV4: + s = "XTP over IP version 4" + case IANAAddressFamilyXTPIPV6: + s = "XTP over IP version 6" + case IANAAddressFamilyXTP: + s = "XTP native mode XTP" + case IANAAddressFamilyFcWWPN: + s = "Fibre Channel World-Wide Port Name" + case IANAAddressFamilyFcWWNN: + s = "Fibre Channel World-Wide Node Name" + case IANAAddressFamilyGWID: + s = "GWID" + case IANAAddressFamilyL2VPN: + s = "AFI for Layer 2 VPN" + default: + s = "Unknown" + } + return +} + +func (t LLDPInterfaceSubtype) String() (s string) { + switch t { + case LLDPInterfaceSubtypeUnknown: + s = "Unknown" + case LLDPInterfaceSubtypeifIndex: + s = "IfIndex" + case LLDPInterfaceSubtypeSysPort: + s = "System Port Number" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerType) String() (s string) { + switch t { + case 0: + s = "Type 2 PSE Device" + case 1: + s = "Type 2 PD Device" + case 2: + s = "Type 1 PSE Device" + case 3: + s = "Type 1 PD Device" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerSource) String() (s string) { + switch t { + // PD Device + case 0: + s = "Unknown" + case 1: + s = "PSE" + case 2: + s = "Local" + case 3: + s = "PSE and Local" + // PSE Device (Actual value + 128) + case 128: + s = "Unknown" + case 129: + s = "Primary Power Source" + case 130: + s = "Backup Power Source" + default: + s = "Unknown" + } + return +} + +func (t LLDPPowerPriority) String() (s string) { + switch t { + case 0: + s = "Unknown" + case 1: + s = "Critical" + case 2: + s = "High" + case 3: + s = "Low" + default: + s = "Unknown" + } + return +} + +func (t LLDPMediaSubtype) String() (s string) { + switch t { + case LLDPMediaTypeCapabilities: + s = "Media Capabilities " + case LLDPMediaTypeNetwork: + s = "Network Policy" + case LLDPMediaTypeLocation: + s = "Location Identification" + case LLDPMediaTypePower: + s = "Extended Power-via-MDI" + case LLDPMediaTypeHardware: + s = "Hardware Revision" + case LLDPMediaTypeFirmware: + s = "Firmware Revision" + case LLDPMediaTypeSoftware: + s = "Software Revision" + case LLDPMediaTypeSerial: + s = "Serial Number" + case LLDPMediaTypeManufacturer: + s = "Manufacturer" + case LLDPMediaTypeModel: + s = "Model" + case LLDPMediaTypeAssetID: + s = "Asset ID" + default: + s = "Unknown" + } + return +} + +func (t LLDPMediaClass) String() (s string) { + switch t { + case LLDPMediaClassUndefined: + s = "Undefined" + case LLDPMediaClassEndpointI: + s = "Endpoint Class I" + case LLDPMediaClassEndpointII: + s = "Endpoint Class II" + case LLDPMediaClassEndpointIII: + s = "Endpoint Class III" + case LLDPMediaClassNetwork: + s = "Network connectivity " + default: + s = "Unknown" + } + return +} + +func (t LLDPApplicationType) String() (s string) { + switch t { + case LLDPAppTypeReserved: + s = "Reserved" + case LLDPAppTypeVoice: + s = "Voice" + case LLDPappTypeVoiceSignaling: + s = "Voice Signaling" + case LLDPappTypeGuestVoice: + s = "Guest Voice" + case LLDPappTypeGuestVoiceSignaling: + s = "Guest Voice Signaling" + case LLDPappTypeSoftphoneVoice: + s = "Softphone Voice" + case LLDPappTypeVideoConferencing: + s = "Video Conferencing" + case LLDPappTypeStreamingVideo: + s = "Streaming Video" + case LLDPappTypeVideoSignaling: + s = "Video Signaling" + default: + s = "Unknown" + } + return +} + +func (t LLDPLocationFormat) String() (s string) { + switch t { + case LLDPLocationFormatInvalid: + s = "Invalid" + case LLDPLocationFormatCoordinate: + s = "Coordinate-based LCI" + case LLDPLocationFormatAddress: + s = "Address-based LCO" + case LLDPLocationFormatECS: + s = "ECS ELIN" + default: + s = "Unknown" + } + return +} + +func (t LLDPLocationAddressType) String() (s string) { + switch t { + case LLDPLocationAddressTypeLanguage: + s = "Language" + case LLDPLocationAddressTypeNational: + s = "National subdivisions (province, state, etc)" + case LLDPLocationAddressTypeCounty: + s = "County, parish, district" + case LLDPLocationAddressTypeCity: + s = "City, township" + case LLDPLocationAddressTypeCityDivision: + s = "City division, borough, ward" + case LLDPLocationAddressTypeNeighborhood: + s = "Neighborhood, block" + case LLDPLocationAddressTypeStreet: + s = "Street" + case LLDPLocationAddressTypeLeadingStreet: + s = "Leading street direction" + case LLDPLocationAddressTypeTrailingStreet: + s = "Trailing street suffix" + case LLDPLocationAddressTypeStreetSuffix: + s = "Street suffix" + case LLDPLocationAddressTypeHouseNum: + s = "House number" + case LLDPLocationAddressTypeHouseSuffix: + s = "House number suffix" + case LLDPLocationAddressTypeLandmark: + s = "Landmark or vanity address" + case LLDPLocationAddressTypeAdditional: + s = "Additional location information" + case LLDPLocationAddressTypeName: + s = "Name" + case LLDPLocationAddressTypePostal: + s = "Postal/ZIP code" + case LLDPLocationAddressTypeBuilding: + s = "Building" + case LLDPLocationAddressTypeUnit: + s = "Unit" + case LLDPLocationAddressTypeFloor: + s = "Floor" + case LLDPLocationAddressTypeRoom: + s = "Room number" + case LLDPLocationAddressTypePlace: + s = "Place type" + case LLDPLocationAddressTypeScript: + s = "Script" + default: + s = "Unknown" + } + return +} + +func checkLLDPTLVLen(v LinkLayerDiscoveryValue, l int) (err error) { + if len(v.Value) < l { + err = fmt.Errorf("Invalid TLV %v length %d (wanted mimimum %v", v.Type, len(v.Value), l) + } + return +} + +func checkLLDPOrgSpecificLen(o LLDPOrgSpecificTLV, l int) (err error) { + if len(o.Info) < l { + err = fmt.Errorf("Invalid Org Specific TLV %v length %d (wanted minimum %v)", o.SubType, len(o.Info), l) + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/loopback.go b/vendor/github.com/google/gopacket/layers/loopback.go new file mode 100644 index 00000000..839f7607 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/loopback.go @@ -0,0 +1,80 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// Loopback contains the header for loopback encapsulation. This header is +// used by both BSD and OpenBSD style loopback decoding (pcap's DLT_NULL +// and DLT_LOOP, respectively). +type Loopback struct { + BaseLayer + Family ProtocolFamily +} + +// LayerType returns LayerTypeLoopback. +func (l *Loopback) LayerType() gopacket.LayerType { return LayerTypeLoopback } + +// DecodeFromBytes decodes the given bytes into this layer. +func (l *Loopback) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + return errors.New("Loopback packet too small") + } + + // The protocol could be either big-endian or little-endian, we're + // not sure. But we're PRETTY sure that the value is less than + // 256, so we can check the first two bytes. + var prot uint32 + if data[0] == 0 && data[1] == 0 { + prot = binary.BigEndian.Uint32(data[:4]) + } else { + prot = binary.LittleEndian.Uint32(data[:4]) + } + if prot > 0xFF { + return fmt.Errorf("Invalid loopback protocol %q", data[:4]) + } + + l.Family = ProtocolFamily(prot) + l.BaseLayer = BaseLayer{data[:4], data[4:]} + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (l *Loopback) CanDecode() gopacket.LayerClass { + return LayerTypeLoopback +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (l *Loopback) NextLayerType() gopacket.LayerType { + return l.Family.LayerType() +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (l *Loopback) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + binary.LittleEndian.PutUint32(bytes, uint32(l.Family)) + return nil +} + +func decodeLoopback(data []byte, p gopacket.PacketBuilder) error { + l := Loopback{} + if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil { + return err + } + p.AddLayer(&l) + return p.NextDecoder(l.Family) +} diff --git a/vendor/github.com/google/gopacket/layers/mldv1.go b/vendor/github.com/google/gopacket/layers/mldv1.go new file mode 100644 index 00000000..e1bb1dc0 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/mldv1.go @@ -0,0 +1,182 @@ +// Copyright 2018 GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "net" + "time" + + "github.com/google/gopacket" +) + +// MLDv1Message represents the common structure of all MLDv1 messages +type MLDv1Message struct { + BaseLayer + // 3.4. Maximum Response Delay + MaximumResponseDelay time.Duration + // 3.6. Multicast Address + // Zero in general query + // Specific IPv6 multicast address otherwise + MulticastAddress net.IP +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (m *MLDv1Message) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 20 { + df.SetTruncated() + return errors.New("ICMP layer less than 20 bytes for Multicast Listener Query Message V1") + } + + m.MaximumResponseDelay = time.Duration(binary.BigEndian.Uint16(data[0:2])) * time.Millisecond + // data[2:4] is reserved and not used in mldv1 + m.MulticastAddress = data[4:20] + + return nil +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (*MLDv1Message) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (m *MLDv1Message) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf, err := b.PrependBytes(20) + if err != nil { + return err + } + + if m.MaximumResponseDelay < 0 { + return errors.New("maximum response delay must not be negative") + } + dms := m.MaximumResponseDelay / time.Millisecond + if dms > math.MaxUint16 { + return fmt.Errorf("maximum response delay %dms is more than the allowed 65535ms", dms) + } + binary.BigEndian.PutUint16(buf[0:2], uint16(dms)) + + copy(buf[2:4], []byte{0x0, 0x0}) + + ma16 := m.MulticastAddress.To16() + if ma16 == nil { + return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress) + } + copy(buf[4:20], ma16) + + return nil +} + +// Sums this layer up nicely formatted +func (m *MLDv1Message) String() string { + return fmt.Sprintf( + "Maximum Response Delay: %dms, Multicast Address: %s", + m.MaximumResponseDelay/time.Millisecond, + m.MulticastAddress) +} + +// MLDv1MulticastListenerQueryMessage are sent by the router to determine +// whether there are multicast listeners on the link. +// https://tools.ietf.org/html/rfc2710 Page 5 +type MLDv1MulticastListenerQueryMessage struct { + MLDv1Message +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (m *MLDv1MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + err := m.MLDv1Message.DecodeFromBytes(data, df) + if err != nil { + return err + } + + if len(data) > 20 { + m.Payload = data[20:] + } + + return nil +} + +// LayerType returns LayerTypeMLDv1MulticastListenerQuery. +func (*MLDv1MulticastListenerQueryMessage) LayerType() gopacket.LayerType { + return LayerTypeMLDv1MulticastListenerQuery +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (*MLDv1MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass { + return LayerTypeMLDv1MulticastListenerQuery +} + +// IsGeneralQuery is true when this is a general query. +// In a Query message, the Multicast Address field is set to zero when +// sending a General Query. +// https://tools.ietf.org/html/rfc2710#section-3.6 +func (m *MLDv1MulticastListenerQueryMessage) IsGeneralQuery() bool { + return net.IPv6zero.Equal(m.MulticastAddress) +} + +// IsSpecificQuery is true when this is not a general query. +// In a Query message, the Multicast Address field is set to a specific +// IPv6 multicast address when sending a Multicast-Address-Specific Query. +// https://tools.ietf.org/html/rfc2710#section-3.6 +func (m *MLDv1MulticastListenerQueryMessage) IsSpecificQuery() bool { + return !m.IsGeneralQuery() +} + +// MLDv1MulticastListenerReportMessage is sent by a client listening on +// a specific multicast address to indicate that it is (still) listening +// on the specific multicast address. +// https://tools.ietf.org/html/rfc2710 Page 6 +type MLDv1MulticastListenerReportMessage struct { + MLDv1Message +} + +// LayerType returns LayerTypeMLDv1MulticastListenerReport. +func (*MLDv1MulticastListenerReportMessage) LayerType() gopacket.LayerType { + return LayerTypeMLDv1MulticastListenerReport +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (*MLDv1MulticastListenerReportMessage) CanDecode() gopacket.LayerClass { + return LayerTypeMLDv1MulticastListenerReport +} + +// MLDv1MulticastListenerDoneMessage should be sent by a client when it ceases +// to listen to a multicast address on an interface. +// https://tools.ietf.org/html/rfc2710 Page 7 +type MLDv1MulticastListenerDoneMessage struct { + MLDv1Message +} + +// LayerType returns LayerTypeMLDv1MulticastListenerDone. +func (*MLDv1MulticastListenerDoneMessage) LayerType() gopacket.LayerType { + return LayerTypeMLDv1MulticastListenerDone +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (*MLDv1MulticastListenerDoneMessage) CanDecode() gopacket.LayerClass { + return LayerTypeMLDv1MulticastListenerDone +} + +func decodeMLDv1MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error { + m := &MLDv1MulticastListenerReportMessage{} + return decodingLayerDecoder(m, data, p) +} + +func decodeMLDv1MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error { + m := &MLDv1MulticastListenerQueryMessage{} + return decodingLayerDecoder(m, data, p) +} + +func decodeMLDv1MulticastListenerDone(data []byte, p gopacket.PacketBuilder) error { + m := &MLDv1MulticastListenerDoneMessage{} + return decodingLayerDecoder(m, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/mldv2.go b/vendor/github.com/google/gopacket/layers/mldv2.go new file mode 100644 index 00000000..05100a52 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/mldv2.go @@ -0,0 +1,619 @@ +// Copyright 2018 GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "net" + "time" + + "github.com/google/gopacket" +) + +const ( + // S Flag bit is 1 + mldv2STrue uint8 = 0x8 + + // S Flag value mask + // mldv2STrue & mldv2SMask == mldv2STrue // true + // 0x1 & mldv2SMask == mldv2STrue // true + // 0x0 & mldv2SMask == mldv2STrue // false + mldv2SMask uint8 = 0x8 + + // QRV value mask + mldv2QRVMask uint8 = 0x7 +) + +// MLDv2MulticastListenerQueryMessage are sent by multicast routers to query the +// multicast listening state of neighboring interfaces. +// https://tools.ietf.org/html/rfc3810#section-5.1 +// +// Some information, like Maximum Response Code and Multicast Address are in the +// previous layer LayerTypeMLDv1MulticastListenerQuery +type MLDv2MulticastListenerQueryMessage struct { + BaseLayer + // 5.1.3. Maximum Response Delay COde + MaximumResponseCode uint16 + // 5.1.5. Multicast Address + // Zero in general query + // Specific IPv6 multicast address otherwise + MulticastAddress net.IP + // 5.1.7. S Flag (Suppress Router-Side Processing) + SuppressRoutersideProcessing bool + // 5.1.8. QRV (Querier's Robustness Variable) + QueriersRobustnessVariable uint8 + // 5.1.9. QQIC (Querier's Query Interval Code) + QueriersQueryIntervalCode uint8 + // 5.1.10. Number of Sources (N) + NumberOfSources uint16 + // 5.1.11 Source Address [i] + SourceAddresses []net.IP +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (m *MLDv2MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 24 { + df.SetTruncated() + return errors.New("ICMP layer less than 24 bytes for Multicast Listener Query Message V2") + } + + m.MaximumResponseCode = binary.BigEndian.Uint16(data[0:2]) + // ignore data[2:4] as per https://tools.ietf.org/html/rfc3810#section-5.1.4 + m.MulticastAddress = data[4:20] + m.SuppressRoutersideProcessing = (data[20] & mldv2SMask) == mldv2STrue + m.QueriersRobustnessVariable = data[20] & mldv2QRVMask + m.QueriersQueryIntervalCode = data[21] + + m.NumberOfSources = binary.BigEndian.Uint16(data[22:24]) + + var end int + for i := uint16(0); i < m.NumberOfSources; i++ { + begin := 24 + (int(i) * 16) + end = begin + 16 + + if end > len(data) { + df.SetTruncated() + return fmt.Errorf("ICMP layer less than %d bytes for Multicast Listener Query Message V2", end) + } + + m.SourceAddresses = append(m.SourceAddresses, data[begin:end]) + } + + return nil +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (*MLDv2MulticastListenerQueryMessage) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (m *MLDv2MulticastListenerQueryMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := m.serializeSourceAddressesTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(24) + if err != nil { + return err + } + + binary.BigEndian.PutUint16(buf[0:2], m.MaximumResponseCode) + copy(buf[2:4], []byte{0x00, 0x00}) // set reserved bytes to zero + + ma16 := m.MulticastAddress.To16() + if ma16 == nil { + return fmt.Errorf("invalid MulticastAddress '%s'", m.MulticastAddress) + } + copy(buf[4:20], ma16) + + byte20 := m.QueriersRobustnessVariable & mldv2QRVMask + if m.SuppressRoutersideProcessing { + byte20 |= mldv2STrue + } else { + byte20 &= ^mldv2STrue // the complement of mldv2STrue + } + byte20 &= 0x0F // set reserved bits to zero + buf[20] = byte20 + + binary.BigEndian.PutUint16(buf[22:24], m.NumberOfSources) + buf[21] = m.QueriersQueryIntervalCode + + return nil +} + +// writes each source address to the buffer preserving the order +func (m *MLDv2MulticastListenerQueryMessage) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + numberOfSourceAddresses := len(m.SourceAddresses) + if numberOfSourceAddresses > math.MaxUint16 { + return fmt.Errorf( + "there are more than %d source addresses, but 65535 is the maximum number of supported addresses", + numberOfSourceAddresses) + } + + if opts.FixLengths { + m.NumberOfSources = uint16(numberOfSourceAddresses) + } + + lastSAIdx := numberOfSourceAddresses - 1 + for k := range m.SourceAddresses { + i := lastSAIdx - k // reverse order + + buf, err := b.PrependBytes(16) + if err != nil { + return err + } + + sa16 := m.SourceAddresses[i].To16() + if sa16 == nil { + return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i]) + } + copy(buf[0:16], sa16) + } + + return nil +} + +// String sums this layer up nicely formatted +func (m *MLDv2MulticastListenerQueryMessage) String() string { + return fmt.Sprintf( + "Maximum Response Code: %#x (%dms), Multicast Address: %s, Suppress Routerside Processing: %t, QRV: %#x, QQIC: %#x (%ds), Number of Source Address: %d (actual: %d), Source Addresses: %s", + m.MaximumResponseCode, + m.MaximumResponseDelay(), + m.MulticastAddress, + m.SuppressRoutersideProcessing, + m.QueriersRobustnessVariable, + m.QueriersQueryIntervalCode, + m.QQI()/time.Second, + m.NumberOfSources, + len(m.SourceAddresses), + m.SourceAddresses) +} + +// LayerType returns LayerTypeMLDv2MulticastListenerQuery. +func (*MLDv2MulticastListenerQueryMessage) LayerType() gopacket.LayerType { + return LayerTypeMLDv2MulticastListenerQuery +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (*MLDv2MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass { + return LayerTypeMLDv2MulticastListenerQuery +} + +// QQI calculates the Querier's Query Interval based on the QQIC +// according to https://tools.ietf.org/html/rfc3810#section-5.1.9 +func (m *MLDv2MulticastListenerQueryMessage) QQI() time.Duration { + data := m.QueriersQueryIntervalCode + if data < 128 { + return time.Second * time.Duration(data) + } + + exp := uint16(data) & 0x70 >> 4 + mant := uint16(data) & 0x0F + return time.Second * time.Duration(mant|0x1000<<(exp+3)) +} + +// SetQQI calculates and updates the Querier's Query Interval Code (QQIC) +// according to https://tools.ietf.org/html/rfc3810#section-5.1.9 +func (m *MLDv2MulticastListenerQueryMessage) SetQQI(d time.Duration) error { + if d < 0 { + m.QueriersQueryIntervalCode = 0 + return errors.New("QQI duration is negative") + } + + if d == 0 { + m.QueriersQueryIntervalCode = 0 + return nil + } + + dms := d / time.Second + if dms < 128 { + m.QueriersQueryIntervalCode = uint8(dms) + } + + if dms > 31744 { // mant=0xF, exp=0x7 + m.QueriersQueryIntervalCode = 0xFF + return fmt.Errorf("QQI duration %ds is, maximum allowed is 31744s", dms) + } + + value := uint16(dms) // ok, because 31744 < math.MaxUint16 + exp := uint8(7) + for mask := uint16(0x4000); exp > 0; exp-- { + if mask&value != 0 { + break + } + + mask >>= 1 + } + + mant := uint8(0x000F & (value >> (exp + 3))) + sig := uint8(0x10) + m.QueriersQueryIntervalCode = sig | exp<<4 | mant + + return nil +} + +// MaximumResponseDelay returns the Maximum Response Delay based on the +// Maximum Response Code according to +// https://tools.ietf.org/html/rfc3810#section-5.1.3 +func (m *MLDv2MulticastListenerQueryMessage) MaximumResponseDelay() time.Duration { + if m.MaximumResponseCode < 0x8000 { + return time.Duration(m.MaximumResponseCode) + } + + exp := m.MaximumResponseCode & 0x7000 >> 12 + mant := m.MaximumResponseCode & 0x0FFF + + return time.Millisecond * time.Duration(mant|0x1000<<(exp+3)) +} + +// SetMLDv2MaximumResponseDelay updates the Maximum Response Code according to +// https://tools.ietf.org/html/rfc3810#section-5.1.3 +func (m *MLDv2MulticastListenerQueryMessage) SetMLDv2MaximumResponseDelay(d time.Duration) error { + if d == 0 { + m.MaximumResponseCode = 0 + return nil + } + + if d < 0 { + return errors.New("maximum response delay must not be negative") + } + + dms := d / time.Millisecond + + if dms < 32768 { + m.MaximumResponseCode = uint16(dms) + } + + if dms > 4193280 { // mant=0xFFF, exp=0x7 + return fmt.Errorf("maximum response delay %dms is bigger the than maximum of 4193280ms", dms) + } + + value := uint32(dms) // ok, because 4193280 < math.MaxUint32 + exp := uint8(7) + for mask := uint32(0x40000000); exp > 0; exp-- { + if mask&value != 0 { + break + } + + mask >>= 1 + } + + mant := uint16(0x00000FFF & (value >> (exp + 3))) + sig := uint16(0x1000) + m.MaximumResponseCode = sig | uint16(exp)<<12 | mant + return nil +} + +// MLDv2MulticastListenerReportMessage is sent by an IP node to report the +// current multicast listening state, or changes therein. +// https://tools.ietf.org/html/rfc3810#section-5.2 +type MLDv2MulticastListenerReportMessage struct { + BaseLayer + // 5.2.3. Nr of Mcast Address Records + NumberOfMulticastAddressRecords uint16 + // 5.2.4. Multicast Address Record [i] + MulticastAddressRecords []MLDv2MulticastAddressRecord +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (m *MLDv2MulticastListenerReportMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return errors.New("ICMP layer less than 4 bytes for Multicast Listener Report Message V2") + } + + // ignore data[0:2] as per RFC + // https://tools.ietf.org/html/rfc3810#section-5.2.1 + m.NumberOfMulticastAddressRecords = binary.BigEndian.Uint16(data[2:4]) + + begin := 4 + for i := uint16(0); i < m.NumberOfMulticastAddressRecords; i++ { + mar := MLDv2MulticastAddressRecord{} + read, err := mar.decode(data[begin:], df) + if err != nil { + return err + } + + m.MulticastAddressRecords = append(m.MulticastAddressRecords, mar) + + begin += read + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (m *MLDv2MulticastListenerReportMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + lastItemIdx := len(m.MulticastAddressRecords) - 1 + for k := range m.MulticastAddressRecords { + i := lastItemIdx - k // reverse order + + err := m.MulticastAddressRecords[i].serializeTo(b, opts) + if err != nil { + return err + } + } + + if opts.FixLengths { + numberOfMAR := len(m.MulticastAddressRecords) + if numberOfMAR > math.MaxUint16 { + return fmt.Errorf( + "%d multicast address records added, but the maximum is 65535", + numberOfMAR) + } + + m.NumberOfMulticastAddressRecords = uint16(numberOfMAR) + } + + buf, err := b.PrependBytes(4) + if err != nil { + return err + } + + copy(buf[0:2], []byte{0x0, 0x0}) + binary.BigEndian.PutUint16(buf[2:4], m.NumberOfMulticastAddressRecords) + return nil +} + +// Sums this layer up nicely formatted +func (m *MLDv2MulticastListenerReportMessage) String() string { + return fmt.Sprintf( + "Number of Mcast Addr Records: %d (actual %d), Multicast Address Records: %+v", + m.NumberOfMulticastAddressRecords, + len(m.MulticastAddressRecords), + m.MulticastAddressRecords) +} + +// LayerType returns LayerTypeMLDv2MulticastListenerQuery. +func (*MLDv2MulticastListenerReportMessage) LayerType() gopacket.LayerType { + return LayerTypeMLDv2MulticastListenerReport +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (*MLDv2MulticastListenerReportMessage) CanDecode() gopacket.LayerClass { + return LayerTypeMLDv2MulticastListenerReport +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (*MLDv2MulticastListenerReportMessage) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// MLDv2MulticastAddressRecordType holds the type of a +// Multicast Address Record, according to +// https://tools.ietf.org/html/rfc3810#section-5.2.5 and +// https://tools.ietf.org/html/rfc3810#section-5.2.12 +type MLDv2MulticastAddressRecordType uint8 + +const ( + // MLDv2MulticastAddressRecordTypeModeIsIncluded stands for + // MODE_IS_INCLUDE - indicates that the interface has a filter + // mode of INCLUDE for the specified multicast address. + MLDv2MulticastAddressRecordTypeModeIsIncluded MLDv2MulticastAddressRecordType = 1 + // MLDv2MulticastAddressRecordTypeModeIsExcluded stands for + // MODE_IS_EXCLUDE - indicates that the interface has a filter + // mode of EXCLUDE for the specified multicast address. + MLDv2MulticastAddressRecordTypeModeIsExcluded MLDv2MulticastAddressRecordType = 2 + // MLDv2MulticastAddressRecordTypeChangeToIncludeMode stands for + // CHANGE_TO_INCLUDE_MODE - indicates that the interface has + // changed to INCLUDE filter mode for the specified multicast + // address. + MLDv2MulticastAddressRecordTypeChangeToIncludeMode MLDv2MulticastAddressRecordType = 3 + // MLDv2MulticastAddressRecordTypeChangeToExcludeMode stands for + // CHANGE_TO_EXCLUDE_MODE - indicates that the interface has + // changed to EXCLUDE filter mode for the specified multicast + // address + MLDv2MulticastAddressRecordTypeChangeToExcludeMode MLDv2MulticastAddressRecordType = 4 + // MLDv2MulticastAddressRecordTypeAllowNewSources stands for + // ALLOW_NEW_SOURCES - indicates that the Source Address [i] + // fields in this Multicast Address Record contain a list of + // the additional sources that the node wishes to listen to, + // for packets sent to the specified multicast address. + MLDv2MulticastAddressRecordTypeAllowNewSources MLDv2MulticastAddressRecordType = 5 + // MLDv2MulticastAddressRecordTypeBlockOldSources stands for + // BLOCK_OLD_SOURCES - indicates that the Source Address [i] + // fields in this Multicast Address Record contain a list of + // the sources that the node no longer wishes to listen to, + // for packets sent to the specified multicast address. + MLDv2MulticastAddressRecordTypeBlockOldSources MLDv2MulticastAddressRecordType = 6 +) + +// Human readable record types +// Naming follows https://tools.ietf.org/html/rfc3810#section-5.2.12 +func (m MLDv2MulticastAddressRecordType) String() string { + switch m { + case MLDv2MulticastAddressRecordTypeModeIsIncluded: + return "MODE_IS_INCLUDE" + case MLDv2MulticastAddressRecordTypeModeIsExcluded: + return "MODE_IS_EXCLUDE" + case MLDv2MulticastAddressRecordTypeChangeToIncludeMode: + return "CHANGE_TO_INCLUDE_MODE" + case MLDv2MulticastAddressRecordTypeChangeToExcludeMode: + return "CHANGE_TO_EXCLUDE_MODE" + case MLDv2MulticastAddressRecordTypeAllowNewSources: + return "ALLOW_NEW_SOURCES" + case MLDv2MulticastAddressRecordTypeBlockOldSources: + return "BLOCK_OLD_SOURCES" + default: + return fmt.Sprintf("UNKNOWN(%d)", m) + } +} + +// MLDv2MulticastAddressRecord contains information on the sender listening to a +// single multicast address on the interface the report is sent. +// https://tools.ietf.org/html/rfc3810#section-5.2.4 +type MLDv2MulticastAddressRecord struct { + // 5.2.5. Record Type + RecordType MLDv2MulticastAddressRecordType + // 5.2.6. Auxiliary Data Length (number of 32-bit words) + AuxDataLen uint8 + // 5.2.7. Number Of Sources (N) + N uint16 + // 5.2.8. Multicast Address + MulticastAddress net.IP + // 5.2.9 Source Address [i] + SourceAddresses []net.IP + // 5.2.10 Auxiliary Data + AuxiliaryData []byte +} + +// decodes a multicast address record from bytes +func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) { + if len(data) < 20 { + df.SetTruncated() + return 0, errors.New( + "Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record") + } + + m.RecordType = MLDv2MulticastAddressRecordType(data[0]) + m.AuxDataLen = data[1] + m.N = binary.BigEndian.Uint16(data[2:4]) + m.MulticastAddress = data[4:20] + + for i := uint16(0); i < m.N; i++ { + begin := 20 + (int(i) * 16) + end := begin + 16 + + if len(data) < end { + df.SetTruncated() + return begin, fmt.Errorf( + "Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", end) + } + + m.SourceAddresses = append(m.SourceAddresses, data[begin:end]) + } + + expectedLengthWithouAuxData := 20 + (int(m.N) * 16) + expectedTotalLength := (int(m.AuxDataLen) * 4) + expectedLengthWithouAuxData // *4 because AuxDataLen are 32bit words + if len(data) < expectedTotalLength { + return expectedLengthWithouAuxData, fmt.Errorf( + "Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", + expectedLengthWithouAuxData) + } + + m.AuxiliaryData = data[expectedLengthWithouAuxData:expectedTotalLength] + + return expectedTotalLength, nil +} + +// String sums this layer up nicely formatted +func (m *MLDv2MulticastAddressRecord) String() string { + return fmt.Sprintf( + "RecordType: %d (%s), AuxDataLen: %d [32-bit words], N: %d, Multicast Address: %s, SourceAddresses: %s, Auxiliary Data: %#x", + m.RecordType, + m.RecordType.String(), + m.AuxDataLen, + m.N, + m.MulticastAddress.To16(), + m.SourceAddresses, + m.AuxiliaryData) +} + +// serializes a multicast address record +func (m *MLDv2MulticastAddressRecord) serializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if err := m.serializeAuxiliaryDataTo(b, opts); err != nil { + return err + } + + if err := m.serializeSourceAddressesTo(b, opts); err != nil { + return err + } + + buf, err := b.PrependBytes(20) + if err != nil { + return err + } + + buf[0] = uint8(m.RecordType) + buf[1] = m.AuxDataLen + binary.BigEndian.PutUint16(buf[2:4], m.N) + + ma16 := m.MulticastAddress.To16() + if ma16 == nil { + return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress) + } + copy(buf[4:20], ma16) + + return nil +} + +// serializes the auxiliary data of a multicast address record +func (m *MLDv2MulticastAddressRecord) serializeAuxiliaryDataTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if remainder := len(m.AuxiliaryData) % 4; remainder != 0 { + zeroWord := []byte{0x0, 0x0, 0x0, 0x0} + m.AuxiliaryData = append(m.AuxiliaryData, zeroWord[:remainder]...) + } + + if opts.FixLengths { + auxDataLen := len(m.AuxiliaryData) / 4 + + if auxDataLen > math.MaxUint8 { + return fmt.Errorf("auxilary data is %d 32-bit words, but the maximum is 255 32-bit words", auxDataLen) + } + + m.AuxDataLen = uint8(auxDataLen) + } + + buf, err := b.PrependBytes(len(m.AuxiliaryData)) + if err != nil { + return err + } + + copy(buf, m.AuxiliaryData) + return nil +} + +// serializes the source addresses of a multicast address record preserving the order +func (m *MLDv2MulticastAddressRecord) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if opts.FixLengths { + numberOfSourceAddresses := len(m.SourceAddresses) + + if numberOfSourceAddresses > math.MaxUint16 { + return fmt.Errorf( + "%d source addresses added, but the maximum is 65535", + numberOfSourceAddresses) + } + + m.N = uint16(numberOfSourceAddresses) + } + + lastItemIdx := len(m.SourceAddresses) - 1 + for k := range m.SourceAddresses { + i := lastItemIdx - k // reverse order + + buf, err := b.PrependBytes(16) + if err != nil { + return err + } + + sa16 := m.SourceAddresses[i].To16() + if sa16 == nil { + return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i]) + } + copy(buf, sa16) + } + + return nil +} + +func decodeMLDv2MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error { + m := &MLDv2MulticastListenerReportMessage{} + return decodingLayerDecoder(m, data, p) +} + +func decodeMLDv2MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error { + m := &MLDv2MulticastListenerQueryMessage{} + return decodingLayerDecoder(m, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/modbustcp.go b/vendor/github.com/google/gopacket/layers/modbustcp.go new file mode 100644 index 00000000..bafbd743 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/modbustcp.go @@ -0,0 +1,150 @@ +// Copyright 2018, The GoPacket Authors, All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// +//****************************************************************************** + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +//****************************************************************************** +// +// ModbusTCP Decoding Layer +// ------------------------------------------ +// This file provides a GoPacket decoding layer for ModbusTCP. +// +//****************************************************************************** + +const mbapRecordSizeInBytes int = 7 +const modbusPDUMinimumRecordSizeInBytes int = 2 +const modbusPDUMaximumRecordSizeInBytes int = 253 + +// ModbusProtocol type +type ModbusProtocol uint16 + +// ModbusProtocol known values. +const ( + ModbusProtocolModbus ModbusProtocol = 0 +) + +func (mp ModbusProtocol) String() string { + switch mp { + default: + return "Unknown" + case ModbusProtocolModbus: + return "Modbus" + } +} + +//****************************************************************************** + +// ModbusTCP Type +// -------- +// Type ModbusTCP implements the DecodingLayer interface. Each ModbusTCP object +// represents in a structured form the MODBUS Application Protocol header (MBAP) record present as the TCP +// payload in an ModbusTCP TCP packet. +// +type ModbusTCP struct { + BaseLayer // Stores the packet bytes and payload (Modbus PDU) bytes . + + TransactionIdentifier uint16 // Identification of a MODBUS Request/Response transaction + ProtocolIdentifier ModbusProtocol // It is used for intra-system multiplexing + Length uint16 // Number of following bytes (includes 1 byte for UnitIdentifier + Modbus data length + UnitIdentifier uint8 // Identification of a remote slave connected on a serial line or on other buses +} + +//****************************************************************************** + +// LayerType returns the layer type of the ModbusTCP object, which is LayerTypeModbusTCP. +func (d *ModbusTCP) LayerType() gopacket.LayerType { + return LayerTypeModbusTCP +} + +//****************************************************************************** + +// decodeModbusTCP analyses a byte slice and attempts to decode it as an ModbusTCP +// record of a TCP packet. +// +// If it succeeds, it loads p with information about the packet and returns nil. +// If it fails, it returns an error (non nil). +// +// This function is employed in layertypes.go to register the ModbusTCP layer. +func decodeModbusTCP(data []byte, p gopacket.PacketBuilder) error { + + // Attempt to decode the byte slice. + d := &ModbusTCP{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + // If the decoding worked, add the layer to the packet and set it + // as the application layer too, if there isn't already one. + p.AddLayer(d) + p.SetApplicationLayer(d) + + return p.NextDecoder(d.NextLayerType()) + +} + +//****************************************************************************** + +// DecodeFromBytes analyses a byte slice and attempts to decode it as an ModbusTCP +// record of a TCP packet. +// +// Upon succeeds, it loads the ModbusTCP object with information about the packet +// and returns nil. +// Upon failure, it returns an error (non nil). +func (d *ModbusTCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + // If the data block is too short to be a MBAP record, then return an error. + if len(data) < mbapRecordSizeInBytes+modbusPDUMinimumRecordSizeInBytes { + df.SetTruncated() + return errors.New("ModbusTCP packet too short") + } + + if len(data) > mbapRecordSizeInBytes+modbusPDUMaximumRecordSizeInBytes { + df.SetTruncated() + return errors.New("ModbusTCP packet too long") + } + + // ModbusTCP type embeds type BaseLayer which contains two fields: + // Contents is supposed to contain the bytes of the data at this level (MPBA). + // Payload is supposed to contain the payload of this level (PDU). + d.BaseLayer = BaseLayer{Contents: data[:mbapRecordSizeInBytes], Payload: data[mbapRecordSizeInBytes:len(data)]} + + // Extract the fields from the block of bytes. + // The fields can just be copied in big endian order. + d.TransactionIdentifier = binary.BigEndian.Uint16(data[:2]) + d.ProtocolIdentifier = ModbusProtocol(binary.BigEndian.Uint16(data[2:4])) + d.Length = binary.BigEndian.Uint16(data[4:6]) + + // Length should have the size of the payload plus one byte (size of UnitIdentifier) + if d.Length != uint16(len(d.BaseLayer.Payload)+1) { + df.SetTruncated() + return errors.New("ModbusTCP packet with wrong field value (Length)") + } + d.UnitIdentifier = uint8(data[6]) + + return nil +} + +//****************************************************************************** + +// NextLayerType returns the layer type of the ModbusTCP payload, which is LayerTypePayload. +func (d *ModbusTCP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +//****************************************************************************** + +// Payload returns Modbus Protocol Data Unit (PDU) composed by Function Code and Data, it is carried within ModbusTCP packets +func (d *ModbusTCP) Payload() []byte { + return d.BaseLayer.Payload +} diff --git a/vendor/github.com/google/gopacket/layers/mpls.go b/vendor/github.com/google/gopacket/layers/mpls.go new file mode 100644 index 00000000..83079a09 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/mpls.go @@ -0,0 +1,87 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +// MPLS is the MPLS packet header. +type MPLS struct { + BaseLayer + Label uint32 + TrafficClass uint8 + StackBottom bool + TTL uint8 +} + +// LayerType returns gopacket.LayerTypeMPLS. +func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS } + +// ProtocolGuessingDecoder attempts to guess the protocol of the bytes it's +// given, then decode the packet accordingly. Its algorithm for guessing is: +// If the packet starts with byte 0x45-0x4F: IPv4 +// If the packet starts with byte 0x60-0x6F: IPv6 +// Otherwise: Error +// See draft-hsmit-isis-aal5mux-00.txt for more detail on this approach. +type ProtocolGuessingDecoder struct{} + +func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error { + switch data[0] { + // 0x40 | header_len, where header_len is at least 5. + case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f: + return decodeIPv4(data, p) + // IPv6 can start with any byte whose first 4 bits are 0x6. + case 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f: + return decodeIPv6(data, p) + } + return errors.New("Unable to guess protocol of packet data") +} + +// MPLSPayloadDecoder is the decoder used to data encapsulated by each MPLS +// layer. MPLS contains no type information, so we have to explicitly decide +// which decoder to use. This is initially set to ProtocolGuessingDecoder, our +// simple attempt at guessing protocols based on the first few bytes of data +// available to us. However, if you know that in your environment MPLS always +// encapsulates a specific protocol, you may reset this. +var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{} + +func decodeMPLS(data []byte, p gopacket.PacketBuilder) error { + decoded := binary.BigEndian.Uint32(data[:4]) + mpls := &MPLS{ + Label: decoded >> 12, + TrafficClass: uint8(decoded>>9) & 0x7, + StackBottom: decoded&0x100 != 0, + TTL: uint8(decoded), + BaseLayer: BaseLayer{data[:4], data[4:]}, + } + p.AddLayer(mpls) + if mpls.StackBottom { + return p.NextDecoder(MPLSPayloadDecoder) + } + return p.NextDecoder(gopacket.DecodeFunc(decodeMPLS)) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (m *MPLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + encoded := m.Label << 12 + encoded |= uint32(m.TrafficClass) << 9 + encoded |= uint32(m.TTL) + if m.StackBottom { + encoded |= 0x100 + } + binary.BigEndian.PutUint32(bytes, encoded) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/ndp.go b/vendor/github.com/google/gopacket/layers/ndp.go new file mode 100644 index 00000000..f7ca1b26 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ndp.go @@ -0,0 +1,611 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Enum types courtesy of... +// http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-ndp.c + +package layers + +import ( + "fmt" + "github.com/google/gopacket" + "net" +) + +type NDPChassisType uint8 + +// Nortel Chassis Types +const ( + NDPChassisother NDPChassisType = 1 + NDPChassis3000 NDPChassisType = 2 + NDPChassis3030 NDPChassisType = 3 + NDPChassis2310 NDPChassisType = 4 + NDPChassis2810 NDPChassisType = 5 + NDPChassis2912 NDPChassisType = 6 + NDPChassis2914 NDPChassisType = 7 + NDPChassis271x NDPChassisType = 8 + NDPChassis2813 NDPChassisType = 9 + NDPChassis2814 NDPChassisType = 10 + NDPChassis2915 NDPChassisType = 11 + NDPChassis5000 NDPChassisType = 12 + NDPChassis2813SA NDPChassisType = 13 + NDPChassis2814SA NDPChassisType = 14 + NDPChassis810M NDPChassisType = 15 + NDPChassisEthercell NDPChassisType = 16 + NDPChassis5005 NDPChassisType = 17 + NDPChassisAlcatelEWC NDPChassisType = 18 + NDPChassis2715SA NDPChassisType = 20 + NDPChassis2486 NDPChassisType = 21 + NDPChassis28000series NDPChassisType = 22 + NDPChassis23000series NDPChassisType = 23 + NDPChassis5DN00xseries NDPChassisType = 24 + NDPChassisBayStackEthernet NDPChassisType = 25 + NDPChassis23100series NDPChassisType = 26 + NDPChassis100BaseTHub NDPChassisType = 27 + NDPChassis3000FastEthernet NDPChassisType = 28 + NDPChassisOrionSwitch NDPChassisType = 29 + NDPChassisDDS NDPChassisType = 31 + NDPChassisCentillion6slot NDPChassisType = 32 + NDPChassisCentillion12slot NDPChassisType = 33 + NDPChassisCentillion1slot NDPChassisType = 34 + NDPChassisBayStack301 NDPChassisType = 35 + NDPChassisBayStackTokenRingHub NDPChassisType = 36 + NDPChassisFVCMultimediaSwitch NDPChassisType = 37 + NDPChassisSwitchNode NDPChassisType = 38 + NDPChassisBayStack302Switch NDPChassisType = 39 + NDPChassisBayStack350Switch NDPChassisType = 40 + NDPChassisBayStack150EthernetHub NDPChassisType = 41 + NDPChassisCentillion50NSwitch NDPChassisType = 42 + NDPChassisCentillion50TSwitch NDPChassisType = 43 + NDPChassisBayStack303304Switches NDPChassisType = 44 + NDPChassisBayStack200EthernetHub NDPChassisType = 45 + NDPChassisBayStack25010100EthernetHub NDPChassisType = 46 + NDPChassisBayStack450101001000Switches NDPChassisType = 48 + NDPChassisBayStack41010100Switches NDPChassisType = 49 + NDPChassisPassport1200L3Switch NDPChassisType = 50 + NDPChassisPassport1250L3Switch NDPChassisType = 51 + NDPChassisPassport1100L3Switch NDPChassisType = 52 + NDPChassisPassport1150L3Switch NDPChassisType = 53 + NDPChassisPassport1050L3Switch NDPChassisType = 54 + NDPChassisPassport1051L3Switch NDPChassisType = 55 + NDPChassisPassport8610L3Switch NDPChassisType = 56 + NDPChassisPassport8606L3Switch NDPChassisType = 57 + NDPChassisPassport8010 NDPChassisType = 58 + NDPChassisPassport8006 NDPChassisType = 59 + NDPChassisBayStack670wirelessaccesspoint NDPChassisType = 60 + NDPChassisPassport740 NDPChassisType = 61 + NDPChassisPassport750 NDPChassisType = 62 + NDPChassisPassport790 NDPChassisType = 63 + NDPChassisBusinessPolicySwitch200010100Switches NDPChassisType = 64 + NDPChassisPassport8110L2Switch NDPChassisType = 65 + NDPChassisPassport8106L2Switch NDPChassisType = 66 + NDPChassisBayStack3580GigSwitch NDPChassisType = 67 + NDPChassisBayStack10PowerSupplyUnit NDPChassisType = 68 + NDPChassisBayStack42010100Switch NDPChassisType = 69 + NDPChassisOPTeraMetro1200EthernetServiceModule NDPChassisType = 70 + NDPChassisOPTera8010co NDPChassisType = 71 + NDPChassisOPTera8610coL3Switch NDPChassisType = 72 + NDPChassisOPTera8110coL2Switch NDPChassisType = 73 + NDPChassisOPTera8003 NDPChassisType = 74 + NDPChassisOPTera8603L3Switch NDPChassisType = 75 + NDPChassisOPTera8103L2Switch NDPChassisType = 76 + NDPChassisBayStack380101001000Switch NDPChassisType = 77 + NDPChassisEthernetSwitch47048T NDPChassisType = 78 + NDPChassisOPTeraMetro1450EthernetServiceModule NDPChassisType = 79 + NDPChassisOPTeraMetro1400EthernetServiceModule NDPChassisType = 80 + NDPChassisAlteonSwitchFamily NDPChassisType = 81 + NDPChassisEthernetSwitch46024TPWR NDPChassisType = 82 + NDPChassisOPTeraMetro8010OPML2Switch NDPChassisType = 83 + NDPChassisOPTeraMetro8010coOPML2Switch NDPChassisType = 84 + NDPChassisOPTeraMetro8006OPML2Switch NDPChassisType = 85 + NDPChassisOPTeraMetro8003OPML2Switch NDPChassisType = 86 + NDPChassisAlteon180e NDPChassisType = 87 + NDPChassisAlteonAD3 NDPChassisType = 88 + NDPChassisAlteon184 NDPChassisType = 89 + NDPChassisAlteonAD4 NDPChassisType = 90 + NDPChassisPassport1424L3Switch NDPChassisType = 91 + NDPChassisPassport1648L3Switch NDPChassisType = 92 + NDPChassisPassport1612L3Switch NDPChassisType = 93 + NDPChassisPassport1624L3Switch NDPChassisType = 94 + NDPChassisBayStack38024FFiber1000Switch NDPChassisType = 95 + NDPChassisEthernetRoutingSwitch551024T NDPChassisType = 96 + NDPChassisEthernetRoutingSwitch551048T NDPChassisType = 97 + NDPChassisEthernetSwitch47024T NDPChassisType = 98 + NDPChassisNortelNetworksWirelessLANAccessPoint2220 NDPChassisType = 99 + NDPChassisPassportRBS2402L3Switch NDPChassisType = 100 + NDPChassisAlteonApplicationSwitch2424 NDPChassisType = 101 + NDPChassisAlteonApplicationSwitch2224 NDPChassisType = 102 + NDPChassisAlteonApplicationSwitch2208 NDPChassisType = 103 + NDPChassisAlteonApplicationSwitch2216 NDPChassisType = 104 + NDPChassisAlteonApplicationSwitch3408 NDPChassisType = 105 + NDPChassisAlteonApplicationSwitch3416 NDPChassisType = 106 + NDPChassisNortelNetworksWirelessLANSecuritySwitch2250 NDPChassisType = 107 + NDPChassisEthernetSwitch42548T NDPChassisType = 108 + NDPChassisEthernetSwitch42524T NDPChassisType = 109 + NDPChassisNortelNetworksWirelessLANAccessPoint2221 NDPChassisType = 110 + NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch NDPChassisType = 111 + NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch NDPChassisType = 112 + NDPChassisPassport830010slotchassis NDPChassisType = 113 + NDPChassisPassport83006slotchassis NDPChassisType = 114 + NDPChassisEthernetRoutingSwitch552024TPWR NDPChassisType = 115 + NDPChassisEthernetRoutingSwitch552048TPWR NDPChassisType = 116 + NDPChassisNortelNetworksVPNGateway3050 NDPChassisType = 117 + NDPChassisAlteonSSL31010100 NDPChassisType = 118 + NDPChassisAlteonSSL31010100Fiber NDPChassisType = 119 + NDPChassisAlteonSSL31010100FIPS NDPChassisType = 120 + NDPChassisAlteonSSL410101001000 NDPChassisType = 121 + NDPChassisAlteonSSL410101001000Fiber NDPChassisType = 122 + NDPChassisAlteonApplicationSwitch2424SSL NDPChassisType = 123 + NDPChassisEthernetSwitch32524T NDPChassisType = 124 + NDPChassisEthernetSwitch32524G NDPChassisType = 125 + NDPChassisNortelNetworksWirelessLANAccessPoint2225 NDPChassisType = 126 + NDPChassisNortelNetworksWirelessLANSecuritySwitch2270 NDPChassisType = 127 + NDPChassis24portEthernetSwitch47024TPWR NDPChassisType = 128 + NDPChassis48portEthernetSwitch47048TPWR NDPChassisType = 129 + NDPChassisEthernetRoutingSwitch553024TFD NDPChassisType = 130 + NDPChassisEthernetSwitch351024T NDPChassisType = 131 + NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch NDPChassisType = 132 + NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch NDPChassisType = 133 + NDPChassisNortelSecureAccessSwitch NDPChassisType = 134 + NDPChassisNortelNetworksVPNGateway3070 NDPChassisType = 135 + NDPChassisOPTeraMetro3500 NDPChassisType = 136 + NDPChassisSMBBES101024T NDPChassisType = 137 + NDPChassisSMBBES101048T NDPChassisType = 138 + NDPChassisSMBBES102024TPWR NDPChassisType = 139 + NDPChassisSMBBES102048TPWR NDPChassisType = 140 + NDPChassisSMBBES201024T NDPChassisType = 141 + NDPChassisSMBBES201048T NDPChassisType = 142 + NDPChassisSMBBES202024TPWR NDPChassisType = 143 + NDPChassisSMBBES202048TPWR NDPChassisType = 144 + NDPChassisSMBBES11024T NDPChassisType = 145 + NDPChassisSMBBES11048T NDPChassisType = 146 + NDPChassisSMBBES12024TPWR NDPChassisType = 147 + NDPChassisSMBBES12048TPWR NDPChassisType = 148 + NDPChassisSMBBES21024T NDPChassisType = 149 + NDPChassisSMBBES21048T NDPChassisType = 150 + NDPChassisSMBBES22024TPWR NDPChassisType = 151 + NDPChassisSMBBES22048TPWR NDPChassisType = 152 + NDPChassisOME6500 NDPChassisType = 153 + NDPChassisEthernetRoutingSwitch4548GT NDPChassisType = 154 + NDPChassisEthernetRoutingSwitch4548GTPWR NDPChassisType = 155 + NDPChassisEthernetRoutingSwitch4550T NDPChassisType = 156 + NDPChassisEthernetRoutingSwitch4550TPWR NDPChassisType = 157 + NDPChassisEthernetRoutingSwitch4526FX NDPChassisType = 158 + NDPChassisEthernetRoutingSwitch250026T NDPChassisType = 159 + NDPChassisEthernetRoutingSwitch250026TPWR NDPChassisType = 160 + NDPChassisEthernetRoutingSwitch250050T NDPChassisType = 161 + NDPChassisEthernetRoutingSwitch250050TPWR NDPChassisType = 162 +) + +type NDPBackplaneType uint8 + +// Nortel Backplane Types +const ( + NDPBackplaneOther NDPBackplaneType = 1 + NDPBackplaneEthernet NDPBackplaneType = 2 + NDPBackplaneEthernetTokenring NDPBackplaneType = 3 + NDPBackplaneEthernetFDDI NDPBackplaneType = 4 + NDPBackplaneEthernetTokenringFDDI NDPBackplaneType = 5 + NDPBackplaneEthernetTokenringRedundantPower NDPBackplaneType = 6 + NDPBackplaneEthernetTokenringFDDIRedundantPower NDPBackplaneType = 7 + NDPBackplaneTokenRing NDPBackplaneType = 8 + NDPBackplaneEthernetTokenringFastEthernet NDPBackplaneType = 9 + NDPBackplaneEthernetFastEthernet NDPBackplaneType = 10 + NDPBackplaneEthernetTokenringFastEthernetRedundantPower NDPBackplaneType = 11 + NDPBackplaneEthernetFastEthernetGigabitEthernet NDPBackplaneType = 12 +) + +type NDPState uint8 + +// Device State +const ( + NDPStateTopology NDPState = 1 + NDPStateHeartbeat NDPState = 2 + NDPStateNew NDPState = 3 +) + +// NortelDiscovery is a packet layer containing the Nortel Discovery Protocol. +type NortelDiscovery struct { + BaseLayer + IPAddress net.IP + SegmentID []byte + Chassis NDPChassisType + Backplane NDPBackplaneType + State NDPState + NumLinks uint8 +} + +// LayerType returns gopacket.LayerTypeNortelDiscovery. +func (c *NortelDiscovery) LayerType() gopacket.LayerType { + return LayerTypeNortelDiscovery +} + +func decodeNortelDiscovery(data []byte, p gopacket.PacketBuilder) error { + c := &NortelDiscovery{} + if len(data) < 11 { + return fmt.Errorf("Invalid NortelDiscovery packet length %d", len(data)) + } + c.IPAddress = data[0:4] + c.SegmentID = data[4:7] + c.Chassis = NDPChassisType(data[7]) + c.Backplane = NDPBackplaneType(data[8]) + c.State = NDPState(data[9]) + c.NumLinks = uint8(data[10]) + p.AddLayer(c) + return nil +} + +func (t NDPChassisType) String() (s string) { + switch t { + case NDPChassisother: + s = "other" + case NDPChassis3000: + s = "3000" + case NDPChassis3030: + s = "3030" + case NDPChassis2310: + s = "2310" + case NDPChassis2810: + s = "2810" + case NDPChassis2912: + s = "2912" + case NDPChassis2914: + s = "2914" + case NDPChassis271x: + s = "271x" + case NDPChassis2813: + s = "2813" + case NDPChassis2814: + s = "2814" + case NDPChassis2915: + s = "2915" + case NDPChassis5000: + s = "5000" + case NDPChassis2813SA: + s = "2813SA" + case NDPChassis2814SA: + s = "2814SA" + case NDPChassis810M: + s = "810M" + case NDPChassisEthercell: + s = "Ethercell" + case NDPChassis5005: + s = "5005" + case NDPChassisAlcatelEWC: + s = "Alcatel Ethernet workgroup conc." + case NDPChassis2715SA: + s = "2715SA" + case NDPChassis2486: + s = "2486" + case NDPChassis28000series: + s = "28000 series" + case NDPChassis23000series: + s = "23000 series" + case NDPChassis5DN00xseries: + s = "5DN00x series" + case NDPChassisBayStackEthernet: + s = "BayStack Ethernet" + case NDPChassis23100series: + s = "23100 series" + case NDPChassis100BaseTHub: + s = "100Base-T Hub" + case NDPChassis3000FastEthernet: + s = "3000 Fast Ethernet" + case NDPChassisOrionSwitch: + s = "Orion switch" + case NDPChassisDDS: + s = "DDS" + case NDPChassisCentillion6slot: + s = "Centillion (6 slot)" + case NDPChassisCentillion12slot: + s = "Centillion (12 slot)" + case NDPChassisCentillion1slot: + s = "Centillion (1 slot)" + case NDPChassisBayStack301: + s = "BayStack 301" + case NDPChassisBayStackTokenRingHub: + s = "BayStack TokenRing Hub" + case NDPChassisFVCMultimediaSwitch: + s = "FVC Multimedia Switch" + case NDPChassisSwitchNode: + s = "Switch Node" + case NDPChassisBayStack302Switch: + s = "BayStack 302 Switch" + case NDPChassisBayStack350Switch: + s = "BayStack 350 Switch" + case NDPChassisBayStack150EthernetHub: + s = "BayStack 150 Ethernet Hub" + case NDPChassisCentillion50NSwitch: + s = "Centillion 50N switch" + case NDPChassisCentillion50TSwitch: + s = "Centillion 50T switch" + case NDPChassisBayStack303304Switches: + s = "BayStack 303 and 304 Switches" + case NDPChassisBayStack200EthernetHub: + s = "BayStack 200 Ethernet Hub" + case NDPChassisBayStack25010100EthernetHub: + s = "BayStack 250 10/100 Ethernet Hub" + case NDPChassisBayStack450101001000Switches: + s = "BayStack 450 10/100/1000 Switches" + case NDPChassisBayStack41010100Switches: + s = "BayStack 410 10/100 Switches" + case NDPChassisPassport1200L3Switch: + s = "Passport 1200 L3 Switch" + case NDPChassisPassport1250L3Switch: + s = "Passport 1250 L3 Switch" + case NDPChassisPassport1100L3Switch: + s = "Passport 1100 L3 Switch" + case NDPChassisPassport1150L3Switch: + s = "Passport 1150 L3 Switch" + case NDPChassisPassport1050L3Switch: + s = "Passport 1050 L3 Switch" + case NDPChassisPassport1051L3Switch: + s = "Passport 1051 L3 Switch" + case NDPChassisPassport8610L3Switch: + s = "Passport 8610 L3 Switch" + case NDPChassisPassport8606L3Switch: + s = "Passport 8606 L3 Switch" + case NDPChassisPassport8010: + s = "Passport 8010" + case NDPChassisPassport8006: + s = "Passport 8006" + case NDPChassisBayStack670wirelessaccesspoint: + s = "BayStack 670 wireless access point" + case NDPChassisPassport740: + s = "Passport 740" + case NDPChassisPassport750: + s = "Passport 750" + case NDPChassisPassport790: + s = "Passport 790" + case NDPChassisBusinessPolicySwitch200010100Switches: + s = "Business Policy Switch 2000 10/100 Switches" + case NDPChassisPassport8110L2Switch: + s = "Passport 8110 L2 Switch" + case NDPChassisPassport8106L2Switch: + s = "Passport 8106 L2 Switch" + case NDPChassisBayStack3580GigSwitch: + s = "BayStack 3580 Gig Switch" + case NDPChassisBayStack10PowerSupplyUnit: + s = "BayStack 10 Power Supply Unit" + case NDPChassisBayStack42010100Switch: + s = "BayStack 420 10/100 Switch" + case NDPChassisOPTeraMetro1200EthernetServiceModule: + s = "OPTera Metro 1200 Ethernet Service Module" + case NDPChassisOPTera8010co: + s = "OPTera 8010co" + case NDPChassisOPTera8610coL3Switch: + s = "OPTera 8610co L3 switch" + case NDPChassisOPTera8110coL2Switch: + s = "OPTera 8110co L2 switch" + case NDPChassisOPTera8003: + s = "OPTera 8003" + case NDPChassisOPTera8603L3Switch: + s = "OPTera 8603 L3 switch" + case NDPChassisOPTera8103L2Switch: + s = "OPTera 8103 L2 switch" + case NDPChassisBayStack380101001000Switch: + s = "BayStack 380 10/100/1000 Switch" + case NDPChassisEthernetSwitch47048T: + s = "Ethernet Switch 470-48T" + case NDPChassisOPTeraMetro1450EthernetServiceModule: + s = "OPTera Metro 1450 Ethernet Service Module" + case NDPChassisOPTeraMetro1400EthernetServiceModule: + s = "OPTera Metro 1400 Ethernet Service Module" + case NDPChassisAlteonSwitchFamily: + s = "Alteon Switch Family" + case NDPChassisEthernetSwitch46024TPWR: + s = "Ethernet Switch 460-24T-PWR" + case NDPChassisOPTeraMetro8010OPML2Switch: + s = "OPTera Metro 8010 OPM L2 Switch" + case NDPChassisOPTeraMetro8010coOPML2Switch: + s = "OPTera Metro 8010co OPM L2 Switch" + case NDPChassisOPTeraMetro8006OPML2Switch: + s = "OPTera Metro 8006 OPM L2 Switch" + case NDPChassisOPTeraMetro8003OPML2Switch: + s = "OPTera Metro 8003 OPM L2 Switch" + case NDPChassisAlteon180e: + s = "Alteon 180e" + case NDPChassisAlteonAD3: + s = "Alteon AD3" + case NDPChassisAlteon184: + s = "Alteon 184" + case NDPChassisAlteonAD4: + s = "Alteon AD4" + case NDPChassisPassport1424L3Switch: + s = "Passport 1424 L3 switch" + case NDPChassisPassport1648L3Switch: + s = "Passport 1648 L3 switch" + case NDPChassisPassport1612L3Switch: + s = "Passport 1612 L3 switch" + case NDPChassisPassport1624L3Switch: + s = "Passport 1624 L3 switch" + case NDPChassisBayStack38024FFiber1000Switch: + s = "BayStack 380-24F Fiber 1000 Switch" + case NDPChassisEthernetRoutingSwitch551024T: + s = "Ethernet Routing Switch 5510-24T" + case NDPChassisEthernetRoutingSwitch551048T: + s = "Ethernet Routing Switch 5510-48T" + case NDPChassisEthernetSwitch47024T: + s = "Ethernet Switch 470-24T" + case NDPChassisNortelNetworksWirelessLANAccessPoint2220: + s = "Nortel Networks Wireless LAN Access Point 2220" + case NDPChassisPassportRBS2402L3Switch: + s = "Passport RBS 2402 L3 switch" + case NDPChassisAlteonApplicationSwitch2424: + s = "Alteon Application Switch 2424" + case NDPChassisAlteonApplicationSwitch2224: + s = "Alteon Application Switch 2224" + case NDPChassisAlteonApplicationSwitch2208: + s = "Alteon Application Switch 2208" + case NDPChassisAlteonApplicationSwitch2216: + s = "Alteon Application Switch 2216" + case NDPChassisAlteonApplicationSwitch3408: + s = "Alteon Application Switch 3408" + case NDPChassisAlteonApplicationSwitch3416: + s = "Alteon Application Switch 3416" + case NDPChassisNortelNetworksWirelessLANSecuritySwitch2250: + s = "Nortel Networks Wireless LAN SecuritySwitch 2250" + case NDPChassisEthernetSwitch42548T: + s = "Ethernet Switch 425-48T" + case NDPChassisEthernetSwitch42524T: + s = "Ethernet Switch 425-24T" + case NDPChassisNortelNetworksWirelessLANAccessPoint2221: + s = "Nortel Networks Wireless LAN Access Point 2221" + case NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch: + s = "Nortel Metro Ethernet Service Unit 24-T SPF switch" + case NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch: + s = " Nortel Metro Ethernet Service Unit 24-T LX DC switch" + case NDPChassisPassport830010slotchassis: + s = "Passport 8300 10-slot chassis" + case NDPChassisPassport83006slotchassis: + s = "Passport 8300 6-slot chassis" + case NDPChassisEthernetRoutingSwitch552024TPWR: + s = "Ethernet Routing Switch 5520-24T-PWR" + case NDPChassisEthernetRoutingSwitch552048TPWR: + s = "Ethernet Routing Switch 5520-48T-PWR" + case NDPChassisNortelNetworksVPNGateway3050: + s = "Nortel Networks VPN Gateway 3050" + case NDPChassisAlteonSSL31010100: + s = "Alteon SSL 310 10/100" + case NDPChassisAlteonSSL31010100Fiber: + s = "Alteon SSL 310 10/100 Fiber" + case NDPChassisAlteonSSL31010100FIPS: + s = "Alteon SSL 310 10/100 FIPS" + case NDPChassisAlteonSSL410101001000: + s = "Alteon SSL 410 10/100/1000" + case NDPChassisAlteonSSL410101001000Fiber: + s = "Alteon SSL 410 10/100/1000 Fiber" + case NDPChassisAlteonApplicationSwitch2424SSL: + s = "Alteon Application Switch 2424-SSL" + case NDPChassisEthernetSwitch32524T: + s = "Ethernet Switch 325-24T" + case NDPChassisEthernetSwitch32524G: + s = "Ethernet Switch 325-24G" + case NDPChassisNortelNetworksWirelessLANAccessPoint2225: + s = "Nortel Networks Wireless LAN Access Point 2225" + case NDPChassisNortelNetworksWirelessLANSecuritySwitch2270: + s = "Nortel Networks Wireless LAN SecuritySwitch 2270" + case NDPChassis24portEthernetSwitch47024TPWR: + s = "24-port Ethernet Switch 470-24T-PWR" + case NDPChassis48portEthernetSwitch47048TPWR: + s = "48-port Ethernet Switch 470-48T-PWR" + case NDPChassisEthernetRoutingSwitch553024TFD: + s = "Ethernet Routing Switch 5530-24TFD" + case NDPChassisEthernetSwitch351024T: + s = "Ethernet Switch 3510-24T" + case NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch: + s = "Nortel Metro Ethernet Service Unit 12G AC L3 switch" + case NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch: + s = "Nortel Metro Ethernet Service Unit 12G DC L3 switch" + case NDPChassisNortelSecureAccessSwitch: + s = "Nortel Secure Access Switch" + case NDPChassisNortelNetworksVPNGateway3070: + s = "Nortel Networks VPN Gateway 3070" + case NDPChassisOPTeraMetro3500: + s = "OPTera Metro 3500" + case NDPChassisSMBBES101024T: + s = "SMB BES 1010 24T" + case NDPChassisSMBBES101048T: + s = "SMB BES 1010 48T" + case NDPChassisSMBBES102024TPWR: + s = "SMB BES 1020 24T PWR" + case NDPChassisSMBBES102048TPWR: + s = "SMB BES 1020 48T PWR" + case NDPChassisSMBBES201024T: + s = "SMB BES 2010 24T" + case NDPChassisSMBBES201048T: + s = "SMB BES 2010 48T" + case NDPChassisSMBBES202024TPWR: + s = "SMB BES 2020 24T PWR" + case NDPChassisSMBBES202048TPWR: + s = "SMB BES 2020 48T PWR" + case NDPChassisSMBBES11024T: + s = "SMB BES 110 24T" + case NDPChassisSMBBES11048T: + s = "SMB BES 110 48T" + case NDPChassisSMBBES12024TPWR: + s = "SMB BES 120 24T PWR" + case NDPChassisSMBBES12048TPWR: + s = "SMB BES 120 48T PWR" + case NDPChassisSMBBES21024T: + s = "SMB BES 210 24T" + case NDPChassisSMBBES21048T: + s = "SMB BES 210 48T" + case NDPChassisSMBBES22024TPWR: + s = "SMB BES 220 24T PWR" + case NDPChassisSMBBES22048TPWR: + s = "SMB BES 220 48T PWR" + case NDPChassisOME6500: + s = "OME 6500" + case NDPChassisEthernetRoutingSwitch4548GT: + s = "Ethernet Routing Switch 4548GT" + case NDPChassisEthernetRoutingSwitch4548GTPWR: + s = "Ethernet Routing Switch 4548GT-PWR" + case NDPChassisEthernetRoutingSwitch4550T: + s = "Ethernet Routing Switch 4550T" + case NDPChassisEthernetRoutingSwitch4550TPWR: + s = "Ethernet Routing Switch 4550T-PWR" + case NDPChassisEthernetRoutingSwitch4526FX: + s = "Ethernet Routing Switch 4526FX" + case NDPChassisEthernetRoutingSwitch250026T: + s = "Ethernet Routing Switch 2500-26T" + case NDPChassisEthernetRoutingSwitch250026TPWR: + s = "Ethernet Routing Switch 2500-26T-PWR" + case NDPChassisEthernetRoutingSwitch250050T: + s = "Ethernet Routing Switch 2500-50T" + case NDPChassisEthernetRoutingSwitch250050TPWR: + s = "Ethernet Routing Switch 2500-50T-PWR" + default: + s = "Unknown" + } + return +} + +func (t NDPBackplaneType) String() (s string) { + switch t { + case NDPBackplaneOther: + s = "Other" + case NDPBackplaneEthernet: + s = "Ethernet" + case NDPBackplaneEthernetTokenring: + s = "Ethernet and Tokenring" + case NDPBackplaneEthernetFDDI: + s = "Ethernet and FDDI" + case NDPBackplaneEthernetTokenringFDDI: + s = "Ethernet, Tokenring and FDDI" + case NDPBackplaneEthernetTokenringRedundantPower: + s = "Ethernet and Tokenring with redundant power" + case NDPBackplaneEthernetTokenringFDDIRedundantPower: + s = "Ethernet, Tokenring, FDDI with redundant power" + case NDPBackplaneTokenRing: + s = "Token Ring" + case NDPBackplaneEthernetTokenringFastEthernet: + s = "Ethernet, Tokenring and Fast Ethernet" + case NDPBackplaneEthernetFastEthernet: + s = "Ethernet and Fast Ethernet" + case NDPBackplaneEthernetTokenringFastEthernetRedundantPower: + s = "Ethernet, Tokenring, Fast Ethernet with redundant power" + case NDPBackplaneEthernetFastEthernetGigabitEthernet: + s = "Ethernet, Fast Ethernet and Gigabit Ethernet" + default: + s = "Unknown" + } + return +} + +func (t NDPState) String() (s string) { + switch t { + case NDPStateTopology: + s = "Topology Change" + case NDPStateHeartbeat: + s = "Heartbeat" + case NDPStateNew: + s = "New" + default: + s = "Unknown" + } + return +} diff --git a/vendor/github.com/google/gopacket/layers/ntp.go b/vendor/github.com/google/gopacket/layers/ntp.go new file mode 100644 index 00000000..33c15b3b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ntp.go @@ -0,0 +1,416 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. +// +//****************************************************************************** + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +//****************************************************************************** +// +// Network Time Protocol (NTP) Decoding Layer +// ------------------------------------------ +// This file provides a GoPacket decoding layer for NTP. +// +//****************************************************************************** +// +// About The Network Time Protocol (NTP) +// ------------------------------------- +// NTP is a protocol that enables computers on the internet to set their +// clocks to the correct time (or to a time that is acceptably close to the +// correct time). NTP runs on top of UDP. +// +// There have been a series of versions of the NTP protocol. The latest +// version is V4 and is specified in RFC 5905: +// http://www.ietf.org/rfc/rfc5905.txt +// +//****************************************************************************** +// +// References +// ---------- +// +// Wikipedia's NTP entry: +// https://en.wikipedia.org/wiki/Network_Time_Protocol +// This is the best place to get an overview of NTP. +// +// Network Time Protocol Home Website: +// http://www.ntp.org/ +// This appears to be the official website of NTP. +// +// List of current NTP Protocol RFCs: +// http://www.ntp.org/rfc.html +// +// RFC 958: "Network Time Protocol (NTP)" (1985) +// https://tools.ietf.org/html/rfc958 +// This is the original NTP specification. +// +// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992) +// https://tools.ietf.org/html/rfc1305 +// The protocol was updated in 1992 yielding NTP V3. +// +// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010) +// https://www.ietf.org/rfc/rfc5905.txt +// The protocol was updated in 2010 yielding NTP V4. +// V4 is backwards compatible with all previous versions of NTP. +// +// RFC 5906: "Network Time Protocol Version 4: Autokey Specification" +// https://tools.ietf.org/html/rfc5906 +// This document addresses the security of the NTP protocol +// and is probably not relevant to this package. +// +// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)" +// https://tools.ietf.org/html/rfc5907 +// This document addresses the management of NTP servers and +// is probably not relevant to this package. +// +// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6" +// https://tools.ietf.org/html/rfc5908 +// This document addresses the use of NTP in DHCPv6 and is +// probably not relevant to this package. +// +// "Let's make a NTP Client in C" +// https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html +// This web page contains useful information about the details of NTP, +// including an NTP record struture in C, and C code. +// +// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)" +// http://what-when-how.com/computer-network-time-synchronization/ +// ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/ +// This web page contains useful information on the details of NTP. +// +// "Technical information - NTP Data Packet" +// https://www.meinbergglobal.com/english/info/ntp-packet.htm +// This page has a helpful diagram of an NTP V4 packet. +// +//****************************************************************************** +// +// Obsolete References +// ------------------- +// +// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989) +// https://tools.ietf.org/html/rfc1119 +// Version 2 was drafted in 1989. +// It is unclear whether V2 was ever implememented or whether the +// ideas ended up in V3 (which was implemented in 1992). +// +// RFC 1361: "Simple Network Time Protocol (SNTP)" +// https://tools.ietf.org/html/rfc1361 +// This document is obsoleted by RFC 1769 and is included only for completeness. +// +// RFC 1769: "Simple Network Time Protocol (SNTP)" +// https://tools.ietf.org/html/rfc1769 +// This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness. +// +// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI" +// https://tools.ietf.org/html/rfc2030 +// This document is obsoleted by RFC 4330 and is included only for completeness. +// +// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI" +// https://tools.ietf.org/html/rfc4330 +// This document is obsoleted by RFC 5905 and is included only for completeness. +// +//****************************************************************************** +// +// Endian And Bit Numbering Issues +// ------------------------------- +// +// Endian and bit numbering issues can be confusing. Here is some +// clarification: +// +// ENDIAN: Values are sent big endian. +// https://en.wikipedia.org/wiki/Endianness +// +// BIT NUMBERING: Bits are numbered 0 upwards from the most significant +// bit to the least significant bit. This means that if there is a 32-bit +// value, the most significant bit is called bit 0 and the least +// significant bit is called bit 31. +// +// See RFC 791 Appendix B for more discussion. +// +//****************************************************************************** +// +// NTP V3 and V4 Packet Format +// --------------------------- +// NTP packets are UDP packets whose payload contains an NTP record. +// +// The NTP RFC defines the format of the NTP record. +// +// There have been four versions of the protocol: +// +// V1 in 1985 +// V2 in 1989 +// V3 in 1992 +// V4 in 2010 +// +// It is clear that V1 and V2 are obsolete, and there is no need to +// cater for these formats. +// +// V3 and V4 essentially use the same format, with V4 adding some optional +// fields on the end. So this package supports the V3 and V4 formats. +// +// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains +// the following diagram for the NTP record format: + +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |LI | VN |Mode | Stratum | Poll | Precision | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Root Delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Root Dispersion | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reference ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Reference Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Origin Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Receive Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// + Transmit Timestamp (64) + +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Extension Field 1 (variable) . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Extension Field 2 (variable) . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Key Identifier | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// | dgst (128) | +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// From http://www.ietf.org/rfc/rfc5905.txt +// +// The fields "Extension Field 1 (variable)" and later are optional fields, +// and so we can set a minimum NTP record size of 48 bytes. +// +const ntpMinimumRecordSizeInBytes int = 48 + +//****************************************************************************** + +// NTP Type +// -------- +// Type NTP implements the DecodingLayer interface. Each NTP object +// represents in a structured form the NTP record present as the UDP +// payload in an NTP UDP packet. +// + +type NTPLeapIndicator uint8 +type NTPVersion uint8 +type NTPMode uint8 +type NTPStratum uint8 +type NTPLog2Seconds int8 +type NTPFixed16Seconds uint32 +type NTPReferenceID uint32 +type NTPTimestamp uint64 + +type NTP struct { + BaseLayer // Stores the packet bytes and payload bytes. + + LeapIndicator NTPLeapIndicator // [0,3]. Indicates whether leap second(s) is to be added. + Version NTPVersion // [0,7]. Version of the NTP protocol. + Mode NTPMode // [0,7]. Mode. + Stratum NTPStratum // [0,255]. Stratum of time server in the server tree. + Poll NTPLog2Seconds // [-128,127]. The maximum interval between successive messages, in log2 seconds. + Precision NTPLog2Seconds // [-128,127]. The precision of the system clock, in log2 seconds. + RootDelay NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16. + RootDispersion NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16. + ReferenceID NTPReferenceID // ID code of reference clock [0,2^32-1]. + ReferenceTimestamp NTPTimestamp // Most recent timestamp from the reference clock. + OriginTimestamp NTPTimestamp // Local time when request was sent from local host. + ReceiveTimestamp NTPTimestamp // Local time (on server) that request arrived at server host. + TransmitTimestamp NTPTimestamp // Local time (on server) that request departed server host. + + // FIX: This package should analyse the extension fields and represent the extension fields too. + ExtensionBytes []byte // Just put extensions in a byte slice. +} + +//****************************************************************************** + +// LayerType returns the layer type of the NTP object, which is LayerTypeNTP. +func (d *NTP) LayerType() gopacket.LayerType { + return LayerTypeNTP +} + +//****************************************************************************** + +// decodeNTP analyses a byte slice and attempts to decode it as an NTP +// record of a UDP packet. +// +// If it succeeds, it loads p with information about the packet and returns nil. +// If it fails, it returns an error (non nil). +// +// This function is employed in layertypes.go to register the NTP layer. +func decodeNTP(data []byte, p gopacket.PacketBuilder) error { + + // Attempt to decode the byte slice. + d := &NTP{} + err := d.DecodeFromBytes(data, p) + if err != nil { + return err + } + + // If the decoding worked, add the layer to the packet and set it + // as the application layer too, if there isn't already one. + p.AddLayer(d) + p.SetApplicationLayer(d) + + return nil +} + +//****************************************************************************** + +// DecodeFromBytes analyses a byte slice and attempts to decode it as an NTP +// record of a UDP packet. +// +// Upon succeeds, it loads the NTP object with information about the packet +// and returns nil. +// Upon failure, it returns an error (non nil). +func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + // If the data block is too short to be a NTP record, then return an error. + if len(data) < ntpMinimumRecordSizeInBytes { + df.SetTruncated() + return errors.New("NTP packet too short") + } + + // RFC 5905 does not appear to define a maximum NTP record length. + // The protocol allows "extension fields" to be included in the record, + // and states about these fields:" + // + // "While the minimum field length containing required fields is + // four words (16 octets), a maximum field length remains to be + // established." + // + // For this reason, the packet length is not checked here for being too long. + + // NTP type embeds type BaseLayer which contains two fields: + // Contents is supposed to contain the bytes of the data at this level. + // Payload is supposed to contain the payload of this level. + // Here we set the baselayer to be the bytes of the NTP record. + d.BaseLayer = BaseLayer{Contents: data[:len(data)]} + + // Extract the fields from the block of bytes. + // To make sense of this, refer to the packet diagram + // above and the section on endian conventions. + + // The first few fields are all packed into the first 32 bits. Unpack them. + f := data[0] + d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6) + d.Version = NTPVersion((f & 0x38) >> 3) + d.Mode = NTPMode(f & 0x07) + d.Stratum = NTPStratum(data[1]) + d.Poll = NTPLog2Seconds(data[2]) + d.Precision = NTPLog2Seconds(data[3]) + + // The remaining fields can just be copied in big endian order. + d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8])) + d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12])) + d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16])) + d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24])) + d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32])) + d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40])) + d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48])) + + // This layer does not attempt to analyse the extension bytes. + // But if there are any, we'd like the user to know. So we just + // place them all in an ExtensionBytes field. + d.ExtensionBytes = data[48:] + + // Return no error. + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (d *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes) + if err != nil { + return err + } + + // Pack the first few fields into the first 32 bits. + h := uint8(0) + h |= (uint8(d.LeapIndicator) << 6) & 0xC0 + h |= (uint8(d.Version) << 3) & 0x38 + h |= (uint8(d.Mode)) & 0x07 + data[0] = byte(h) + data[1] = byte(d.Stratum) + data[2] = byte(d.Poll) + data[3] = byte(d.Precision) + + // The remaining fields can just be copied in big endian order. + binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay)) + binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion)) + binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID)) + binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp)) + binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp)) + binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp)) + binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp)) + + ex, err := b.AppendBytes(len(d.ExtensionBytes)) + if err != nil { + return err + } + copy(ex, d.ExtensionBytes) + + return nil +} + +//****************************************************************************** + +// CanDecode returns a set of layers that NTP objects can decode. +// As NTP objects can only decide the NTP layer, we can return just that layer. +// Apparently a single layer type implements LayerClass. +func (d *NTP) CanDecode() gopacket.LayerClass { + return LayerTypeNTP +} + +//****************************************************************************** + +// NextLayerType specifies the next layer that GoPacket should attempt to +// analyse after this (NTP) layer. As NTP packets do not contain any payload +// bytes, there are no further layers to analyse. +func (d *NTP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +//****************************************************************************** + +// NTP packets do not carry any data payload, so the empty byte slice is retured. +// In Go, a nil slice is functionally identical to an empty slice, so we +// return nil to avoid a heap allocation. +func (d *NTP) Payload() []byte { + return nil +} + +//****************************************************************************** +//* End Of NTP File * +//****************************************************************************** diff --git a/vendor/github.com/google/gopacket/layers/ospf.go b/vendor/github.com/google/gopacket/layers/ospf.go new file mode 100644 index 00000000..4f5473d0 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ospf.go @@ -0,0 +1,715 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// OSPFType denotes what kind of OSPF type it is +type OSPFType uint8 + +// Potential values for OSPF.Type. +const ( + OSPFHello OSPFType = 1 + OSPFDatabaseDescription OSPFType = 2 + OSPFLinkStateRequest OSPFType = 3 + OSPFLinkStateUpdate OSPFType = 4 + OSPFLinkStateAcknowledgment OSPFType = 5 +) + +// LSA Function Codes for LSAheader.LSType +const ( + RouterLSAtypeV2 = 0x1 + RouterLSAtype = 0x2001 + NetworkLSAtypeV2 = 0x2 + NetworkLSAtype = 0x2002 + SummaryLSANetworktypeV2 = 0x3 + InterAreaPrefixLSAtype = 0x2003 + SummaryLSAASBRtypeV2 = 0x4 + InterAreaRouterLSAtype = 0x2004 + ASExternalLSAtypeV2 = 0x5 + ASExternalLSAtype = 0x4005 + NSSALSAtype = 0x2007 + NSSALSAtypeV2 = 0x7 + LinkLSAtype = 0x0008 + IntraAreaPrefixLSAtype = 0x2009 +) + +// String conversions for OSPFType +func (i OSPFType) String() string { + switch i { + case OSPFHello: + return "Hello" + case OSPFDatabaseDescription: + return "Database Description" + case OSPFLinkStateRequest: + return "Link State Request" + case OSPFLinkStateUpdate: + return "Link State Update" + case OSPFLinkStateAcknowledgment: + return "Link State Acknowledgment" + default: + return "" + } +} + +// Prefix extends IntraAreaPrefixLSA +type Prefix struct { + PrefixLength uint8 + PrefixOptions uint8 + Metric uint16 + AddressPrefix []byte +} + +// IntraAreaPrefixLSA is the struct from RFC 5340 A.4.10. +type IntraAreaPrefixLSA struct { + NumOfPrefixes uint16 + RefLSType uint16 + RefLinkStateID uint32 + RefAdvRouter uint32 + Prefixes []Prefix +} + +// LinkLSA is the struct from RFC 5340 A.4.9. +type LinkLSA struct { + RtrPriority uint8 + Options uint32 + LinkLocalAddress []byte + NumOfPrefixes uint32 + Prefixes []Prefix +} + +// ASExternalLSAV2 is the struct from RFC 2328 A.4.5. +type ASExternalLSAV2 struct { + NetworkMask uint32 + ExternalBit uint8 + Metric uint32 + ForwardingAddress uint32 + ExternalRouteTag uint32 +} + +// ASExternalLSA is the struct from RFC 5340 A.4.7. +type ASExternalLSA struct { + Flags uint8 + Metric uint32 + PrefixLength uint8 + PrefixOptions uint8 + RefLSType uint16 + AddressPrefix []byte + ForwardingAddress []byte + ExternalRouteTag uint32 + RefLinkStateID uint32 +} + +// InterAreaRouterLSA is the struct from RFC 5340 A.4.6. +type InterAreaRouterLSA struct { + Options uint32 + Metric uint32 + DestinationRouterID uint32 +} + +// InterAreaPrefixLSA is the struct from RFC 5340 A.4.5. +type InterAreaPrefixLSA struct { + Metric uint32 + PrefixLength uint8 + PrefixOptions uint8 + AddressPrefix []byte +} + +// NetworkLSA is the struct from RFC 5340 A.4.4. +type NetworkLSA struct { + Options uint32 + AttachedRouter []uint32 +} + +// NetworkLSAV2 is the struct from RFC 2328 A.4.3. +type NetworkLSAV2 struct { + NetworkMask uint32 + AttachedRouter []uint32 +} + +// RouterV2 extends RouterLSAV2 +type RouterV2 struct { + Type uint8 + LinkID uint32 + LinkData uint32 + Metric uint16 +} + +// RouterLSAV2 is the struct from RFC 2328 A.4.2. +type RouterLSAV2 struct { + Flags uint8 + Links uint16 + Routers []RouterV2 +} + +// Router extends RouterLSA +type Router struct { + Type uint8 + Metric uint16 + InterfaceID uint32 + NeighborInterfaceID uint32 + NeighborRouterID uint32 +} + +// RouterLSA is the struct from RFC 5340 A.4.3. +type RouterLSA struct { + Flags uint8 + Options uint32 + Routers []Router +} + +// LSAheader is the struct from RFC 5340 A.4.2 and RFC 2328 A.4.1. +type LSAheader struct { + LSAge uint16 + LSType uint16 + LinkStateID uint32 + AdvRouter uint32 + LSSeqNumber uint32 + LSChecksum uint16 + Length uint16 + LSOptions uint8 +} + +// LSA links LSAheader with the structs from RFC 5340 A.4. +type LSA struct { + LSAheader + Content interface{} +} + +// LSUpdate is the struct from RFC 5340 A.3.5. +type LSUpdate struct { + NumOfLSAs uint32 + LSAs []LSA +} + +// LSReq is the struct from RFC 5340 A.3.4. +type LSReq struct { + LSType uint16 + LSID uint32 + AdvRouter uint32 +} + +// DbDescPkg is the struct from RFC 5340 A.3.3. +type DbDescPkg struct { + Options uint32 + InterfaceMTU uint16 + Flags uint16 + DDSeqNumber uint32 + LSAinfo []LSAheader +} + +// HelloPkg is the struct from RFC 5340 A.3.2. +type HelloPkg struct { + InterfaceID uint32 + RtrPriority uint8 + Options uint32 + HelloInterval uint16 + RouterDeadInterval uint32 + DesignatedRouterID uint32 + BackupDesignatedRouterID uint32 + NeighborID []uint32 +} + +// HelloPkgV2 extends the HelloPkg struct with OSPFv2 information +type HelloPkgV2 struct { + HelloPkg + NetworkMask uint32 +} + +// OSPF is a basic OSPF packet header with common fields of Version 2 and Version 3. +type OSPF struct { + Version uint8 + Type OSPFType + PacketLength uint16 + RouterID uint32 + AreaID uint32 + Checksum uint16 + Content interface{} +} + +//OSPFv2 extend the OSPF head with version 2 specific fields +type OSPFv2 struct { + BaseLayer + OSPF + AuType uint16 + Authentication uint64 +} + +// OSPFv3 extend the OSPF head with version 3 specific fields +type OSPFv3 struct { + BaseLayer + OSPF + Instance uint8 + Reserved uint8 +} + +// getLSAsv2 parses the LSA information from the packet for OSPFv2 +func getLSAsv2(num uint32, data []byte) ([]LSA, error) { + var lsas []LSA + var i uint32 = 0 + var offset uint32 = 0 + for ; i < num; i++ { + lstype := uint16(data[offset+3]) + lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20]) + content, err := extractLSAInformation(lstype, lsalength, data[offset:]) + if err != nil { + return nil, fmt.Errorf("Could not extract Link State type.") + } + lsa := LSA{ + LSAheader: LSAheader{ + LSAge: binary.BigEndian.Uint16(data[offset : offset+2]), + LSOptions: data[offset+2], + LSType: lstype, + LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]), + AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]), + LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]), + Length: lsalength, + }, + Content: content, + } + lsas = append(lsas, lsa) + offset += uint32(lsalength) + } + return lsas, nil +} + +// extractLSAInformation extracts all the LSA information +func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{}, error) { + if lsalength < 20 { + return nil, fmt.Errorf("Link State header length %v too short, %v required", lsalength, 20) + } + if len(data) < int(lsalength) { + return nil, fmt.Errorf("Link State header length %v too short, %v required", len(data), lsalength) + } + var content interface{} + switch lstype { + case RouterLSAtypeV2: + var routers []RouterV2 + var j uint32 + for j = 24; j < uint32(lsalength); j += 12 { + if len(data) < int(j+12) { + return nil, errors.New("LSAtypeV2 too small") + } + router := RouterV2{ + LinkID: binary.BigEndian.Uint32(data[j : j+4]), + LinkData: binary.BigEndian.Uint32(data[j+4 : j+8]), + Type: uint8(data[j+8]), + Metric: binary.BigEndian.Uint16(data[j+10 : j+12]), + } + routers = append(routers, router) + } + if len(data) < 24 { + return nil, errors.New("LSAtypeV2 too small") + } + links := binary.BigEndian.Uint16(data[22:24]) + content = RouterLSAV2{ + Flags: data[20], + Links: links, + Routers: routers, + } + case NSSALSAtypeV2: + fallthrough + case ASExternalLSAtypeV2: + content = ASExternalLSAV2{ + NetworkMask: binary.BigEndian.Uint32(data[20:24]), + ExternalBit: data[24] & 0x80, + Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF, + ForwardingAddress: binary.BigEndian.Uint32(data[28:32]), + ExternalRouteTag: binary.BigEndian.Uint32(data[32:36]), + } + case NetworkLSAtypeV2: + var routers []uint32 + var j uint32 + for j = 24; j < uint32(lsalength); j += 4 { + routers = append(routers, binary.BigEndian.Uint32(data[j:j+4])) + } + content = NetworkLSAV2{ + NetworkMask: binary.BigEndian.Uint32(data[20:24]), + AttachedRouter: routers, + } + case RouterLSAtype: + var routers []Router + var j uint32 + for j = 24; j < uint32(lsalength); j += 16 { + router := Router{ + Type: uint8(data[j]), + Metric: binary.BigEndian.Uint16(data[j+2 : j+4]), + InterfaceID: binary.BigEndian.Uint32(data[j+4 : j+8]), + NeighborInterfaceID: binary.BigEndian.Uint32(data[j+8 : j+12]), + NeighborRouterID: binary.BigEndian.Uint32(data[j+12 : j+16]), + } + routers = append(routers, router) + } + content = RouterLSA{ + Flags: uint8(data[20]), + Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + Routers: routers, + } + case NetworkLSAtype: + var routers []uint32 + var j uint32 + for j = 24; j < uint32(lsalength); j += 4 { + routers = append(routers, binary.BigEndian.Uint32(data[j:j+4])) + } + content = NetworkLSA{ + Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + AttachedRouter: routers, + } + case InterAreaPrefixLSAtype: + content = InterAreaPrefixLSA{ + Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + PrefixLength: uint8(data[24]), + PrefixOptions: uint8(data[25]), + AddressPrefix: data[28:uint32(lsalength)], + } + case InterAreaRouterLSAtype: + content = InterAreaRouterLSA{ + Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF, + DestinationRouterID: binary.BigEndian.Uint32(data[28:32]), + } + case ASExternalLSAtype: + fallthrough + case NSSALSAtype: + flags := uint8(data[20]) + prefixLen := uint8(data[24]) / 8 + var forwardingAddress []byte + if (flags & 0x02) == 0x02 { + forwardingAddress = data[28+uint32(prefixLen) : 28+uint32(prefixLen)+16] + } + content = ASExternalLSA{ + Flags: flags, + Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + PrefixLength: prefixLen, + PrefixOptions: uint8(data[25]), + RefLSType: binary.BigEndian.Uint16(data[26:28]), + AddressPrefix: data[28 : 28+uint32(prefixLen)], + ForwardingAddress: forwardingAddress, + } + case LinkLSAtype: + var prefixes []Prefix + var prefixOffset uint32 = 44 + var j uint32 + numOfPrefixes := binary.BigEndian.Uint32(data[40:44]) + for j = 0; j < numOfPrefixes; j++ { + prefixLen := uint8(data[prefixOffset]) + prefix := Prefix{ + PrefixLength: prefixLen, + PrefixOptions: uint8(data[prefixOffset+1]), + AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8], + } + prefixes = append(prefixes, prefix) + prefixOffset = prefixOffset + 4 + uint32(prefixLen)/8 + } + content = LinkLSA{ + RtrPriority: uint8(data[20]), + Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF, + LinkLocalAddress: data[24:40], + NumOfPrefixes: numOfPrefixes, + Prefixes: prefixes, + } + case IntraAreaPrefixLSAtype: + var prefixes []Prefix + var prefixOffset uint32 = 32 + var j uint16 + numOfPrefixes := binary.BigEndian.Uint16(data[20:22]) + for j = 0; j < numOfPrefixes; j++ { + prefixLen := uint8(data[prefixOffset]) + prefix := Prefix{ + PrefixLength: prefixLen, + PrefixOptions: uint8(data[prefixOffset+1]), + Metric: binary.BigEndian.Uint16(data[prefixOffset+2 : prefixOffset+4]), + AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8], + } + prefixes = append(prefixes, prefix) + prefixOffset = prefixOffset + 4 + uint32(prefixLen) + } + content = IntraAreaPrefixLSA{ + NumOfPrefixes: numOfPrefixes, + RefLSType: binary.BigEndian.Uint16(data[22:24]), + RefLinkStateID: binary.BigEndian.Uint32(data[24:28]), + RefAdvRouter: binary.BigEndian.Uint32(data[28:32]), + Prefixes: prefixes, + } + default: + return nil, fmt.Errorf("Unknown Link State type.") + } + return content, nil +} + +// getLSAs parses the LSA information from the packet for OSPFv3 +func getLSAs(num uint32, data []byte) ([]LSA, error) { + var lsas []LSA + var i uint32 = 0 + var offset uint32 = 0 + for ; i < num; i++ { + var content interface{} + lstype := binary.BigEndian.Uint16(data[offset+2 : offset+4]) + lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20]) + + content, err := extractLSAInformation(lstype, lsalength, data[offset:]) + if err != nil { + return nil, fmt.Errorf("Could not extract Link State type.") + } + lsa := LSA{ + LSAheader: LSAheader{ + LSAge: binary.BigEndian.Uint16(data[offset : offset+2]), + LSType: lstype, + LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]), + AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]), + LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]), + Length: lsalength, + }, + Content: content, + } + lsas = append(lsas, lsa) + offset += uint32(lsalength) + } + return lsas, nil +} + +// DecodeFromBytes decodes the given bytes into the OSPF layer. +func (ospf *OSPFv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 24 { + return fmt.Errorf("Packet too smal for OSPF Version 2") + } + + ospf.Version = uint8(data[0]) + ospf.Type = OSPFType(data[1]) + ospf.PacketLength = binary.BigEndian.Uint16(data[2:4]) + ospf.RouterID = binary.BigEndian.Uint32(data[4:8]) + ospf.AreaID = binary.BigEndian.Uint32(data[8:12]) + ospf.Checksum = binary.BigEndian.Uint16(data[12:14]) + ospf.AuType = binary.BigEndian.Uint16(data[14:16]) + ospf.Authentication = binary.BigEndian.Uint64(data[16:24]) + + switch ospf.Type { + case OSPFHello: + var neighbors []uint32 + for i := 44; uint16(i+4) <= ospf.PacketLength; i += 4 { + neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4])) + } + ospf.Content = HelloPkgV2{ + NetworkMask: binary.BigEndian.Uint32(data[24:28]), + HelloPkg: HelloPkg{ + HelloInterval: binary.BigEndian.Uint16(data[28:30]), + Options: uint32(data[30]), + RtrPriority: uint8(data[31]), + RouterDeadInterval: binary.BigEndian.Uint32(data[32:36]), + DesignatedRouterID: binary.BigEndian.Uint32(data[36:40]), + BackupDesignatedRouterID: binary.BigEndian.Uint32(data[40:44]), + NeighborID: neighbors, + }, + } + case OSPFDatabaseDescription: + var lsas []LSAheader + for i := 32; uint16(i+20) <= ospf.PacketLength; i += 20 { + lsa := LSAheader{ + LSAge: binary.BigEndian.Uint16(data[i : i+2]), + LSType: binary.BigEndian.Uint16(data[i+2 : i+4]), + LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]), + LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]), + Length: binary.BigEndian.Uint16(data[i+18 : i+20]), + } + lsas = append(lsas, lsa) + } + ospf.Content = DbDescPkg{ + InterfaceMTU: binary.BigEndian.Uint16(data[24:26]), + Options: uint32(data[26]), + Flags: uint16(data[27]), + DDSeqNumber: binary.BigEndian.Uint32(data[28:32]), + LSAinfo: lsas, + } + case OSPFLinkStateRequest: + var lsrs []LSReq + for i := 24; uint16(i+12) <= ospf.PacketLength; i += 12 { + lsr := LSReq{ + LSType: binary.BigEndian.Uint16(data[i+2 : i+4]), + LSID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + } + lsrs = append(lsrs, lsr) + } + ospf.Content = lsrs + case OSPFLinkStateUpdate: + num := binary.BigEndian.Uint32(data[24:28]) + + lsas, err := getLSAsv2(num, data[28:]) + if err != nil { + return fmt.Errorf("Cannot parse Link State Update packet: %v", err) + } + ospf.Content = LSUpdate{ + NumOfLSAs: num, + LSAs: lsas, + } + case OSPFLinkStateAcknowledgment: + var lsas []LSAheader + for i := 24; uint16(i+20) <= ospf.PacketLength; i += 20 { + lsa := LSAheader{ + LSAge: binary.BigEndian.Uint16(data[i : i+2]), + LSOptions: data[i+2], + LSType: uint16(data[i+3]), + LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]), + LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]), + Length: binary.BigEndian.Uint16(data[i+18 : i+20]), + } + lsas = append(lsas, lsa) + } + ospf.Content = lsas + } + return nil +} + +// DecodeFromBytes decodes the given bytes into the OSPF layer. +func (ospf *OSPFv3) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + if len(data) < 16 { + return fmt.Errorf("Packet too smal for OSPF Version 3") + } + + ospf.Version = uint8(data[0]) + ospf.Type = OSPFType(data[1]) + ospf.PacketLength = binary.BigEndian.Uint16(data[2:4]) + ospf.RouterID = binary.BigEndian.Uint32(data[4:8]) + ospf.AreaID = binary.BigEndian.Uint32(data[8:12]) + ospf.Checksum = binary.BigEndian.Uint16(data[12:14]) + ospf.Instance = uint8(data[14]) + ospf.Reserved = uint8(data[15]) + + switch ospf.Type { + case OSPFHello: + var neighbors []uint32 + for i := 36; uint16(i+4) <= ospf.PacketLength; i += 4 { + neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4])) + } + ospf.Content = HelloPkg{ + InterfaceID: binary.BigEndian.Uint32(data[16:20]), + RtrPriority: uint8(data[20]), + Options: binary.BigEndian.Uint32(data[21:25]) >> 8, + HelloInterval: binary.BigEndian.Uint16(data[24:26]), + RouterDeadInterval: uint32(binary.BigEndian.Uint16(data[26:28])), + DesignatedRouterID: binary.BigEndian.Uint32(data[28:32]), + BackupDesignatedRouterID: binary.BigEndian.Uint32(data[32:36]), + NeighborID: neighbors, + } + case OSPFDatabaseDescription: + var lsas []LSAheader + for i := 28; uint16(i+20) <= ospf.PacketLength; i += 20 { + lsa := LSAheader{ + LSAge: binary.BigEndian.Uint16(data[i : i+2]), + LSType: binary.BigEndian.Uint16(data[i+2 : i+4]), + LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]), + LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]), + Length: binary.BigEndian.Uint16(data[i+18 : i+20]), + } + lsas = append(lsas, lsa) + } + ospf.Content = DbDescPkg{ + Options: binary.BigEndian.Uint32(data[16:20]) & 0x00FFFFFF, + InterfaceMTU: binary.BigEndian.Uint16(data[20:22]), + Flags: binary.BigEndian.Uint16(data[22:24]), + DDSeqNumber: binary.BigEndian.Uint32(data[24:28]), + LSAinfo: lsas, + } + case OSPFLinkStateRequest: + var lsrs []LSReq + for i := 16; uint16(i+12) <= ospf.PacketLength; i += 12 { + lsr := LSReq{ + LSType: binary.BigEndian.Uint16(data[i+2 : i+4]), + LSID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + } + lsrs = append(lsrs, lsr) + } + ospf.Content = lsrs + case OSPFLinkStateUpdate: + num := binary.BigEndian.Uint32(data[16:20]) + lsas, err := getLSAs(num, data[20:]) + if err != nil { + return fmt.Errorf("Cannot parse Link State Update packet: %v", err) + } + ospf.Content = LSUpdate{ + NumOfLSAs: num, + LSAs: lsas, + } + + case OSPFLinkStateAcknowledgment: + var lsas []LSAheader + for i := 16; uint16(i+20) <= ospf.PacketLength; i += 20 { + lsa := LSAheader{ + LSAge: binary.BigEndian.Uint16(data[i : i+2]), + LSType: binary.BigEndian.Uint16(data[i+2 : i+4]), + LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]), + AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]), + LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]), + LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]), + Length: binary.BigEndian.Uint16(data[i+18 : i+20]), + } + lsas = append(lsas, lsa) + } + ospf.Content = lsas + default: + } + + return nil +} + +// LayerType returns LayerTypeOSPF +func (ospf *OSPFv2) LayerType() gopacket.LayerType { + return LayerTypeOSPF +} +func (ospf *OSPFv3) LayerType() gopacket.LayerType { + return LayerTypeOSPF +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (ospf *OSPFv2) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} +func (ospf *OSPFv3) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (ospf *OSPFv2) CanDecode() gopacket.LayerClass { + return LayerTypeOSPF +} +func (ospf *OSPFv3) CanDecode() gopacket.LayerClass { + return LayerTypeOSPF +} + +func decodeOSPF(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 14 { + return fmt.Errorf("Packet too smal for OSPF") + } + + switch uint8(data[0]) { + case 2: + ospf := &OSPFv2{} + return decodingLayerDecoder(ospf, data, p) + case 3: + ospf := &OSPFv3{} + return decodingLayerDecoder(ospf, data, p) + default: + } + + return fmt.Errorf("Unable to determine OSPF type.") +} diff --git a/vendor/github.com/google/gopacket/layers/pflog.go b/vendor/github.com/google/gopacket/layers/pflog.go new file mode 100644 index 00000000..9dbbd90d --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/pflog.go @@ -0,0 +1,84 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +type PFDirection uint8 + +const ( + PFDirectionInOut PFDirection = 0 + PFDirectionIn PFDirection = 1 + PFDirectionOut PFDirection = 2 +) + +// PFLog provides the layer for 'pf' packet-filter logging, as described at +// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4 +type PFLog struct { + BaseLayer + Length uint8 + Family ProtocolFamily + Action, Reason uint8 + IFName, Ruleset []byte + RuleNum, SubruleNum uint32 + UID uint32 + PID int32 + RuleUID uint32 + RulePID int32 + Direction PFDirection + // The remainder is padding +} + +func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 60 { + df.SetTruncated() + return errors.New("PFLog data less than 60 bytes") + } + pf.Length = data[0] + pf.Family = ProtocolFamily(data[1]) + pf.Action = data[2] + pf.Reason = data[3] + pf.IFName = data[4:20] + pf.Ruleset = data[20:36] + pf.RuleNum = binary.BigEndian.Uint32(data[36:40]) + pf.SubruleNum = binary.BigEndian.Uint32(data[40:44]) + pf.UID = binary.BigEndian.Uint32(data[44:48]) + pf.PID = int32(binary.BigEndian.Uint32(data[48:52])) + pf.RuleUID = binary.BigEndian.Uint32(data[52:56]) + pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60])) + pf.Direction = PFDirection(data[60]) + if pf.Length%4 != 1 { + return errors.New("PFLog header length should be 3 less than multiple of 4") + } + actualLength := int(pf.Length) + 3 + if len(data) < actualLength { + return fmt.Errorf("PFLog data size < %d", actualLength) + } + pf.Contents = data[:actualLength] + pf.Payload = data[actualLength:] + return nil +} + +// LayerType returns layers.LayerTypePFLog +func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog } + +func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog } + +func (pf *PFLog) NextLayerType() gopacket.LayerType { + return pf.Family.LayerType() +} + +func decodePFLog(data []byte, p gopacket.PacketBuilder) error { + pf := &PFLog{} + return decodingLayerDecoder(pf, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/ports.go b/vendor/github.com/google/gopacket/layers/ports.go new file mode 100644 index 00000000..1e3f42ef --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ports.go @@ -0,0 +1,156 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "fmt" + "strconv" + + "github.com/google/gopacket" +) + +// TCPPort is a port in a TCP layer. +type TCPPort uint16 + +// UDPPort is a port in a UDP layer. +type UDPPort uint16 + +// RUDPPort is a port in a RUDP layer. +type RUDPPort uint8 + +// SCTPPort is a port in a SCTP layer. +type SCTPPort uint16 + +// UDPLitePort is a port in a UDPLite layer. +type UDPLitePort uint16 + +// RUDPPortNames contains the string names for all RUDP ports. +var RUDPPortNames = map[RUDPPort]string{} + +// UDPLitePortNames contains the string names for all UDPLite ports. +var UDPLitePortNames = map[UDPLitePort]string{} + +// {TCP,UDP,SCTP}PortNames can be found in iana_ports.go + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// TCPPortNames. +func (a TCPPort) String() string { + if name, ok := TCPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// LayerType returns a LayerType that would be able to decode the +// application payload. It uses some well-known ports such as 53 for +// DNS. +// +// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers. +func (a TCPPort) LayerType() gopacket.LayerType { + lt := tcpPortLayerType[uint16(a)] + if lt != 0 { + return lt + } + return gopacket.LayerTypePayload +} + +var tcpPortLayerType = [65536]gopacket.LayerType{ + 53: LayerTypeDNS, + 443: LayerTypeTLS, // https + 502: LayerTypeModbusTCP, // modbustcp + 636: LayerTypeTLS, // ldaps + 989: LayerTypeTLS, // ftps-data + 990: LayerTypeTLS, // ftps + 992: LayerTypeTLS, // telnets + 993: LayerTypeTLS, // imaps + 994: LayerTypeTLS, // ircs + 995: LayerTypeTLS, // pop3s + 5061: LayerTypeTLS, // ips +} + +// RegisterTCPPortLayerType creates a new mapping between a TCPPort +// and an underlaying LayerType. +func RegisterTCPPortLayerType(port TCPPort, layerType gopacket.LayerType) { + tcpPortLayerType[port] = layerType +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// UDPPortNames. +func (a UDPPort) String() string { + if name, ok := UDPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// LayerType returns a LayerType that would be able to decode the +// application payload. It uses some well-known ports such as 53 for +// DNS. +// +// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers. +func (a UDPPort) LayerType() gopacket.LayerType { + lt := udpPortLayerType[uint16(a)] + if lt != 0 { + return lt + } + return gopacket.LayerTypePayload +} + +var udpPortLayerType = [65536]gopacket.LayerType{ + 53: LayerTypeDNS, + 123: LayerTypeNTP, + 4789: LayerTypeVXLAN, + 67: LayerTypeDHCPv4, + 68: LayerTypeDHCPv4, + 546: LayerTypeDHCPv6, + 547: LayerTypeDHCPv6, + 5060: LayerTypeSIP, + 6343: LayerTypeSFlow, + 6081: LayerTypeGeneve, + 3784: LayerTypeBFD, + 2152: LayerTypeGTPv1U, + 623: LayerTypeRMCP, + 1812: LayerTypeRADIUS, +} + +// RegisterUDPPortLayerType creates a new mapping between a UDPPort +// and an underlaying LayerType. +func RegisterUDPPortLayerType(port UDPPort, layerType gopacket.LayerType) { + udpPortLayerType[port] = layerType +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// RUDPPortNames. +func (a RUDPPort) String() string { + if name, ok := RUDPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// SCTPPortNames. +func (a SCTPPort) String() string { + if name, ok := SCTPPortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} + +// String returns the port as "number(name)" if there's a well-known port name, +// or just "number" if there isn't. Well-known names are stored in +// UDPLitePortNames. +func (a UDPLitePort) String() string { + if name, ok := UDPLitePortNames[a]; ok { + return fmt.Sprintf("%d(%s)", a, name) + } + return strconv.Itoa(int(a)) +} diff --git a/vendor/github.com/google/gopacket/layers/ppp.go b/vendor/github.com/google/gopacket/layers/ppp.go new file mode 100644 index 00000000..e534d698 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/ppp.go @@ -0,0 +1,88 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +// PPP is the layer for PPP encapsulation headers. +type PPP struct { + BaseLayer + PPPType PPPType + HasPPTPHeader bool +} + +// PPPEndpoint is a singleton endpoint for PPP. Since there is no actual +// addressing for the two ends of a PPP connection, we use a singleton value +// named 'point' for each endpoint. +var PPPEndpoint = gopacket.NewEndpoint(EndpointPPP, nil) + +// PPPFlow is a singleton flow for PPP. Since there is no actual addressing for +// the two ends of a PPP connection, we use a singleton value to represent the +// flow for all PPP connections. +var PPPFlow = gopacket.NewFlow(EndpointPPP, nil, nil) + +// LayerType returns LayerTypePPP +func (p *PPP) LayerType() gopacket.LayerType { return LayerTypePPP } + +// LinkFlow returns PPPFlow. +func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow } + +func decodePPP(data []byte, p gopacket.PacketBuilder) error { + ppp := &PPP{} + offset := 0 + if data[0] == 0xff && data[1] == 0x03 { + offset = 2 + ppp.HasPPTPHeader = true + } + if data[offset]&0x1 == 0 { + if data[offset+1]&0x1 == 0 { + return errors.New("PPP has invalid type") + } + ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[offset : offset+2])) + ppp.Contents = data[offset : offset+2] + ppp.Payload = data[offset+2:] + } else { + ppp.PPPType = PPPType(data[offset]) + ppp.Contents = data[offset : offset+1] + ppp.Payload = data[offset+1:] + } + p.AddLayer(ppp) + p.SetLinkLayer(ppp) + return p.NextDecoder(ppp.PPPType) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + if p.PPPType&0x100 == 0 { + bytes, err := b.PrependBytes(2) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(p.PPPType)) + } else { + bytes, err := b.PrependBytes(1) + if err != nil { + return err + } + bytes[0] = uint8(p.PPPType) + } + if p.HasPPTPHeader { + bytes, err := b.PrependBytes(2) + if err != nil { + return err + } + bytes[0] = 0xff + bytes[1] = 0x03 + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/pppoe.go b/vendor/github.com/google/gopacket/layers/pppoe.go new file mode 100644 index 00000000..14cd63a1 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/pppoe.go @@ -0,0 +1,60 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// PPPoE is the layer for PPPoE encapsulation headers. +type PPPoE struct { + BaseLayer + Version uint8 + Type uint8 + Code PPPoECode + SessionId uint16 + Length uint16 +} + +// LayerType returns gopacket.LayerTypePPPoE. +func (p *PPPoE) LayerType() gopacket.LayerType { + return LayerTypePPPoE +} + +// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516). +func decodePPPoE(data []byte, p gopacket.PacketBuilder) error { + pppoe := &PPPoE{ + Version: data[0] >> 4, + Type: data[0] & 0x0F, + Code: PPPoECode(data[1]), + SessionId: binary.BigEndian.Uint16(data[2:4]), + Length: binary.BigEndian.Uint16(data[4:6]), + } + pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]} + p.AddLayer(pppoe) + return p.NextDecoder(pppoe.Code) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + payload := b.Bytes() + bytes, err := b.PrependBytes(6) + if err != nil { + return err + } + bytes[0] = (p.Version << 4) | p.Type + bytes[1] = byte(p.Code) + binary.BigEndian.PutUint16(bytes[2:], p.SessionId) + if opts.FixLengths { + p.Length = uint16(len(payload)) + } + binary.BigEndian.PutUint16(bytes[4:], p.Length) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/prism.go b/vendor/github.com/google/gopacket/layers/prism.go new file mode 100644 index 00000000..e1711e7f --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/prism.go @@ -0,0 +1,146 @@ +// Copyright 2015 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// http://www.tcpdump.org/linktypes/LINKTYPE_IEEE802_11_PRISM.html + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +func decodePrismValue(data []byte, pv *PrismValue) { + pv.DID = PrismDID(binary.LittleEndian.Uint32(data[0:4])) + pv.Status = binary.LittleEndian.Uint16(data[4:6]) + pv.Length = binary.LittleEndian.Uint16(data[6:8]) + pv.Data = data[8 : 8+pv.Length] +} + +type PrismDID uint32 + +const ( + PrismDIDType1HostTime PrismDID = 0x10044 + PrismDIDType2HostTime PrismDID = 0x01041 + PrismDIDType1MACTime PrismDID = 0x20044 + PrismDIDType2MACTime PrismDID = 0x02041 + PrismDIDType1Channel PrismDID = 0x30044 + PrismDIDType2Channel PrismDID = 0x03041 + PrismDIDType1RSSI PrismDID = 0x40044 + PrismDIDType2RSSI PrismDID = 0x04041 + PrismDIDType1SignalQuality PrismDID = 0x50044 + PrismDIDType2SignalQuality PrismDID = 0x05041 + PrismDIDType1Signal PrismDID = 0x60044 + PrismDIDType2Signal PrismDID = 0x06041 + PrismDIDType1Noise PrismDID = 0x70044 + PrismDIDType2Noise PrismDID = 0x07041 + PrismDIDType1Rate PrismDID = 0x80044 + PrismDIDType2Rate PrismDID = 0x08041 + PrismDIDType1TransmittedFrameIndicator PrismDID = 0x90044 + PrismDIDType2TransmittedFrameIndicator PrismDID = 0x09041 + PrismDIDType1FrameLength PrismDID = 0xA0044 + PrismDIDType2FrameLength PrismDID = 0x0A041 +) + +const ( + PrismType1MessageCode uint16 = 0x00000044 + PrismType2MessageCode uint16 = 0x00000041 +) + +func (p PrismDID) String() string { + dids := map[PrismDID]string{ + PrismDIDType1HostTime: "Host Time", + PrismDIDType2HostTime: "Host Time", + PrismDIDType1MACTime: "MAC Time", + PrismDIDType2MACTime: "MAC Time", + PrismDIDType1Channel: "Channel", + PrismDIDType2Channel: "Channel", + PrismDIDType1RSSI: "RSSI", + PrismDIDType2RSSI: "RSSI", + PrismDIDType1SignalQuality: "Signal Quality", + PrismDIDType2SignalQuality: "Signal Quality", + PrismDIDType1Signal: "Signal", + PrismDIDType2Signal: "Signal", + PrismDIDType1Noise: "Noise", + PrismDIDType2Noise: "Noise", + PrismDIDType1Rate: "Rate", + PrismDIDType2Rate: "Rate", + PrismDIDType1TransmittedFrameIndicator: "Transmitted Frame Indicator", + PrismDIDType2TransmittedFrameIndicator: "Transmitted Frame Indicator", + PrismDIDType1FrameLength: "Frame Length", + PrismDIDType2FrameLength: "Frame Length", + } + + if str, ok := dids[p]; ok { + return str + } + + return "Unknown DID" +} + +type PrismValue struct { + DID PrismDID + Status uint16 + Length uint16 + Data []byte +} + +func (pv *PrismValue) IsSupplied() bool { + return pv.Status == 1 +} + +var ErrPrismExpectedMoreData = errors.New("Expected more data.") +var ErrPrismInvalidCode = errors.New("Invalid header code.") + +func decodePrismHeader(data []byte, p gopacket.PacketBuilder) error { + d := &PrismHeader{} + return decodingLayerDecoder(d, data, p) +} + +type PrismHeader struct { + BaseLayer + Code uint16 + Length uint16 + DeviceName string + Values []PrismValue +} + +func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader } + +func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Code = binary.LittleEndian.Uint16(data[0:4]) + m.Length = binary.LittleEndian.Uint16(data[4:8]) + m.DeviceName = string(data[8:24]) + m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]} + + switch m.Code { + case PrismType1MessageCode: + fallthrough + case PrismType2MessageCode: + // valid message code + default: + return ErrPrismInvalidCode + } + + offset := uint16(24) + + m.Values = make([]PrismValue, (m.Length-offset)/12) + for i := 0; i < len(m.Values); i++ { + decodePrismValue(data[offset:offset+12], &m.Values[i]) + offset += 12 + } + + if offset != m.Length { + return ErrPrismExpectedMoreData + } + + return nil +} + +func (m *PrismHeader) CanDecode() gopacket.LayerClass { return LayerTypePrismHeader } +func (m *PrismHeader) NextLayerType() gopacket.LayerType { return LayerTypeDot11 } diff --git a/vendor/github.com/google/gopacket/layers/radiotap.go b/vendor/github.com/google/gopacket/layers/radiotap.go new file mode 100644 index 00000000..d09559f7 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/radiotap.go @@ -0,0 +1,1076 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "hash/crc32" + "strings" + + "github.com/google/gopacket" +) + +// align calculates the number of bytes needed to align with the width +// on the offset, returning the number of bytes we need to skip to +// align to the offset (width). +func align(offset uint16, width uint16) uint16 { + return ((((offset) + ((width) - 1)) & (^((width) - 1))) - offset) +} + +type RadioTapPresent uint32 + +const ( + RadioTapPresentTSFT RadioTapPresent = 1 << iota + RadioTapPresentFlags + RadioTapPresentRate + RadioTapPresentChannel + RadioTapPresentFHSS + RadioTapPresentDBMAntennaSignal + RadioTapPresentDBMAntennaNoise + RadioTapPresentLockQuality + RadioTapPresentTxAttenuation + RadioTapPresentDBTxAttenuation + RadioTapPresentDBMTxPower + RadioTapPresentAntenna + RadioTapPresentDBAntennaSignal + RadioTapPresentDBAntennaNoise + RadioTapPresentRxFlags + RadioTapPresentTxFlags + RadioTapPresentRtsRetries + RadioTapPresentDataRetries + _ + RadioTapPresentMCS + RadioTapPresentAMPDUStatus + RadioTapPresentVHT + RadioTapPresentEXT RadioTapPresent = 1 << 31 +) + +func (r RadioTapPresent) TSFT() bool { + return r&RadioTapPresentTSFT != 0 +} +func (r RadioTapPresent) Flags() bool { + return r&RadioTapPresentFlags != 0 +} +func (r RadioTapPresent) Rate() bool { + return r&RadioTapPresentRate != 0 +} +func (r RadioTapPresent) Channel() bool { + return r&RadioTapPresentChannel != 0 +} +func (r RadioTapPresent) FHSS() bool { + return r&RadioTapPresentFHSS != 0 +} +func (r RadioTapPresent) DBMAntennaSignal() bool { + return r&RadioTapPresentDBMAntennaSignal != 0 +} +func (r RadioTapPresent) DBMAntennaNoise() bool { + return r&RadioTapPresentDBMAntennaNoise != 0 +} +func (r RadioTapPresent) LockQuality() bool { + return r&RadioTapPresentLockQuality != 0 +} +func (r RadioTapPresent) TxAttenuation() bool { + return r&RadioTapPresentTxAttenuation != 0 +} +func (r RadioTapPresent) DBTxAttenuation() bool { + return r&RadioTapPresentDBTxAttenuation != 0 +} +func (r RadioTapPresent) DBMTxPower() bool { + return r&RadioTapPresentDBMTxPower != 0 +} +func (r RadioTapPresent) Antenna() bool { + return r&RadioTapPresentAntenna != 0 +} +func (r RadioTapPresent) DBAntennaSignal() bool { + return r&RadioTapPresentDBAntennaSignal != 0 +} +func (r RadioTapPresent) DBAntennaNoise() bool { + return r&RadioTapPresentDBAntennaNoise != 0 +} +func (r RadioTapPresent) RxFlags() bool { + return r&RadioTapPresentRxFlags != 0 +} +func (r RadioTapPresent) TxFlags() bool { + return r&RadioTapPresentTxFlags != 0 +} +func (r RadioTapPresent) RtsRetries() bool { + return r&RadioTapPresentRtsRetries != 0 +} +func (r RadioTapPresent) DataRetries() bool { + return r&RadioTapPresentDataRetries != 0 +} +func (r RadioTapPresent) MCS() bool { + return r&RadioTapPresentMCS != 0 +} +func (r RadioTapPresent) AMPDUStatus() bool { + return r&RadioTapPresentAMPDUStatus != 0 +} +func (r RadioTapPresent) VHT() bool { + return r&RadioTapPresentVHT != 0 +} +func (r RadioTapPresent) EXT() bool { + return r&RadioTapPresentEXT != 0 +} + +type RadioTapChannelFlags uint16 + +const ( + RadioTapChannelFlagsTurbo RadioTapChannelFlags = 0x0010 // Turbo channel + RadioTapChannelFlagsCCK RadioTapChannelFlags = 0x0020 // CCK channel + RadioTapChannelFlagsOFDM RadioTapChannelFlags = 0x0040 // OFDM channel + RadioTapChannelFlagsGhz2 RadioTapChannelFlags = 0x0080 // 2 GHz spectrum channel. + RadioTapChannelFlagsGhz5 RadioTapChannelFlags = 0x0100 // 5 GHz spectrum channel + RadioTapChannelFlagsPassive RadioTapChannelFlags = 0x0200 // Only passive scan allowed + RadioTapChannelFlagsDynamic RadioTapChannelFlags = 0x0400 // Dynamic CCK-OFDM channel + RadioTapChannelFlagsGFSK RadioTapChannelFlags = 0x0800 // GFSK channel (FHSS PHY) +) + +func (r RadioTapChannelFlags) Turbo() bool { + return r&RadioTapChannelFlagsTurbo != 0 +} +func (r RadioTapChannelFlags) CCK() bool { + return r&RadioTapChannelFlagsCCK != 0 +} +func (r RadioTapChannelFlags) OFDM() bool { + return r&RadioTapChannelFlagsOFDM != 0 +} +func (r RadioTapChannelFlags) Ghz2() bool { + return r&RadioTapChannelFlagsGhz2 != 0 +} +func (r RadioTapChannelFlags) Ghz5() bool { + return r&RadioTapChannelFlagsGhz5 != 0 +} +func (r RadioTapChannelFlags) Passive() bool { + return r&RadioTapChannelFlagsPassive != 0 +} +func (r RadioTapChannelFlags) Dynamic() bool { + return r&RadioTapChannelFlagsDynamic != 0 +} +func (r RadioTapChannelFlags) GFSK() bool { + return r&RadioTapChannelFlagsGFSK != 0 +} + +// String provides a human readable string for RadioTapChannelFlags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the RadioTapChannelFlags value, not its string. +func (a RadioTapChannelFlags) String() string { + var out bytes.Buffer + if a.Turbo() { + out.WriteString("Turbo,") + } + if a.CCK() { + out.WriteString("CCK,") + } + if a.OFDM() { + out.WriteString("OFDM,") + } + if a.Ghz2() { + out.WriteString("Ghz2,") + } + if a.Ghz5() { + out.WriteString("Ghz5,") + } + if a.Passive() { + out.WriteString("Passive,") + } + if a.Dynamic() { + out.WriteString("Dynamic,") + } + if a.GFSK() { + out.WriteString("GFSK,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type RadioTapFlags uint8 + +const ( + RadioTapFlagsCFP RadioTapFlags = 1 << iota // sent/received during CFP + RadioTapFlagsShortPreamble // sent/received * with short * preamble + RadioTapFlagsWEP // sent/received * with WEP encryption + RadioTapFlagsFrag // sent/received * with fragmentation + RadioTapFlagsFCS // frame includes FCS + RadioTapFlagsDatapad // frame has padding between * 802.11 header and payload * (to 32-bit boundary) + RadioTapFlagsBadFCS // does not pass FCS check + RadioTapFlagsShortGI // HT short GI +) + +func (r RadioTapFlags) CFP() bool { + return r&RadioTapFlagsCFP != 0 +} +func (r RadioTapFlags) ShortPreamble() bool { + return r&RadioTapFlagsShortPreamble != 0 +} +func (r RadioTapFlags) WEP() bool { + return r&RadioTapFlagsWEP != 0 +} +func (r RadioTapFlags) Frag() bool { + return r&RadioTapFlagsFrag != 0 +} +func (r RadioTapFlags) FCS() bool { + return r&RadioTapFlagsFCS != 0 +} +func (r RadioTapFlags) Datapad() bool { + return r&RadioTapFlagsDatapad != 0 +} +func (r RadioTapFlags) BadFCS() bool { + return r&RadioTapFlagsBadFCS != 0 +} +func (r RadioTapFlags) ShortGI() bool { + return r&RadioTapFlagsShortGI != 0 +} + +// String provides a human readable string for RadioTapFlags. +// This string is possibly subject to change over time; if you're storing this +// persistently, you should probably store the RadioTapFlags value, not its string. +func (a RadioTapFlags) String() string { + var out bytes.Buffer + if a.CFP() { + out.WriteString("CFP,") + } + if a.ShortPreamble() { + out.WriteString("SHORT-PREAMBLE,") + } + if a.WEP() { + out.WriteString("WEP,") + } + if a.Frag() { + out.WriteString("FRAG,") + } + if a.FCS() { + out.WriteString("FCS,") + } + if a.Datapad() { + out.WriteString("DATAPAD,") + } + if a.ShortGI() { + out.WriteString("SHORT-GI,") + } + + if length := out.Len(); length > 0 { + return string(out.Bytes()[:length-1]) // strip final comma + } + return "" +} + +type RadioTapRate uint8 + +func (a RadioTapRate) String() string { + return fmt.Sprintf("%v Mb/s", 0.5*float32(a)) +} + +type RadioTapChannelFrequency uint16 + +func (a RadioTapChannelFrequency) String() string { + return fmt.Sprintf("%d MHz", a) +} + +type RadioTapRxFlags uint16 + +const ( + RadioTapRxFlagsBadPlcp RadioTapRxFlags = 0x0002 +) + +func (self RadioTapRxFlags) BadPlcp() bool { + return self&RadioTapRxFlagsBadPlcp != 0 +} + +func (self RadioTapRxFlags) String() string { + if self.BadPlcp() { + return "BADPLCP" + } + return "" +} + +type RadioTapTxFlags uint16 + +const ( + RadioTapTxFlagsFail RadioTapTxFlags = 1 << iota + RadioTapTxFlagsCTS + RadioTapTxFlagsRTS + RadioTapTxFlagsNoACK +) + +func (self RadioTapTxFlags) Fail() bool { return self&RadioTapTxFlagsFail != 0 } +func (self RadioTapTxFlags) CTS() bool { return self&RadioTapTxFlagsCTS != 0 } +func (self RadioTapTxFlags) RTS() bool { return self&RadioTapTxFlagsRTS != 0 } +func (self RadioTapTxFlags) NoACK() bool { return self&RadioTapTxFlagsNoACK != 0 } + +func (self RadioTapTxFlags) String() string { + var tokens []string + if self.Fail() { + tokens = append(tokens, "Fail") + } + if self.CTS() { + tokens = append(tokens, "CTS") + } + if self.RTS() { + tokens = append(tokens, "RTS") + } + if self.NoACK() { + tokens = append(tokens, "NoACK") + } + return strings.Join(tokens, ",") +} + +type RadioTapMCS struct { + Known RadioTapMCSKnown + Flags RadioTapMCSFlags + MCS uint8 +} + +func (self RadioTapMCS) String() string { + var tokens []string + if self.Known.Bandwidth() { + token := "?" + switch self.Flags.Bandwidth() { + case 0: + token = "20" + case 1: + token = "40" + case 2: + token = "40(20L)" + case 3: + token = "40(20U)" + } + tokens = append(tokens, token) + } + if self.Known.MCSIndex() { + tokens = append(tokens, fmt.Sprintf("MCSIndex#%d", self.MCS)) + } + if self.Known.GuardInterval() { + if self.Flags.ShortGI() { + tokens = append(tokens, fmt.Sprintf("shortGI")) + } else { + tokens = append(tokens, fmt.Sprintf("longGI")) + } + } + if self.Known.HTFormat() { + if self.Flags.Greenfield() { + tokens = append(tokens, fmt.Sprintf("HT-greenfield")) + } else { + tokens = append(tokens, fmt.Sprintf("HT-mixed")) + } + } + if self.Known.FECType() { + if self.Flags.FECLDPC() { + tokens = append(tokens, fmt.Sprintf("LDPC")) + } else { + tokens = append(tokens, fmt.Sprintf("BCC")) + } + } + if self.Known.STBC() { + tokens = append(tokens, fmt.Sprintf("STBC#%d", self.Flags.STBC())) + } + if self.Known.NESS() { + num := 0 + if self.Known.NESS1() { + num |= 0x02 + } + if self.Flags.NESS0() { + num |= 0x01 + } + tokens = append(tokens, fmt.Sprintf("num-of-ESS#%d", num)) + } + return strings.Join(tokens, ",") +} + +type RadioTapMCSKnown uint8 + +const ( + RadioTapMCSKnownBandwidth RadioTapMCSKnown = 1 << iota + RadioTapMCSKnownMCSIndex + RadioTapMCSKnownGuardInterval + RadioTapMCSKnownHTFormat + RadioTapMCSKnownFECType + RadioTapMCSKnownSTBC + RadioTapMCSKnownNESS + RadioTapMCSKnownNESS1 +) + +func (self RadioTapMCSKnown) Bandwidth() bool { return self&RadioTapMCSKnownBandwidth != 0 } +func (self RadioTapMCSKnown) MCSIndex() bool { return self&RadioTapMCSKnownMCSIndex != 0 } +func (self RadioTapMCSKnown) GuardInterval() bool { return self&RadioTapMCSKnownGuardInterval != 0 } +func (self RadioTapMCSKnown) HTFormat() bool { return self&RadioTapMCSKnownHTFormat != 0 } +func (self RadioTapMCSKnown) FECType() bool { return self&RadioTapMCSKnownFECType != 0 } +func (self RadioTapMCSKnown) STBC() bool { return self&RadioTapMCSKnownSTBC != 0 } +func (self RadioTapMCSKnown) NESS() bool { return self&RadioTapMCSKnownNESS != 0 } +func (self RadioTapMCSKnown) NESS1() bool { return self&RadioTapMCSKnownNESS1 != 0 } + +type RadioTapMCSFlags uint8 + +const ( + RadioTapMCSFlagsBandwidthMask RadioTapMCSFlags = 0x03 + RadioTapMCSFlagsShortGI = 0x04 + RadioTapMCSFlagsGreenfield = 0x08 + RadioTapMCSFlagsFECLDPC = 0x10 + RadioTapMCSFlagsSTBCMask = 0x60 + RadioTapMCSFlagsNESS0 = 0x80 +) + +func (self RadioTapMCSFlags) Bandwidth() int { + return int(self & RadioTapMCSFlagsBandwidthMask) +} +func (self RadioTapMCSFlags) ShortGI() bool { return self&RadioTapMCSFlagsShortGI != 0 } +func (self RadioTapMCSFlags) Greenfield() bool { return self&RadioTapMCSFlagsGreenfield != 0 } +func (self RadioTapMCSFlags) FECLDPC() bool { return self&RadioTapMCSFlagsFECLDPC != 0 } +func (self RadioTapMCSFlags) STBC() int { + return int(self&RadioTapMCSFlagsSTBCMask) >> 5 +} +func (self RadioTapMCSFlags) NESS0() bool { return self&RadioTapMCSFlagsNESS0 != 0 } + +type RadioTapAMPDUStatus struct { + Reference uint32 + Flags RadioTapAMPDUStatusFlags + CRC uint8 +} + +func (self RadioTapAMPDUStatus) String() string { + tokens := []string{ + fmt.Sprintf("ref#%x", self.Reference), + } + if self.Flags.ReportZerolen() && self.Flags.IsZerolen() { + tokens = append(tokens, fmt.Sprintf("zero-length")) + } + if self.Flags.LastKnown() && self.Flags.IsLast() { + tokens = append(tokens, "last") + } + if self.Flags.DelimCRCErr() { + tokens = append(tokens, "delimiter CRC error") + } + if self.Flags.DelimCRCKnown() { + tokens = append(tokens, fmt.Sprintf("delimiter-CRC=%02x", self.CRC)) + } + return strings.Join(tokens, ",") +} + +type RadioTapAMPDUStatusFlags uint16 + +const ( + RadioTapAMPDUStatusFlagsReportZerolen RadioTapAMPDUStatusFlags = 1 << iota + RadioTapAMPDUIsZerolen + RadioTapAMPDULastKnown + RadioTapAMPDUIsLast + RadioTapAMPDUDelimCRCErr + RadioTapAMPDUDelimCRCKnown +) + +func (self RadioTapAMPDUStatusFlags) ReportZerolen() bool { + return self&RadioTapAMPDUStatusFlagsReportZerolen != 0 +} +func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 } +func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 } +func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 } +func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 } +func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool { + return self&RadioTapAMPDUDelimCRCKnown != 0 +} + +type RadioTapVHT struct { + Known RadioTapVHTKnown + Flags RadioTapVHTFlags + Bandwidth uint8 + MCSNSS [4]RadioTapVHTMCSNSS + Coding uint8 + GroupId uint8 + PartialAID uint16 +} + +func (self RadioTapVHT) String() string { + var tokens []string + if self.Known.STBC() { + if self.Flags.STBC() { + tokens = append(tokens, "STBC") + } else { + tokens = append(tokens, "no STBC") + } + } + if self.Known.TXOPPSNotAllowed() { + if self.Flags.TXOPPSNotAllowed() { + tokens = append(tokens, "TXOP doze not allowed") + } else { + tokens = append(tokens, "TXOP doze allowed") + } + } + if self.Known.GI() { + if self.Flags.SGI() { + tokens = append(tokens, "short GI") + } else { + tokens = append(tokens, "long GI") + } + } + if self.Known.SGINSYMDisambiguation() { + if self.Flags.SGINSYMMod() { + tokens = append(tokens, "NSYM mod 10=9") + } else { + tokens = append(tokens, "NSYM mod 10!=9 or no short GI") + } + } + if self.Known.LDPCExtraOFDMSymbol() { + if self.Flags.LDPCExtraOFDMSymbol() { + tokens = append(tokens, "LDPC extra OFDM symbols") + } else { + tokens = append(tokens, "no LDPC extra OFDM symbols") + } + } + if self.Known.Beamformed() { + if self.Flags.Beamformed() { + tokens = append(tokens, "beamformed") + } else { + tokens = append(tokens, "no beamformed") + } + } + if self.Known.Bandwidth() { + token := "?" + switch self.Bandwidth & 0x1f { + case 0: + token = "20" + case 1: + token = "40" + case 2: + token = "40(20L)" + case 3: + token = "40(20U)" + case 4: + token = "80" + case 5: + token = "80(40L)" + case 6: + token = "80(40U)" + case 7: + token = "80(20LL)" + case 8: + token = "80(20LU)" + case 9: + token = "80(20UL)" + case 10: + token = "80(20UU)" + case 11: + token = "160" + case 12: + token = "160(80L)" + case 13: + token = "160(80U)" + case 14: + token = "160(40LL)" + case 15: + token = "160(40LU)" + case 16: + token = "160(40UL)" + case 17: + token = "160(40UU)" + case 18: + token = "160(20LLL)" + case 19: + token = "160(20LLU)" + case 20: + token = "160(20LUL)" + case 21: + token = "160(20LUU)" + case 22: + token = "160(20ULL)" + case 23: + token = "160(20ULU)" + case 24: + token = "160(20UUL)" + case 25: + token = "160(20UUU)" + } + tokens = append(tokens, token) + } + for i, MCSNSS := range self.MCSNSS { + if MCSNSS.Present() { + fec := "?" + switch self.Coding & (1 << uint8(i)) { + case 0: + fec = "BCC" + case 1: + fec = "LDPC" + } + tokens = append(tokens, fmt.Sprintf("user%d(%s,%s)", i, MCSNSS.String(), fec)) + } + } + if self.Known.GroupId() { + tokens = append(tokens, + fmt.Sprintf("group=%d", self.GroupId)) + } + if self.Known.PartialAID() { + tokens = append(tokens, + fmt.Sprintf("partial-AID=%d", self.PartialAID)) + } + return strings.Join(tokens, ",") +} + +type RadioTapVHTKnown uint16 + +const ( + RadioTapVHTKnownSTBC RadioTapVHTKnown = 1 << iota + RadioTapVHTKnownTXOPPSNotAllowed + RadioTapVHTKnownGI + RadioTapVHTKnownSGINSYMDisambiguation + RadioTapVHTKnownLDPCExtraOFDMSymbol + RadioTapVHTKnownBeamformed + RadioTapVHTKnownBandwidth + RadioTapVHTKnownGroupId + RadioTapVHTKnownPartialAID +) + +func (self RadioTapVHTKnown) STBC() bool { return self&RadioTapVHTKnownSTBC != 0 } +func (self RadioTapVHTKnown) TXOPPSNotAllowed() bool { + return self&RadioTapVHTKnownTXOPPSNotAllowed != 0 +} +func (self RadioTapVHTKnown) GI() bool { return self&RadioTapVHTKnownGI != 0 } +func (self RadioTapVHTKnown) SGINSYMDisambiguation() bool { + return self&RadioTapVHTKnownSGINSYMDisambiguation != 0 +} +func (self RadioTapVHTKnown) LDPCExtraOFDMSymbol() bool { + return self&RadioTapVHTKnownLDPCExtraOFDMSymbol != 0 +} +func (self RadioTapVHTKnown) Beamformed() bool { return self&RadioTapVHTKnownBeamformed != 0 } +func (self RadioTapVHTKnown) Bandwidth() bool { return self&RadioTapVHTKnownBandwidth != 0 } +func (self RadioTapVHTKnown) GroupId() bool { return self&RadioTapVHTKnownGroupId != 0 } +func (self RadioTapVHTKnown) PartialAID() bool { return self&RadioTapVHTKnownPartialAID != 0 } + +type RadioTapVHTFlags uint8 + +const ( + RadioTapVHTFlagsSTBC RadioTapVHTFlags = 1 << iota + RadioTapVHTFlagsTXOPPSNotAllowed + RadioTapVHTFlagsSGI + RadioTapVHTFlagsSGINSYMMod + RadioTapVHTFlagsLDPCExtraOFDMSymbol + RadioTapVHTFlagsBeamformed +) + +func (self RadioTapVHTFlags) STBC() bool { return self&RadioTapVHTFlagsSTBC != 0 } +func (self RadioTapVHTFlags) TXOPPSNotAllowed() bool { + return self&RadioTapVHTFlagsTXOPPSNotAllowed != 0 +} +func (self RadioTapVHTFlags) SGI() bool { return self&RadioTapVHTFlagsSGI != 0 } +func (self RadioTapVHTFlags) SGINSYMMod() bool { return self&RadioTapVHTFlagsSGINSYMMod != 0 } +func (self RadioTapVHTFlags) LDPCExtraOFDMSymbol() bool { + return self&RadioTapVHTFlagsLDPCExtraOFDMSymbol != 0 +} +func (self RadioTapVHTFlags) Beamformed() bool { return self&RadioTapVHTFlagsBeamformed != 0 } + +type RadioTapVHTMCSNSS uint8 + +func (self RadioTapVHTMCSNSS) Present() bool { + return self&0x0F != 0 +} + +func (self RadioTapVHTMCSNSS) String() string { + return fmt.Sprintf("NSS#%dMCS#%d", uint32(self&0xf), uint32(self>>4)) +} + +func decodeRadioTap(data []byte, p gopacket.PacketBuilder) error { + d := &RadioTap{} + // TODO: Should we set LinkLayer here? And implement LinkFlow + return decodingLayerDecoder(d, data, p) +} + +type RadioTap struct { + BaseLayer + + // Version 0. Only increases for drastic changes, introduction of compatible new fields does not count. + Version uint8 + // Length of the whole header in bytes, including it_version, it_pad, it_len, and data fields. + Length uint16 + // Present is a bitmap telling which fields are present. Set bit 31 (0x80000000) to extend the bitmap by another 32 bits. Additional extensions are made by setting bit 31. + Present RadioTapPresent + // TSFT: value in microseconds of the MAC's 64-bit 802.11 Time Synchronization Function timer when the first bit of the MPDU arrived at the MAC. For received frames, only. + TSFT uint64 + Flags RadioTapFlags + // Rate Tx/Rx data rate + Rate RadioTapRate + // ChannelFrequency Tx/Rx frequency in MHz, followed by flags + ChannelFrequency RadioTapChannelFrequency + ChannelFlags RadioTapChannelFlags + // FHSS For frequency-hopping radios, the hop set (first byte) and pattern (second byte). + FHSS uint16 + // DBMAntennaSignal RF signal power at the antenna, decibel difference from one milliwatt. + DBMAntennaSignal int8 + // DBMAntennaNoise RF noise power at the antenna, decibel difference from one milliwatt. + DBMAntennaNoise int8 + // LockQuality Quality of Barker code lock. Unitless. Monotonically nondecreasing with "better" lock strength. Called "Signal Quality" in datasheets. + LockQuality uint16 + // TxAttenuation Transmit power expressed as unitless distance from max power set at factory calibration. 0 is max power. Monotonically nondecreasing with lower power levels. + TxAttenuation uint16 + // DBTxAttenuation Transmit power expressed as decibel distance from max power set at factory calibration. 0 is max power. Monotonically nondecreasing with lower power levels. + DBTxAttenuation uint16 + // DBMTxPower Transmit power expressed as dBm (decibels from a 1 milliwatt reference). This is the absolute power level measured at the antenna port. + DBMTxPower int8 + // Antenna Unitless indication of the Rx/Tx antenna for this packet. The first antenna is antenna 0. + Antenna uint8 + // DBAntennaSignal RF signal power at the antenna, decibel difference from an arbitrary, fixed reference. + DBAntennaSignal uint8 + // DBAntennaNoise RF noise power at the antenna, decibel difference from an arbitrary, fixed reference point. + DBAntennaNoise uint8 + // + RxFlags RadioTapRxFlags + TxFlags RadioTapTxFlags + RtsRetries uint8 + DataRetries uint8 + MCS RadioTapMCS + AMPDUStatus RadioTapAMPDUStatus + VHT RadioTapVHT +} + +func (m *RadioTap) LayerType() gopacket.LayerType { return LayerTypeRadioTap } + +func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return errors.New("RadioTap too small") + } + m.Version = uint8(data[0]) + m.Length = binary.LittleEndian.Uint16(data[2:4]) + m.Present = RadioTapPresent(binary.LittleEndian.Uint32(data[4:8])) + + offset := uint16(4) + + for (binary.LittleEndian.Uint32(data[offset:offset+4]) & 0x80000000) != 0 { + // This parser only handles standard radiotap namespace, + // and expects all fields are packed in the first it_present. + // Extended bitmap will be just ignored. + offset += 4 + } + offset += 4 // skip the bitmap + + if m.Present.TSFT() { + offset += align(offset, 8) + m.TSFT = binary.LittleEndian.Uint64(data[offset : offset+8]) + offset += 8 + } + if m.Present.Flags() { + m.Flags = RadioTapFlags(data[offset]) + offset++ + } + if m.Present.Rate() { + m.Rate = RadioTapRate(data[offset]) + offset++ + } + if m.Present.Channel() { + offset += align(offset, 2) + m.ChannelFrequency = RadioTapChannelFrequency(binary.LittleEndian.Uint16(data[offset : offset+2])) + offset += 2 + m.ChannelFlags = RadioTapChannelFlags(binary.LittleEndian.Uint16(data[offset : offset+2])) + offset += 2 + } + if m.Present.FHSS() { + m.FHSS = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBMAntennaSignal() { + m.DBMAntennaSignal = int8(data[offset]) + offset++ + } + if m.Present.DBMAntennaNoise() { + m.DBMAntennaNoise = int8(data[offset]) + offset++ + } + if m.Present.LockQuality() { + offset += align(offset, 2) + m.LockQuality = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.TxAttenuation() { + offset += align(offset, 2) + m.TxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBTxAttenuation() { + offset += align(offset, 2) + m.DBTxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2]) + offset += 2 + } + if m.Present.DBMTxPower() { + m.DBMTxPower = int8(data[offset]) + offset++ + } + if m.Present.Antenna() { + m.Antenna = uint8(data[offset]) + offset++ + } + if m.Present.DBAntennaSignal() { + m.DBAntennaSignal = uint8(data[offset]) + offset++ + } + if m.Present.DBAntennaNoise() { + m.DBAntennaNoise = uint8(data[offset]) + offset++ + } + if m.Present.RxFlags() { + offset += align(offset, 2) + m.RxFlags = RadioTapRxFlags(binary.LittleEndian.Uint16(data[offset:])) + offset += 2 + } + if m.Present.TxFlags() { + offset += align(offset, 2) + m.TxFlags = RadioTapTxFlags(binary.LittleEndian.Uint16(data[offset:])) + offset += 2 + } + if m.Present.RtsRetries() { + m.RtsRetries = uint8(data[offset]) + offset++ + } + if m.Present.DataRetries() { + m.DataRetries = uint8(data[offset]) + offset++ + } + if m.Present.MCS() { + m.MCS = RadioTapMCS{ + RadioTapMCSKnown(data[offset]), + RadioTapMCSFlags(data[offset+1]), + uint8(data[offset+2]), + } + offset += 3 + } + if m.Present.AMPDUStatus() { + offset += align(offset, 4) + m.AMPDUStatus = RadioTapAMPDUStatus{ + Reference: binary.LittleEndian.Uint32(data[offset:]), + Flags: RadioTapAMPDUStatusFlags(binary.LittleEndian.Uint16(data[offset+4:])), + CRC: uint8(data[offset+6]), + } + offset += 8 + } + if m.Present.VHT() { + offset += align(offset, 2) + m.VHT = RadioTapVHT{ + Known: RadioTapVHTKnown(binary.LittleEndian.Uint16(data[offset:])), + Flags: RadioTapVHTFlags(data[offset+2]), + Bandwidth: uint8(data[offset+3]), + MCSNSS: [4]RadioTapVHTMCSNSS{ + RadioTapVHTMCSNSS(data[offset+4]), + RadioTapVHTMCSNSS(data[offset+5]), + RadioTapVHTMCSNSS(data[offset+6]), + RadioTapVHTMCSNSS(data[offset+7]), + }, + Coding: uint8(data[offset+8]), + GroupId: uint8(data[offset+9]), + PartialAID: binary.LittleEndian.Uint16(data[offset+10:]), + } + offset += 12 + } + + payload := data[m.Length:] + + // Remove non standard padding used by some Wi-Fi drivers + if m.Flags.Datapad() && + payload[0]&0xC == 0x8 { //&& // Data frame + headlen := 24 + if payload[0]&0x8C == 0x88 { // QoS + headlen += 2 + } + if payload[1]&0x3 == 0x3 { // 4 addresses + headlen += 2 + } + if headlen%4 == 2 { + payload = append(payload[:headlen], payload[headlen+2:len(payload)]...) + } + } + + if !m.Flags.FCS() { + // Dot11.DecodeFromBytes() expects FCS present and performs a hard chop on the checksum + // If a user is handing in subslices or packets from a buffered stream, the capacity of the slice + // may extend beyond the len, rather than expecting callers to enforce cap==len on every packet + // we take the hit in this one case and do a reallocation. If the user DOES enforce cap==len + // then the reallocation will happen anyway on the append. This is requried because the append + // write to the memory directly after the payload if there is sufficient capacity, which callers + // may not expect. + reallocPayload := make([]byte, len(payload)+4) + copy(reallocPayload[0:len(payload)], payload) + h := crc32.NewIEEE() + h.Write(payload) + binary.LittleEndian.PutUint32(reallocPayload[len(payload):], h.Sum32()) + payload = reallocPayload + } + m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: payload} + + return nil +} + +func (m RadioTap) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + buf := make([]byte, 1024) + + buf[0] = m.Version + buf[1] = 0 + + binary.LittleEndian.PutUint32(buf[4:8], uint32(m.Present)) + + offset := uint16(4) + + for (binary.LittleEndian.Uint32(buf[offset:offset+4]) & 0x80000000) != 0 { + offset += 4 + } + + offset += 4 + + if m.Present.TSFT() { + offset += align(offset, 8) + binary.LittleEndian.PutUint64(buf[offset:offset+8], m.TSFT) + offset += 8 + } + + if m.Present.Flags() { + buf[offset] = uint8(m.Flags) + offset++ + } + + if m.Present.Rate() { + buf[offset] = uint8(m.Rate) + offset++ + } + + if m.Present.Channel() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFrequency)) + offset += 2 + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFlags)) + offset += 2 + } + + if m.Present.FHSS() { + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.FHSS) + offset += 2 + } + + if m.Present.DBMAntennaSignal() { + buf[offset] = byte(m.DBMAntennaSignal) + offset++ + } + + if m.Present.DBMAntennaNoise() { + buf[offset] = byte(m.DBMAntennaNoise) + offset++ + } + + if m.Present.LockQuality() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.LockQuality) + offset += 2 + } + + if m.Present.TxAttenuation() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.TxAttenuation) + offset += 2 + } + + if m.Present.DBTxAttenuation() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], m.DBTxAttenuation) + offset += 2 + } + + if m.Present.DBMTxPower() { + buf[offset] = byte(m.DBMTxPower) + offset++ + } + + if m.Present.Antenna() { + buf[offset] = uint8(m.Antenna) + offset++ + } + + if m.Present.DBAntennaSignal() { + buf[offset] = uint8(m.DBAntennaSignal) + offset++ + } + + if m.Present.DBAntennaNoise() { + buf[offset] = uint8(m.DBAntennaNoise) + offset++ + } + + if m.Present.RxFlags() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.RxFlags)) + offset += 2 + } + + if m.Present.TxFlags() { + offset += align(offset, 2) + binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.TxFlags)) + offset += 2 + } + + if m.Present.RtsRetries() { + buf[offset] = m.RtsRetries + offset++ + } + + if m.Present.DataRetries() { + buf[offset] = m.DataRetries + offset++ + } + + if m.Present.MCS() { + buf[offset] = uint8(m.MCS.Known) + buf[offset+1] = uint8(m.MCS.Flags) + buf[offset+2] = uint8(m.MCS.MCS) + + offset += 3 + } + + if m.Present.AMPDUStatus() { + offset += align(offset, 4) + + binary.LittleEndian.PutUint32(buf[offset:offset+4], m.AMPDUStatus.Reference) + binary.LittleEndian.PutUint16(buf[offset+4:offset+6], uint16(m.AMPDUStatus.Flags)) + + buf[offset+6] = m.AMPDUStatus.CRC + + offset += 8 + } + + if m.Present.VHT() { + offset += align(offset, 2) + + binary.LittleEndian.PutUint16(buf[offset:], uint16(m.VHT.Known)) + + buf[offset+2] = uint8(m.VHT.Flags) + buf[offset+3] = uint8(m.VHT.Bandwidth) + buf[offset+4] = uint8(m.VHT.MCSNSS[0]) + buf[offset+5] = uint8(m.VHT.MCSNSS[1]) + buf[offset+6] = uint8(m.VHT.MCSNSS[2]) + buf[offset+7] = uint8(m.VHT.MCSNSS[3]) + buf[offset+8] = uint8(m.VHT.Coding) + buf[offset+9] = uint8(m.VHT.GroupId) + + binary.LittleEndian.PutUint16(buf[offset+10:offset+12], m.VHT.PartialAID) + + offset += 12 + } + + packetBuf, err := b.PrependBytes(int(offset)) + + if err != nil { + return err + } + + if opts.FixLengths { + m.Length = offset + } + + binary.LittleEndian.PutUint16(buf[2:4], m.Length) + + copy(packetBuf, buf) + + return nil +} + +func (m *RadioTap) CanDecode() gopacket.LayerClass { return LayerTypeRadioTap } +func (m *RadioTap) NextLayerType() gopacket.LayerType { return LayerTypeDot11 } diff --git a/vendor/github.com/google/gopacket/layers/radius.go b/vendor/github.com/google/gopacket/layers/radius.go new file mode 100644 index 00000000..c43ea294 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/radius.go @@ -0,0 +1,560 @@ +// Copyright 2020 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file in the root of the source tree. + +package layers + +import ( + "encoding/binary" + "fmt" + + "github.com/google/gopacket" +) + +const ( + // RFC 2865 3. Packet Format + // `The minimum length is 20 and maximum length is 4096.` + radiusMinimumRecordSizeInBytes int = 20 + radiusMaximumRecordSizeInBytes int = 4096 + + // RFC 2865 5. Attributes + // `The Length field is one octet, and indicates the length of this Attribute including the Type, Length and Value fields.` + // `The Value field is zero or more octets and contains information specific to the Attribute.` + radiusAttributesMinimumRecordSizeInBytes int = 2 +) + +// RADIUS represents a Remote Authentication Dial In User Service layer. +type RADIUS struct { + BaseLayer + + Code RADIUSCode + Identifier RADIUSIdentifier + Length RADIUSLength + Authenticator RADIUSAuthenticator + Attributes []RADIUSAttribute +} + +// RADIUSCode represents packet type. +type RADIUSCode uint8 + +// constants that define RADIUSCode. +const ( + RADIUSCodeAccessRequest RADIUSCode = 1 // RFC2865 3. Packet Format + RADIUSCodeAccessAccept RADIUSCode = 2 // RFC2865 3. Packet Format + RADIUSCodeAccessReject RADIUSCode = 3 // RFC2865 3. Packet Format + RADIUSCodeAccountingRequest RADIUSCode = 4 // RFC2865 3. Packet Format + RADIUSCodeAccountingResponse RADIUSCode = 5 // RFC2865 3. Packet Format + RADIUSCodeAccessChallenge RADIUSCode = 11 // RFC2865 3. Packet Format + RADIUSCodeStatusServer RADIUSCode = 12 // RFC2865 3. Packet Format (experimental) + RADIUSCodeStatusClient RADIUSCode = 13 // RFC2865 3. Packet Format (experimental) + RADIUSCodeReserved RADIUSCode = 255 // RFC2865 3. Packet Format +) + +// String returns a string version of a RADIUSCode. +func (t RADIUSCode) String() (s string) { + switch t { + case RADIUSCodeAccessRequest: + s = "Access-Request" + case RADIUSCodeAccessAccept: + s = "Access-Accept" + case RADIUSCodeAccessReject: + s = "Access-Reject" + case RADIUSCodeAccountingRequest: + s = "Accounting-Request" + case RADIUSCodeAccountingResponse: + s = "Accounting-Response" + case RADIUSCodeAccessChallenge: + s = "Access-Challenge" + case RADIUSCodeStatusServer: + s = "Status-Server" + case RADIUSCodeStatusClient: + s = "Status-Client" + case RADIUSCodeReserved: + s = "Reserved" + default: + s = fmt.Sprintf("Unknown(%d)", t) + } + return +} + +// RADIUSIdentifier represents packet identifier. +type RADIUSIdentifier uint8 + +// RADIUSLength represents packet length. +type RADIUSLength uint16 + +// RADIUSAuthenticator represents authenticator. +type RADIUSAuthenticator [16]byte + +// RADIUSAttribute represents attributes. +type RADIUSAttribute struct { + Type RADIUSAttributeType + Length RADIUSAttributeLength + Value RADIUSAttributeValue +} + +// RADIUSAttributeType represents attribute type. +type RADIUSAttributeType uint8 + +// constants that define RADIUSAttributeType. +const ( + RADIUSAttributeTypeUserName RADIUSAttributeType = 1 // RFC2865 5.1. User-Name + RADIUSAttributeTypeUserPassword RADIUSAttributeType = 2 // RFC2865 5.2. User-Password + RADIUSAttributeTypeCHAPPassword RADIUSAttributeType = 3 // RFC2865 5.3. CHAP-Password + RADIUSAttributeTypeNASIPAddress RADIUSAttributeType = 4 // RFC2865 5.4. NAS-IP-Address + RADIUSAttributeTypeNASPort RADIUSAttributeType = 5 // RFC2865 5.5. NAS-Port + RADIUSAttributeTypeServiceType RADIUSAttributeType = 6 // RFC2865 5.6. Service-Type + RADIUSAttributeTypeFramedProtocol RADIUSAttributeType = 7 // RFC2865 5.7. Framed-Protocol + RADIUSAttributeTypeFramedIPAddress RADIUSAttributeType = 8 // RFC2865 5.8. Framed-IP-Address + RADIUSAttributeTypeFramedIPNetmask RADIUSAttributeType = 9 // RFC2865 5.9. Framed-IP-Netmask + RADIUSAttributeTypeFramedRouting RADIUSAttributeType = 10 // RFC2865 5.10. Framed-Routing + RADIUSAttributeTypeFilterId RADIUSAttributeType = 11 // RFC2865 5.11. Filter-Id + RADIUSAttributeTypeFramedMTU RADIUSAttributeType = 12 // RFC2865 5.12. Framed-MTU + RADIUSAttributeTypeFramedCompression RADIUSAttributeType = 13 // RFC2865 5.13. Framed-Compression + RADIUSAttributeTypeLoginIPHost RADIUSAttributeType = 14 // RFC2865 5.14. Login-IP-Host + RADIUSAttributeTypeLoginService RADIUSAttributeType = 15 // RFC2865 5.15. Login-Service + RADIUSAttributeTypeLoginTCPPort RADIUSAttributeType = 16 // RFC2865 5.16. Login-TCP-Port + RADIUSAttributeTypeReplyMessage RADIUSAttributeType = 18 // RFC2865 5.18. Reply-Message + RADIUSAttributeTypeCallbackNumber RADIUSAttributeType = 19 // RFC2865 5.19. Callback-Number + RADIUSAttributeTypeCallbackId RADIUSAttributeType = 20 // RFC2865 5.20. Callback-Id + RADIUSAttributeTypeFramedRoute RADIUSAttributeType = 22 // RFC2865 5.22. Framed-Route + RADIUSAttributeTypeFramedIPXNetwork RADIUSAttributeType = 23 // RFC2865 5.23. Framed-IPX-Network + RADIUSAttributeTypeState RADIUSAttributeType = 24 // RFC2865 5.24. State + RADIUSAttributeTypeClass RADIUSAttributeType = 25 // RFC2865 5.25. Class + RADIUSAttributeTypeVendorSpecific RADIUSAttributeType = 26 // RFC2865 5.26. Vendor-Specific + RADIUSAttributeTypeSessionTimeout RADIUSAttributeType = 27 // RFC2865 5.27. Session-Timeout + RADIUSAttributeTypeIdleTimeout RADIUSAttributeType = 28 // RFC2865 5.28. Idle-Timeout + RADIUSAttributeTypeTerminationAction RADIUSAttributeType = 29 // RFC2865 5.29. Termination-Action + RADIUSAttributeTypeCalledStationId RADIUSAttributeType = 30 // RFC2865 5.30. Called-Station-Id + RADIUSAttributeTypeCallingStationId RADIUSAttributeType = 31 // RFC2865 5.31. Calling-Station-Id + RADIUSAttributeTypeNASIdentifier RADIUSAttributeType = 32 // RFC2865 5.32. NAS-Identifier + RADIUSAttributeTypeProxyState RADIUSAttributeType = 33 // RFC2865 5.33. Proxy-State + RADIUSAttributeTypeLoginLATService RADIUSAttributeType = 34 // RFC2865 5.34. Login-LAT-Service + RADIUSAttributeTypeLoginLATNode RADIUSAttributeType = 35 // RFC2865 5.35. Login-LAT-Node + RADIUSAttributeTypeLoginLATGroup RADIUSAttributeType = 36 // RFC2865 5.36. Login-LAT-Group + RADIUSAttributeTypeFramedAppleTalkLink RADIUSAttributeType = 37 // RFC2865 5.37. Framed-AppleTalk-Link + RADIUSAttributeTypeFramedAppleTalkNetwork RADIUSAttributeType = 38 // RFC2865 5.38. Framed-AppleTalk-Network + RADIUSAttributeTypeFramedAppleTalkZone RADIUSAttributeType = 39 // RFC2865 5.39. Framed-AppleTalk-Zone + RADIUSAttributeTypeAcctStatusType RADIUSAttributeType = 40 // RFC2866 5.1. Acct-Status-Type + RADIUSAttributeTypeAcctDelayTime RADIUSAttributeType = 41 // RFC2866 5.2. Acct-Delay-Time + RADIUSAttributeTypeAcctInputOctets RADIUSAttributeType = 42 // RFC2866 5.3. Acct-Input-Octets + RADIUSAttributeTypeAcctOutputOctets RADIUSAttributeType = 43 // RFC2866 5.4. Acct-Output-Octets + RADIUSAttributeTypeAcctSessionId RADIUSAttributeType = 44 // RFC2866 5.5. Acct-Session-Id + RADIUSAttributeTypeAcctAuthentic RADIUSAttributeType = 45 // RFC2866 5.6. Acct-Authentic + RADIUSAttributeTypeAcctSessionTime RADIUSAttributeType = 46 // RFC2866 5.7. Acct-Session-Time + RADIUSAttributeTypeAcctInputPackets RADIUSAttributeType = 47 // RFC2866 5.8. Acct-Input-Packets + RADIUSAttributeTypeAcctOutputPackets RADIUSAttributeType = 48 // RFC2866 5.9. Acct-Output-Packets + RADIUSAttributeTypeAcctTerminateCause RADIUSAttributeType = 49 // RFC2866 5.10. Acct-Terminate-Cause + RADIUSAttributeTypeAcctMultiSessionId RADIUSAttributeType = 50 // RFC2866 5.11. Acct-Multi-Session-Id + RADIUSAttributeTypeAcctLinkCount RADIUSAttributeType = 51 // RFC2866 5.12. Acct-Link-Count + RADIUSAttributeTypeAcctInputGigawords RADIUSAttributeType = 52 // RFC2869 5.1. Acct-Input-Gigawords + RADIUSAttributeTypeAcctOutputGigawords RADIUSAttributeType = 53 // RFC2869 5.2. Acct-Output-Gigawords + RADIUSAttributeTypeEventTimestamp RADIUSAttributeType = 55 // RFC2869 5.3. Event-Timestamp + RADIUSAttributeTypeCHAPChallenge RADIUSAttributeType = 60 // RFC2865 5.40. CHAP-Challenge + RADIUSAttributeTypeNASPortType RADIUSAttributeType = 61 // RFC2865 5.41. NAS-Port-Type + RADIUSAttributeTypePortLimit RADIUSAttributeType = 62 // RFC2865 5.42. Port-Limit + RADIUSAttributeTypeLoginLATPort RADIUSAttributeType = 63 // RFC2865 5.43. Login-LAT-Port + RADIUSAttributeTypeTunnelType RADIUSAttributeType = 64 // RFC2868 3.1. Tunnel-Type + RADIUSAttributeTypeTunnelMediumType RADIUSAttributeType = 65 // RFC2868 3.2. Tunnel-Medium-Type + RADIUSAttributeTypeTunnelClientEndpoint RADIUSAttributeType = 66 // RFC2868 3.3. Tunnel-Client-Endpoint + RADIUSAttributeTypeTunnelServerEndpoint RADIUSAttributeType = 67 // RFC2868 3.4. Tunnel-Server-Endpoint + RADIUSAttributeTypeAcctTunnelConnection RADIUSAttributeType = 68 // RFC2867 4.1. Acct-Tunnel-Connection + RADIUSAttributeTypeTunnelPassword RADIUSAttributeType = 69 // RFC2868 3.5. Tunnel-Password + RADIUSAttributeTypeARAPPassword RADIUSAttributeType = 70 // RFC2869 5.4. ARAP-Password + RADIUSAttributeTypeARAPFeatures RADIUSAttributeType = 71 // RFC2869 5.5. ARAP-Features + RADIUSAttributeTypeARAPZoneAccess RADIUSAttributeType = 72 // RFC2869 5.6. ARAP-Zone-Access + RADIUSAttributeTypeARAPSecurity RADIUSAttributeType = 73 // RFC2869 5.7. ARAP-Security + RADIUSAttributeTypeARAPSecurityData RADIUSAttributeType = 74 // RFC2869 5.8. ARAP-Security-Data + RADIUSAttributeTypePasswordRetry RADIUSAttributeType = 75 // RFC2869 5.9. Password-Retry + RADIUSAttributeTypePrompt RADIUSAttributeType = 76 // RFC2869 5.10. Prompt + RADIUSAttributeTypeConnectInfo RADIUSAttributeType = 77 // RFC2869 5.11. Connect-Info + RADIUSAttributeTypeConfigurationToken RADIUSAttributeType = 78 // RFC2869 5.12. Configuration-Token + RADIUSAttributeTypeEAPMessage RADIUSAttributeType = 79 // RFC2869 5.13. EAP-Message + RADIUSAttributeTypeMessageAuthenticator RADIUSAttributeType = 80 // RFC2869 5.14. Message-Authenticator + RADIUSAttributeTypeTunnelPrivateGroupID RADIUSAttributeType = 81 // RFC2868 3.6. Tunnel-Private-Group-ID + RADIUSAttributeTypeTunnelAssignmentID RADIUSAttributeType = 82 // RFC2868 3.7. Tunnel-Assignment-ID + RADIUSAttributeTypeTunnelPreference RADIUSAttributeType = 83 // RFC2868 3.8. Tunnel-Preference + RADIUSAttributeTypeARAPChallengeResponse RADIUSAttributeType = 84 // RFC2869 5.15. ARAP-Challenge-Response + RADIUSAttributeTypeAcctInterimInterval RADIUSAttributeType = 85 // RFC2869 5.16. Acct-Interim-Interval + RADIUSAttributeTypeAcctTunnelPacketsLost RADIUSAttributeType = 86 // RFC2867 4.2. Acct-Tunnel-Packets-Lost + RADIUSAttributeTypeNASPortId RADIUSAttributeType = 87 // RFC2869 5.17. NAS-Port-Id + RADIUSAttributeTypeFramedPool RADIUSAttributeType = 88 // RFC2869 5.18. Framed-Pool + RADIUSAttributeTypeTunnelClientAuthID RADIUSAttributeType = 90 // RFC2868 3.9. Tunnel-Client-Auth-ID + RADIUSAttributeTypeTunnelServerAuthID RADIUSAttributeType = 91 // RFC2868 3.10. Tunnel-Server-Auth-ID +) + +// RADIUSAttributeType represents attribute length. +type RADIUSAttributeLength uint8 + +// RADIUSAttributeType represents attribute value. +type RADIUSAttributeValue []byte + +// String returns a string version of a RADIUSAttributeType. +func (t RADIUSAttributeType) String() (s string) { + switch t { + case RADIUSAttributeTypeUserName: + s = "User-Name" + case RADIUSAttributeTypeUserPassword: + s = "User-Password" + case RADIUSAttributeTypeCHAPPassword: + s = "CHAP-Password" + case RADIUSAttributeTypeNASIPAddress: + s = "NAS-IP-Address" + case RADIUSAttributeTypeNASPort: + s = "NAS-Port" + case RADIUSAttributeTypeServiceType: + s = "Service-Type" + case RADIUSAttributeTypeFramedProtocol: + s = "Framed-Protocol" + case RADIUSAttributeTypeFramedIPAddress: + s = "Framed-IP-Address" + case RADIUSAttributeTypeFramedIPNetmask: + s = "Framed-IP-Netmask" + case RADIUSAttributeTypeFramedRouting: + s = "Framed-Routing" + case RADIUSAttributeTypeFilterId: + s = "Filter-Id" + case RADIUSAttributeTypeFramedMTU: + s = "Framed-MTU" + case RADIUSAttributeTypeFramedCompression: + s = "Framed-Compression" + case RADIUSAttributeTypeLoginIPHost: + s = "Login-IP-Host" + case RADIUSAttributeTypeLoginService: + s = "Login-Service" + case RADIUSAttributeTypeLoginTCPPort: + s = "Login-TCP-Port" + case RADIUSAttributeTypeReplyMessage: + s = "Reply-Message" + case RADIUSAttributeTypeCallbackNumber: + s = "Callback-Number" + case RADIUSAttributeTypeCallbackId: + s = "Callback-Id" + case RADIUSAttributeTypeFramedRoute: + s = "Framed-Route" + case RADIUSAttributeTypeFramedIPXNetwork: + s = "Framed-IPX-Network" + case RADIUSAttributeTypeState: + s = "State" + case RADIUSAttributeTypeClass: + s = "Class" + case RADIUSAttributeTypeVendorSpecific: + s = "Vendor-Specific" + case RADIUSAttributeTypeSessionTimeout: + s = "Session-Timeout" + case RADIUSAttributeTypeIdleTimeout: + s = "Idle-Timeout" + case RADIUSAttributeTypeTerminationAction: + s = "Termination-Action" + case RADIUSAttributeTypeCalledStationId: + s = "Called-Station-Id" + case RADIUSAttributeTypeCallingStationId: + s = "Calling-Station-Id" + case RADIUSAttributeTypeNASIdentifier: + s = "NAS-Identifier" + case RADIUSAttributeTypeProxyState: + s = "Proxy-State" + case RADIUSAttributeTypeLoginLATService: + s = "Login-LAT-Service" + case RADIUSAttributeTypeLoginLATNode: + s = "Login-LAT-Node" + case RADIUSAttributeTypeLoginLATGroup: + s = "Login-LAT-Group" + case RADIUSAttributeTypeFramedAppleTalkLink: + s = "Framed-AppleTalk-Link" + case RADIUSAttributeTypeFramedAppleTalkNetwork: + s = "Framed-AppleTalk-Network" + case RADIUSAttributeTypeFramedAppleTalkZone: + s = "Framed-AppleTalk-Zone" + case RADIUSAttributeTypeAcctStatusType: + s = "Acct-Status-Type" + case RADIUSAttributeTypeAcctDelayTime: + s = "Acct-Delay-Time" + case RADIUSAttributeTypeAcctInputOctets: + s = "Acct-Input-Octets" + case RADIUSAttributeTypeAcctOutputOctets: + s = "Acct-Output-Octets" + case RADIUSAttributeTypeAcctSessionId: + s = "Acct-Session-Id" + case RADIUSAttributeTypeAcctAuthentic: + s = "Acct-Authentic" + case RADIUSAttributeTypeAcctSessionTime: + s = "Acct-Session-Time" + case RADIUSAttributeTypeAcctInputPackets: + s = "Acct-Input-Packets" + case RADIUSAttributeTypeAcctOutputPackets: + s = "Acct-Output-Packets" + case RADIUSAttributeTypeAcctTerminateCause: + s = "Acct-Terminate-Cause" + case RADIUSAttributeTypeAcctMultiSessionId: + s = "Acct-Multi-Session-Id" + case RADIUSAttributeTypeAcctLinkCount: + s = "Acct-Link-Count" + case RADIUSAttributeTypeAcctInputGigawords: + s = "Acct-Input-Gigawords" + case RADIUSAttributeTypeAcctOutputGigawords: + s = "Acct-Output-Gigawords" + case RADIUSAttributeTypeEventTimestamp: + s = "Event-Timestamp" + case RADIUSAttributeTypeCHAPChallenge: + s = "CHAP-Challenge" + case RADIUSAttributeTypeNASPortType: + s = "NAS-Port-Type" + case RADIUSAttributeTypePortLimit: + s = "Port-Limit" + case RADIUSAttributeTypeLoginLATPort: + s = "Login-LAT-Port" + case RADIUSAttributeTypeTunnelType: + s = "Tunnel-Type" + case RADIUSAttributeTypeTunnelMediumType: + s = "Tunnel-Medium-Type" + case RADIUSAttributeTypeTunnelClientEndpoint: + s = "Tunnel-Client-Endpoint" + case RADIUSAttributeTypeTunnelServerEndpoint: + s = "Tunnel-Server-Endpoint" + case RADIUSAttributeTypeAcctTunnelConnection: + s = "Acct-Tunnel-Connection" + case RADIUSAttributeTypeTunnelPassword: + s = "Tunnel-Password" + case RADIUSAttributeTypeARAPPassword: + s = "ARAP-Password" + case RADIUSAttributeTypeARAPFeatures: + s = "ARAP-Features" + case RADIUSAttributeTypeARAPZoneAccess: + s = "ARAP-Zone-Access" + case RADIUSAttributeTypeARAPSecurity: + s = "ARAP-Security" + case RADIUSAttributeTypeARAPSecurityData: + s = "ARAP-Security-Data" + case RADIUSAttributeTypePasswordRetry: + s = "Password-Retry" + case RADIUSAttributeTypePrompt: + s = "Prompt" + case RADIUSAttributeTypeConnectInfo: + s = "Connect-Info" + case RADIUSAttributeTypeConfigurationToken: + s = "Configuration-Token" + case RADIUSAttributeTypeEAPMessage: + s = "EAP-Message" + case RADIUSAttributeTypeMessageAuthenticator: + s = "Message-Authenticator" + case RADIUSAttributeTypeTunnelPrivateGroupID: + s = "Tunnel-Private-Group-ID" + case RADIUSAttributeTypeTunnelAssignmentID: + s = "Tunnel-Assignment-ID" + case RADIUSAttributeTypeTunnelPreference: + s = "Tunnel-Preference" + case RADIUSAttributeTypeARAPChallengeResponse: + s = "ARAP-Challenge-Response" + case RADIUSAttributeTypeAcctInterimInterval: + s = "Acct-Interim-Interval" + case RADIUSAttributeTypeAcctTunnelPacketsLost: + s = "Acct-Tunnel-Packets-Lost" + case RADIUSAttributeTypeNASPortId: + s = "NAS-Port-Id" + case RADIUSAttributeTypeFramedPool: + s = "Framed-Pool" + case RADIUSAttributeTypeTunnelClientAuthID: + s = "Tunnel-Client-Auth-ID" + case RADIUSAttributeTypeTunnelServerAuthID: + s = "Tunnel-Server-Auth-ID" + default: + s = fmt.Sprintf("Unknown(%d)", t) + } + return +} + +// Len returns the length of a RADIUS packet. +func (radius *RADIUS) Len() (int, error) { + n := radiusMinimumRecordSizeInBytes + for _, v := range radius.Attributes { + alen, err := attributeValueLength(v.Value) + if err != nil { + return 0, err + } + n += int(alen) + 2 // Added Type and Length + } + return n, nil +} + +// LayerType returns LayerTypeRADIUS. +func (radius *RADIUS) LayerType() gopacket.LayerType { + return LayerTypeRADIUS +} + +// DecodeFromBytes decodes the given bytes into this layer. +func (radius *RADIUS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) > radiusMaximumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS length %d too big", len(data)) + } + + if len(data) < radiusMinimumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS length %d too short", len(data)) + } + + radius.BaseLayer = BaseLayer{Contents: data} + + radius.Code = RADIUSCode(data[0]) + radius.Identifier = RADIUSIdentifier(data[1]) + radius.Length = RADIUSLength(binary.BigEndian.Uint16(data[2:4])) + + if int(radius.Length) > radiusMaximumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS length %d too big", radius.Length) + } + + if int(radius.Length) < radiusMinimumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS length %d too short", radius.Length) + } + + // RFC 2865 3. Packet Format + // `If the packet is shorter than the Length field indicates, it MUST be silently discarded.` + if int(radius.Length) > len(data) { + df.SetTruncated() + return fmt.Errorf("RADIUS length %d too big", radius.Length) + } + + // RFC 2865 3. Packet Format + // `Octets outside the range of the Length field MUST be treated as padding and ignored on reception.` + if int(radius.Length) < len(data) { + df.SetTruncated() + data = data[:radius.Length] + } + + copy(radius.Authenticator[:], data[4:20]) + + if len(data) == radiusMinimumRecordSizeInBytes { + return nil + } + + pos := radiusMinimumRecordSizeInBytes + for { + if len(data) == pos { + break + } + + if len(data[pos:]) < radiusAttributesMinimumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS attributes length %d too short", len(data[pos:])) + } + + attr := RADIUSAttribute{} + attr.Type = RADIUSAttributeType(data[pos]) + attr.Length = RADIUSAttributeLength(data[pos+1]) + + if int(attr.Length) > len(data[pos:]) { + df.SetTruncated() + return fmt.Errorf("RADIUS attributes length %d too big", attr.Length) + } + + if int(attr.Length) < radiusAttributesMinimumRecordSizeInBytes { + df.SetTruncated() + return fmt.Errorf("RADIUS attributes length %d too short", attr.Length) + } + + if int(attr.Length) > radiusAttributesMinimumRecordSizeInBytes { + attr.Value = make([]byte, attr.Length-2) + copy(attr.Value[:], data[pos+2:pos+int(attr.Length)]) + radius.Attributes = append(radius.Attributes, attr) + } + + pos += int(attr.Length) + } + + for _, v := range radius.Attributes { + if v.Type == RADIUSAttributeTypeEAPMessage { + radius.BaseLayer.Payload = append(radius.BaseLayer.Payload, v.Value...) + } + } + + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (radius *RADIUS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + plen, err := radius.Len() + if err != nil { + return err + } + + if opts.FixLengths { + radius.Length = RADIUSLength(plen) + } + + data, err := b.PrependBytes(plen) + if err != nil { + return err + } + + data[0] = byte(radius.Code) + data[1] = byte(radius.Identifier) + binary.BigEndian.PutUint16(data[2:], uint16(radius.Length)) + copy(data[4:20], radius.Authenticator[:]) + + pos := radiusMinimumRecordSizeInBytes + for _, v := range radius.Attributes { + if opts.FixLengths { + v.Length, err = attributeValueLength(v.Value) + if err != nil { + return err + } + } + + data[pos] = byte(v.Type) + data[pos+1] = byte(v.Length) + copy(data[pos+2:], v.Value[:]) + + pos += len(v.Value) + 2 // Added Type and Length + } + + return nil +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode. +func (radius *RADIUS) CanDecode() gopacket.LayerClass { + return LayerTypeRADIUS +} + +// NextLayerType returns the layer type contained by this DecodingLayer. +func (radius *RADIUS) NextLayerType() gopacket.LayerType { + if len(radius.BaseLayer.Payload) > 0 { + return LayerTypeEAP + } else { + return gopacket.LayerTypeZero + } +} + +// Payload returns the EAP Type-Data for EAP-Message attributes. +func (radius *RADIUS) Payload() []byte { + return radius.BaseLayer.Payload +} + +func decodeRADIUS(data []byte, p gopacket.PacketBuilder) error { + radius := &RADIUS{} + err := radius.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(radius) + p.SetApplicationLayer(radius) + next := radius.NextLayerType() + if next == gopacket.LayerTypeZero { + return nil + } + return p.NextDecoder(next) +} + +func attributeValueLength(v []byte) (RADIUSAttributeLength, error) { + n := len(v) + if n > 255 { + return 0, fmt.Errorf("RADIUS attribute value length %d too long", n) + } else { + return RADIUSAttributeLength(n), nil + } +} diff --git a/vendor/github.com/google/gopacket/layers/rmcp.go b/vendor/github.com/google/gopacket/layers/rmcp.go new file mode 100644 index 00000000..5474fee4 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/rmcp.go @@ -0,0 +1,170 @@ +// Copyright 2019 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license that can be found +// in the LICENSE file in the root of the source tree. + +package layers + +// This file implements the ASF-RMCP header specified in section 3.2.2.2 of +// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf + +import ( + "fmt" + + "github.com/google/gopacket" +) + +// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a +// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined) +// are currently reserved. +type RMCPClass uint8 + +// LayerType returns the payload layer type corresponding to a RMCP class. +func (c RMCPClass) LayerType() gopacket.LayerType { + if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 { + return lt + } + return gopacket.LayerTypePayload +} + +func (c RMCPClass) String() string { + return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType()) +} + +const ( + // RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower + // values are considered legacy, while higher values are reserved by the + // specification. + RMCPVersion1 uint8 = 0x06 + + // RMCPNormal indicates a "normal" message, i.e. not an acknowledgement. + RMCPNormal uint8 = 0 + + // RMCPAck indicates a message is acknowledging a received normal message. + RMCPAck uint8 = 1 << 7 + + // RMCPClassASF identifies an RMCP message as containing an ASF-RMCP + // payload. + RMCPClassASF RMCPClass = 0x06 + + // RMCPClassIPMI identifies an RMCP message as containing an IPMI payload. + RMCPClassIPMI RMCPClass = 0x07 + + // RMCPClassOEM identifies an RMCP message as containing an OEM-defined + // payload. + RMCPClassOEM RMCPClass = 0x08 +) + +var ( + rmcpClassLayerTypes = [16]gopacket.LayerType{ + RMCPClassASF: LayerTypeASF, + // RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not + // implemented, so we return LayerTypePayload + } +) + +// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of +// a certain class should processed by the provided layer type. This overrides +// any existing registrations, including defaults. +func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) { + rmcpClassLayerTypes[c] = l +} + +// RMCP describes the format of an RMCP header, which forms a UDP payload. See +// section 3.2.2.2. +type RMCP struct { + BaseLayer + + // Version identifies the version of the RMCP header. 0x06 indicates RMCP + // v1.0; lower values are legacy, higher values are reserved. + Version uint8 + + // Sequence is the sequence number assicated with the message. Note that + // this rolls over to 0 after 254, not 255. Seq num 255 indicates the + // receiver must not send an ACK. + Sequence uint8 + + // Ack indicates whether this packet is an acknowledgement. If it is, the + // payload will be empty. + Ack bool + + // Class idicates the structure of the payload. There are only 2^4 valid + // values, however there is no uint4 data type. N.B. the Ack bit has been + // split off into another field. The most significant 4 bits of this field + // will always be 0. + Class RMCPClass +} + +// LayerType returns LayerTypeRMCP. It partially satisfies Layer and +// SerializableLayer. +func (*RMCP) LayerType() gopacket.LayerType { + return LayerTypeRMCP +} + +// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer. +func (r *RMCP) CanDecode() gopacket.LayerClass { + return r.LayerType() +} + +// DecodeFromBytes makes the layer represent the provided bytes. It partially +// satisfies DecodingLayer. +func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 4 { + df.SetTruncated() + return fmt.Errorf("invalid RMCP header, length %v less than 4", + len(data)) + } + + r.BaseLayer.Contents = data[:4] + r.BaseLayer.Payload = data[4:] + + r.Version = uint8(data[0]) + // 1 byte reserved + r.Sequence = uint8(data[2]) + r.Ack = data[3]&RMCPAck != 0 + r.Class = RMCPClass(data[3] & 0xF) + return nil +} + +// NextLayerType returns the data layer of this RMCP layer. This partially +// satisfies DecodingLayer. +func (r *RMCP) NextLayerType() gopacket.LayerType { + return r.Class.LayerType() +} + +// Payload returns the data layer. It partially satisfies ApplicationLayer. +func (r *RMCP) Payload() []byte { + return r.BaseLayer.Payload +} + +// SerializeTo writes the serialized fom of this layer into the SerializeBuffer, +// partially satisfying SerializableLayer. +func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error { + // The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths + // to work around issues in LAN chips. This is no longer necessary as of + // IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The + // same approach is taken by FreeIPMI: + // http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836 + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + bytes[0] = r.Version + bytes[1] = 0x00 + bytes[2] = r.Sequence + bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer + return nil +} + +// decodeRMCP decodes the byte slice into an RMCP type, and sets the application +// layer to it. +func decodeRMCP(data []byte, p gopacket.PacketBuilder) error { + rmcp := &RMCP{} + err := rmcp.DecodeFromBytes(data, p) + p.AddLayer(rmcp) + p.SetApplicationLayer(rmcp) + if err != nil { + return err + } + return p.NextDecoder(rmcp.NextLayerType()) +} diff --git a/vendor/github.com/google/gopacket/layers/rudp.go b/vendor/github.com/google/gopacket/layers/rudp.go new file mode 100644 index 00000000..8435129b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/rudp.go @@ -0,0 +1,93 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + "github.com/google/gopacket" +) + +type RUDP struct { + BaseLayer + SYN, ACK, EACK, RST, NUL bool + Version uint8 + HeaderLength uint8 + SrcPort, DstPort RUDPPort + DataLength uint16 + Seq, Ack, Checksum uint32 + VariableHeaderArea []byte + // RUDPHeaderSyn contains SYN information for the RUDP packet, + // if the SYN flag is set + *RUDPHeaderSYN + // RUDPHeaderEack contains EACK information for the RUDP packet, + // if the EACK flag is set. + *RUDPHeaderEACK +} + +type RUDPHeaderSYN struct { + MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16 +} + +type RUDPHeaderEACK struct { + SeqsReceivedOK []uint32 +} + +// LayerType returns gopacket.LayerTypeRUDP. +func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP } + +func decodeRUDP(data []byte, p gopacket.PacketBuilder) error { + r := &RUDP{ + SYN: data[0]&0x80 != 0, + ACK: data[0]&0x40 != 0, + EACK: data[0]&0x20 != 0, + RST: data[0]&0x10 != 0, + NUL: data[0]&0x08 != 0, + Version: data[0] & 0x3, + HeaderLength: data[1], + SrcPort: RUDPPort(data[2]), + DstPort: RUDPPort(data[3]), + DataLength: binary.BigEndian.Uint16(data[4:6]), + Seq: binary.BigEndian.Uint32(data[6:10]), + Ack: binary.BigEndian.Uint32(data[10:14]), + Checksum: binary.BigEndian.Uint32(data[14:18]), + } + if r.HeaderLength < 9 { + return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength) + } + hlen := int(r.HeaderLength) * 2 + r.Contents = data[:hlen] + r.Payload = data[hlen : hlen+int(r.DataLength)] + r.VariableHeaderArea = data[18:hlen] + headerData := r.VariableHeaderArea + switch { + case r.SYN: + if len(headerData) != 6 { + return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData)) + } + r.RUDPHeaderSYN = &RUDPHeaderSYN{ + MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]), + MaxSegmentSize: binary.BigEndian.Uint16(headerData[2:4]), + OptionFlags: binary.BigEndian.Uint16(headerData[4:6]), + } + case r.EACK: + if len(headerData)%4 != 0 { + return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData)) + } + r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)} + for i := 0; i < len(headerData); i += 4 { + r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4]) + } + } + p.AddLayer(r) + p.SetTransportLayer(r) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +func (r *RUDP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)}) +} diff --git a/vendor/github.com/google/gopacket/layers/sctp.go b/vendor/github.com/google/gopacket/layers/sctp.go new file mode 100644 index 00000000..511176e5 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sctp.go @@ -0,0 +1,746 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "hash/crc32" + + "github.com/google/gopacket" +) + +// SCTP contains information on the top level of an SCTP packet. +type SCTP struct { + BaseLayer + SrcPort, DstPort SCTPPort + VerificationTag uint32 + Checksum uint32 + sPort, dPort []byte +} + +// LayerType returns gopacket.LayerTypeSCTP +func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP } + +func decodeSCTP(data []byte, p gopacket.PacketBuilder) error { + sctp := &SCTP{} + err := sctp.DecodeFromBytes(data, p) + p.AddLayer(sctp) + p.SetTransportLayer(sctp) + if err != nil { + return err + } + return p.NextDecoder(sctpChunkTypePrefixDecoder) +} + +var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix) + +// TransportFlow returns a flow based on the source and destination SCTP port. +func (s *SCTP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort) +} + +func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error { + chunkType := SCTPChunkType(data[0]) + return chunkType.Decode(data, p) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(12) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort)) + binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag) + if opts.ComputeChecksums { + // Note: MakeTable(Castagnoli) actually only creates the table once, then + // passes back a singleton on every other call, so this shouldn't cause + // excessive memory allocation. + binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli))) + } + return nil +} + +func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 12 { + return errors.New("Invalid SCTP common header length") + } + sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2])) + sctp.sPort = data[:2] + sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4])) + sctp.dPort = data[2:4] + sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8]) + sctp.Checksum = binary.BigEndian.Uint32(data[8:12]) + sctp.BaseLayer = BaseLayer{data[:12], data[12:]} + + return nil +} + +func (t *SCTP) CanDecode() gopacket.LayerClass { + return LayerTypeSCTP +} + +func (t *SCTP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// SCTPChunk contains the common fields in all SCTP chunks. +type SCTPChunk struct { + BaseLayer + Type SCTPChunkType + Flags uint8 + Length uint16 + // ActualLength is the total length of an SCTP chunk, including padding. + // SCTP chunks start and end on 4-byte boundaries. So if a chunk has a length + // of 18, it means that it has data up to and including byte 18, then padding + // up to the next 4-byte boundary, 20. In this case, Length would be 18, and + // ActualLength would be 20. + ActualLength int +} + +func roundUpToNearest4(i int) int { + if i%4 == 0 { + return i + } + return i + 4 - (i % 4) +} + +func decodeSCTPChunk(data []byte) (SCTPChunk, error) { + length := binary.BigEndian.Uint16(data[2:4]) + if length < 4 { + return SCTPChunk{}, errors.New("invalid SCTP chunk length") + } + actual := roundUpToNearest4(int(length)) + ct := SCTPChunkType(data[0]) + + // For SCTP Data, use a separate layer for the payload + delta := 0 + if ct == SCTPChunkTypeData { + delta = int(actual) - int(length) + actual = 16 + } + + return SCTPChunk{ + Type: ct, + Flags: data[1], + Length: length, + ActualLength: actual, + BaseLayer: BaseLayer{data[:actual], data[actual : len(data)-delta]}, + }, nil +} + +// SCTPParameter is a TLV parameter inside a SCTPChunk. +type SCTPParameter struct { + Type uint16 + Length uint16 + ActualLength int + Value []byte +} + +func decodeSCTPParameter(data []byte) SCTPParameter { + length := binary.BigEndian.Uint16(data[2:4]) + return SCTPParameter{ + Type: binary.BigEndian.Uint16(data[0:2]), + Length: length, + Value: data[4:length], + ActualLength: roundUpToNearest4(int(length)), + } +} + +func (p SCTPParameter) Bytes() []byte { + length := 4 + len(p.Value) + data := make([]byte, roundUpToNearest4(length)) + binary.BigEndian.PutUint16(data[0:2], p.Type) + binary.BigEndian.PutUint16(data[2:4], uint16(length)) + copy(data[4:], p.Value) + return data +} + +// SCTPUnknownChunkType is the layer type returned when we don't recognize the +// chunk type. Since there's a length in a known location, we can skip over +// it even if we don't know what it is, and continue parsing the rest of the +// chunks. This chunk is stored as an ErrorLayer in the packet. +type SCTPUnknownChunkType struct { + SCTPChunk + bytes []byte +} + +func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPUnknownChunkType{SCTPChunk: chunk} + sc.bytes = data[:sc.ActualLength] + p.AddLayer(sc) + p.SetErrorLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(s.ActualLength) + if err != nil { + return err + } + copy(bytes, s.bytes) + return nil +} + +// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType. +func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType } + +// Payload returns all bytes in this header, including the decoded Type, Length, +// and Flags. +func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes } + +// Error implements ErrorLayer. +func (s *SCTPUnknownChunkType) Error() error { + return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type) +} + +// SCTPData is the SCTP Data chunk layer. +type SCTPData struct { + SCTPChunk + Unordered, BeginFragment, EndFragment bool + TSN uint32 + StreamId uint16 + StreamSequence uint16 + PayloadProtocol SCTPPayloadProtocol +} + +// LayerType returns gopacket.LayerTypeSCTPData. +func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData } + +// SCTPPayloadProtocol represents a payload protocol +type SCTPPayloadProtocol uint32 + +// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml +const ( + SCTPProtocolReserved SCTPPayloadProtocol = 0 + SCTPPayloadUIA = 1 + SCTPPayloadM2UA = 2 + SCTPPayloadM3UA = 3 + SCTPPayloadSUA = 4 + SCTPPayloadM2PA = 5 + SCTPPayloadV5UA = 6 + SCTPPayloadH248 = 7 + SCTPPayloadBICC = 8 + SCTPPayloadTALI = 9 + SCTPPayloadDUA = 10 + SCTPPayloadASAP = 11 + SCTPPayloadENRP = 12 + SCTPPayloadH323 = 13 + SCTPPayloadQIPC = 14 + SCTPPayloadSIMCO = 15 + SCTPPayloadDDPSegment = 16 + SCTPPayloadDDPStream = 17 + SCTPPayloadS1AP = 18 +) + +func (p SCTPPayloadProtocol) String() string { + switch p { + case SCTPProtocolReserved: + return "Reserved" + case SCTPPayloadUIA: + return "UIA" + case SCTPPayloadM2UA: + return "M2UA" + case SCTPPayloadM3UA: + return "M3UA" + case SCTPPayloadSUA: + return "SUA" + case SCTPPayloadM2PA: + return "M2PA" + case SCTPPayloadV5UA: + return "V5UA" + case SCTPPayloadH248: + return "H.248" + case SCTPPayloadBICC: + return "BICC" + case SCTPPayloadTALI: + return "TALI" + case SCTPPayloadDUA: + return "DUA" + case SCTPPayloadASAP: + return "ASAP" + case SCTPPayloadENRP: + return "ENRP" + case SCTPPayloadH323: + return "H.323" + case SCTPPayloadQIPC: + return "QIPC" + case SCTPPayloadSIMCO: + return "SIMCO" + case SCTPPayloadDDPSegment: + return "DDPSegment" + case SCTPPayloadDDPStream: + return "DDPStream" + case SCTPPayloadS1AP: + return "S1AP" + } + return fmt.Sprintf("Unknown(%d)", p) +} + +func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPData{ + SCTPChunk: chunk, + Unordered: data[1]&0x4 != 0, + BeginFragment: data[1]&0x2 != 0, + EndFragment: data[1]&0x1 != 0, + TSN: binary.BigEndian.Uint32(data[4:8]), + StreamId: binary.BigEndian.Uint16(data[8:10]), + StreamSequence: binary.BigEndian.Uint16(data[10:12]), + PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])), + } + // Length is the length in bytes of the data, INCLUDING the 16-byte header. + p.AddLayer(sc) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + payload := b.Bytes() + // Pad the payload to a 32 bit boundary + if rem := len(payload) % 4; rem != 0 { + b.AppendBytes(4 - rem) + } + length := 16 + bytes, err := b.PrependBytes(length) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + flags := uint8(0) + if sc.Unordered { + flags |= 0x4 + } + if sc.BeginFragment { + flags |= 0x2 + } + if sc.EndFragment { + flags |= 0x1 + } + bytes[1] = flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload))) + binary.BigEndian.PutUint32(bytes[4:8], sc.TSN) + binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId) + binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence) + binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol)) + return nil +} + +// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet. +type SCTPInitParameter SCTPParameter + +// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck +// messages. +type SCTPInit struct { + SCTPChunk + InitiateTag uint32 + AdvertisedReceiverWindowCredit uint32 + OutboundStreams, InboundStreams uint16 + InitialTSN uint32 + Parameters []SCTPInitParameter +} + +// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck. +func (sc *SCTPInit) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeInitAck { + return LayerTypeSCTPInitAck + } + // sc.Type == SCTPChunkTypeInit + return LayerTypeSCTPInit +} + +func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPInit{ + SCTPChunk: chunk, + InitiateTag: binary.BigEndian.Uint32(data[4:8]), + AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]), + OutboundStreams: binary.BigEndian.Uint16(data[12:14]), + InboundStreams: binary.BigEndian.Uint16(data[14:16]), + InitialTSN: binary.BigEndian.Uint32(data[16:20]), + } + paramData := data[20:sc.ActualLength] + for len(paramData) > 0 { + p := SCTPInitParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 20 + len(payload) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag) + binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit) + binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams) + binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams) + binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN) + copy(bytes[20:], payload) + return nil +} + +// SCTPSack is the SCTP Selective ACK chunk layer. +type SCTPSack struct { + SCTPChunk + CumulativeTSNAck uint32 + AdvertisedReceiverWindowCredit uint32 + NumGapACKs, NumDuplicateTSNs uint16 + GapACKs []uint16 + DuplicateTSNs []uint32 +} + +// LayerType return LayerTypeSCTPSack +func (sc *SCTPSack) LayerType() gopacket.LayerType { + return LayerTypeSCTPSack +} + +func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPSack{ + SCTPChunk: chunk, + CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]), + AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]), + NumGapACKs: binary.BigEndian.Uint16(data[12:14]), + NumDuplicateTSNs: binary.BigEndian.Uint16(data[14:16]), + } + // We maximize gapAcks and dupTSNs here so we're not allocating tons + // of memory based on a user-controlable field. Our maximums are not exact, + // but should give us sane defaults... we'll still hit slice boundaries and + // fail if the user-supplied values are too high (in the for loops below), but + // the amount of memory we'll have allocated because of that should be small + // (< sc.ActualLength) + gapAcks := sc.SCTPChunk.ActualLength / 2 + dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4 + if gapAcks > int(sc.NumGapACKs) { + gapAcks = int(sc.NumGapACKs) + } + if dupTSNs > int(sc.NumDuplicateTSNs) { + dupTSNs = int(sc.NumDuplicateTSNs) + } + sc.GapACKs = make([]uint16, 0, gapAcks) + sc.DuplicateTSNs = make([]uint32, 0, dupTSNs) + bytesRemaining := data[16:] + for i := 0; i < int(sc.NumGapACKs); i++ { + sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2])) + bytesRemaining = bytesRemaining[2:] + } + for i := 0; i < int(sc.NumDuplicateTSNs); i++ { + sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4])) + bytesRemaining = bytesRemaining[4:] + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck) + binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit) + binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs))) + binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs))) + for i, v := range sc.GapACKs { + binary.BigEndian.PutUint16(bytes[16+i*2:], v) + } + offset := 16 + 2*len(sc.GapACKs) + for i, v := range sc.DuplicateTSNs { + binary.BigEndian.PutUint32(bytes[offset+i*4:], v) + } + return nil +} + +// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and +// heartbeat ack layers. +type SCTPHeartbeatParameter SCTPParameter + +// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack. +type SCTPHeartbeat struct { + SCTPChunk + Parameters []SCTPHeartbeatParameter +} + +// LayerType returns gopacket.LayerTypeSCTPHeartbeat. +func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeHeartbeatAck { + return LayerTypeSCTPHeartbeatAck + } + // sc.Type == SCTPChunkTypeHeartbeat + return LayerTypeSCTPHeartbeat +} + +func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPHeartbeat{ + SCTPChunk: chunk, + } + paramData := data[4:sc.Length] + for len(paramData) > 0 { + p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 4 + len(payload) + + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], payload) + return nil +} + +// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers. +type SCTPErrorParameter SCTPParameter + +// SCTPError is the SCTP error layer, also used for SCTP aborts. +type SCTPError struct { + SCTPChunk + Parameters []SCTPErrorParameter +} + +// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError. +func (sc *SCTPError) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeAbort { + return LayerTypeSCTPAbort + } + // sc.Type == SCTPChunkTypeError + return LayerTypeSCTPError +} + +func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error { + // remarkably similar to decodeSCTPHeartbeat ;) + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPError{ + SCTPChunk: chunk, + } + paramData := data[4:sc.Length] + for len(paramData) > 0 { + p := SCTPErrorParameter(decodeSCTPParameter(paramData)) + paramData = paramData[p.ActualLength:] + sc.Parameters = append(sc.Parameters, p) + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var payload []byte + for _, param := range sc.Parameters { + payload = append(payload, SCTPParameter(param).Bytes()...) + } + length := 4 + len(payload) + + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], payload) + return nil +} + +// SCTPShutdown is the SCTP shutdown layer. +type SCTPShutdown struct { + SCTPChunk + CumulativeTSNAck uint32 +} + +// LayerType returns gopacket.LayerTypeSCTPShutdown. +func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown } + +func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPShutdown{ + SCTPChunk: chunk, + CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]), + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 8) + binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck) + return nil +} + +// SCTPShutdownAck is the SCTP shutdown layer. +type SCTPShutdownAck struct { + SCTPChunk +} + +// LayerType returns gopacket.LayerTypeSCTPShutdownAck. +func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck } + +func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPShutdownAck{ + SCTPChunk: chunk, + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 4) + return nil +} + +// SCTPCookieEcho is the SCTP Cookie Echo layer. +type SCTPCookieEcho struct { + SCTPChunk + Cookie []byte +} + +// LayerType returns gopacket.LayerTypeSCTPCookieEcho. +func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho } + +func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPCookieEcho{ + SCTPChunk: chunk, + } + sc.Cookie = data[4:sc.Length] + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + length := 4 + len(sc.Cookie) + bytes, err := b.PrependBytes(roundUpToNearest4(length)) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], uint16(length)) + copy(bytes[4:], sc.Cookie) + return nil +} + +// This struct is used by all empty SCTP chunks (currently CookieAck and +// ShutdownComplete). +type SCTPEmptyLayer struct { + SCTPChunk +} + +// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or +// LayerTypeSCTPCookieAck. +func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType { + if sc.Type == SCTPChunkTypeShutdownComplete { + return LayerTypeSCTPShutdownComplete + } + // sc.Type == SCTPChunkTypeCookieAck + return LayerTypeSCTPCookieAck +} + +func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error { + chunk, err := decodeSCTPChunk(data) + if err != nil { + return err + } + sc := &SCTPEmptyLayer{ + SCTPChunk: chunk, + } + p.AddLayer(sc) + return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)) +} + +// SerializeTo is for gopacket.SerializableLayer. +func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(4) + if err != nil { + return err + } + bytes[0] = uint8(sc.Type) + bytes[1] = sc.Flags + binary.BigEndian.PutUint16(bytes[2:4], 4) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/sflow.go b/vendor/github.com/google/gopacket/layers/sflow.go new file mode 100644 index 00000000..bc1c9733 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sflow.go @@ -0,0 +1,2567 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +/* +This layer decodes SFlow version 5 datagrams. + +The specification can be found here: http://sflow.org/sflow_version_5.txt + +Additional developer information about sflow can be found at: +http://sflow.org/developers/specifications.php + +And SFlow in general: +http://sflow.org/index.php + +Two forms of sample data are defined: compact and expanded. The +Specification has this to say: + + Compact and expand forms of counter and flow samples are defined. + An agent must not mix compact/expanded encodings. If an agent + will never use ifIndex numbers >= 2^24 then it must use compact + encodings for all interfaces. Otherwise the expanded formats must + be used for all interfaces. + +This decoder only supports the compact form, because that is the only +one for which data was available. + +The datagram is composed of one or more samples of type flow or counter, +and each sample is composed of one or more records describing the sample. +A sample is a single instance of sampled inforamtion, and each record in +the sample gives additional / supplimentary information about the sample. + +The following sample record types are supported: + + Raw Packet Header + opaque = flow_data; enterprise = 0; format = 1 + + Extended Switch Data + opaque = flow_data; enterprise = 0; format = 1001 + + Extended Router Data + opaque = flow_data; enterprise = 0; format = 1002 + + Extended Gateway Data + opaque = flow_data; enterprise = 0; format = 1003 + + Extended User Data + opaque = flow_data; enterprise = 0; format = 1004 + + Extended URL Data + opaque = flow_data; enterprise = 0; format = 1005 + +The following types of counter records are supported: + + Generic Interface Counters - see RFC 2233 + opaque = counter_data; enterprise = 0; format = 1 + + Ethernet Interface Counters - see RFC 2358 + opaque = counter_data; enterprise = 0; format = 2 + +SFlow is encoded using XDR (RFC4506). There are a few places +where the standard 4-byte fields are partitioned into two +bitfields of different lengths. I'm not sure why the designers +chose to pack together two values like this in some places, and +in others they use the entire 4-byte value to store a number that +will never be more than a few bits. In any case, there are a couple +of types defined to handle the decoding of these bitfields, and +that's why they're there. */ + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + + "github.com/google/gopacket" +) + +// SFlowRecord holds both flow sample records and counter sample records. +// A Record is the structure that actually holds the sampled data +// and / or counters. +type SFlowRecord interface { +} + +// SFlowDataSource encodes a 2-bit SFlowSourceFormat in its most significant +// 2 bits, and an SFlowSourceValue in its least significant 30 bits. +// These types and values define the meaning of the inteface information +// presented in the sample metadata. +type SFlowDataSource int32 + +func (sdc SFlowDataSource) decode() (SFlowSourceFormat, SFlowSourceValue) { + leftField := sdc >> 30 + rightField := uint32(0x3FFFFFFF) & uint32(sdc) + return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) +} + +type SFlowDataSourceExpanded struct { + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue +} + +func (sdce SFlowDataSourceExpanded) decode() (SFlowSourceFormat, SFlowSourceValue) { + leftField := sdce.SourceIDClass >> 30 + rightField := uint32(0x3FFFFFFF) & uint32(sdce.SourceIDIndex) + return SFlowSourceFormat(leftField), SFlowSourceValue(rightField) +} + +type SFlowSourceFormat uint32 + +type SFlowSourceValue uint32 + +const ( + SFlowTypeSingleInterface SFlowSourceFormat = 0 + SFlowTypePacketDiscarded SFlowSourceFormat = 1 + SFlowTypeMultipleDestinations SFlowSourceFormat = 2 +) + +func (sdf SFlowSourceFormat) String() string { + switch sdf { + case SFlowTypeSingleInterface: + return "Single Interface" + case SFlowTypePacketDiscarded: + return "Packet Discarded" + case SFlowTypeMultipleDestinations: + return "Multiple Destinations" + default: + return "UNKNOWN" + } +} + +func decodeSFlow(data []byte, p gopacket.PacketBuilder) error { + s := &SFlowDatagram{} + err := s.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(s) + p.SetApplicationLayer(s) + return nil +} + +// SFlowDatagram is the outermost container which holds some basic information +// about the reporting agent, and holds at least one sample record +type SFlowDatagram struct { + BaseLayer + + DatagramVersion uint32 + AgentAddress net.IP + SubAgentID uint32 + SequenceNumber uint32 + AgentUptime uint32 + SampleCount uint32 + FlowSamples []SFlowFlowSample + CounterSamples []SFlowCounterSample +} + +// An SFlow datagram's outer container has the following +// structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sFlow version (2|4|5) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int IP version of the Agent (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Agent IP address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sub agent id | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int datagram sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int switch uptime in ms | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int n samples in datagram | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / n samples / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// SFlowDataFormat encodes the EnterpriseID in the most +// significant 12 bits, and the SampleType in the least significant +// 20 bits. +type SFlowDataFormat uint32 + +func (sdf SFlowDataFormat) decode() (SFlowEnterpriseID, SFlowSampleType) { + leftField := sdf >> 12 + rightField := uint32(0xFFF) & uint32(sdf) + return SFlowEnterpriseID(leftField), SFlowSampleType(rightField) +} + +// SFlowEnterpriseID is used to differentiate between the +// official SFlow standard, and other, vendor-specific +// types of flow data. (Similiar to SNMP's enterprise MIB +// OIDs) Only the office SFlow Enterprise ID is decoded +// here. +type SFlowEnterpriseID uint32 + +const ( + SFlowStandard SFlowEnterpriseID = 0 +) + +func (eid SFlowEnterpriseID) String() string { + switch eid { + case SFlowStandard: + return "Standard SFlow" + default: + return "" + } +} + +func (eid SFlowEnterpriseID) GetType() SFlowEnterpriseID { + return SFlowStandard +} + +// SFlowSampleType specifies the type of sample. Only flow samples +// and counter samples are supported +type SFlowSampleType uint32 + +const ( + SFlowTypeFlowSample SFlowSampleType = 1 + SFlowTypeCounterSample SFlowSampleType = 2 + SFlowTypeExpandedFlowSample SFlowSampleType = 3 + SFlowTypeExpandedCounterSample SFlowSampleType = 4 +) + +func (st SFlowSampleType) GetType() SFlowSampleType { + switch st { + case SFlowTypeFlowSample: + return SFlowTypeFlowSample + case SFlowTypeCounterSample: + return SFlowTypeCounterSample + case SFlowTypeExpandedFlowSample: + return SFlowTypeExpandedFlowSample + case SFlowTypeExpandedCounterSample: + return SFlowTypeExpandedCounterSample + default: + panic("Invalid Sample Type") + } +} + +func (st SFlowSampleType) String() string { + switch st { + case SFlowTypeFlowSample: + return "Flow Sample" + case SFlowTypeCounterSample: + return "Counter Sample" + case SFlowTypeExpandedFlowSample: + return "Expanded Flow Sample" + case SFlowTypeExpandedCounterSample: + return "Expanded Counter Sample" + default: + return "" + } +} + +func (s *SFlowDatagram) LayerType() gopacket.LayerType { return LayerTypeSFlow } + +func (d *SFlowDatagram) Payload() []byte { return nil } + +func (d *SFlowDatagram) CanDecode() gopacket.LayerClass { return LayerTypeSFlow } + +func (d *SFlowDatagram) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } + +// SFlowIPType determines what form the IP address being decoded will +// take. This is an XDR union type allowing for both IPv4 and IPv6 +type SFlowIPType uint32 + +const ( + SFlowIPv4 SFlowIPType = 1 + SFlowIPv6 SFlowIPType = 2 +) + +func (s SFlowIPType) String() string { + switch s { + case SFlowIPv4: + return "IPv4" + case SFlowIPv6: + return "IPv6" + default: + return "" + } +} + +func (s SFlowIPType) Length() int { + switch s { + case SFlowIPv4: + return 4 + case SFlowIPv6: + return 16 + default: + return 0 + } +} + +func (s *SFlowDatagram) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + var agentAddressType SFlowIPType + + data, s.DatagramVersion = data[4:], binary.BigEndian.Uint32(data[:4]) + data, agentAddressType = data[4:], SFlowIPType(binary.BigEndian.Uint32(data[:4])) + data, s.AgentAddress = data[agentAddressType.Length():], data[:agentAddressType.Length()] + data, s.SubAgentID = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.SequenceNumber = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.AgentUptime = data[4:], binary.BigEndian.Uint32(data[:4]) + data, s.SampleCount = data[4:], binary.BigEndian.Uint32(data[:4]) + + if s.SampleCount < 1 { + return fmt.Errorf("SFlow Datagram has invalid sample length: %d", s.SampleCount) + } + for i := uint32(0); i < s.SampleCount; i++ { + sdf := SFlowDataFormat(binary.BigEndian.Uint32(data[:4])) + _, sampleType := sdf.decode() + switch sampleType { + case SFlowTypeFlowSample: + if flowSample, err := decodeFlowSample(&data, false); err == nil { + s.FlowSamples = append(s.FlowSamples, flowSample) + } else { + return err + } + case SFlowTypeCounterSample: + if counterSample, err := decodeCounterSample(&data, false); err == nil { + s.CounterSamples = append(s.CounterSamples, counterSample) + } else { + return err + } + case SFlowTypeExpandedFlowSample: + if flowSample, err := decodeFlowSample(&data, true); err == nil { + s.FlowSamples = append(s.FlowSamples, flowSample) + } else { + return err + } + case SFlowTypeExpandedCounterSample: + if counterSample, err := decodeCounterSample(&data, true); err == nil { + s.CounterSamples = append(s.CounterSamples, counterSample) + } else { + return err + } + + default: + return fmt.Errorf("Unsupported SFlow sample type %d", sampleType) + } + } + return nil +} + +// SFlowFlowSample represents a sampled packet and contains +// one or more records describing the packet +type SFlowFlowSample struct { + EnterpriseID SFlowEnterpriseID + Format SFlowSampleType + SampleLength uint32 + SequenceNumber uint32 + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue + SamplingRate uint32 + SamplePool uint32 + Dropped uint32 + InputInterfaceFormat uint32 + InputInterface uint32 + OutputInterfaceFormat uint32 + OutputInterface uint32 + RecordCount uint32 + Records []SFlowRecord +} + +// Flow samples have the following structure. Note +// the bit fields to encode the Enterprise ID and the +// Flow record format: type 1 + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | sample length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |id type | src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sampling rate | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample pool | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int drops | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input ifIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output ifIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / flow records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// Flow samples have the following structure. +// Flow record format: type 3 + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | sample length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int src id type | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sampling rate | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample pool | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int drops | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input interface format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int input interface value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output interface format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int output interface value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / flow records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowFlowDataFormat uint32 + +func (fdf SFlowFlowDataFormat) decode() (SFlowEnterpriseID, SFlowFlowRecordType) { + leftField := fdf >> 12 + rightField := uint32(0xFFF) & uint32(fdf) + return SFlowEnterpriseID(leftField), SFlowFlowRecordType(rightField) +} + +func (fs SFlowFlowSample) GetRecords() []SFlowRecord { + return fs.Records +} + +func (fs SFlowFlowSample) GetType() SFlowSampleType { + return SFlowTypeFlowSample +} + +func skipRecord(data *[]byte) { + recordLength := int(binary.BigEndian.Uint32((*data)[4:])) + *data = (*data)[(recordLength+((4-recordLength)%4))+8:] +} + +func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) { + s := SFlowFlowSample{} + var sdf SFlowDataFormat + *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + var sdc SFlowDataSource + + s.EnterpriseID, s.Format = sdf.decode() + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if expanded { + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4])) + } else { + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) + s.SourceIDClass, s.SourceIDIndex = sdc.decode() + } + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + if expanded { + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + } else { + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + } + if len(*data) < 4 { + return SFlowFlowSample{}, errors.New("ethernet counters too small") + } + *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + for i := uint32(0); i < s.RecordCount; i++ { + rdf := SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + enterpriseID, flowRecordType := rdf.decode() + + // Try to decode when EnterpriseID is 0 signaling + // default sflow structs are used according specification + // Unexpected behavior detected for e.g. with pmacct + if enterpriseID == 0 { + switch flowRecordType { + case SFlowTypeRawPacketFlow: + if record, err := decodeRawPacketFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedUserFlow: + if record, err := decodeExtendedUserFlow(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedUrlFlow: + if record, err := decodeExtendedURLRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedSwitchFlow: + if record, err := decodeExtendedSwitchFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedRouterFlow: + if record, err := decodeExtendedRouterFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedGatewayFlow: + if record, err := decodeExtendedGatewayFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeEthernetFrameFlow: + if record, err := decodeEthernetFrameFlowRecord(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeIpv4Flow: + if record, err := decodeSFlowIpv4Record(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeIpv6Flow: + if record, err := decodeSFlowIpv6Record(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedMlpsFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsFlow") + case SFlowTypeExtendedNatFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedNatFlow") + case SFlowTypeExtendedMlpsTunnelFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsTunnelFlow") + case SFlowTypeExtendedMlpsVcFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsVcFlow") + case SFlowTypeExtendedMlpsFecFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsFecFlow") + case SFlowTypeExtendedMlpsLvpFecFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow") + case SFlowTypeExtendedVlanFlow: + // TODO + skipRecord(data) + return s, errors.New("skipping TypeExtendedVlanFlow") + case SFlowTypeExtendedIpv4TunnelEgressFlow: + if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv4TunnelIngressFlow: + if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv6TunnelEgressFlow: + if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedIpv6TunnelIngressFlow: + if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedDecapsulateEgressFlow: + if record, err := decodeExtendedDecapsulateEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedDecapsulateIngressFlow: + if record, err := decodeExtendedDecapsulateIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedVniEgressFlow: + if record, err := decodeExtendedVniEgress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeExtendedVniIngressFlow: + if record, err := decodeExtendedVniIngress(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + default: + return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType) + } + } else { + skipRecord(data) + } + } + return s, nil +} + +// Counter samples report information about various counter +// objects. Typically these are items like IfInOctets, or +// CPU / Memory stats, etc. SFlow will report these at regular +// intervals as configured on the agent. If one were sufficiently +// industrious, this could be used to replace the typical +// SNMP polling used for such things. +type SFlowCounterSample struct { + EnterpriseID SFlowEnterpriseID + Format SFlowSampleType + SampleLength uint32 + SequenceNumber uint32 + SourceIDClass SFlowSourceFormat + SourceIDIndex SFlowSourceValue + RecordCount uint32 + Records []SFlowRecord +} + +// Counter samples have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int sample sequence number | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// |id type | src id index value | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | int number of records | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter records / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowCounterDataFormat uint32 + +func (cdf SFlowCounterDataFormat) decode() (SFlowEnterpriseID, SFlowCounterRecordType) { + leftField := cdf >> 12 + rightField := uint32(0xFFF) & uint32(cdf) + return SFlowEnterpriseID(leftField), SFlowCounterRecordType(rightField) +} + +// GetRecords will return a slice of interface types +// representing records. A type switch can be used to +// get at the underlying SFlowCounterRecordType. +func (cs SFlowCounterSample) GetRecords() []SFlowRecord { + return cs.Records +} + +// GetType will report the type of sample. Only the +// compact form of counter samples is supported +func (cs SFlowCounterSample) GetType() SFlowSampleType { + return SFlowTypeCounterSample +} + +type SFlowCounterRecordType uint32 + +const ( + SFlowTypeGenericInterfaceCounters SFlowCounterRecordType = 1 + SFlowTypeEthernetInterfaceCounters SFlowCounterRecordType = 2 + SFlowTypeTokenRingInterfaceCounters SFlowCounterRecordType = 3 + SFlowType100BaseVGInterfaceCounters SFlowCounterRecordType = 4 + SFlowTypeVLANCounters SFlowCounterRecordType = 5 + SFlowTypeLACPCounters SFlowCounterRecordType = 7 + SFlowTypeProcessorCounters SFlowCounterRecordType = 1001 + SFlowTypeOpenflowPortCounters SFlowCounterRecordType = 1004 + SFlowTypePORTNAMECounters SFlowCounterRecordType = 1005 + SFLowTypeAPPRESOURCESCounters SFlowCounterRecordType = 2203 + SFlowTypeOVSDPCounters SFlowCounterRecordType = 2207 +) + +func (cr SFlowCounterRecordType) String() string { + switch cr { + case SFlowTypeGenericInterfaceCounters: + return "Generic Interface Counters" + case SFlowTypeEthernetInterfaceCounters: + return "Ethernet Interface Counters" + case SFlowTypeTokenRingInterfaceCounters: + return "Token Ring Interface Counters" + case SFlowType100BaseVGInterfaceCounters: + return "100BaseVG Interface Counters" + case SFlowTypeVLANCounters: + return "VLAN Counters" + case SFlowTypeLACPCounters: + return "LACP Counters" + case SFlowTypeProcessorCounters: + return "Processor Counters" + case SFlowTypeOpenflowPortCounters: + return "Openflow Port Counters" + case SFlowTypePORTNAMECounters: + return "PORT NAME Counters" + case SFLowTypeAPPRESOURCESCounters: + return "App Resources Counters" + case SFlowTypeOVSDPCounters: + return "OVSDP Counters" + default: + return "" + + } +} + +func decodeCounterSample(data *[]byte, expanded bool) (SFlowCounterSample, error) { + s := SFlowCounterSample{} + var sdc SFlowDataSource + var sdce SFlowDataSourceExpanded + var sdf SFlowDataFormat + + *data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + s.EnterpriseID, s.Format = sdf.decode() + *data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if expanded { + *data, sdce = (*data)[8:], SFlowDataSourceExpanded{SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])), SFlowSourceValue(binary.BigEndian.Uint32((*data)[4:8]))} + s.SourceIDClass, s.SourceIDIndex = sdce.decode() + } else { + *data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4])) + s.SourceIDClass, s.SourceIDIndex = sdc.decode() + } + *data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + for i := uint32(0); i < s.RecordCount; i++ { + cdf := SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + _, counterRecordType := cdf.decode() + switch counterRecordType { + case SFlowTypeGenericInterfaceCounters: + if record, err := decodeGenericInterfaceCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeEthernetInterfaceCounters: + if record, err := decodeEthernetCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeTokenRingInterfaceCounters: + skipRecord(data) + return s, errors.New("skipping TypeTokenRingInterfaceCounters") + case SFlowType100BaseVGInterfaceCounters: + skipRecord(data) + return s, errors.New("skipping Type100BaseVGInterfaceCounters") + case SFlowTypeVLANCounters: + if record, err := decodeVLANCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeLACPCounters: + if record, err := decodeLACPCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeProcessorCounters: + if record, err := decodeProcessorCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeOpenflowPortCounters: + if record, err := decodeOpenflowportCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypePORTNAMECounters: + if record, err := decodePortnameCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFLowTypeAPPRESOURCESCounters: + if record, err := decodeAppresourcesCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + case SFlowTypeOVSDPCounters: + if record, err := decodeOVSDPCounters(data); err == nil { + s.Records = append(s.Records, record) + } else { + return s, err + } + default: + return s, fmt.Errorf("Invalid counter record type: %d", counterRecordType) + } + } + return s, nil +} + +// SFlowBaseFlowRecord holds the fields common to all records +// of type SFlowFlowRecordType +type SFlowBaseFlowRecord struct { + EnterpriseID SFlowEnterpriseID + Format SFlowFlowRecordType + FlowDataLength uint32 +} + +func (bfr SFlowBaseFlowRecord) GetType() SFlowFlowRecordType { + return bfr.Format +} + +// SFlowFlowRecordType denotes what kind of Flow Record is +// represented. See RFC 3176 +type SFlowFlowRecordType uint32 + +const ( + SFlowTypeRawPacketFlow SFlowFlowRecordType = 1 + SFlowTypeEthernetFrameFlow SFlowFlowRecordType = 2 + SFlowTypeIpv4Flow SFlowFlowRecordType = 3 + SFlowTypeIpv6Flow SFlowFlowRecordType = 4 + SFlowTypeExtendedSwitchFlow SFlowFlowRecordType = 1001 + SFlowTypeExtendedRouterFlow SFlowFlowRecordType = 1002 + SFlowTypeExtendedGatewayFlow SFlowFlowRecordType = 1003 + SFlowTypeExtendedUserFlow SFlowFlowRecordType = 1004 + SFlowTypeExtendedUrlFlow SFlowFlowRecordType = 1005 + SFlowTypeExtendedMlpsFlow SFlowFlowRecordType = 1006 + SFlowTypeExtendedNatFlow SFlowFlowRecordType = 1007 + SFlowTypeExtendedMlpsTunnelFlow SFlowFlowRecordType = 1008 + SFlowTypeExtendedMlpsVcFlow SFlowFlowRecordType = 1009 + SFlowTypeExtendedMlpsFecFlow SFlowFlowRecordType = 1010 + SFlowTypeExtendedMlpsLvpFecFlow SFlowFlowRecordType = 1011 + SFlowTypeExtendedVlanFlow SFlowFlowRecordType = 1012 + SFlowTypeExtendedIpv4TunnelEgressFlow SFlowFlowRecordType = 1023 + SFlowTypeExtendedIpv4TunnelIngressFlow SFlowFlowRecordType = 1024 + SFlowTypeExtendedIpv6TunnelEgressFlow SFlowFlowRecordType = 1025 + SFlowTypeExtendedIpv6TunnelIngressFlow SFlowFlowRecordType = 1026 + SFlowTypeExtendedDecapsulateEgressFlow SFlowFlowRecordType = 1027 + SFlowTypeExtendedDecapsulateIngressFlow SFlowFlowRecordType = 1028 + SFlowTypeExtendedVniEgressFlow SFlowFlowRecordType = 1029 + SFlowTypeExtendedVniIngressFlow SFlowFlowRecordType = 1030 +) + +func (rt SFlowFlowRecordType) String() string { + switch rt { + case SFlowTypeRawPacketFlow: + return "Raw Packet Flow Record" + case SFlowTypeEthernetFrameFlow: + return "Ethernet Frame Flow Record" + case SFlowTypeIpv4Flow: + return "IPv4 Flow Record" + case SFlowTypeIpv6Flow: + return "IPv6 Flow Record" + case SFlowTypeExtendedSwitchFlow: + return "Extended Switch Flow Record" + case SFlowTypeExtendedRouterFlow: + return "Extended Router Flow Record" + case SFlowTypeExtendedGatewayFlow: + return "Extended Gateway Flow Record" + case SFlowTypeExtendedUserFlow: + return "Extended User Flow Record" + case SFlowTypeExtendedUrlFlow: + return "Extended URL Flow Record" + case SFlowTypeExtendedMlpsFlow: + return "Extended MPLS Flow Record" + case SFlowTypeExtendedNatFlow: + return "Extended NAT Flow Record" + case SFlowTypeExtendedMlpsTunnelFlow: + return "Extended MPLS Tunnel Flow Record" + case SFlowTypeExtendedMlpsVcFlow: + return "Extended MPLS VC Flow Record" + case SFlowTypeExtendedMlpsFecFlow: + return "Extended MPLS FEC Flow Record" + case SFlowTypeExtendedMlpsLvpFecFlow: + return "Extended MPLS LVP FEC Flow Record" + case SFlowTypeExtendedVlanFlow: + return "Extended VLAN Flow Record" + case SFlowTypeExtendedIpv4TunnelEgressFlow: + return "Extended IPv4 Tunnel Egress Record" + case SFlowTypeExtendedIpv4TunnelIngressFlow: + return "Extended IPv4 Tunnel Ingress Record" + case SFlowTypeExtendedIpv6TunnelEgressFlow: + return "Extended IPv6 Tunnel Egress Record" + case SFlowTypeExtendedIpv6TunnelIngressFlow: + return "Extended IPv6 Tunnel Ingress Record" + case SFlowTypeExtendedDecapsulateEgressFlow: + return "Extended Decapsulate Egress Record" + case SFlowTypeExtendedDecapsulateIngressFlow: + return "Extended Decapsulate Ingress Record" + case SFlowTypeExtendedVniEgressFlow: + return "Extended VNI Ingress Record" + case SFlowTypeExtendedVniIngressFlow: + return "Extended VNI Ingress Record" + default: + return "" + } +} + +// SFlowRawPacketFlowRecords hold information about a sampled +// packet grabbed as it transited the agent. This is +// perhaps the most useful and interesting record type, +// as it holds the headers of the sampled packet and +// can be used to build up a complete picture of the +// traffic patterns on a network. +// +// The raw packet header is sent back into gopacket for +// decoding, and the resulting gopackt.Packet is stored +// in the Header member +type SFlowRawPacketFlowRecord struct { + SFlowBaseFlowRecord + HeaderProtocol SFlowRawHeaderProtocol + FrameLength uint32 + PayloadRemoved uint32 + HeaderLength uint32 + Header gopacket.Packet +} + +// Raw packet record types have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Header Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Frame Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Payload Removed | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Header Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// \ Header \ +// \ \ +// \ \ +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowRawHeaderProtocol uint32 + +const ( + SFlowProtoEthernet SFlowRawHeaderProtocol = 1 + SFlowProtoISO88024 SFlowRawHeaderProtocol = 2 + SFlowProtoISO88025 SFlowRawHeaderProtocol = 3 + SFlowProtoFDDI SFlowRawHeaderProtocol = 4 + SFlowProtoFrameRelay SFlowRawHeaderProtocol = 5 + SFlowProtoX25 SFlowRawHeaderProtocol = 6 + SFlowProtoPPP SFlowRawHeaderProtocol = 7 + SFlowProtoSMDS SFlowRawHeaderProtocol = 8 + SFlowProtoAAL5 SFlowRawHeaderProtocol = 9 + SFlowProtoAAL5_IP SFlowRawHeaderProtocol = 10 /* e.g. Cisco AAL5 mux */ + SFlowProtoIPv4 SFlowRawHeaderProtocol = 11 + SFlowProtoIPv6 SFlowRawHeaderProtocol = 12 + SFlowProtoMPLS SFlowRawHeaderProtocol = 13 + SFlowProtoPOS SFlowRawHeaderProtocol = 14 /* RFC 1662, 2615 */ +) + +func (sfhp SFlowRawHeaderProtocol) String() string { + switch sfhp { + case SFlowProtoEthernet: + return "ETHERNET-ISO88023" + case SFlowProtoISO88024: + return "ISO88024-TOKENBUS" + case SFlowProtoISO88025: + return "ISO88025-TOKENRING" + case SFlowProtoFDDI: + return "FDDI" + case SFlowProtoFrameRelay: + return "FRAME-RELAY" + case SFlowProtoX25: + return "X25" + case SFlowProtoPPP: + return "PPP" + case SFlowProtoSMDS: + return "SMDS" + case SFlowProtoAAL5: + return "AAL5" + case SFlowProtoAAL5_IP: + return "AAL5-IP" + case SFlowProtoIPv4: + return "IPv4" + case SFlowProtoIPv6: + return "IPv6" + case SFlowProtoMPLS: + return "MPLS" + case SFlowProtoPOS: + return "POS" + } + return "UNKNOWN" +} + +func decodeRawPacketFlowRecord(data *[]byte) (SFlowRawPacketFlowRecord, error) { + rec := SFlowRawPacketFlowRecord{} + header := []byte{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.HeaderProtocol = (*data)[4:], SFlowRawHeaderProtocol(binary.BigEndian.Uint32((*data)[:4])) + *data, rec.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.PayloadRemoved = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.HeaderLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + headerLenWithPadding := int(rec.HeaderLength + ((4 - rec.HeaderLength) % 4)) + *data, header = (*data)[headerLenWithPadding:], (*data)[:headerLenWithPadding] + rec.Header = gopacket.NewPacket(header, LayerTypeEthernet, gopacket.Default) + return rec, nil +} + +// SFlowExtendedSwitchFlowRecord give additional information +// about the sampled packet if it's available. It's mainly +// useful for getting at the incoming and outgoing VLANs +// An agent may or may not provide this information. +type SFlowExtendedSwitchFlowRecord struct { + SFlowBaseFlowRecord + IncomingVLAN uint32 + IncomingVLANPriority uint32 + OutgoingVLAN uint32 + OutgoingVLANPriority uint32 +} + +// Extended switch records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Incoming VLAN | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Incoming VLAN Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Outgoing VLAN | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Outgoing VLAN Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +func decodeExtendedSwitchFlowRecord(data *[]byte) (SFlowExtendedSwitchFlowRecord, error) { + es := SFlowExtendedSwitchFlowRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + es.EnterpriseID, es.Format = fdf.decode() + *data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.IncomingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.IncomingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.OutgoingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.OutgoingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return es, nil +} + +// SFlowExtendedRouterFlowRecord gives additional information +// about the layer 3 routing information used to forward +// the packet +type SFlowExtendedRouterFlowRecord struct { + SFlowBaseFlowRecord + NextHop net.IP + NextHopSourceMask uint32 + NextHopDestinationMask uint32 +} + +// Extended router records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IP version of next hop router (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Next Hop address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Next Hop Source Mask | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Next Hop Destination Mask | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +func decodeExtendedRouterFlowRecord(data *[]byte) (SFlowExtendedRouterFlowRecord, error) { + er := SFlowExtendedRouterFlowRecord{} + var fdf SFlowFlowDataFormat + var extendedRouterAddressType SFlowIPType + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + er.EnterpriseID, er.Format = fdf.decode() + *data, er.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, extendedRouterAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) + *data, er.NextHop = (*data)[extendedRouterAddressType.Length():], (*data)[:extendedRouterAddressType.Length()] + *data, er.NextHopSourceMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, er.NextHopDestinationMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return er, nil +} + +// SFlowExtendedGatewayFlowRecord describes information treasured by +// nework engineers everywhere: AS path information listing which +// BGP peer sent the packet, and various other BGP related info. +// This information is vital because it gives a picture of how much +// traffic is being sent from / received by various BGP peers. + +// Extended gateway records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IP version of next hop router (1=v4|2=v6) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Next Hop address (v4=4byte|v6=16byte) / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Peer AS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS Path Count | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / AS Path / Sequence / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Communities / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Local Pref | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// AS Path / Sequence: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | AS Source Type (Path=1 / Sequence=2) | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Path / Sequence length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Path / Sequence Members / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +// Communities: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | communitiy length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / communitiy Members / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowExtendedGatewayFlowRecord struct { + SFlowBaseFlowRecord + NextHop net.IP + AS uint32 + SourceAS uint32 + PeerAS uint32 + ASPathCount uint32 + ASPath []SFlowASDestination + Communities []uint32 + LocalPref uint32 +} + +type SFlowASPathType uint32 + +const ( + SFlowASSet SFlowASPathType = 1 + SFlowASSequence SFlowASPathType = 2 +) + +func (apt SFlowASPathType) String() string { + switch apt { + case SFlowASSet: + return "AS Set" + case SFlowASSequence: + return "AS Sequence" + default: + return "" + } +} + +type SFlowASDestination struct { + Type SFlowASPathType + Count uint32 + Members []uint32 +} + +func (asd SFlowASDestination) String() string { + switch asd.Type { + case SFlowASSet: + return fmt.Sprint("AS Set:", asd.Members) + case SFlowASSequence: + return fmt.Sprint("AS Sequence:", asd.Members) + default: + return "" + } +} + +func (ad *SFlowASDestination) decodePath(data *[]byte) { + *data, ad.Type = (*data)[4:], SFlowASPathType(binary.BigEndian.Uint32((*data)[:4])) + *data, ad.Count = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + ad.Members = make([]uint32, ad.Count) + for i := uint32(0); i < ad.Count; i++ { + var member uint32 + *data, member = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + ad.Members[i] = member + } +} + +func decodeExtendedGatewayFlowRecord(data *[]byte) (SFlowExtendedGatewayFlowRecord, error) { + eg := SFlowExtendedGatewayFlowRecord{} + var fdf SFlowFlowDataFormat + var extendedGatewayAddressType SFlowIPType + var communitiesLength uint32 + var community uint32 + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eg.EnterpriseID, eg.Format = fdf.decode() + *data, eg.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, extendedGatewayAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4])) + *data, eg.NextHop = (*data)[extendedGatewayAddressType.Length():], (*data)[:extendedGatewayAddressType.Length()] + *data, eg.AS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.SourceAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.PeerAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eg.ASPathCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + for i := uint32(0); i < eg.ASPathCount; i++ { + asPath := SFlowASDestination{} + asPath.decodePath(data) + eg.ASPath = append(eg.ASPath, asPath) + } + *data, communitiesLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + eg.Communities = make([]uint32, communitiesLength) + for j := uint32(0); j < communitiesLength; j++ { + *data, community = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + eg.Communities[j] = community + } + *data, eg.LocalPref = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return eg, nil +} + +// ************************************************** +// Extended URL Flow Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | direction | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | URL | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Host | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowURLDirection uint32 + +const ( + SFlowURLsrc SFlowURLDirection = 1 + SFlowURLdst SFlowURLDirection = 2 +) + +func (urld SFlowURLDirection) String() string { + switch urld { + case SFlowURLsrc: + return "Source address is the server" + case SFlowURLdst: + return "Destination address is the server" + default: + return "" + } +} + +type SFlowExtendedURLRecord struct { + SFlowBaseFlowRecord + Direction SFlowURLDirection + URL string + Host string +} + +func decodeExtendedURLRecord(data *[]byte) (SFlowExtendedURLRecord, error) { + eur := SFlowExtendedURLRecord{} + var fdf SFlowFlowDataFormat + var urlLen uint32 + var urlLenWithPad int + var hostLen uint32 + var hostLenWithPad int + var urlBytes []byte + var hostBytes []byte + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eur.EnterpriseID, eur.Format = fdf.decode() + *data, eur.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eur.Direction = (*data)[4:], SFlowURLDirection(binary.BigEndian.Uint32((*data)[:4])) + *data, urlLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + urlLenWithPad = int(urlLen + ((4 - urlLen) % 4)) + *data, urlBytes = (*data)[urlLenWithPad:], (*data)[:urlLenWithPad] + eur.URL = string(urlBytes[:urlLen]) + *data, hostLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + hostLenWithPad = int(hostLen + ((4 - hostLen) % 4)) + *data, hostBytes = (*data)[hostLenWithPad:], (*data)[:hostLenWithPad] + eur.Host = string(hostBytes[:hostLen]) + return eur, nil +} + +// ************************************************** +// Extended User Flow Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Character Set | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source User Id | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination Character Set | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination User ID | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowExtendedUserFlow struct { + SFlowBaseFlowRecord + SourceCharSet SFlowCharSet + SourceUserID string + DestinationCharSet SFlowCharSet + DestinationUserID string +} + +type SFlowCharSet uint32 + +const ( + SFlowCSunknown SFlowCharSet = 2 + SFlowCSASCII SFlowCharSet = 3 + SFlowCSISOLatin1 SFlowCharSet = 4 + SFlowCSISOLatin2 SFlowCharSet = 5 + SFlowCSISOLatin3 SFlowCharSet = 6 + SFlowCSISOLatin4 SFlowCharSet = 7 + SFlowCSISOLatinCyrillic SFlowCharSet = 8 + SFlowCSISOLatinArabic SFlowCharSet = 9 + SFlowCSISOLatinGreek SFlowCharSet = 10 + SFlowCSISOLatinHebrew SFlowCharSet = 11 + SFlowCSISOLatin5 SFlowCharSet = 12 + SFlowCSISOLatin6 SFlowCharSet = 13 + SFlowCSISOTextComm SFlowCharSet = 14 + SFlowCSHalfWidthKatakana SFlowCharSet = 15 + SFlowCSJISEncoding SFlowCharSet = 16 + SFlowCSShiftJIS SFlowCharSet = 17 + SFlowCSEUCPkdFmtJapanese SFlowCharSet = 18 + SFlowCSEUCFixWidJapanese SFlowCharSet = 19 + SFlowCSISO4UnitedKingdom SFlowCharSet = 20 + SFlowCSISO11SwedishForNames SFlowCharSet = 21 + SFlowCSISO15Italian SFlowCharSet = 22 + SFlowCSISO17Spanish SFlowCharSet = 23 + SFlowCSISO21German SFlowCharSet = 24 + SFlowCSISO60DanishNorwegian SFlowCharSet = 25 + SFlowCSISO69French SFlowCharSet = 26 + SFlowCSISO10646UTF1 SFlowCharSet = 27 + SFlowCSISO646basic1983 SFlowCharSet = 28 + SFlowCSINVARIANT SFlowCharSet = 29 + SFlowCSISO2IntlRefVersion SFlowCharSet = 30 + SFlowCSNATSSEFI SFlowCharSet = 31 + SFlowCSNATSSEFIADD SFlowCharSet = 32 + SFlowCSNATSDANO SFlowCharSet = 33 + SFlowCSNATSDANOADD SFlowCharSet = 34 + SFlowCSISO10Swedish SFlowCharSet = 35 + SFlowCSKSC56011987 SFlowCharSet = 36 + SFlowCSISO2022KR SFlowCharSet = 37 + SFlowCSEUCKR SFlowCharSet = 38 + SFlowCSISO2022JP SFlowCharSet = 39 + SFlowCSISO2022JP2 SFlowCharSet = 40 + SFlowCSISO13JISC6220jp SFlowCharSet = 41 + SFlowCSISO14JISC6220ro SFlowCharSet = 42 + SFlowCSISO16Portuguese SFlowCharSet = 43 + SFlowCSISO18Greek7Old SFlowCharSet = 44 + SFlowCSISO19LatinGreek SFlowCharSet = 45 + SFlowCSISO25French SFlowCharSet = 46 + SFlowCSISO27LatinGreek1 SFlowCharSet = 47 + SFlowCSISO5427Cyrillic SFlowCharSet = 48 + SFlowCSISO42JISC62261978 SFlowCharSet = 49 + SFlowCSISO47BSViewdata SFlowCharSet = 50 + SFlowCSISO49INIS SFlowCharSet = 51 + SFlowCSISO50INIS8 SFlowCharSet = 52 + SFlowCSISO51INISCyrillic SFlowCharSet = 53 + SFlowCSISO54271981 SFlowCharSet = 54 + SFlowCSISO5428Greek SFlowCharSet = 55 + SFlowCSISO57GB1988 SFlowCharSet = 56 + SFlowCSISO58GB231280 SFlowCharSet = 57 + SFlowCSISO61Norwegian2 SFlowCharSet = 58 + SFlowCSISO70VideotexSupp1 SFlowCharSet = 59 + SFlowCSISO84Portuguese2 SFlowCharSet = 60 + SFlowCSISO85Spanish2 SFlowCharSet = 61 + SFlowCSISO86Hungarian SFlowCharSet = 62 + SFlowCSISO87JISX0208 SFlowCharSet = 63 + SFlowCSISO88Greek7 SFlowCharSet = 64 + SFlowCSISO89ASMO449 SFlowCharSet = 65 + SFlowCSISO90 SFlowCharSet = 66 + SFlowCSISO91JISC62291984a SFlowCharSet = 67 + SFlowCSISO92JISC62991984b SFlowCharSet = 68 + SFlowCSISO93JIS62291984badd SFlowCharSet = 69 + SFlowCSISO94JIS62291984hand SFlowCharSet = 70 + SFlowCSISO95JIS62291984handadd SFlowCharSet = 71 + SFlowCSISO96JISC62291984kana SFlowCharSet = 72 + SFlowCSISO2033 SFlowCharSet = 73 + SFlowCSISO99NAPLPS SFlowCharSet = 74 + SFlowCSISO102T617bit SFlowCharSet = 75 + SFlowCSISO103T618bit SFlowCharSet = 76 + SFlowCSISO111ECMACyrillic SFlowCharSet = 77 + SFlowCSa71 SFlowCharSet = 78 + SFlowCSa72 SFlowCharSet = 79 + SFlowCSISO123CSAZ24341985gr SFlowCharSet = 80 + SFlowCSISO88596E SFlowCharSet = 81 + SFlowCSISO88596I SFlowCharSet = 82 + SFlowCSISO128T101G2 SFlowCharSet = 83 + SFlowCSISO88598E SFlowCharSet = 84 + SFlowCSISO88598I SFlowCharSet = 85 + SFlowCSISO139CSN369103 SFlowCharSet = 86 + SFlowCSISO141JUSIB1002 SFlowCharSet = 87 + SFlowCSISO143IECP271 SFlowCharSet = 88 + SFlowCSISO146Serbian SFlowCharSet = 89 + SFlowCSISO147Macedonian SFlowCharSet = 90 + SFlowCSISO150 SFlowCharSet = 91 + SFlowCSISO151Cuba SFlowCharSet = 92 + SFlowCSISO6937Add SFlowCharSet = 93 + SFlowCSISO153GOST1976874 SFlowCharSet = 94 + SFlowCSISO8859Supp SFlowCharSet = 95 + SFlowCSISO10367Box SFlowCharSet = 96 + SFlowCSISO158Lap SFlowCharSet = 97 + SFlowCSISO159JISX02121990 SFlowCharSet = 98 + SFlowCSISO646Danish SFlowCharSet = 99 + SFlowCSUSDK SFlowCharSet = 100 + SFlowCSDKUS SFlowCharSet = 101 + SFlowCSKSC5636 SFlowCharSet = 102 + SFlowCSUnicode11UTF7 SFlowCharSet = 103 + SFlowCSISO2022CN SFlowCharSet = 104 + SFlowCSISO2022CNEXT SFlowCharSet = 105 + SFlowCSUTF8 SFlowCharSet = 106 + SFlowCSISO885913 SFlowCharSet = 109 + SFlowCSISO885914 SFlowCharSet = 110 + SFlowCSISO885915 SFlowCharSet = 111 + SFlowCSISO885916 SFlowCharSet = 112 + SFlowCSGBK SFlowCharSet = 113 + SFlowCSGB18030 SFlowCharSet = 114 + SFlowCSOSDEBCDICDF0415 SFlowCharSet = 115 + SFlowCSOSDEBCDICDF03IRV SFlowCharSet = 116 + SFlowCSOSDEBCDICDF041 SFlowCharSet = 117 + SFlowCSISO115481 SFlowCharSet = 118 + SFlowCSKZ1048 SFlowCharSet = 119 + SFlowCSUnicode SFlowCharSet = 1000 + SFlowCSUCS4 SFlowCharSet = 1001 + SFlowCSUnicodeASCII SFlowCharSet = 1002 + SFlowCSUnicodeLatin1 SFlowCharSet = 1003 + SFlowCSUnicodeJapanese SFlowCharSet = 1004 + SFlowCSUnicodeIBM1261 SFlowCharSet = 1005 + SFlowCSUnicodeIBM1268 SFlowCharSet = 1006 + SFlowCSUnicodeIBM1276 SFlowCharSet = 1007 + SFlowCSUnicodeIBM1264 SFlowCharSet = 1008 + SFlowCSUnicodeIBM1265 SFlowCharSet = 1009 + SFlowCSUnicode11 SFlowCharSet = 1010 + SFlowCSSCSU SFlowCharSet = 1011 + SFlowCSUTF7 SFlowCharSet = 1012 + SFlowCSUTF16BE SFlowCharSet = 1013 + SFlowCSUTF16LE SFlowCharSet = 1014 + SFlowCSUTF16 SFlowCharSet = 1015 + SFlowCSCESU8 SFlowCharSet = 1016 + SFlowCSUTF32 SFlowCharSet = 1017 + SFlowCSUTF32BE SFlowCharSet = 1018 + SFlowCSUTF32LE SFlowCharSet = 1019 + SFlowCSBOCU1 SFlowCharSet = 1020 + SFlowCSWindows30Latin1 SFlowCharSet = 2000 + SFlowCSWindows31Latin1 SFlowCharSet = 2001 + SFlowCSWindows31Latin2 SFlowCharSet = 2002 + SFlowCSWindows31Latin5 SFlowCharSet = 2003 + SFlowCSHPRoman8 SFlowCharSet = 2004 + SFlowCSAdobeStandardEncoding SFlowCharSet = 2005 + SFlowCSVenturaUS SFlowCharSet = 2006 + SFlowCSVenturaInternational SFlowCharSet = 2007 + SFlowCSDECMCS SFlowCharSet = 2008 + SFlowCSPC850Multilingual SFlowCharSet = 2009 + SFlowCSPCp852 SFlowCharSet = 2010 + SFlowCSPC8CodePage437 SFlowCharSet = 2011 + SFlowCSPC8DanishNorwegian SFlowCharSet = 2012 + SFlowCSPC862LatinHebrew SFlowCharSet = 2013 + SFlowCSPC8Turkish SFlowCharSet = 2014 + SFlowCSIBMSymbols SFlowCharSet = 2015 + SFlowCSIBMThai SFlowCharSet = 2016 + SFlowCSHPLegal SFlowCharSet = 2017 + SFlowCSHPPiFont SFlowCharSet = 2018 + SFlowCSHPMath8 SFlowCharSet = 2019 + SFlowCSHPPSMath SFlowCharSet = 2020 + SFlowCSHPDesktop SFlowCharSet = 2021 + SFlowCSVenturaMath SFlowCharSet = 2022 + SFlowCSMicrosoftPublishing SFlowCharSet = 2023 + SFlowCSWindows31J SFlowCharSet = 2024 + SFlowCSGB2312 SFlowCharSet = 2025 + SFlowCSBig5 SFlowCharSet = 2026 + SFlowCSMacintosh SFlowCharSet = 2027 + SFlowCSIBM037 SFlowCharSet = 2028 + SFlowCSIBM038 SFlowCharSet = 2029 + SFlowCSIBM273 SFlowCharSet = 2030 + SFlowCSIBM274 SFlowCharSet = 2031 + SFlowCSIBM275 SFlowCharSet = 2032 + SFlowCSIBM277 SFlowCharSet = 2033 + SFlowCSIBM278 SFlowCharSet = 2034 + SFlowCSIBM280 SFlowCharSet = 2035 + SFlowCSIBM281 SFlowCharSet = 2036 + SFlowCSIBM284 SFlowCharSet = 2037 + SFlowCSIBM285 SFlowCharSet = 2038 + SFlowCSIBM290 SFlowCharSet = 2039 + SFlowCSIBM297 SFlowCharSet = 2040 + SFlowCSIBM420 SFlowCharSet = 2041 + SFlowCSIBM423 SFlowCharSet = 2042 + SFlowCSIBM424 SFlowCharSet = 2043 + SFlowCSIBM500 SFlowCharSet = 2044 + SFlowCSIBM851 SFlowCharSet = 2045 + SFlowCSIBM855 SFlowCharSet = 2046 + SFlowCSIBM857 SFlowCharSet = 2047 + SFlowCSIBM860 SFlowCharSet = 2048 + SFlowCSIBM861 SFlowCharSet = 2049 + SFlowCSIBM863 SFlowCharSet = 2050 + SFlowCSIBM864 SFlowCharSet = 2051 + SFlowCSIBM865 SFlowCharSet = 2052 + SFlowCSIBM868 SFlowCharSet = 2053 + SFlowCSIBM869 SFlowCharSet = 2054 + SFlowCSIBM870 SFlowCharSet = 2055 + SFlowCSIBM871 SFlowCharSet = 2056 + SFlowCSIBM880 SFlowCharSet = 2057 + SFlowCSIBM891 SFlowCharSet = 2058 + SFlowCSIBM903 SFlowCharSet = 2059 + SFlowCSIBBM904 SFlowCharSet = 2060 + SFlowCSIBM905 SFlowCharSet = 2061 + SFlowCSIBM918 SFlowCharSet = 2062 + SFlowCSIBM1026 SFlowCharSet = 2063 + SFlowCSIBMEBCDICATDE SFlowCharSet = 2064 + SFlowCSEBCDICATDEA SFlowCharSet = 2065 + SFlowCSEBCDICCAFR SFlowCharSet = 2066 + SFlowCSEBCDICDKNO SFlowCharSet = 2067 + SFlowCSEBCDICDKNOA SFlowCharSet = 2068 + SFlowCSEBCDICFISE SFlowCharSet = 2069 + SFlowCSEBCDICFISEA SFlowCharSet = 2070 + SFlowCSEBCDICFR SFlowCharSet = 2071 + SFlowCSEBCDICIT SFlowCharSet = 2072 + SFlowCSEBCDICPT SFlowCharSet = 2073 + SFlowCSEBCDICES SFlowCharSet = 2074 + SFlowCSEBCDICESA SFlowCharSet = 2075 + SFlowCSEBCDICESS SFlowCharSet = 2076 + SFlowCSEBCDICUK SFlowCharSet = 2077 + SFlowCSEBCDICUS SFlowCharSet = 2078 + SFlowCSUnknown8BiT SFlowCharSet = 2079 + SFlowCSMnemonic SFlowCharSet = 2080 + SFlowCSMnem SFlowCharSet = 2081 + SFlowCSVISCII SFlowCharSet = 2082 + SFlowCSVIQR SFlowCharSet = 2083 + SFlowCSKOI8R SFlowCharSet = 2084 + SFlowCSHZGB2312 SFlowCharSet = 2085 + SFlowCSIBM866 SFlowCharSet = 2086 + SFlowCSPC775Baltic SFlowCharSet = 2087 + SFlowCSKOI8U SFlowCharSet = 2088 + SFlowCSIBM00858 SFlowCharSet = 2089 + SFlowCSIBM00924 SFlowCharSet = 2090 + SFlowCSIBM01140 SFlowCharSet = 2091 + SFlowCSIBM01141 SFlowCharSet = 2092 + SFlowCSIBM01142 SFlowCharSet = 2093 + SFlowCSIBM01143 SFlowCharSet = 2094 + SFlowCSIBM01144 SFlowCharSet = 2095 + SFlowCSIBM01145 SFlowCharSet = 2096 + SFlowCSIBM01146 SFlowCharSet = 2097 + SFlowCSIBM01147 SFlowCharSet = 2098 + SFlowCSIBM01148 SFlowCharSet = 2099 + SFlowCSIBM01149 SFlowCharSet = 2100 + SFlowCSBig5HKSCS SFlowCharSet = 2101 + SFlowCSIBM1047 SFlowCharSet = 2102 + SFlowCSPTCP154 SFlowCharSet = 2103 + SFlowCSAmiga1251 SFlowCharSet = 2104 + SFlowCSKOI7switched SFlowCharSet = 2105 + SFlowCSBRF SFlowCharSet = 2106 + SFlowCSTSCII SFlowCharSet = 2107 + SFlowCSCP51932 SFlowCharSet = 2108 + SFlowCSWindows874 SFlowCharSet = 2109 + SFlowCSWindows1250 SFlowCharSet = 2250 + SFlowCSWindows1251 SFlowCharSet = 2251 + SFlowCSWindows1252 SFlowCharSet = 2252 + SFlowCSWindows1253 SFlowCharSet = 2253 + SFlowCSWindows1254 SFlowCharSet = 2254 + SFlowCSWindows1255 SFlowCharSet = 2255 + SFlowCSWindows1256 SFlowCharSet = 2256 + SFlowCSWindows1257 SFlowCharSet = 2257 + SFlowCSWindows1258 SFlowCharSet = 2258 + SFlowCSTIS620 SFlowCharSet = 2259 + SFlowCS50220 SFlowCharSet = 2260 + SFlowCSreserved SFlowCharSet = 3000 +) + +func decodeExtendedUserFlow(data *[]byte) (SFlowExtendedUserFlow, error) { + eu := SFlowExtendedUserFlow{} + var fdf SFlowFlowDataFormat + var srcUserLen uint32 + var srcUserLenWithPad int + var srcUserBytes []byte + var dstUserLen uint32 + var dstUserLenWithPad int + var dstUserBytes []byte + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + eu.EnterpriseID, eu.Format = fdf.decode() + *data, eu.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, eu.SourceCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) + *data, srcUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + srcUserLenWithPad = int(srcUserLen + ((4 - srcUserLen) % 4)) + *data, srcUserBytes = (*data)[srcUserLenWithPad:], (*data)[:srcUserLenWithPad] + eu.SourceUserID = string(srcUserBytes[:srcUserLen]) + *data, eu.DestinationCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4])) + *data, dstUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + dstUserLenWithPad = int(dstUserLen + ((4 - dstUserLen) % 4)) + *data, dstUserBytes = (*data)[dstUserLenWithPad:], (*data)[:dstUserLenWithPad] + eu.DestinationUserID = string(dstUserBytes[:dstUserLen]) + return eu, nil +} + +// ************************************************** +// Packet IP version 4 Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destionation Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TCP Flags | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TOS | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowIpv4Record struct { + // The length of the IP packet excluding ower layer encapsulations + Length uint32 + // IP Protocol type (for example, TCP = 6, UDP = 17) + Protocol uint32 + // Source IP Address + IPSrc net.IP + // Destination IP Address + IPDst net.IP + // TCP/UDP source port number or equivalent + PortSrc uint32 + // TCP/UDP destination port number or equivalent + PortDst uint32 + // TCP flags + TCPFlags uint32 + // IP type of service + TOS uint32 +} + +func decodeSFlowIpv4Record(data *[]byte) (SFlowIpv4Record, error) { + si := SFlowIpv4Record{} + + *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.IPSrc = (*data)[4:], net.IP((*data)[:4]) + *data, si.IPDst = (*data)[4:], net.IP((*data)[:4]) + *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TOS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return si, nil +} + +// ************************************************** +// Packet IP version 6 Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Protocol | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination IPv4 | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destionation Port | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TCP Flags | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Priority | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowIpv6Record struct { + // The length of the IP packet excluding ower layer encapsulations + Length uint32 + // IP Protocol type (for example, TCP = 6, UDP = 17) + Protocol uint32 + // Source IP Address + IPSrc net.IP + // Destination IP Address + IPDst net.IP + // TCP/UDP source port number or equivalent + PortSrc uint32 + // TCP/UDP destination port number or equivalent + PortDst uint32 + // TCP flags + TCPFlags uint32 + // IP priority + Priority uint32 +} + +func decodeSFlowIpv6Record(data *[]byte) (SFlowIpv6Record, error) { + si := SFlowIpv6Record{} + + *data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.IPSrc = (*data)[16:], net.IP((*data)[:16]) + *data, si.IPDst = (*data)[16:], net.IP((*data)[:16]) + *data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, si.Priority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return si, nil +} + +// ************************************************** +// Extended IPv4 Tunnel Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 4 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv4TunnelEgressRecord struct { + SFlowBaseFlowRecord + SFlowIpv4Record SFlowIpv4Record +} + +func decodeExtendedIpv4TunnelEgress(data *[]byte) (SFlowExtendedIpv4TunnelEgressRecord, error) { + rec := SFlowExtendedIpv4TunnelEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv4 Tunnel Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 4 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv4TunnelIngressRecord struct { + SFlowBaseFlowRecord + SFlowIpv4Record SFlowIpv4Record +} + +func decodeExtendedIpv4TunnelIngress(data *[]byte) (SFlowExtendedIpv4TunnelIngressRecord, error) { + rec := SFlowExtendedIpv4TunnelIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv6 Tunnel Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 6 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv6TunnelEgressRecord struct { + SFlowBaseFlowRecord + SFlowIpv6Record +} + +func decodeExtendedIpv6TunnelEgress(data *[]byte) (SFlowExtendedIpv6TunnelEgressRecord, error) { + rec := SFlowExtendedIpv6TunnelEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) + + return rec, nil +} + +// ************************************************** +// Extended IPv6 Tunnel Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / Packet IP version 6 Record / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedIpv6TunnelIngressRecord struct { + SFlowBaseFlowRecord + SFlowIpv6Record +} + +func decodeExtendedIpv6TunnelIngress(data *[]byte) (SFlowExtendedIpv6TunnelIngressRecord, error) { + rec := SFlowExtendedIpv6TunnelIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data) + + return rec, nil +} + +// ************************************************** +// Extended Decapsulate Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Inner Header Offset | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedDecapsulateEgressRecord struct { + SFlowBaseFlowRecord + InnerHeaderOffset uint32 +} + +func decodeExtendedDecapsulateEgress(data *[]byte) (SFlowExtendedDecapsulateEgressRecord, error) { + rec := SFlowExtendedDecapsulateEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended Decapsulate Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Inner Header Offset | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedDecapsulateIngressRecord struct { + SFlowBaseFlowRecord + InnerHeaderOffset uint32 +} + +func decodeExtendedDecapsulateIngress(data *[]byte) (SFlowExtendedDecapsulateIngressRecord, error) { + rec := SFlowExtendedDecapsulateIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended VNI Egress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | VNI | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedVniEgressRecord struct { + SFlowBaseFlowRecord + VNI uint32 +} + +func decodeExtendedVniEgress(data *[]byte) (SFlowExtendedVniEgressRecord, error) { + rec := SFlowExtendedVniEgressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Extended VNI Ingress +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | VNI | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type SFlowExtendedVniIngressRecord struct { + SFlowBaseFlowRecord + VNI uint32 +} + +func decodeExtendedVniIngress(data *[]byte) (SFlowExtendedVniIngressRecord, error) { + rec := SFlowExtendedVniIngressRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + rec.EnterpriseID, rec.Format = fdf.decode() + *data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return rec, nil +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter data / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowBaseCounterRecord struct { + EnterpriseID SFlowEnterpriseID + Format SFlowCounterRecordType + FlowDataLength uint32 +} + +func (bcr SFlowBaseCounterRecord) GetType() SFlowCounterRecordType { + switch bcr.Format { + case SFlowTypeGenericInterfaceCounters: + return SFlowTypeGenericInterfaceCounters + case SFlowTypeEthernetInterfaceCounters: + return SFlowTypeEthernetInterfaceCounters + case SFlowTypeTokenRingInterfaceCounters: + return SFlowTypeTokenRingInterfaceCounters + case SFlowType100BaseVGInterfaceCounters: + return SFlowType100BaseVGInterfaceCounters + case SFlowTypeVLANCounters: + return SFlowTypeVLANCounters + case SFlowTypeLACPCounters: + return SFlowTypeLACPCounters + case SFlowTypeProcessorCounters: + return SFlowTypeProcessorCounters + case SFlowTypeOpenflowPortCounters: + return SFlowTypeOpenflowPortCounters + case SFlowTypePORTNAMECounters: + return SFlowTypePORTNAMECounters + case SFLowTypeAPPRESOURCESCounters: + return SFLowTypeAPPRESOURCESCounters + case SFlowTypeOVSDPCounters: + return SFlowTypeOVSDPCounters + } + unrecognized := fmt.Sprint("Unrecognized counter record type:", bcr.Format) + panic(unrecognized) +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfIndex | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfType | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfSpeed | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfDirection | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfStatus | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IFInOctets | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInUcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInMulticastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInBroadcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInDiscards | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | InInErrors | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfInUnknownProtos | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutOctets | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutUcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutMulticastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutBroadcastPkts | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOutDiscards | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfOUtErrors | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | IfPromiscouousMode | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowGenericInterfaceCounters struct { + SFlowBaseCounterRecord + IfIndex uint32 + IfType uint32 + IfSpeed uint64 + IfDirection uint32 + IfStatus uint32 + IfInOctets uint64 + IfInUcastPkts uint32 + IfInMulticastPkts uint32 + IfInBroadcastPkts uint32 + IfInDiscards uint32 + IfInErrors uint32 + IfInUnknownProtos uint32 + IfOutOctets uint64 + IfOutUcastPkts uint32 + IfOutMulticastPkts uint32 + IfOutBroadcastPkts uint32 + IfOutDiscards uint32 + IfOutErrors uint32 + IfPromiscuousMode uint32 +} + +func decodeGenericInterfaceCounters(data *[]byte) (SFlowGenericInterfaceCounters, error) { + gic := SFlowGenericInterfaceCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + gic.EnterpriseID, gic.Format = cdf.decode() + *data, gic.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfIndex = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfType = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfSpeed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfDirection = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfStatus = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfInUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfInUnknownProtos = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, gic.IfOutUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfOutErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, gic.IfPromiscuousMode = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return gic, nil +} + +// ************************************************** +// Counter Record +// ************************************************** + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / counter data / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowEthernetCounters struct { + SFlowBaseCounterRecord + AlignmentErrors uint32 + FCSErrors uint32 + SingleCollisionFrames uint32 + MultipleCollisionFrames uint32 + SQETestErrors uint32 + DeferredTransmissions uint32 + LateCollisions uint32 + ExcessiveCollisions uint32 + InternalMacTransmitErrors uint32 + CarrierSenseErrors uint32 + FrameTooLongs uint32 + InternalMacReceiveErrors uint32 + SymbolErrors uint32 +} + +func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) { + ec := SFlowEthernetCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + ec.EnterpriseID, ec.Format = cdf.decode() + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + if len(*data) < 4 { + return SFlowEthernetCounters{}, errors.New("ethernet counters too small") + } + *data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return ec, nil +} + +// VLAN Counter + +type SFlowVLANCounters struct { + SFlowBaseCounterRecord + VlanID uint32 + Octets uint64 + UcastPkts uint32 + MulticastPkts uint32 + BroadcastPkts uint32 + Discards uint32 +} + +func decodeVLANCounters(data *[]byte) (SFlowVLANCounters, error) { + vc := SFlowVLANCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + vc.EnterpriseID, vc.Format = cdf.decode() + vc.EnterpriseID, vc.Format = cdf.decode() + *data, vc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, vc.VlanID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, vc.Octets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, vc.UcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, vc.MulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, vc.BroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, vc.Discards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return vc, nil +} + +//SFLLACPportState : SFlow LACP Port State (All(4) - 32 bit) +type SFLLACPPortState struct { + PortStateAll uint32 +} + +//LACPcounters : LACP SFlow Counters ( 64 Bytes ) +type SFlowLACPCounters struct { + SFlowBaseCounterRecord + ActorSystemID net.HardwareAddr + PartnerSystemID net.HardwareAddr + AttachedAggID uint32 + LacpPortState SFLLACPPortState + LACPDUsRx uint32 + MarkerPDUsRx uint32 + MarkerResponsePDUsRx uint32 + UnknownRx uint32 + IllegalRx uint32 + LACPDUsTx uint32 + MarkerPDUsTx uint32 + MarkerResponsePDUsTx uint32 +} + +func decodeLACPCounters(data *[]byte) (SFlowLACPCounters, error) { + la := SFlowLACPCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + la.EnterpriseID, la.Format = cdf.decode() + *data, la.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.ActorSystemID = (*data)[6:], (*data)[:6] + *data = (*data)[2:] // remove padding + *data, la.PartnerSystemID = (*data)[6:], (*data)[:6] + *data = (*data)[2:] //remove padding + *data, la.AttachedAggID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.LacpPortState.PortStateAll = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.LACPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.MarkerPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.MarkerResponsePDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.UnknownRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.IllegalRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.LACPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.MarkerPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, la.MarkerResponsePDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return la, nil + +} + +// ************************************************** +// Processor Counter Record +// ************************************************** +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | counter length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | FiveSecCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | OneMinCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | GiveMinCpu | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | TotalMemory | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | FreeMemory | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +type SFlowProcessorCounters struct { + SFlowBaseCounterRecord + FiveSecCpu uint32 // 5 second average CPU utilization + OneMinCpu uint32 // 1 minute average CPU utilization + FiveMinCpu uint32 // 5 minute average CPU utilization + TotalMemory uint64 // total memory (in bytes) + FreeMemory uint64 // free memory (in bytes) +} + +func decodeProcessorCounters(data *[]byte) (SFlowProcessorCounters, error) { + pc := SFlowProcessorCounters{} + var cdf SFlowCounterDataFormat + var high32, low32 uint32 + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + pc.EnterpriseID, pc.Format = cdf.decode() + *data, pc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + *data, pc.FiveSecCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, pc.OneMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, pc.FiveMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + pc.TotalMemory = (uint64(high32) << 32) + uint64(low32) + *data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + pc.FreeMemory = (uint64(high32)) + uint64(low32) + + return pc, nil +} + +// SFlowEthernetFrameFlowRecord give additional information +// about the sampled packet if it's available. +// An agent may or may not provide this information. +type SFlowEthernetFrameFlowRecord struct { + SFlowBaseFlowRecord + FrameLength uint32 + SrcMac net.HardwareAddr + DstMac net.HardwareAddr + Type uint32 +} + +// Ethernet frame flow records have the following structure: + +// 0 15 31 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 20 bit Interprise (0) |12 bit format | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | record length | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Source Mac Address | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Destination Mac Address | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | Ethernet Packet Type | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +func decodeEthernetFrameFlowRecord(data *[]byte) (SFlowEthernetFrameFlowRecord, error) { + es := SFlowEthernetFrameFlowRecord{} + var fdf SFlowFlowDataFormat + + *data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4])) + es.EnterpriseID, es.Format = fdf.decode() + *data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + *data, es.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, es.SrcMac = (*data)[8:], net.HardwareAddr((*data)[:6]) + *data, es.DstMac = (*data)[8:], net.HardwareAddr((*data)[:6]) + *data, es.Type = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + return es, nil +} + +//SFlowOpenflowPortCounters : OVS-Sflow OpenFlow Port Counter ( 20 Bytes ) +type SFlowOpenflowPortCounters struct { + SFlowBaseCounterRecord + DatapathID uint64 + PortNo uint32 +} + +func decodeOpenflowportCounters(data *[]byte) (SFlowOpenflowPortCounters, error) { + ofp := SFlowOpenflowPortCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + ofp.EnterpriseID, ofp.Format = cdf.decode() + *data, ofp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, ofp.DatapathID = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, ofp.PortNo = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return ofp, nil +} + +//SFlowAppresourcesCounters : OVS_Sflow App Resources Counter ( 48 Bytes ) +type SFlowAppresourcesCounters struct { + SFlowBaseCounterRecord + UserTime uint32 + SystemTime uint32 + MemUsed uint64 + MemMax uint64 + FdOpen uint32 + FdMax uint32 + ConnOpen uint32 + ConnMax uint32 +} + +func decodeAppresourcesCounters(data *[]byte) (SFlowAppresourcesCounters, error) { + app := SFlowAppresourcesCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + app.EnterpriseID, app.Format = cdf.decode() + *data, app.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.UserTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.SystemTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.MemUsed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, app.MemMax = (*data)[8:], binary.BigEndian.Uint64((*data)[:8]) + *data, app.FdOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.FdMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.ConnOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, app.ConnMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return app, nil +} + +//SFlowOVSDPCounters : OVS-Sflow DataPath Counter ( 32 Bytes ) +type SFlowOVSDPCounters struct { + SFlowBaseCounterRecord + NHit uint32 + NMissed uint32 + NLost uint32 + NMaskHit uint32 + NFlows uint32 + NMasks uint32 +} + +func decodeOVSDPCounters(data *[]byte) (SFlowOVSDPCounters, error) { + dp := SFlowOVSDPCounters{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + dp.EnterpriseID, dp.Format = cdf.decode() + *data, dp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NMissed = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NLost = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NMaskHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NFlows = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + *data, dp.NMasks = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + + return dp, nil +} + +//SFlowPORTNAME : OVS-Sflow PORTNAME Counter Sampletype ( 20 Bytes ) +type SFlowPORTNAME struct { + SFlowBaseCounterRecord + Len uint32 + Str string +} + +func decodeString(data *[]byte) (len uint32, str string) { + *data, len = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + str = string((*data)[:len]) + if (len % 4) != 0 { + len += 4 - len%4 + } + *data = (*data)[len:] + return +} + +func decodePortnameCounters(data *[]byte) (SFlowPORTNAME, error) { + pn := SFlowPORTNAME{} + var cdf SFlowCounterDataFormat + + *data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4])) + pn.EnterpriseID, pn.Format = cdf.decode() + *data, pn.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4]) + pn.Len, pn.Str = decodeString(data) + + return pn, nil +} diff --git a/vendor/github.com/google/gopacket/layers/sip.go b/vendor/github.com/google/gopacket/layers/sip.go new file mode 100644 index 00000000..70afdb5c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/sip.go @@ -0,0 +1,542 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/google/gopacket" +) + +// SIPVersion defines the different versions of the SIP Protocol +type SIPVersion uint8 + +// Represents all the versions of SIP protocol +const ( + SIPVersion1 SIPVersion = 1 + SIPVersion2 SIPVersion = 2 +) + +func (sv SIPVersion) String() string { + switch sv { + default: + // Defaulting to SIP/2.0 + return "SIP/2.0" + case SIPVersion1: + return "SIP/1.0" + case SIPVersion2: + return "SIP/2.0" + } +} + +// GetSIPVersion is used to get SIP version constant +func GetSIPVersion(version string) (SIPVersion, error) { + switch strings.ToUpper(version) { + case "SIP/1.0": + return SIPVersion1, nil + case "SIP/2.0": + return SIPVersion2, nil + default: + return 0, fmt.Errorf("Unknown SIP version: '%s'", version) + + } +} + +// SIPMethod defines the different methods of the SIP Protocol +// defined in the different RFC's +type SIPMethod uint16 + +// Here are all the SIP methods +const ( + SIPMethodInvite SIPMethod = 1 // INVITE [RFC3261] + SIPMethodAck SIPMethod = 2 // ACK [RFC3261] + SIPMethodBye SIPMethod = 3 // BYE [RFC3261] + SIPMethodCancel SIPMethod = 4 // CANCEL [RFC3261] + SIPMethodOptions SIPMethod = 5 // OPTIONS [RFC3261] + SIPMethodRegister SIPMethod = 6 // REGISTER [RFC3261] + SIPMethodPrack SIPMethod = 7 // PRACK [RFC3262] + SIPMethodSubscribe SIPMethod = 8 // SUBSCRIBE [RFC6665] + SIPMethodNotify SIPMethod = 9 // NOTIFY [RFC6665] + SIPMethodPublish SIPMethod = 10 // PUBLISH [RFC3903] + SIPMethodInfo SIPMethod = 11 // INFO [RFC6086] + SIPMethodRefer SIPMethod = 12 // REFER [RFC3515] + SIPMethodMessage SIPMethod = 13 // MESSAGE [RFC3428] + SIPMethodUpdate SIPMethod = 14 // UPDATE [RFC3311] + SIPMethodPing SIPMethod = 15 // PING [https://tools.ietf.org/html/draft-fwmiller-ping-03] +) + +func (sm SIPMethod) String() string { + switch sm { + default: + return "Unknown method" + case SIPMethodInvite: + return "INVITE" + case SIPMethodAck: + return "ACK" + case SIPMethodBye: + return "BYE" + case SIPMethodCancel: + return "CANCEL" + case SIPMethodOptions: + return "OPTIONS" + case SIPMethodRegister: + return "REGISTER" + case SIPMethodPrack: + return "PRACK" + case SIPMethodSubscribe: + return "SUBSCRIBE" + case SIPMethodNotify: + return "NOTIFY" + case SIPMethodPublish: + return "PUBLISH" + case SIPMethodInfo: + return "INFO" + case SIPMethodRefer: + return "REFER" + case SIPMethodMessage: + return "MESSAGE" + case SIPMethodUpdate: + return "UPDATE" + case SIPMethodPing: + return "PING" + } +} + +// GetSIPMethod returns the constant of a SIP method +// from its string +func GetSIPMethod(method string) (SIPMethod, error) { + switch strings.ToUpper(method) { + case "INVITE": + return SIPMethodInvite, nil + case "ACK": + return SIPMethodAck, nil + case "BYE": + return SIPMethodBye, nil + case "CANCEL": + return SIPMethodCancel, nil + case "OPTIONS": + return SIPMethodOptions, nil + case "REGISTER": + return SIPMethodRegister, nil + case "PRACK": + return SIPMethodPrack, nil + case "SUBSCRIBE": + return SIPMethodSubscribe, nil + case "NOTIFY": + return SIPMethodNotify, nil + case "PUBLISH": + return SIPMethodPublish, nil + case "INFO": + return SIPMethodInfo, nil + case "REFER": + return SIPMethodRefer, nil + case "MESSAGE": + return SIPMethodMessage, nil + case "UPDATE": + return SIPMethodUpdate, nil + case "PING": + return SIPMethodPing, nil + default: + return 0, fmt.Errorf("Unknown SIP method: '%s'", method) + } +} + +// Here is a correspondance between long header names and short +// as defined in rfc3261 in section 20 +var compactSipHeadersCorrespondance = map[string]string{ + "accept-contact": "a", + "allow-events": "u", + "call-id": "i", + "contact": "m", + "content-encoding": "e", + "content-length": "l", + "content-type": "c", + "event": "o", + "from": "f", + "identity": "y", + "refer-to": "r", + "referred-by": "b", + "reject-contact": "j", + "request-disposition": "d", + "session-expires": "x", + "subject": "s", + "supported": "k", + "to": "t", + "via": "v", +} + +// SIP object will contains information about decoded SIP packet. +// -> The SIP Version +// -> The SIP Headers (in a map[string][]string because of multiple headers with the same name +// -> The SIP Method +// -> The SIP Response code (if it's a response) +// -> The SIP Status line (if it's a response) +// You can easily know the type of the packet with the IsResponse boolean +// +type SIP struct { + BaseLayer + + // Base information + Version SIPVersion + Method SIPMethod + Headers map[string][]string + + // Request + RequestURI string + + // Response + IsResponse bool + ResponseCode int + ResponseStatus string + + // Private fields + cseq int64 + contentLength int64 + lastHeaderParsed string +} + +// decodeSIP decodes the byte slice into a SIP type. It also +// setups the application Layer in PacketBuilder. +func decodeSIP(data []byte, p gopacket.PacketBuilder) error { + s := NewSIP() + err := s.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(s) + p.SetApplicationLayer(s) + return nil +} + +// NewSIP instantiates a new empty SIP object +func NewSIP() *SIP { + s := new(SIP) + s.Headers = make(map[string][]string) + return s +} + +// LayerType returns gopacket.LayerTypeSIP. +func (s *SIP) LayerType() gopacket.LayerType { + return LayerTypeSIP +} + +// Payload returns the base layer payload +func (s *SIP) Payload() []byte { + return s.BaseLayer.Payload +} + +// CanDecode returns the set of layer types that this DecodingLayer can decode +func (s *SIP) CanDecode() gopacket.LayerClass { + return LayerTypeSIP +} + +// NextLayerType returns the layer type contained by this DecodingLayer +func (s *SIP) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +// DecodeFromBytes decodes the slice into the SIP struct. +func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + // Init some vars for parsing follow-up + var countLines int + var line []byte + var err error + var offset int + + // Iterate on all lines of the SIP Headers + // and stop when we reach the SDP (aka when the new line + // is at index 0 of the remaining packet) + buffer := bytes.NewBuffer(data) + + for { + + // Read next line + line, err = buffer.ReadBytes(byte('\n')) + if err != nil { + if err == io.EOF { + if len(bytes.Trim(line, "\r\n")) > 0 { + df.SetTruncated() + } + break + } else { + return err + } + } + offset += len(line) + + // Trim the new line delimiters + line = bytes.Trim(line, "\r\n") + + // Empty line, we hit Body + if len(line) == 0 { + break + } + + // First line is the SIP request/response line + // Other lines are headers + if countLines == 0 { + err = s.ParseFirstLine(line) + if err != nil { + return err + } + + } else { + err = s.ParseHeader(line) + if err != nil { + return err + } + } + + countLines++ + } + s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]} + + return nil +} + +// ParseFirstLine will compute the first line of a SIP packet. +// The first line will tell us if it's a request or a response. +// +// Examples of first line of SIP Prococol : +// +// Request : INVITE bob@example.com SIP/2.0 +// Response : SIP/2.0 200 OK +// Response : SIP/2.0 501 Not Implemented +// +func (s *SIP) ParseFirstLine(firstLine []byte) error { + + var err error + + // Splits line by space + splits := strings.SplitN(string(firstLine), " ", 3) + + // We must have at least 3 parts + if len(splits) < 3 { + return fmt.Errorf("invalid first SIP line: '%s'", string(firstLine)) + } + + // Determine the SIP packet type + if strings.HasPrefix(splits[0], "SIP") { + + // --> Response + s.IsResponse = true + + // Validate SIP Version + s.Version, err = GetSIPVersion(splits[0]) + if err != nil { + return err + } + + // Compute code + s.ResponseCode, err = strconv.Atoi(splits[1]) + if err != nil { + return err + } + + // Compute status line + s.ResponseStatus = splits[2] + + } else { + + // --> Request + + // Validate method + s.Method, err = GetSIPMethod(splits[0]) + if err != nil { + return err + } + + s.RequestURI = splits[1] + + // Validate SIP Version + s.Version, err = GetSIPVersion(splits[2]) + if err != nil { + return err + } + } + + return nil +} + +// ParseHeader will parse a SIP Header +// SIP Headers are quite simple, there are colon separated name and value +// Headers can be spread over multiple lines +// +// Examples of header : +// +// CSeq: 1 REGISTER +// Via: SIP/2.0/UDP there.com:5060 +// Authorization:Digest username="UserB", +// realm="MCI WorldCom SIP", +// nonce="1cec4341ae6cbe5a359ea9c8e88df84f", opaque="", +// uri="sip:ss2.wcom.com", response="71ba27c64bd01de719686aa4590d5824" +// +func (s *SIP) ParseHeader(header []byte) (err error) { + + // Ignore empty headers + if len(header) == 0 { + return + } + + // Check if this is the following of last header + // RFC 3261 - 7.3.1 - Header Field Format specify that following lines of + // multiline headers must begin by SP or TAB + if header[0] == '\t' || header[0] == ' ' { + + header = bytes.TrimSpace(header) + s.Headers[s.lastHeaderParsed][len(s.Headers[s.lastHeaderParsed])-1] += fmt.Sprintf(" %s", string(header)) + return + } + + // Find the ':' to separate header name and value + index := bytes.Index(header, []byte(":")) + if index >= 0 { + + headerName := strings.ToLower(string(bytes.Trim(header[:index], " "))) + headerValue := string(bytes.Trim(header[index+1:], " ")) + + // Add header to object + s.Headers[headerName] = append(s.Headers[headerName], headerValue) + s.lastHeaderParsed = headerName + + // Compute specific headers + err = s.ParseSpecificHeaders(headerName, headerValue) + if err != nil { + return err + } + } + + return nil +} + +// ParseSpecificHeaders will parse some specific key values from +// specific headers like CSeq or Content-Length integer values +func (s *SIP) ParseSpecificHeaders(headerName string, headerValue string) (err error) { + + switch headerName { + case "cseq": + + // CSeq header value is formatted like that : + // CSeq: 123 INVITE + // We split the value to parse Cseq integer value, and method + splits := strings.Split(headerValue, " ") + if len(splits) > 1 { + + // Parse Cseq + s.cseq, err = strconv.ParseInt(splits[0], 10, 64) + if err != nil { + return err + } + + // Validate method + if s.IsResponse { + s.Method, err = GetSIPMethod(splits[1]) + if err != nil { + return err + } + } + } + + case "content-length": + + // Parse Content-Length + s.contentLength, err = strconv.ParseInt(headerValue, 10, 64) + if err != nil { + return err + } + } + + return nil +} + +// GetAllHeaders will return the full headers of the +// current SIP packets in a map[string][]string +func (s *SIP) GetAllHeaders() map[string][]string { + return s.Headers +} + +// GetHeader will return all the headers with +// the specified name. +func (s *SIP) GetHeader(headerName string) []string { + headerName = strings.ToLower(headerName) + h := make([]string, 0) + if _, ok := s.Headers[headerName]; ok { + return s.Headers[headerName] + } + compactHeader := compactSipHeadersCorrespondance[headerName] + if _, ok := s.Headers[compactHeader]; ok { + return s.Headers[compactHeader] + } + return h +} + +// GetFirstHeader will return the first header with +// the specified name. If the current SIP packet has multiple +// headers with the same name, it returns the first. +func (s *SIP) GetFirstHeader(headerName string) string { + headers := s.GetHeader(headerName) + if len(headers) > 0 { + return headers[0] + } + return "" +} + +// +// Some handy getters for most used SIP headers +// + +// GetAuthorization will return the Authorization +// header of the current SIP packet +func (s *SIP) GetAuthorization() string { + return s.GetFirstHeader("Authorization") +} + +// GetFrom will return the From +// header of the current SIP packet +func (s *SIP) GetFrom() string { + return s.GetFirstHeader("From") +} + +// GetTo will return the To +// header of the current SIP packet +func (s *SIP) GetTo() string { + return s.GetFirstHeader("To") +} + +// GetContact will return the Contact +// header of the current SIP packet +func (s *SIP) GetContact() string { + return s.GetFirstHeader("Contact") +} + +// GetCallID will return the Call-ID +// header of the current SIP packet +func (s *SIP) GetCallID() string { + return s.GetFirstHeader("Call-ID") +} + +// GetUserAgent will return the User-Agent +// header of the current SIP packet +func (s *SIP) GetUserAgent() string { + return s.GetFirstHeader("User-Agent") +} + +// GetContentLength will return the parsed integer +// Content-Length header of the current SIP packet +func (s *SIP) GetContentLength() int64 { + return s.contentLength +} + +// GetCSeq will return the parsed integer CSeq header +// header of the current SIP packet +func (s *SIP) GetCSeq() int64 { + return s.cseq +} diff --git a/vendor/github.com/google/gopacket/layers/stp.go b/vendor/github.com/google/gopacket/layers/stp.go new file mode 100644 index 00000000..bde7d7c8 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/stp.go @@ -0,0 +1,27 @@ +// Copyright 2017 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message. +type STP struct { + BaseLayer +} + +// LayerType returns gopacket.LayerTypeSTP. +func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP } + +func decodeSTP(data []byte, p gopacket.PacketBuilder) error { + stp := &STP{} + stp.Contents = data[:] + // TODO: parse the STP protocol into actual subfields. + p.AddLayer(stp) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/tcp.go b/vendor/github.com/google/gopacket/layers/tcp.go new file mode 100644 index 00000000..bcdeb4b3 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcp.go @@ -0,0 +1,341 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// TCP is the layer for TCP headers. +type TCP struct { + BaseLayer + SrcPort, DstPort TCPPort + Seq uint32 + Ack uint32 + DataOffset uint8 + FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool + Window uint16 + Checksum uint16 + Urgent uint16 + sPort, dPort []byte + Options []TCPOption + Padding []byte + opts [4]TCPOption + tcpipchecksum +} + +// TCPOptionKind represents a TCP option code. +type TCPOptionKind uint8 + +const ( + TCPOptionKindEndList = 0 + TCPOptionKindNop = 1 + TCPOptionKindMSS = 2 // len = 4 + TCPOptionKindWindowScale = 3 // len = 3 + TCPOptionKindSACKPermitted = 4 // len = 2 + TCPOptionKindSACK = 5 // len = n + TCPOptionKindEcho = 6 // len = 6, obsolete + TCPOptionKindEchoReply = 7 // len = 6, obsolete + TCPOptionKindTimestamps = 8 // len = 10 + TCPOptionKindPartialOrderConnectionPermitted = 9 // len = 2, obsolete + TCPOptionKindPartialOrderServiceProfile = 10 // len = 3, obsolete + TCPOptionKindCC = 11 // obsolete + TCPOptionKindCCNew = 12 // obsolete + TCPOptionKindCCEcho = 13 // obsolete + TCPOptionKindAltChecksum = 14 // len = 3, obsolete + TCPOptionKindAltChecksumData = 15 // len = n, obsolete +) + +func (k TCPOptionKind) String() string { + switch k { + case TCPOptionKindEndList: + return "EndList" + case TCPOptionKindNop: + return "NOP" + case TCPOptionKindMSS: + return "MSS" + case TCPOptionKindWindowScale: + return "WindowScale" + case TCPOptionKindSACKPermitted: + return "SACKPermitted" + case TCPOptionKindSACK: + return "SACK" + case TCPOptionKindEcho: + return "Echo" + case TCPOptionKindEchoReply: + return "EchoReply" + case TCPOptionKindTimestamps: + return "Timestamps" + case TCPOptionKindPartialOrderConnectionPermitted: + return "PartialOrderConnectionPermitted" + case TCPOptionKindPartialOrderServiceProfile: + return "PartialOrderServiceProfile" + case TCPOptionKindCC: + return "CC" + case TCPOptionKindCCNew: + return "CCNew" + case TCPOptionKindCCEcho: + return "CCEcho" + case TCPOptionKindAltChecksum: + return "AltChecksum" + case TCPOptionKindAltChecksumData: + return "AltChecksumData" + default: + return fmt.Sprintf("Unknown(%d)", k) + } +} + +type TCPOption struct { + OptionType TCPOptionKind + OptionLength uint8 + OptionData []byte +} + +func (t TCPOption) String() string { + hd := hex.EncodeToString(t.OptionData) + if len(hd) > 0 { + hd = " 0x" + hd + } + switch t.OptionType { + case TCPOptionKindMSS: + if len(t.OptionData) >= 2 { + return fmt.Sprintf("TCPOption(%s:%v%s)", + t.OptionType, + binary.BigEndian.Uint16(t.OptionData), + hd) + } + + case TCPOptionKindTimestamps: + if len(t.OptionData) == 8 { + return fmt.Sprintf("TCPOption(%s:%v/%v%s)", + t.OptionType, + binary.BigEndian.Uint32(t.OptionData[:4]), + binary.BigEndian.Uint32(t.OptionData[4:8]), + hd) + } + } + return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd) +} + +// LayerType returns gopacket.LayerTypeTCP +func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP } + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var optionLength int + for _, o := range t.Options { + switch o.OptionType { + case 0, 1: + optionLength += 1 + default: + optionLength += 2 + len(o.OptionData) + } + } + if opts.FixLengths { + if rem := optionLength % 4; rem != 0 { + t.Padding = lotsOfZeros[:4-rem] + } + t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4) + } + bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding)) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort)) + binary.BigEndian.PutUint32(bytes[4:], t.Seq) + binary.BigEndian.PutUint32(bytes[8:], t.Ack) + binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset()) + binary.BigEndian.PutUint16(bytes[14:], t.Window) + binary.BigEndian.PutUint16(bytes[18:], t.Urgent) + start := 20 + for _, o := range t.Options { + bytes[start] = byte(o.OptionType) + switch o.OptionType { + case 0, 1: + start++ + default: + if opts.FixLengths { + o.OptionLength = uint8(len(o.OptionData) + 2) + } + bytes[start+1] = o.OptionLength + copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData) + start += len(o.OptionData) + 2 + } + } + copy(bytes[start:], t.Padding) + if opts.ComputeChecksums { + // zero out checksum bytes in current serialization. + bytes[16] = 0 + bytes[17] = 0 + csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP) + if err != nil { + return err + } + t.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[16:], t.Checksum) + return nil +} + +func (t *TCP) ComputeChecksum() (uint16, error) { + return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP) +} + +func (t *TCP) flagsAndOffset() uint16 { + f := uint16(t.DataOffset) << 12 + if t.FIN { + f |= 0x0001 + } + if t.SYN { + f |= 0x0002 + } + if t.RST { + f |= 0x0004 + } + if t.PSH { + f |= 0x0008 + } + if t.ACK { + f |= 0x0010 + } + if t.URG { + f |= 0x0020 + } + if t.ECE { + f |= 0x0040 + } + if t.CWR { + f |= 0x0080 + } + if t.NS { + f |= 0x0100 + } + return f +} + +func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 20 { + df.SetTruncated() + return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data)) + } + tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2])) + tcp.sPort = data[0:2] + tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4])) + tcp.dPort = data[2:4] + tcp.Seq = binary.BigEndian.Uint32(data[4:8]) + tcp.Ack = binary.BigEndian.Uint32(data[8:12]) + tcp.DataOffset = data[12] >> 4 + tcp.FIN = data[13]&0x01 != 0 + tcp.SYN = data[13]&0x02 != 0 + tcp.RST = data[13]&0x04 != 0 + tcp.PSH = data[13]&0x08 != 0 + tcp.ACK = data[13]&0x10 != 0 + tcp.URG = data[13]&0x20 != 0 + tcp.ECE = data[13]&0x40 != 0 + tcp.CWR = data[13]&0x80 != 0 + tcp.NS = data[12]&0x01 != 0 + tcp.Window = binary.BigEndian.Uint16(data[14:16]) + tcp.Checksum = binary.BigEndian.Uint16(data[16:18]) + tcp.Urgent = binary.BigEndian.Uint16(data[18:20]) + if tcp.Options == nil { + // Pre-allocate to avoid allocating a slice. + tcp.Options = tcp.opts[:0] + } else { + tcp.Options = tcp.Options[:0] + } + tcp.Padding = tcp.Padding[:0] + if tcp.DataOffset < 5 { + return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset) + } + dataStart := int(tcp.DataOffset) * 4 + if dataStart > len(data) { + df.SetTruncated() + tcp.Payload = nil + tcp.Contents = data + return errors.New("TCP data offset greater than packet length") + } + tcp.Contents = data[:dataStart] + tcp.Payload = data[dataStart:] + // From here on, data points just to the header options. + data = data[20:dataStart] +OPTIONS: + for len(data) > 0 { + tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])}) + opt := &tcp.Options[len(tcp.Options)-1] + switch opt.OptionType { + case TCPOptionKindEndList: // End of options + opt.OptionLength = 1 + tcp.Padding = data[1:] + break OPTIONS + case TCPOptionKindNop: // 1 byte padding + opt.OptionLength = 1 + default: + if len(data) < 2 { + df.SetTruncated() + return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data)) + } + opt.OptionLength = data[1] + if opt.OptionLength < 2 { + return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength) + } else if int(opt.OptionLength) > len(data) { + df.SetTruncated() + return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data)) + } + opt.OptionData = data[2:opt.OptionLength] + } + data = data[opt.OptionLength:] + } + return nil +} + +func (t *TCP) CanDecode() gopacket.LayerClass { + return LayerTypeTCP +} + +func (t *TCP) NextLayerType() gopacket.LayerType { + lt := t.DstPort.LayerType() + if lt == gopacket.LayerTypePayload { + lt = t.SrcPort.LayerType() + } + return lt +} + +func decodeTCP(data []byte, p gopacket.PacketBuilder) error { + tcp := &TCP{} + err := tcp.DecodeFromBytes(data, p) + p.AddLayer(tcp) + p.SetTransportLayer(tcp) + if err != nil { + return err + } + if p.DecodeOptions().DecodeStreamsAsDatagrams { + return p.NextDecoder(tcp.NextLayerType()) + } else { + return p.NextDecoder(gopacket.LayerTypePayload) + } +} + +func (t *TCP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort) +} + +// For testing only +func (t *TCP) SetInternalPortsForTesting() { + t.sPort = make([]byte, 2) + t.dPort = make([]byte, 2) + binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort)) + binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort)) +} diff --git a/vendor/github.com/google/gopacket/layers/tcpip.go b/vendor/github.com/google/gopacket/layers/tcpip.go new file mode 100644 index 00000000..64ba51cc --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tcpip.go @@ -0,0 +1,104 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// Checksum computation for TCP/UDP. +type tcpipchecksum struct { + pseudoheader tcpipPseudoHeader +} + +type tcpipPseudoHeader interface { + pseudoheaderChecksum() (uint32, error) +} + +func (ip *IPv4) pseudoheaderChecksum() (csum uint32, err error) { + if err := ip.AddressTo4(); err != nil { + return 0, err + } + csum += (uint32(ip.SrcIP[0]) + uint32(ip.SrcIP[2])) << 8 + csum += uint32(ip.SrcIP[1]) + uint32(ip.SrcIP[3]) + csum += (uint32(ip.DstIP[0]) + uint32(ip.DstIP[2])) << 8 + csum += uint32(ip.DstIP[1]) + uint32(ip.DstIP[3]) + return csum, nil +} + +func (ip *IPv6) pseudoheaderChecksum() (csum uint32, err error) { + if err := ip.AddressTo16(); err != nil { + return 0, err + } + for i := 0; i < 16; i += 2 { + csum += uint32(ip.SrcIP[i]) << 8 + csum += uint32(ip.SrcIP[i+1]) + csum += uint32(ip.DstIP[i]) << 8 + csum += uint32(ip.DstIP[i+1]) + } + return csum, nil +} + +// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any +// initial checksum data that's already been computed. +func tcpipChecksum(data []byte, csum uint32) uint16 { + // to handle odd lengths, we loop to length - 1, incrementing by 2, then + // handle the last byte specifically by checking against the original + // length. + length := len(data) - 1 + for i := 0; i < length; i += 2 { + // For our test packet, doing this manually is about 25% faster + // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16. + csum += uint32(data[i]) << 8 + csum += uint32(data[i+1]) + } + if len(data)%2 == 1 { + csum += uint32(data[length]) << 8 + } + for csum > 0xffff { + csum = (csum >> 16) + (csum & 0xffff) + } + return ^uint16(csum) +} + +// computeChecksum computes a TCP or UDP checksum. headerAndPayload is the +// serialized TCP or UDP header plus its payload, with the checksum zero'd +// out. headerProtocol is the IP protocol number of the upper-layer header. +func (c *tcpipchecksum) computeChecksum(headerAndPayload []byte, headerProtocol IPProtocol) (uint16, error) { + if c.pseudoheader == nil { + return 0, errors.New("TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use") + } + length := uint32(len(headerAndPayload)) + csum, err := c.pseudoheader.pseudoheaderChecksum() + if err != nil { + return 0, err + } + csum += uint32(headerProtocol) + csum += length & 0xffff + csum += length >> 16 + return tcpipChecksum(headerAndPayload, csum), nil +} + +// SetNetworkLayerForChecksum tells this layer which network layer is wrapping it. +// This is needed for computing the checksum when serializing, since TCP/IP transport +// layer checksums depends on fields in the IPv4 or IPv6 layer that contains it. +// The passed in layer must be an *IPv4 or *IPv6. +func (i *tcpipchecksum) SetNetworkLayerForChecksum(l gopacket.NetworkLayer) error { + switch v := l.(type) { + case *IPv4: + i.pseudoheader = v + case *IPv6: + i.pseudoheader = v + default: + return fmt.Errorf("cannot use layer type %v for tcp checksum network layer", l.LayerType()) + } + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/test_creator.py b/vendor/github.com/google/gopacket/layers/test_creator.py new file mode 100644 index 00000000..c92d2765 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/test_creator.py @@ -0,0 +1,103 @@ +#!/usr/bin/python +# Copyright 2012 Google, Inc. All rights reserved. + +"""TestCreator creates test templates from pcap files.""" + +import argparse +import base64 +import glob +import re +import string +import subprocess +import sys + + +class Packet(object): + """Helper class encapsulating packet from a pcap file.""" + + def __init__(self, packet_lines): + self.packet_lines = packet_lines + self.data = self._DecodeText(packet_lines) + + @classmethod + def _DecodeText(cls, packet_lines): + packet_bytes = [] + # First line is timestamp and stuff, skip it. + # Format: 0x0010: 0000 0020 3aff 3ffe 0000 0000 0000 0000 ....:.?......... + + for line in packet_lines[1:]: + m = re.match(r'\s+0x[a-f\d]+:\s+((?:[\da-f]{2,4}\s)*)', line, re.IGNORECASE) + if m is None: continue + for hexpart in m.group(1).split(): + packet_bytes.append(base64.b16decode(hexpart.upper())) + return ''.join(packet_bytes) + + def Test(self, name, link_type): + """Yields a test using this packet, as a set of lines.""" + yield '// testPacket%s is the packet:' % name + for line in self.packet_lines: + yield '// ' + line + yield 'var testPacket%s = []byte{' % name + data = list(self.data) + while data: + linebytes, data = data[:16], data[16:] + yield ''.join(['\t'] + ['0x%02x, ' % ord(c) for c in linebytes]) + yield '}' + yield 'func TestPacket%s(t *testing.T) {' % name + yield '\tp := gopacket.NewPacket(testPacket%s, LinkType%s, gopacket.Default)' % (name, link_type) + yield '\tif p.ErrorLayer() != nil {' + yield '\t\tt.Error("Failed to decode packet:", p.ErrorLayer().Error())' + yield '\t}' + yield '\tcheckLayers(p, []gopacket.LayerType{LayerType%s, FILL_ME_IN_WITH_ACTUAL_LAYERS}, t)' % link_type + yield '}' + yield 'func BenchmarkDecodePacket%s(b *testing.B) {' % name + yield '\tfor i := 0; i < b.N; i++ {' + yield '\t\tgopacket.NewPacket(testPacket%s, LinkType%s, gopacket.NoCopy)' % (name, link_type) + yield '\t}' + yield '}' + + + +def GetTcpdumpOutput(filename): + """Runs tcpdump on the given file, returning output as string.""" + return subprocess.check_output( + ['tcpdump', '-XX', '-s', '0', '-n', '-r', filename]) + + +def TcpdumpOutputToPackets(output): + """Reads a pcap file with TCPDump, yielding Packet objects.""" + pdata = [] + for line in output.splitlines(): + if line[0] not in string.whitespace and pdata: + yield Packet(pdata) + pdata = [] + pdata.append(line) + if pdata: + yield Packet(pdata) + + +def main(): + class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter): + def _format_usage(self, usage, actions, groups, prefix=None): + header =('TestCreator creates gopacket tests using a pcap file.\n\n' + 'Tests are written to standard out... they can then be \n' + 'copied into the file of your choice and modified as \n' + 'you see.\n\n') + return header + argparse.ArgumentDefaultsHelpFormatter._format_usage( + self, usage, actions, groups, prefix) + + parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter) + parser.add_argument('--link_type', default='Ethernet', help='the link type (default: %(default)s)') + parser.add_argument('--name', default='Packet%d', help='the layer type, must have "%d" inside it') + parser.add_argument('files', metavar='file.pcap', type=str, nargs='+', help='the files to process') + + args = parser.parse_args() + + for arg in args.files: + for path in glob.glob(arg): + for i, packet in enumerate(TcpdumpOutputToPackets(GetTcpdumpOutput(path))): + print '\n'.join(packet.Test( + args.name % i, args.link_type)) + +if __name__ == '__main__': + main() diff --git a/vendor/github.com/google/gopacket/layers/tls.go b/vendor/github.com/google/gopacket/layers/tls.go new file mode 100644 index 00000000..5a155d45 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tls.go @@ -0,0 +1,283 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + + "github.com/google/gopacket" +) + +// TLSType defines the type of data after the TLS Record +type TLSType uint8 + +// TLSType known values. +const ( + TLSChangeCipherSpec TLSType = 20 + TLSAlert TLSType = 21 + TLSHandshake TLSType = 22 + TLSApplicationData TLSType = 23 + TLSUnknown TLSType = 255 +) + +// String shows the register type nicely formatted +func (tt TLSType) String() string { + switch tt { + default: + return "Unknown" + case TLSChangeCipherSpec: + return "Change Cipher Spec" + case TLSAlert: + return "Alert" + case TLSHandshake: + return "Handshake" + case TLSApplicationData: + return "Application Data" + } +} + +// TLSVersion represents the TLS version in numeric format +type TLSVersion uint16 + +// Strings shows the TLS version nicely formatted +func (tv TLSVersion) String() string { + switch tv { + default: + return "Unknown" + case 0x0200: + return "SSL 2.0" + case 0x0300: + return "SSL 3.0" + case 0x0301: + return "TLS 1.0" + case 0x0302: + return "TLS 1.1" + case 0x0303: + return "TLS 1.2" + case 0x0304: + return "TLS 1.3" + } +} + +// TLS is specified in RFC 5246 +// +// TLS Record Protocol +// 0 1 2 3 4 5 6 7 8 +// +--+--+--+--+--+--+--+--+ +// | Content Type | +// +--+--+--+--+--+--+--+--+ +// | Version (major) | +// +--+--+--+--+--+--+--+--+ +// | Version (minor) | +// +--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+ +// | Length | +// +--+--+--+--+--+--+--+--+ + +// TLS is actually a slide of TLSrecord structures +type TLS struct { + BaseLayer + + // TLS Records + ChangeCipherSpec []TLSChangeCipherSpecRecord + Handshake []TLSHandshakeRecord + AppData []TLSAppDataRecord + Alert []TLSAlertRecord +} + +// TLSRecordHeader contains all the information that each TLS Record types should have +type TLSRecordHeader struct { + ContentType TLSType + Version TLSVersion + Length uint16 +} + +// LayerType returns gopacket.LayerTypeTLS. +func (t *TLS) LayerType() gopacket.LayerType { return LayerTypeTLS } + +// decodeTLS decodes the byte slice into a TLS type. It also +// setups the application Layer in PacketBuilder. +func decodeTLS(data []byte, p gopacket.PacketBuilder) error { + t := &TLS{} + err := t.DecodeFromBytes(data, p) + if err != nil { + return err + } + p.AddLayer(t) + p.SetApplicationLayer(t) + return nil +} + +// DecodeFromBytes decodes the slice into the TLS struct. +func (t *TLS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + t.BaseLayer.Contents = data + t.BaseLayer.Payload = nil + + t.ChangeCipherSpec = t.ChangeCipherSpec[:0] + t.Handshake = t.Handshake[:0] + t.AppData = t.AppData[:0] + t.Alert = t.Alert[:0] + + return t.decodeTLSRecords(data, df) +} + +func (t *TLS) decodeTLSRecords(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 5 { + df.SetTruncated() + return errors.New("TLS record too short") + } + + // since there are no further layers, the baselayer's content is + // pointing to this layer + // TODO: Consider removing this + t.BaseLayer = BaseLayer{Contents: data[:len(data)]} + + var h TLSRecordHeader + h.ContentType = TLSType(data[0]) + h.Version = TLSVersion(binary.BigEndian.Uint16(data[1:3])) + h.Length = binary.BigEndian.Uint16(data[3:5]) + + if h.ContentType.String() == "Unknown" { + return errors.New("Unknown TLS record type") + } + + hl := 5 // header length + tl := hl + int(h.Length) + if len(data) < tl { + df.SetTruncated() + return errors.New("TLS packet length mismatch") + } + + switch h.ContentType { + default: + return errors.New("Unknown TLS record type") + case TLSChangeCipherSpec: + var r TLSChangeCipherSpecRecord + e := r.decodeFromBytes(h, data[hl:tl], df) + if e != nil { + return e + } + t.ChangeCipherSpec = append(t.ChangeCipherSpec, r) + case TLSAlert: + var r TLSAlertRecord + e := r.decodeFromBytes(h, data[hl:tl], df) + if e != nil { + return e + } + t.Alert = append(t.Alert, r) + case TLSHandshake: + var r TLSHandshakeRecord + e := r.decodeFromBytes(h, data[hl:tl], df) + if e != nil { + return e + } + t.Handshake = append(t.Handshake, r) + case TLSApplicationData: + var r TLSAppDataRecord + e := r.decodeFromBytes(h, data[hl:tl], df) + if e != nil { + return e + } + t.AppData = append(t.AppData, r) + } + + if len(data) == tl { + return nil + } + return t.decodeTLSRecords(data[tl:len(data)], df) +} + +// CanDecode implements gopacket.DecodingLayer. +func (t *TLS) CanDecode() gopacket.LayerClass { + return LayerTypeTLS +} + +// NextLayerType implements gopacket.DecodingLayer. +func (t *TLS) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// Payload returns nil, since TLS encrypted payload is inside TLSAppDataRecord +func (t *TLS) Payload() []byte { + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +func (t *TLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + totalLength := 0 + for _, record := range t.ChangeCipherSpec { + if opts.FixLengths { + record.Length = 1 + } + totalLength += 5 + 1 // length of header + record + } + for range t.Handshake { + totalLength += 5 + // TODO + } + for _, record := range t.AppData { + if opts.FixLengths { + record.Length = uint16(len(record.Payload)) + } + totalLength += 5 + len(record.Payload) + } + for _, record := range t.Alert { + if len(record.EncryptedMsg) == 0 { + if opts.FixLengths { + record.Length = 2 + } + totalLength += 5 + 2 + } else { + if opts.FixLengths { + record.Length = uint16(len(record.EncryptedMsg)) + } + totalLength += 5 + len(record.EncryptedMsg) + } + } + data, err := b.PrependBytes(totalLength) + if err != nil { + return err + } + off := 0 + for _, record := range t.ChangeCipherSpec { + off = encodeHeader(record.TLSRecordHeader, data, off) + data[off] = byte(record.Message) + off++ + } + for _, record := range t.Handshake { + off = encodeHeader(record.TLSRecordHeader, data, off) + // TODO + } + for _, record := range t.AppData { + off = encodeHeader(record.TLSRecordHeader, data, off) + copy(data[off:], record.Payload) + off += len(record.Payload) + } + for _, record := range t.Alert { + off = encodeHeader(record.TLSRecordHeader, data, off) + if len(record.EncryptedMsg) == 0 { + data[off] = byte(record.Level) + data[off+1] = byte(record.Description) + off += 2 + } else { + copy(data[off:], record.EncryptedMsg) + off += len(record.EncryptedMsg) + } + } + return nil +} + +func encodeHeader(header TLSRecordHeader, data []byte, offset int) int { + data[offset] = byte(header.ContentType) + binary.BigEndian.PutUint16(data[offset+1:], uint16(header.Version)) + binary.BigEndian.PutUint16(data[offset+3:], header.Length) + + return offset + 5 +} diff --git a/vendor/github.com/google/gopacket/layers/tls_alert.go b/vendor/github.com/google/gopacket/layers/tls_alert.go new file mode 100644 index 00000000..0c5aee02 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tls_alert.go @@ -0,0 +1,165 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// TLSAlertLevel defines the alert level data type +type TLSAlertLevel uint8 + +// TLSAlertDescr defines the alert descrption data type +type TLSAlertDescr uint8 + +const ( + TLSAlertWarning TLSAlertLevel = 1 + TLSAlertFatal TLSAlertLevel = 2 + TLSAlertUnknownLevel TLSAlertLevel = 255 + + TLSAlertCloseNotify TLSAlertDescr = 0 + TLSAlertUnexpectedMessage TLSAlertDescr = 10 + TLSAlertBadRecordMac TLSAlertDescr = 20 + TLSAlertDecryptionFailedRESERVED TLSAlertDescr = 21 + TLSAlertRecordOverflow TLSAlertDescr = 22 + TLSAlertDecompressionFailure TLSAlertDescr = 30 + TLSAlertHandshakeFailure TLSAlertDescr = 40 + TLSAlertNoCertificateRESERVED TLSAlertDescr = 41 + TLSAlertBadCertificate TLSAlertDescr = 42 + TLSAlertUnsupportedCertificate TLSAlertDescr = 43 + TLSAlertCertificateRevoked TLSAlertDescr = 44 + TLSAlertCertificateExpired TLSAlertDescr = 45 + TLSAlertCertificateUnknown TLSAlertDescr = 46 + TLSAlertIllegalParameter TLSAlertDescr = 47 + TLSAlertUnknownCa TLSAlertDescr = 48 + TLSAlertAccessDenied TLSAlertDescr = 49 + TLSAlertDecodeError TLSAlertDescr = 50 + TLSAlertDecryptError TLSAlertDescr = 51 + TLSAlertExportRestrictionRESERVED TLSAlertDescr = 60 + TLSAlertProtocolVersion TLSAlertDescr = 70 + TLSAlertInsufficientSecurity TLSAlertDescr = 71 + TLSAlertInternalError TLSAlertDescr = 80 + TLSAlertUserCanceled TLSAlertDescr = 90 + TLSAlertNoRenegotiation TLSAlertDescr = 100 + TLSAlertUnsupportedExtension TLSAlertDescr = 110 + TLSAlertUnknownDescription TLSAlertDescr = 255 +) + +// TLS Alert +// 0 1 2 3 4 5 6 7 8 +// +--+--+--+--+--+--+--+--+ +// | Level | +// +--+--+--+--+--+--+--+--+ +// | Description | +// +--+--+--+--+--+--+--+--+ + +// TLSAlertRecord contains all the information that each Alert Record type should have +type TLSAlertRecord struct { + TLSRecordHeader + + Level TLSAlertLevel + Description TLSAlertDescr + + EncryptedMsg []byte +} + +// DecodeFromBytes decodes the slice into the TLS struct. +func (t *TLSAlertRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error { + // TLS Record Header + t.ContentType = h.ContentType + t.Version = h.Version + t.Length = h.Length + + if len(data) < 2 { + df.SetTruncated() + return errors.New("TLS Alert packet too short") + } + + if t.Length == 2 { + t.Level = TLSAlertLevel(data[0]) + t.Description = TLSAlertDescr(data[1]) + } else { + t.Level = TLSAlertUnknownLevel + t.Description = TLSAlertUnknownDescription + t.EncryptedMsg = data + } + + return nil +} + +// Strings shows the TLS alert level nicely formatted +func (al TLSAlertLevel) String() string { + switch al { + default: + return fmt.Sprintf("Unknown(%d)", al) + case TLSAlertWarning: + return "Warning" + case TLSAlertFatal: + return "Fatal" + } +} + +// Strings shows the TLS alert description nicely formatted +func (ad TLSAlertDescr) String() string { + switch ad { + default: + return "Unknown" + case TLSAlertCloseNotify: + return "close_notify" + case TLSAlertUnexpectedMessage: + return "unexpected_message" + case TLSAlertBadRecordMac: + return "bad_record_mac" + case TLSAlertDecryptionFailedRESERVED: + return "decryption_failed_RESERVED" + case TLSAlertRecordOverflow: + return "record_overflow" + case TLSAlertDecompressionFailure: + return "decompression_failure" + case TLSAlertHandshakeFailure: + return "handshake_failure" + case TLSAlertNoCertificateRESERVED: + return "no_certificate_RESERVED" + case TLSAlertBadCertificate: + return "bad_certificate" + case TLSAlertUnsupportedCertificate: + return "unsupported_certificate" + case TLSAlertCertificateRevoked: + return "certificate_revoked" + case TLSAlertCertificateExpired: + return "certificate_expired" + case TLSAlertCertificateUnknown: + return "certificate_unknown" + case TLSAlertIllegalParameter: + return "illegal_parameter" + case TLSAlertUnknownCa: + return "unknown_ca" + case TLSAlertAccessDenied: + return "access_denied" + case TLSAlertDecodeError: + return "decode_error" + case TLSAlertDecryptError: + return "decrypt_error" + case TLSAlertExportRestrictionRESERVED: + return "export_restriction_RESERVED" + case TLSAlertProtocolVersion: + return "protocol_version" + case TLSAlertInsufficientSecurity: + return "insufficient_security" + case TLSAlertInternalError: + return "internal_error" + case TLSAlertUserCanceled: + return "user_canceled" + case TLSAlertNoRenegotiation: + return "no_renegotiation" + case TLSAlertUnsupportedExtension: + return "unsupported_extension" + } +} diff --git a/vendor/github.com/google/gopacket/layers/tls_appdata.go b/vendor/github.com/google/gopacket/layers/tls_appdata.go new file mode 100644 index 00000000..dedd1d58 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tls_appdata.go @@ -0,0 +1,34 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + + "github.com/google/gopacket" +) + +// TLSAppDataRecord contains all the information that each AppData Record types should have +type TLSAppDataRecord struct { + TLSRecordHeader + Payload []byte +} + +// DecodeFromBytes decodes the slice into the TLS struct. +func (t *TLSAppDataRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error { + // TLS Record Header + t.ContentType = h.ContentType + t.Version = h.Version + t.Length = h.Length + + if len(data) != int(t.Length) { + return errors.New("TLS Application Data length mismatch") + } + + t.Payload = data + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/tls_cipherspec.go b/vendor/github.com/google/gopacket/layers/tls_cipherspec.go new file mode 100644 index 00000000..8f3dc62b --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tls_cipherspec.go @@ -0,0 +1,64 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "errors" + + "github.com/google/gopacket" +) + +// TLSchangeCipherSpec defines the message value inside ChangeCipherSpec Record +type TLSchangeCipherSpec uint8 + +const ( + TLSChangecipherspecMessage TLSchangeCipherSpec = 1 + TLSChangecipherspecUnknown TLSchangeCipherSpec = 255 +) + +// TLS Change Cipher Spec +// 0 1 2 3 4 5 6 7 8 +// +--+--+--+--+--+--+--+--+ +// | Message | +// +--+--+--+--+--+--+--+--+ + +// TLSChangeCipherSpecRecord defines the type of data inside ChangeCipherSpec Record +type TLSChangeCipherSpecRecord struct { + TLSRecordHeader + + Message TLSchangeCipherSpec +} + +// DecodeFromBytes decodes the slice into the TLS struct. +func (t *TLSChangeCipherSpecRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error { + // TLS Record Header + t.ContentType = h.ContentType + t.Version = h.Version + t.Length = h.Length + + if len(data) != 1 { + df.SetTruncated() + return errors.New("TLS Change Cipher Spec record incorrect length") + } + + t.Message = TLSchangeCipherSpec(data[0]) + if t.Message != TLSChangecipherspecMessage { + t.Message = TLSChangecipherspecUnknown + } + + return nil +} + +// String shows the message value nicely formatted +func (ccs TLSchangeCipherSpec) String() string { + switch ccs { + default: + return "Unknown" + case TLSChangecipherspecMessage: + return "Change Cipher Spec Message" + } +} diff --git a/vendor/github.com/google/gopacket/layers/tls_handshake.go b/vendor/github.com/google/gopacket/layers/tls_handshake.go new file mode 100644 index 00000000..e45e2c7c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/tls_handshake.go @@ -0,0 +1,28 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "github.com/google/gopacket" +) + +// TLSHandshakeRecord defines the structure of a Handshare Record +type TLSHandshakeRecord struct { + TLSRecordHeader +} + +// DecodeFromBytes decodes the slice into the TLS struct. +func (t *TLSHandshakeRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error { + // TLS Record Header + t.ContentType = h.ContentType + t.Version = h.Version + t.Length = h.Length + + // TODO + + return nil +} diff --git a/vendor/github.com/google/gopacket/layers/udp.go b/vendor/github.com/google/gopacket/layers/udp.go new file mode 100644 index 00000000..97e81c69 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/udp.go @@ -0,0 +1,133 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "fmt" + + "github.com/google/gopacket" +) + +// UDP is the layer for UDP headers. +type UDP struct { + BaseLayer + SrcPort, DstPort UDPPort + Length uint16 + Checksum uint16 + sPort, dPort []byte + tcpipchecksum +} + +// LayerType returns gopacket.LayerTypeUDP +func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP } + +func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + df.SetTruncated() + return fmt.Errorf("Invalid UDP header. Length %d less than 8", len(data)) + } + udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2])) + udp.sPort = data[0:2] + udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4])) + udp.dPort = data[2:4] + udp.Length = binary.BigEndian.Uint16(data[4:6]) + udp.Checksum = binary.BigEndian.Uint16(data[6:8]) + udp.BaseLayer = BaseLayer{Contents: data[:8]} + switch { + case udp.Length >= 8: + hlen := int(udp.Length) + if hlen > len(data) { + df.SetTruncated() + hlen = len(data) + } + udp.Payload = data[8:hlen] + case udp.Length == 0: // Jumbogram, use entire rest of data + udp.Payload = data[8:] + default: + return fmt.Errorf("UDP packet too small: %d bytes", udp.Length) + } + return nil +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (u *UDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + var jumbo bool + + payload := b.Bytes() + if _, ok := u.pseudoheader.(*IPv6); ok { + if len(payload)+8 > 65535 { + jumbo = true + } + } + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + binary.BigEndian.PutUint16(bytes, uint16(u.SrcPort)) + binary.BigEndian.PutUint16(bytes[2:], uint16(u.DstPort)) + if opts.FixLengths { + if jumbo { + u.Length = 0 + } else { + u.Length = uint16(len(payload)) + 8 + } + } + binary.BigEndian.PutUint16(bytes[4:], u.Length) + if opts.ComputeChecksums { + // zero out checksum bytes + bytes[6] = 0 + bytes[7] = 0 + csum, err := u.computeChecksum(b.Bytes(), IPProtocolUDP) + if err != nil { + return err + } + u.Checksum = csum + } + binary.BigEndian.PutUint16(bytes[6:], u.Checksum) + return nil +} + +func (u *UDP) CanDecode() gopacket.LayerClass { + return LayerTypeUDP +} + +// NextLayerType use the destination port to select the +// right next decoder. It tries first to decode via the +// destination port, then the source port. +func (u *UDP) NextLayerType() gopacket.LayerType { + if lt := u.DstPort.LayerType(); lt != gopacket.LayerTypePayload { + return lt + } + return u.SrcPort.LayerType() +} + +func decodeUDP(data []byte, p gopacket.PacketBuilder) error { + udp := &UDP{} + err := udp.DecodeFromBytes(data, p) + p.AddLayer(udp) + p.SetTransportLayer(udp) + if err != nil { + return err + } + return p.NextDecoder(udp.NextLayerType()) +} + +func (u *UDP) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointUDPPort, u.sPort, u.dPort) +} + +// For testing only +func (u *UDP) SetInternalPortsForTesting() { + u.sPort = make([]byte, 2) + u.dPort = make([]byte, 2) + binary.BigEndian.PutUint16(u.sPort, uint16(u.SrcPort)) + binary.BigEndian.PutUint16(u.dPort, uint16(u.DstPort)) +} diff --git a/vendor/github.com/google/gopacket/layers/udplite.go b/vendor/github.com/google/gopacket/layers/udplite.go new file mode 100644 index 00000000..7d84c514 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/udplite.go @@ -0,0 +1,44 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// Copyright 2009-2011 Andreas Krennmair. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "github.com/google/gopacket" +) + +// UDPLite is the layer for UDP-Lite headers (rfc 3828). +type UDPLite struct { + BaseLayer + SrcPort, DstPort UDPLitePort + ChecksumCoverage uint16 + Checksum uint16 + sPort, dPort []byte +} + +// LayerType returns gopacket.LayerTypeUDPLite +func (u *UDPLite) LayerType() gopacket.LayerType { return LayerTypeUDPLite } + +func decodeUDPLite(data []byte, p gopacket.PacketBuilder) error { + udp := &UDPLite{ + SrcPort: UDPLitePort(binary.BigEndian.Uint16(data[0:2])), + sPort: data[0:2], + DstPort: UDPLitePort(binary.BigEndian.Uint16(data[2:4])), + dPort: data[2:4], + ChecksumCoverage: binary.BigEndian.Uint16(data[4:6]), + Checksum: binary.BigEndian.Uint16(data[6:8]), + BaseLayer: BaseLayer{data[:8], data[8:]}, + } + p.AddLayer(udp) + p.SetTransportLayer(udp) + return p.NextDecoder(gopacket.LayerTypePayload) +} + +func (u *UDPLite) TransportFlow() gopacket.Flow { + return gopacket.NewFlow(EndpointUDPLitePort, u.sPort, u.dPort) +} diff --git a/vendor/github.com/google/gopacket/layers/usb.go b/vendor/github.com/google/gopacket/layers/usb.go new file mode 100644 index 00000000..d611e0fd --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/usb.go @@ -0,0 +1,292 @@ +// Copyright 2014 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "github.com/google/gopacket" +) + +type USBEventType uint8 + +const ( + USBEventTypeSubmit USBEventType = 'S' + USBEventTypeComplete USBEventType = 'C' + USBEventTypeError USBEventType = 'E' +) + +func (a USBEventType) String() string { + switch a { + case USBEventTypeSubmit: + return "SUBMIT" + case USBEventTypeComplete: + return "COMPLETE" + case USBEventTypeError: + return "ERROR" + default: + return "Unknown event type" + } +} + +type USBRequestBlockSetupRequest uint8 + +const ( + USBRequestBlockSetupRequestGetStatus USBRequestBlockSetupRequest = 0x00 + USBRequestBlockSetupRequestClearFeature USBRequestBlockSetupRequest = 0x01 + USBRequestBlockSetupRequestSetFeature USBRequestBlockSetupRequest = 0x03 + USBRequestBlockSetupRequestSetAddress USBRequestBlockSetupRequest = 0x05 + USBRequestBlockSetupRequestGetDescriptor USBRequestBlockSetupRequest = 0x06 + USBRequestBlockSetupRequestSetDescriptor USBRequestBlockSetupRequest = 0x07 + USBRequestBlockSetupRequestGetConfiguration USBRequestBlockSetupRequest = 0x08 + USBRequestBlockSetupRequestSetConfiguration USBRequestBlockSetupRequest = 0x09 + USBRequestBlockSetupRequestSetIdle USBRequestBlockSetupRequest = 0x0a +) + +func (a USBRequestBlockSetupRequest) String() string { + switch a { + case USBRequestBlockSetupRequestGetStatus: + return "GET_STATUS" + case USBRequestBlockSetupRequestClearFeature: + return "CLEAR_FEATURE" + case USBRequestBlockSetupRequestSetFeature: + return "SET_FEATURE" + case USBRequestBlockSetupRequestSetAddress: + return "SET_ADDRESS" + case USBRequestBlockSetupRequestGetDescriptor: + return "GET_DESCRIPTOR" + case USBRequestBlockSetupRequestSetDescriptor: + return "SET_DESCRIPTOR" + case USBRequestBlockSetupRequestGetConfiguration: + return "GET_CONFIGURATION" + case USBRequestBlockSetupRequestSetConfiguration: + return "SET_CONFIGURATION" + case USBRequestBlockSetupRequestSetIdle: + return "SET_IDLE" + default: + return "UNKNOWN" + } +} + +type USBTransportType uint8 + +const ( + USBTransportTypeTransferIn USBTransportType = 0x80 // Indicates send or receive + USBTransportTypeIsochronous USBTransportType = 0x00 // Isochronous transfers occur continuously and periodically. They typically contain time sensitive information, such as an audio or video stream. + USBTransportTypeInterrupt USBTransportType = 0x01 // Interrupt transfers are typically non-periodic, small device "initiated" communication requiring bounded latency, such as pointing devices or keyboards. + USBTransportTypeControl USBTransportType = 0x02 // Control transfers are typically used for command and status operations. + USBTransportTypeBulk USBTransportType = 0x03 // Bulk transfers can be used for large bursty data, using all remaining available bandwidth, no guarantees on bandwidth or latency, such as file transfers. +) + +type USBDirectionType uint8 + +const ( + USBDirectionTypeUnknown USBDirectionType = iota + USBDirectionTypeIn + USBDirectionTypeOut +) + +func (a USBDirectionType) String() string { + switch a { + case USBDirectionTypeIn: + return "In" + case USBDirectionTypeOut: + return "Out" + default: + return "Unknown direction type" + } +} + +// The reference at http://www.beyondlogic.org/usbnutshell/usb1.shtml contains more information about the protocol. +type USB struct { + BaseLayer + ID uint64 + EventType USBEventType + TransferType USBTransportType + Direction USBDirectionType + EndpointNumber uint8 + DeviceAddress uint8 + BusID uint16 + TimestampSec int64 + TimestampUsec int32 + Setup bool + Data bool + Status int32 + UrbLength uint32 + UrbDataLength uint32 + + UrbInterval uint32 + UrbStartFrame uint32 + UrbCopyOfTransferFlags uint32 + IsoNumDesc uint32 +} + +func (u *USB) LayerType() gopacket.LayerType { return LayerTypeUSB } + +func (m *USB) NextLayerType() gopacket.LayerType { + if m.Setup { + return LayerTypeUSBRequestBlockSetup + } else if m.Data { + } + + return m.TransferType.LayerType() +} + +func decodeUSB(data []byte, p gopacket.PacketBuilder) error { + d := &USB{} + + return decodingLayerDecoder(d, data, p) +} + +func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 40 { + df.SetTruncated() + return errors.New("USB < 40 bytes") + } + m.ID = binary.LittleEndian.Uint64(data[0:8]) + m.EventType = USBEventType(data[8]) + m.TransferType = USBTransportType(data[9]) + + m.EndpointNumber = data[10] & 0x7f + if data[10]&uint8(USBTransportTypeTransferIn) > 0 { + m.Direction = USBDirectionTypeIn + } else { + m.Direction = USBDirectionTypeOut + } + + m.DeviceAddress = data[11] + m.BusID = binary.LittleEndian.Uint16(data[12:14]) + + if uint(data[14]) == 0 { + m.Setup = true + } + + if uint(data[15]) == 0 { + m.Data = true + } + + m.TimestampSec = int64(binary.LittleEndian.Uint64(data[16:24])) + m.TimestampUsec = int32(binary.LittleEndian.Uint32(data[24:28])) + m.Status = int32(binary.LittleEndian.Uint32(data[28:32])) + m.UrbLength = binary.LittleEndian.Uint32(data[32:36]) + m.UrbDataLength = binary.LittleEndian.Uint32(data[36:40]) + + m.Contents = data[:40] + m.Payload = data[40:] + + if m.Setup { + m.Payload = data[40:] + } else if m.Data { + m.Payload = data[uint32(len(data))-m.UrbDataLength:] + } + + // if 64 bit, dissect_linux_usb_pseudo_header_ext + if false { + m.UrbInterval = binary.LittleEndian.Uint32(data[40:44]) + m.UrbStartFrame = binary.LittleEndian.Uint32(data[44:48]) + m.UrbDataLength = binary.LittleEndian.Uint32(data[48:52]) + m.IsoNumDesc = binary.LittleEndian.Uint32(data[52:56]) + m.Contents = data[:56] + m.Payload = data[56:] + } + + // crc5 or crc16 + // eop (end of packet) + + return nil +} + +type USBRequestBlockSetup struct { + BaseLayer + RequestType uint8 + Request USBRequestBlockSetupRequest + Value uint16 + Index uint16 + Length uint16 +} + +func (u *USBRequestBlockSetup) LayerType() gopacket.LayerType { return LayerTypeUSBRequestBlockSetup } + +func (m *USBRequestBlockSetup) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBRequestBlockSetup) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.RequestType = data[0] + m.Request = USBRequestBlockSetupRequest(data[1]) + m.Value = binary.LittleEndian.Uint16(data[2:4]) + m.Index = binary.LittleEndian.Uint16(data[4:6]) + m.Length = binary.LittleEndian.Uint16(data[6:8]) + m.Contents = data[:8] + m.Payload = data[8:] + return nil +} + +func decodeUSBRequestBlockSetup(data []byte, p gopacket.PacketBuilder) error { + d := &USBRequestBlockSetup{} + return decodingLayerDecoder(d, data, p) +} + +type USBControl struct { + BaseLayer +} + +func (u *USBControl) LayerType() gopacket.LayerType { return LayerTypeUSBControl } + +func (m *USBControl) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBControl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBControl(data []byte, p gopacket.PacketBuilder) error { + d := &USBControl{} + return decodingLayerDecoder(d, data, p) +} + +type USBInterrupt struct { + BaseLayer +} + +func (u *USBInterrupt) LayerType() gopacket.LayerType { return LayerTypeUSBInterrupt } + +func (m *USBInterrupt) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBInterrupt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBInterrupt(data []byte, p gopacket.PacketBuilder) error { + d := &USBInterrupt{} + return decodingLayerDecoder(d, data, p) +} + +type USBBulk struct { + BaseLayer +} + +func (u *USBBulk) LayerType() gopacket.LayerType { return LayerTypeUSBBulk } + +func (m *USBBulk) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypePayload +} + +func (m *USBBulk) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + m.Contents = data + return nil +} + +func decodeUSBBulk(data []byte, p gopacket.PacketBuilder) error { + d := &USBBulk{} + return decodingLayerDecoder(d, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/vrrp.go b/vendor/github.com/google/gopacket/layers/vrrp.go new file mode 100644 index 00000000..ffaafe6a --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vrrp.go @@ -0,0 +1,156 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "net" + + "github.com/google/gopacket" +) + +/* + This layer provides decoding for Virtual Router Redundancy Protocol (VRRP) v2. + https://tools.ietf.org/html/rfc3768#section-5 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Auth Type | Adver Int | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | . | + | . | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address (n) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Authentication Data (2) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +type VRRPv2Type uint8 +type VRRPv2AuthType uint8 + +const ( + VRRPv2Advertisement VRRPv2Type = 0x01 // router advertisement +) + +// String conversions for VRRP message types +func (v VRRPv2Type) String() string { + switch v { + case VRRPv2Advertisement: + return "VRRPv2 Advertisement" + default: + return "" + } +} + +const ( + VRRPv2AuthNoAuth VRRPv2AuthType = 0x00 // No Authentication + VRRPv2AuthReserved1 VRRPv2AuthType = 0x01 // Reserved field 1 + VRRPv2AuthReserved2 VRRPv2AuthType = 0x02 // Reserved field 2 +) + +func (v VRRPv2AuthType) String() string { + switch v { + case VRRPv2AuthNoAuth: + return "No Authentication" + case VRRPv2AuthReserved1: + return "Reserved" + case VRRPv2AuthReserved2: + return "Reserved" + default: + return "" + } +} + +// VRRPv2 represents an VRRP v2 message. +type VRRPv2 struct { + BaseLayer + Version uint8 // The version field specifies the VRRP protocol version of this packet (v2) + Type VRRPv2Type // The type field specifies the type of this VRRP packet. The only type defined in v2 is ADVERTISEMENT + VirtualRtrID uint8 // identifies the virtual router this packet is reporting status for + Priority uint8 // specifies the sending VRRP router's priority for the virtual router (100 = default) + CountIPAddr uint8 // The number of IP addresses contained in this VRRP advertisement. + AuthType VRRPv2AuthType // identifies the authentication method being utilized + AdverInt uint8 // The Advertisement interval indicates the time interval (in seconds) between ADVERTISEMENTS. The default is 1 second + Checksum uint16 // used to detect data corruption in the VRRP message. + IPAddress []net.IP // one or more IP addresses associated with the virtual router. Specified in the CountIPAddr field. +} + +// LayerType returns LayerTypeVRRP for VRRP v2 message. +func (v *VRRPv2) LayerType() gopacket.LayerType { return LayerTypeVRRP } + +func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + + v.BaseLayer = BaseLayer{Contents: data[:len(data)]} + v.Version = data[0] >> 4 // high nibble == VRRP version. We're expecting v2 + + v.Type = VRRPv2Type(data[0] & 0x0F) // low nibble == VRRP type. Expecting 1 (advertisement) + if v.Type != 1 { + // rfc3768: A packet with unknown type MUST be discarded. + return errors.New("Unrecognized VRRPv2 type field.") + } + + v.VirtualRtrID = data[1] + v.Priority = data[2] + + v.CountIPAddr = data[3] + if v.CountIPAddr < 1 { + return errors.New("VRRPv2 number of IP addresses is not valid.") + } + + v.AuthType = VRRPv2AuthType(data[4]) + v.AdverInt = uint8(data[5]) + v.Checksum = binary.BigEndian.Uint16(data[6:8]) + + // populate the IPAddress field. The number of addresses is specified in the v.CountIPAddr field + // offset references the starting byte containing the list of ip addresses + offset := 8 + for i := uint8(0); i < v.CountIPAddr; i++ { + v.IPAddress = append(v.IPAddress, data[offset:offset+4]) + offset += 4 + } + + // any trailing packets here may be authentication data and *should* be ignored in v2 as per RFC + // + // 5.3.10. Authentication Data + // + // The authentication string is currently only used to maintain + // backwards compatibility with RFC 2338. It SHOULD be set to zero on + // transmission and ignored on reception. + return nil +} + +// CanDecode specifies the layer type in which we are attempting to unwrap. +func (v *VRRPv2) CanDecode() gopacket.LayerClass { + return LayerTypeVRRP +} + +// NextLayerType specifies the next layer that should be decoded. VRRP does not contain any further payload, so we set to 0 +func (v *VRRPv2) NextLayerType() gopacket.LayerType { + return gopacket.LayerTypeZero +} + +// The VRRP packet does not include payload data. Setting byte slice to nil +func (v *VRRPv2) Payload() []byte { + return nil +} + +// decodeVRRP will parse VRRP v2 +func decodeVRRP(data []byte, p gopacket.PacketBuilder) error { + if len(data) < 8 { + return errors.New("Not a valid VRRP packet. Packet length is too small.") + } + v := &VRRPv2{} + return decodingLayerDecoder(v, data, p) +} diff --git a/vendor/github.com/google/gopacket/layers/vxlan.go b/vendor/github.com/google/gopacket/layers/vxlan.go new file mode 100644 index 00000000..e479cd81 --- /dev/null +++ b/vendor/github.com/google/gopacket/layers/vxlan.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package layers + +import ( + "encoding/binary" + "errors" + "fmt" + + "github.com/google/gopacket" +) + +// VXLAN is specifed in RFC 7348 https://tools.ietf.org/html/rfc7348 +// G, D, A, Group Policy ID from https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// 0 8 16 24 32 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |G|R|R|R|I|R|R|R|R|D|R|R|A|R|R|R| Group Policy ID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | 24 bit VXLAN Network Identifier | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// VXLAN is a VXLAN packet header +type VXLAN struct { + BaseLayer + ValidIDFlag bool // 'I' bit per RFC 7348 + VNI uint32 // 'VXLAN Network Identifier' 24 bits per RFC 7348 + GBPExtension bool // 'G' bit per Group Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 + GBPDontLearn bool // 'D' bit per Group Policy + GBPApplied bool // 'A' bit per Group Policy + GBPGroupPolicyID uint16 // 'Group Policy ID' 16 bits per Group Policy +} + +// LayerType returns LayerTypeVXLAN +func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN } + +// CanDecode returns the layer type this DecodingLayer can decode +func (vx *VXLAN) CanDecode() gopacket.LayerClass { + return LayerTypeVXLAN +} + +// NextLayerType retuns the next layer we should see after vxlan +func (vx *VXLAN) NextLayerType() gopacket.LayerType { + return LayerTypeEthernet +} + +// DecodeFromBytes takes a byte buffer and decodes +func (vx *VXLAN) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { + if len(data) < 8 { + return errors.New("vxlan packet too small") + } + // VNI is a 24bit number, Uint32 requires 32 bits + var buf [4]byte + copy(buf[1:], data[4:7]) + + // RFC 7348 https://tools.ietf.org/html/rfc7348 + vx.ValidIDFlag = data[0]&0x08 > 0 // 'I' bit per RFC7348 + vx.VNI = binary.BigEndian.Uint32(buf[:]) // VXLAN Network Identifier per RFC7348 + + // Group Based Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00 + vx.GBPExtension = data[0]&0x80 > 0 // 'G' bit per the group policy draft + vx.GBPDontLearn = data[1]&0x40 > 0 // 'D' bit - the egress VTEP MUST NOT learn the source address of the encapsulated frame. + vx.GBPApplied = data[1]&0x80 > 0 // 'A' bit - indicates that the group policy has already been applied to this packet. + vx.GBPGroupPolicyID = binary.BigEndian.Uint16(data[2:4]) // Policy ID as per the group policy draft + + // Layer information + const vxlanLength = 8 + vx.Contents = data[:vxlanLength] + vx.Payload = data[vxlanLength:] + + return nil + +} + +func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error { + vx := &VXLAN{} + err := vx.DecodeFromBytes(data, p) + if err != nil { + return err + } + + p.AddLayer(vx) + return p.NextDecoder(LinkTypeEthernet) +} + +// SerializeTo writes the serialized form of this layer into the +// SerializationBuffer, implementing gopacket.SerializableLayer. +// See the docs for gopacket.SerializableLayer for more info. +func (vx *VXLAN) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + bytes, err := b.PrependBytes(8) + if err != nil { + return err + } + + // PrependBytes does not guarantee that bytes are zeroed. Setting flags via OR requires that they start off at zero + bytes[0] = 0 + bytes[1] = 0 + + if vx.ValidIDFlag { + bytes[0] |= 0x08 + } + if vx.GBPExtension { + bytes[0] |= 0x80 + } + if vx.GBPDontLearn { + bytes[1] |= 0x40 + } + if vx.GBPApplied { + bytes[1] |= 0x80 + } + + binary.BigEndian.PutUint16(bytes[2:4], vx.GBPGroupPolicyID) + if vx.VNI >= 1<<24 { + return fmt.Errorf("Virtual Network Identifier = %x exceeds max for 24-bit uint", vx.VNI) + } + binary.BigEndian.PutUint32(bytes[4:8], vx.VNI<<8) + return nil +} diff --git a/vendor/github.com/google/gopacket/layers_decoder.go b/vendor/github.com/google/gopacket/layers_decoder.go new file mode 100644 index 00000000..8c1f108c --- /dev/null +++ b/vendor/github.com/google/gopacket/layers_decoder.go @@ -0,0 +1,101 @@ +// Copyright 2019 The GoPacket Authors. All rights reserved. + +package gopacket + +// Created by gen.go, don't edit manually +// Generated at 2019-06-18 11:37:31.308731293 +0600 +06 m=+0.000842599 + +// LayersDecoder returns DecodingLayerFunc for specified +// DecodingLayerContainer, LayerType value to start decoding with and +// some DecodeFeedback. +func LayersDecoder(dl DecodingLayerContainer, first LayerType, df DecodeFeedback) DecodingLayerFunc { + firstDec, ok := dl.Decoder(first) + if !ok { + return func([]byte, *[]LayerType) (LayerType, error) { + return first, nil + } + } + if dlc, ok := dl.(DecodingLayerSparse); ok { + return func(data []byte, decoded *[]LayerType) (LayerType, error) { + *decoded = (*decoded)[:0] // Truncated decoded layers. + typ := first + decoder := firstDec + for { + if err := decoder.DecodeFromBytes(data, df); err != nil { + return LayerTypeZero, err + } + *decoded = append(*decoded, typ) + typ = decoder.NextLayerType() + if data = decoder.LayerPayload(); len(data) == 0 { + break + } + if decoder, ok = dlc.Decoder(typ); !ok { + return typ, nil + } + } + return LayerTypeZero, nil + } + } + if dlc, ok := dl.(DecodingLayerArray); ok { + return func(data []byte, decoded *[]LayerType) (LayerType, error) { + *decoded = (*decoded)[:0] // Truncated decoded layers. + typ := first + decoder := firstDec + for { + if err := decoder.DecodeFromBytes(data, df); err != nil { + return LayerTypeZero, err + } + *decoded = append(*decoded, typ) + typ = decoder.NextLayerType() + if data = decoder.LayerPayload(); len(data) == 0 { + break + } + if decoder, ok = dlc.Decoder(typ); !ok { + return typ, nil + } + } + return LayerTypeZero, nil + } + } + if dlc, ok := dl.(DecodingLayerMap); ok { + return func(data []byte, decoded *[]LayerType) (LayerType, error) { + *decoded = (*decoded)[:0] // Truncated decoded layers. + typ := first + decoder := firstDec + for { + if err := decoder.DecodeFromBytes(data, df); err != nil { + return LayerTypeZero, err + } + *decoded = append(*decoded, typ) + typ = decoder.NextLayerType() + if data = decoder.LayerPayload(); len(data) == 0 { + break + } + if decoder, ok = dlc.Decoder(typ); !ok { + return typ, nil + } + } + return LayerTypeZero, nil + } + } + dlc := dl + return func(data []byte, decoded *[]LayerType) (LayerType, error) { + *decoded = (*decoded)[:0] // Truncated decoded layers. + typ := first + decoder := firstDec + for { + if err := decoder.DecodeFromBytes(data, df); err != nil { + return LayerTypeZero, err + } + *decoded = append(*decoded, typ) + typ = decoder.NextLayerType() + if data = decoder.LayerPayload(); len(data) == 0 { + break + } + if decoder, ok = dlc.Decoder(typ); !ok { + return typ, nil + } + } + return LayerTypeZero, nil + } +} diff --git a/vendor/github.com/google/gopacket/layertype.go b/vendor/github.com/google/gopacket/layertype.go new file mode 100644 index 00000000..3abfee1e --- /dev/null +++ b/vendor/github.com/google/gopacket/layertype.go @@ -0,0 +1,111 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" + "strconv" +) + +// LayerType is a unique identifier for each type of layer. This enumeration +// does not match with any externally available numbering scheme... it's solely +// usable/useful within this library as a means for requesting layer types +// (see Packet.Layer) and determining which types of layers have been decoded. +// +// New LayerTypes may be created by calling gopacket.RegisterLayerType. +type LayerType int64 + +// LayerTypeMetadata contains metadata associated with each LayerType. +type LayerTypeMetadata struct { + // Name is the string returned by each layer type's String method. + Name string + // Decoder is the decoder to use when the layer type is passed in as a + // Decoder. + Decoder Decoder +} + +type layerTypeMetadata struct { + inUse bool + LayerTypeMetadata +} + +// DecodersByLayerName maps layer names to decoders for those layers. +// This allows users to specify decoders by name to a program and have that +// program pick the correct decoder accordingly. +var DecodersByLayerName = map[string]Decoder{} + +const maxLayerType = 2000 + +var ltMeta [maxLayerType]layerTypeMetadata +var ltMetaMap = map[LayerType]layerTypeMetadata{} + +// RegisterLayerType creates a new layer type and registers it globally. +// The number passed in must be unique, or a runtime panic will occur. Numbers +// 0-999 are reserved for the gopacket library. Numbers 1000-1999 should be +// used for common application-specific types, and are very fast. Any other +// number (negative or >= 2000) may be used for uncommon application-specific +// types, and are somewhat slower (they require a map lookup over an array +// index). +func RegisterLayerType(num int, meta LayerTypeMetadata) LayerType { + if 0 <= num && num < maxLayerType { + if ltMeta[num].inUse { + panic("Layer type already exists") + } + } else { + if ltMetaMap[LayerType(num)].inUse { + panic("Layer type already exists") + } + } + return OverrideLayerType(num, meta) +} + +// OverrideLayerType acts like RegisterLayerType, except that if the layer type +// has already been registered, it overrides the metadata with the passed-in +// metadata intead of panicing. +func OverrideLayerType(num int, meta LayerTypeMetadata) LayerType { + if 0 <= num && num < maxLayerType { + ltMeta[num] = layerTypeMetadata{ + inUse: true, + LayerTypeMetadata: meta, + } + } else { + ltMetaMap[LayerType(num)] = layerTypeMetadata{ + inUse: true, + LayerTypeMetadata: meta, + } + } + DecodersByLayerName[meta.Name] = meta.Decoder + return LayerType(num) +} + +// Decode decodes the given data using the decoder registered with the layer +// type. +func (t LayerType) Decode(data []byte, c PacketBuilder) error { + var d Decoder + if 0 <= int(t) && int(t) < maxLayerType { + d = ltMeta[int(t)].Decoder + } else { + d = ltMetaMap[t].Decoder + } + if d != nil { + return d.Decode(data, c) + } + return fmt.Errorf("Layer type %v has no associated decoder", t) +} + +// String returns the string associated with this layer type. +func (t LayerType) String() (s string) { + if 0 <= int(t) && int(t) < maxLayerType { + s = ltMeta[int(t)].Name + } else { + s = ltMetaMap[t].Name + } + if s == "" { + s = strconv.Itoa(int(t)) + } + return +} diff --git a/vendor/github.com/google/gopacket/packet.go b/vendor/github.com/google/gopacket/packet.go new file mode 100644 index 00000000..3a7c4b3d --- /dev/null +++ b/vendor/github.com/google/gopacket/packet.go @@ -0,0 +1,864 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "os" + "reflect" + "runtime/debug" + "strings" + "syscall" + "time" +) + +// CaptureInfo provides standardized information about a packet captured off +// the wire or read from a file. +type CaptureInfo struct { + // Timestamp is the time the packet was captured, if that is known. + Timestamp time.Time + // CaptureLength is the total number of bytes read off of the wire. + CaptureLength int + // Length is the size of the original packet. Should always be >= + // CaptureLength. + Length int + // InterfaceIndex + InterfaceIndex int + // The packet source can place ancillary data of various types here. + // For example, the afpacket source can report the VLAN of captured + // packets this way. + AncillaryData []interface{} +} + +// PacketMetadata contains metadata for a packet. +type PacketMetadata struct { + CaptureInfo + // Truncated is true if packet decoding logic detects that there are fewer + // bytes in the packet than are detailed in various headers (for example, if + // the number of bytes in the IPv4 contents/payload is less than IPv4.Length). + // This is also set automatically for packets captured off the wire if + // CaptureInfo.CaptureLength < CaptureInfo.Length. + Truncated bool +} + +// Packet is the primary object used by gopacket. Packets are created by a +// Decoder's Decode call. A packet is made up of a set of Data, which +// is broken into a number of Layers as it is decoded. +type Packet interface { + //// Functions for outputting the packet as a human-readable string: + //// ------------------------------------------------------------------ + // String returns a human-readable string representation of the packet. + // It uses LayerString on each layer to output the layer. + String() string + // Dump returns a verbose human-readable string representation of the packet, + // including a hex dump of all layers. It uses LayerDump on each layer to + // output the layer. + Dump() string + + //// Functions for accessing arbitrary packet layers: + //// ------------------------------------------------------------------ + // Layers returns all layers in this packet, computing them as necessary + Layers() []Layer + // Layer returns the first layer in this packet of the given type, or nil + Layer(LayerType) Layer + // LayerClass returns the first layer in this packet of the given class, + // or nil. + LayerClass(LayerClass) Layer + + //// Functions for accessing specific types of packet layers. These functions + //// return the first layer of each type found within the packet. + //// ------------------------------------------------------------------ + // LinkLayer returns the first link layer in the packet + LinkLayer() LinkLayer + // NetworkLayer returns the first network layer in the packet + NetworkLayer() NetworkLayer + // TransportLayer returns the first transport layer in the packet + TransportLayer() TransportLayer + // ApplicationLayer returns the first application layer in the packet + ApplicationLayer() ApplicationLayer + // ErrorLayer is particularly useful, since it returns nil if the packet + // was fully decoded successfully, and non-nil if an error was encountered + // in decoding and the packet was only partially decoded. Thus, its output + // can be used to determine if the entire packet was able to be decoded. + ErrorLayer() ErrorLayer + + //// Functions for accessing data specific to the packet: + //// ------------------------------------------------------------------ + // Data returns the set of bytes that make up this entire packet. + Data() []byte + // Metadata returns packet metadata associated with this packet. + Metadata() *PacketMetadata +} + +// packet contains all the information we need to fulfill the Packet interface, +// and its two "subclasses" (yes, no such thing in Go, bear with me), +// eagerPacket and lazyPacket, provide eager and lazy decoding logic around the +// various functions needed to access this information. +type packet struct { + // data contains the entire packet data for a packet + data []byte + // initialLayers is space for an initial set of layers already created inside + // the packet. + initialLayers [6]Layer + // layers contains each layer we've already decoded + layers []Layer + // last is the last layer added to the packet + last Layer + // metadata is the PacketMetadata for this packet + metadata PacketMetadata + + decodeOptions DecodeOptions + + // Pointers to the various important layers + link LinkLayer + network NetworkLayer + transport TransportLayer + application ApplicationLayer + failure ErrorLayer +} + +func (p *packet) SetTruncated() { + p.metadata.Truncated = true +} + +func (p *packet) SetLinkLayer(l LinkLayer) { + if p.link == nil { + p.link = l + } +} + +func (p *packet) SetNetworkLayer(l NetworkLayer) { + if p.network == nil { + p.network = l + } +} + +func (p *packet) SetTransportLayer(l TransportLayer) { + if p.transport == nil { + p.transport = l + } +} + +func (p *packet) SetApplicationLayer(l ApplicationLayer) { + if p.application == nil { + p.application = l + } +} + +func (p *packet) SetErrorLayer(l ErrorLayer) { + if p.failure == nil { + p.failure = l + } +} + +func (p *packet) AddLayer(l Layer) { + p.layers = append(p.layers, l) + p.last = l +} + +func (p *packet) DumpPacketData() { + fmt.Fprint(os.Stderr, p.packetDump()) + os.Stderr.Sync() +} + +func (p *packet) Metadata() *PacketMetadata { + return &p.metadata +} + +func (p *packet) Data() []byte { + return p.data +} + +func (p *packet) DecodeOptions() *DecodeOptions { + return &p.decodeOptions +} + +func (p *packet) addFinalDecodeError(err error, stack []byte) { + fail := &DecodeFailure{err: err, stack: stack} + if p.last == nil { + fail.data = p.data + } else { + fail.data = p.last.LayerPayload() + } + p.AddLayer(fail) + p.SetErrorLayer(fail) +} + +func (p *packet) recoverDecodeError() { + if !p.decodeOptions.SkipDecodeRecovery { + if r := recover(); r != nil { + p.addFinalDecodeError(fmt.Errorf("%v", r), debug.Stack()) + } + } +} + +// LayerString outputs an individual layer as a string. The layer is output +// in a single line, with no trailing newline. This function is specifically +// designed to do the right thing for most layers... it follows the following +// rules: +// * If the Layer has a String function, just output that. +// * Otherwise, output all exported fields in the layer, recursing into +// exported slices and structs. +// NOTE: This is NOT THE SAME AS fmt's "%#v". %#v will output both exported +// and unexported fields... many times packet layers contain unexported stuff +// that would just mess up the output of the layer, see for example the +// Payload layer and it's internal 'data' field, which contains a large byte +// array that would really mess up formatting. +func LayerString(l Layer) string { + return fmt.Sprintf("%v\t%s", l.LayerType(), layerString(reflect.ValueOf(l), false, false)) +} + +// Dumper dumps verbose information on a value. If a layer type implements +// Dumper, then its LayerDump() string will include the results in its output. +type Dumper interface { + Dump() string +} + +// LayerDump outputs a very verbose string representation of a layer. Its +// output is a concatenation of LayerString(l) and hex.Dump(l.LayerContents()). +// It contains newlines and ends with a newline. +func LayerDump(l Layer) string { + var b bytes.Buffer + b.WriteString(LayerString(l)) + b.WriteByte('\n') + if d, ok := l.(Dumper); ok { + dump := d.Dump() + if dump != "" { + b.WriteString(dump) + if dump[len(dump)-1] != '\n' { + b.WriteByte('\n') + } + } + } + b.WriteString(hex.Dump(l.LayerContents())) + return b.String() +} + +// layerString outputs, recursively, a layer in a "smart" way. See docs for +// LayerString for more details. +// +// Params: +// i - value to write out +// anonymous: if we're currently recursing an anonymous member of a struct +// writeSpace: if we've already written a value in a struct, and need to +// write a space before writing more. This happens when we write various +// anonymous values, and need to keep writing more. +func layerString(v reflect.Value, anonymous bool, writeSpace bool) string { + // Let String() functions take precedence. + if v.CanInterface() { + if s, ok := v.Interface().(fmt.Stringer); ok { + return s.String() + } + } + // Reflect, and spit out all the exported fields as key=value. + switch v.Type().Kind() { + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return "nil" + } + r := v.Elem() + return layerString(r, anonymous, writeSpace) + case reflect.Struct: + var b bytes.Buffer + typ := v.Type() + if !anonymous { + b.WriteByte('{') + } + for i := 0; i < v.NumField(); i++ { + // Check if this is upper-case. + ftype := typ.Field(i) + f := v.Field(i) + if ftype.Anonymous { + anonStr := layerString(f, true, writeSpace) + writeSpace = writeSpace || anonStr != "" + b.WriteString(anonStr) + } else if ftype.PkgPath == "" { // exported + if writeSpace { + b.WriteByte(' ') + } + writeSpace = true + fmt.Fprintf(&b, "%s=%s", typ.Field(i).Name, layerString(f, false, writeSpace)) + } + } + if !anonymous { + b.WriteByte('}') + } + return b.String() + case reflect.Slice: + var b bytes.Buffer + b.WriteByte('[') + if v.Len() > 4 { + fmt.Fprintf(&b, "..%d..", v.Len()) + } else { + for j := 0; j < v.Len(); j++ { + if j != 0 { + b.WriteString(", ") + } + b.WriteString(layerString(v.Index(j), false, false)) + } + } + b.WriteByte(']') + return b.String() + } + return fmt.Sprintf("%v", v.Interface()) +} + +const ( + longBytesLength = 128 +) + +// LongBytesGoString returns a string representation of the byte slice shortened +// using the format '{ ... ( bytes)}' if it +// exceeds a predetermined length. Can be used to avoid filling the display with +// very long byte strings. +func LongBytesGoString(buf []byte) string { + if len(buf) < longBytesLength { + return fmt.Sprintf("%#v", buf) + } + s := fmt.Sprintf("%#v", buf[:longBytesLength-1]) + s = strings.TrimSuffix(s, "}") + return fmt.Sprintf("%s ... (%d bytes)}", s, len(buf)) +} + +func baseLayerString(value reflect.Value) string { + t := value.Type() + content := value.Field(0) + c := make([]byte, content.Len()) + for i := range c { + c[i] = byte(content.Index(i).Uint()) + } + payload := value.Field(1) + p := make([]byte, payload.Len()) + for i := range p { + p[i] = byte(payload.Index(i).Uint()) + } + return fmt.Sprintf("%s{Contents:%s, Payload:%s}", t.String(), + LongBytesGoString(c), + LongBytesGoString(p)) +} + +func layerGoString(i interface{}, b *bytes.Buffer) { + if s, ok := i.(fmt.GoStringer); ok { + b.WriteString(s.GoString()) + return + } + + var v reflect.Value + var ok bool + if v, ok = i.(reflect.Value); !ok { + v = reflect.ValueOf(i) + } + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + if v.Kind() == reflect.Ptr { + b.WriteByte('&') + } + layerGoString(v.Elem().Interface(), b) + case reflect.Struct: + t := v.Type() + b.WriteString(t.String()) + b.WriteByte('{') + for i := 0; i < v.NumField(); i++ { + if i > 0 { + b.WriteString(", ") + } + if t.Field(i).Name == "BaseLayer" { + fmt.Fprintf(b, "BaseLayer:%s", baseLayerString(v.Field(i))) + } else if v.Field(i).Kind() == reflect.Struct { + fmt.Fprintf(b, "%s:", t.Field(i).Name) + layerGoString(v.Field(i), b) + } else if v.Field(i).Kind() == reflect.Ptr { + b.WriteByte('&') + layerGoString(v.Field(i), b) + } else { + fmt.Fprintf(b, "%s:%#v", t.Field(i).Name, v.Field(i)) + } + } + b.WriteByte('}') + default: + fmt.Fprintf(b, "%#v", i) + } +} + +// LayerGoString returns a representation of the layer in Go syntax, +// taking care to shorten "very long" BaseLayer byte slices +func LayerGoString(l Layer) string { + b := new(bytes.Buffer) + layerGoString(l, b) + return b.String() +} + +func (p *packet) packetString() string { + var b bytes.Buffer + fmt.Fprintf(&b, "PACKET: %d bytes", len(p.Data())) + if p.metadata.Truncated { + b.WriteString(", truncated") + } + if p.metadata.Length > 0 { + fmt.Fprintf(&b, ", wire length %d cap length %d", p.metadata.Length, p.metadata.CaptureLength) + } + if !p.metadata.Timestamp.IsZero() { + fmt.Fprintf(&b, " @ %v", p.metadata.Timestamp) + } + b.WriteByte('\n') + for i, l := range p.layers { + fmt.Fprintf(&b, "- Layer %d (%02d bytes) = %s\n", i+1, len(l.LayerContents()), LayerString(l)) + } + return b.String() +} + +func (p *packet) packetDump() string { + var b bytes.Buffer + fmt.Fprintf(&b, "-- FULL PACKET DATA (%d bytes) ------------------------------------\n%s", len(p.data), hex.Dump(p.data)) + for i, l := range p.layers { + fmt.Fprintf(&b, "--- Layer %d ---\n%s", i+1, LayerDump(l)) + } + return b.String() +} + +// eagerPacket is a packet implementation that does eager decoding. Upon +// initial construction, it decodes all the layers it can from packet data. +// eagerPacket implements Packet and PacketBuilder. +type eagerPacket struct { + packet +} + +var errNilDecoder = errors.New("NextDecoder passed nil decoder, probably an unsupported decode type") + +func (p *eagerPacket) NextDecoder(next Decoder) error { + if next == nil { + return errNilDecoder + } + if p.last == nil { + return errors.New("NextDecoder called, but no layers added yet") + } + d := p.last.LayerPayload() + if len(d) == 0 { + return nil + } + // Since we're eager, immediately call the next decoder. + return next.Decode(d, p) +} +func (p *eagerPacket) initialDecode(dec Decoder) { + defer p.recoverDecodeError() + err := dec.Decode(p.data, p) + if err != nil { + p.addFinalDecodeError(err, nil) + } +} +func (p *eagerPacket) LinkLayer() LinkLayer { + return p.link +} +func (p *eagerPacket) NetworkLayer() NetworkLayer { + return p.network +} +func (p *eagerPacket) TransportLayer() TransportLayer { + return p.transport +} +func (p *eagerPacket) ApplicationLayer() ApplicationLayer { + return p.application +} +func (p *eagerPacket) ErrorLayer() ErrorLayer { + return p.failure +} +func (p *eagerPacket) Layers() []Layer { + return p.layers +} +func (p *eagerPacket) Layer(t LayerType) Layer { + for _, l := range p.layers { + if l.LayerType() == t { + return l + } + } + return nil +} +func (p *eagerPacket) LayerClass(lc LayerClass) Layer { + for _, l := range p.layers { + if lc.Contains(l.LayerType()) { + return l + } + } + return nil +} +func (p *eagerPacket) String() string { return p.packetString() } +func (p *eagerPacket) Dump() string { return p.packetDump() } + +// lazyPacket does lazy decoding on its packet data. On construction it does +// no initial decoding. For each function call, it decodes only as many layers +// as are necessary to compute the return value for that function. +// lazyPacket implements Packet and PacketBuilder. +type lazyPacket struct { + packet + next Decoder +} + +func (p *lazyPacket) NextDecoder(next Decoder) error { + if next == nil { + return errNilDecoder + } + p.next = next + return nil +} +func (p *lazyPacket) decodeNextLayer() { + if p.next == nil { + return + } + d := p.data + if p.last != nil { + d = p.last.LayerPayload() + } + next := p.next + p.next = nil + // We've just set p.next to nil, so if we see we have no data, this should be + // the final call we get to decodeNextLayer if we return here. + if len(d) == 0 { + return + } + defer p.recoverDecodeError() + err := next.Decode(d, p) + if err != nil { + p.addFinalDecodeError(err, nil) + } +} +func (p *lazyPacket) LinkLayer() LinkLayer { + for p.link == nil && p.next != nil { + p.decodeNextLayer() + } + return p.link +} +func (p *lazyPacket) NetworkLayer() NetworkLayer { + for p.network == nil && p.next != nil { + p.decodeNextLayer() + } + return p.network +} +func (p *lazyPacket) TransportLayer() TransportLayer { + for p.transport == nil && p.next != nil { + p.decodeNextLayer() + } + return p.transport +} +func (p *lazyPacket) ApplicationLayer() ApplicationLayer { + for p.application == nil && p.next != nil { + p.decodeNextLayer() + } + return p.application +} +func (p *lazyPacket) ErrorLayer() ErrorLayer { + for p.failure == nil && p.next != nil { + p.decodeNextLayer() + } + return p.failure +} +func (p *lazyPacket) Layers() []Layer { + for p.next != nil { + p.decodeNextLayer() + } + return p.layers +} +func (p *lazyPacket) Layer(t LayerType) Layer { + for _, l := range p.layers { + if l.LayerType() == t { + return l + } + } + numLayers := len(p.layers) + for p.next != nil { + p.decodeNextLayer() + for _, l := range p.layers[numLayers:] { + if l.LayerType() == t { + return l + } + } + numLayers = len(p.layers) + } + return nil +} +func (p *lazyPacket) LayerClass(lc LayerClass) Layer { + for _, l := range p.layers { + if lc.Contains(l.LayerType()) { + return l + } + } + numLayers := len(p.layers) + for p.next != nil { + p.decodeNextLayer() + for _, l := range p.layers[numLayers:] { + if lc.Contains(l.LayerType()) { + return l + } + } + numLayers = len(p.layers) + } + return nil +} +func (p *lazyPacket) String() string { p.Layers(); return p.packetString() } +func (p *lazyPacket) Dump() string { p.Layers(); return p.packetDump() } + +// DecodeOptions tells gopacket how to decode a packet. +type DecodeOptions struct { + // Lazy decoding decodes the minimum number of layers needed to return data + // for a packet at each function call. Be careful using this with concurrent + // packet processors, as each call to packet.* could mutate the packet, and + // two concurrent function calls could interact poorly. + Lazy bool + // NoCopy decoding doesn't copy its input buffer into storage that's owned by + // the packet. If you can guarantee that the bytes underlying the slice + // passed into NewPacket aren't going to be modified, this can be faster. If + // there's any chance that those bytes WILL be changed, this will invalidate + // your packets. + NoCopy bool + // SkipDecodeRecovery skips over panic recovery during packet decoding. + // Normally, when packets decode, if a panic occurs, that panic is captured + // by a recover(), and a DecodeFailure layer is added to the packet detailing + // the issue. If this flag is set, panics are instead allowed to continue up + // the stack. + SkipDecodeRecovery bool + // DecodeStreamsAsDatagrams enables routing of application-level layers in the TCP + // decoder. If true, we should try to decode layers after TCP in single packets. + // This is disabled by default because the reassembly package drives the decoding + // of TCP payload data after reassembly. + DecodeStreamsAsDatagrams bool +} + +// Default decoding provides the safest (but slowest) method for decoding +// packets. It eagerly processes all layers (so it's concurrency-safe) and it +// copies its input buffer upon creation of the packet (so the packet remains +// valid if the underlying slice is modified. Both of these take time, +// though, so beware. If you can guarantee that the packet will only be used +// by one goroutine at a time, set Lazy decoding. If you can guarantee that +// the underlying slice won't change, set NoCopy decoding. +var Default = DecodeOptions{} + +// Lazy is a DecodeOptions with just Lazy set. +var Lazy = DecodeOptions{Lazy: true} + +// NoCopy is a DecodeOptions with just NoCopy set. +var NoCopy = DecodeOptions{NoCopy: true} + +// DecodeStreamsAsDatagrams is a DecodeOptions with just DecodeStreamsAsDatagrams set. +var DecodeStreamsAsDatagrams = DecodeOptions{DecodeStreamsAsDatagrams: true} + +// NewPacket creates a new Packet object from a set of bytes. The +// firstLayerDecoder tells it how to interpret the first layer from the bytes, +// future layers will be generated from that first layer automatically. +func NewPacket(data []byte, firstLayerDecoder Decoder, options DecodeOptions) Packet { + if !options.NoCopy { + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + data = dataCopy + } + if options.Lazy { + p := &lazyPacket{ + packet: packet{data: data, decodeOptions: options}, + next: firstLayerDecoder, + } + p.layers = p.initialLayers[:0] + // Crazy craziness: + // If the following return statemet is REMOVED, and Lazy is FALSE, then + // eager packet processing becomes 17% FASTER. No, there is no logical + // explanation for this. However, it's such a hacky micro-optimization that + // we really can't rely on it. It appears to have to do with the size the + // compiler guesses for this function's stack space, since one symptom is + // that with the return statement in place, we more than double calls to + // runtime.morestack/runtime.lessstack. We'll hope the compiler gets better + // over time and we get this optimization for free. Until then, we'll have + // to live with slower packet processing. + return p + } + p := &eagerPacket{ + packet: packet{data: data, decodeOptions: options}, + } + p.layers = p.initialLayers[:0] + p.initialDecode(firstLayerDecoder) + return p +} + +// PacketDataSource is an interface for some source of packet data. Users may +// create their own implementations, or use the existing implementations in +// gopacket/pcap (libpcap, allows reading from live interfaces or from +// pcap files) or gopacket/pfring (PF_RING, allows reading from live +// interfaces). +type PacketDataSource interface { + // ReadPacketData returns the next packet available from this data source. + // It returns: + // data: The bytes of an individual packet. + // ci: Metadata about the capture + // err: An error encountered while reading packet data. If err != nil, + // then data/ci will be ignored. + ReadPacketData() (data []byte, ci CaptureInfo, err error) +} + +// ConcatFinitePacketDataSources returns a PacketDataSource that wraps a set +// of internal PacketDataSources, each of which will stop with io.EOF after +// reading a finite number of packets. The returned PacketDataSource will +// return all packets from the first finite source, followed by all packets from +// the second, etc. Once all finite sources have returned io.EOF, the returned +// source will as well. +func ConcatFinitePacketDataSources(pds ...PacketDataSource) PacketDataSource { + c := concat(pds) + return &c +} + +type concat []PacketDataSource + +func (c *concat) ReadPacketData() (data []byte, ci CaptureInfo, err error) { + for len(*c) > 0 { + data, ci, err = (*c)[0].ReadPacketData() + if err == io.EOF { + *c = (*c)[1:] + continue + } + return + } + return nil, CaptureInfo{}, io.EOF +} + +// ZeroCopyPacketDataSource is an interface to pull packet data from sources +// that allow data to be returned without copying to a user-controlled buffer. +// It's very similar to PacketDataSource, except that the caller must be more +// careful in how the returned buffer is handled. +type ZeroCopyPacketDataSource interface { + // ZeroCopyReadPacketData returns the next packet available from this data source. + // It returns: + // data: The bytes of an individual packet. Unlike with + // PacketDataSource's ReadPacketData, the slice returned here points + // to a buffer owned by the data source. In particular, the bytes in + // this buffer may be changed by future calls to + // ZeroCopyReadPacketData. Do not use the returned buffer after + // subsequent ZeroCopyReadPacketData calls. + // ci: Metadata about the capture + // err: An error encountered while reading packet data. If err != nil, + // then data/ci will be ignored. + ZeroCopyReadPacketData() (data []byte, ci CaptureInfo, err error) +} + +// PacketSource reads in packets from a PacketDataSource, decodes them, and +// returns them. +// +// There are currently two different methods for reading packets in through +// a PacketSource: +// +// Reading With Packets Function +// +// This method is the most convenient and easiest to code, but lacks +// flexibility. Packets returns a 'chan Packet', then asynchronously writes +// packets into that channel. Packets uses a blocking channel, and closes +// it if an io.EOF is returned by the underlying PacketDataSource. All other +// PacketDataSource errors are ignored and discarded. +// for packet := range packetSource.Packets() { +// ... +// } +// +// Reading With NextPacket Function +// +// This method is the most flexible, and exposes errors that may be +// encountered by the underlying PacketDataSource. It's also the fastest +// in a tight loop, since it doesn't have the overhead of a channel +// read/write. However, it requires the user to handle errors, most +// importantly the io.EOF error in cases where packets are being read from +// a file. +// for { +// packet, err := packetSource.NextPacket() +// if err == io.EOF { +// break +// } else if err != nil { +// log.Println("Error:", err) +// continue +// } +// handlePacket(packet) // Do something with each packet. +// } +type PacketSource struct { + source PacketDataSource + decoder Decoder + // DecodeOptions is the set of options to use for decoding each piece + // of packet data. This can/should be changed by the user to reflect the + // way packets should be decoded. + DecodeOptions + c chan Packet +} + +// NewPacketSource creates a packet data source. +func NewPacketSource(source PacketDataSource, decoder Decoder) *PacketSource { + return &PacketSource{ + source: source, + decoder: decoder, + } +} + +// NextPacket returns the next decoded packet from the PacketSource. On error, +// it returns a nil packet and a non-nil error. +func (p *PacketSource) NextPacket() (Packet, error) { + data, ci, err := p.source.ReadPacketData() + if err != nil { + return nil, err + } + packet := NewPacket(data, p.decoder, p.DecodeOptions) + m := packet.Metadata() + m.CaptureInfo = ci + m.Truncated = m.Truncated || ci.CaptureLength < ci.Length + return packet, nil +} + +// packetsToChannel reads in all packets from the packet source and sends them +// to the given channel. This routine terminates when a non-temporary error +// is returned by NextPacket(). +func (p *PacketSource) packetsToChannel() { + defer close(p.c) + for { + packet, err := p.NextPacket() + if err == nil { + p.c <- packet + continue + } + + // Immediately retry for temporary network errors + if nerr, ok := err.(net.Error); ok && nerr.Temporary() { + continue + } + + // Immediately retry for EAGAIN + if err == syscall.EAGAIN { + continue + } + + // Immediately break for known unrecoverable errors + if err == io.EOF || err == io.ErrUnexpectedEOF || + err == io.ErrNoProgress || err == io.ErrClosedPipe || err == io.ErrShortBuffer || + err == syscall.EBADF || + strings.Contains(err.Error(), "use of closed file") { + break + } + + // Sleep briefly and try again + time.Sleep(time.Millisecond * time.Duration(5)) + } +} + +// Packets returns a channel of packets, allowing easy iterating over +// packets. Packets will be asynchronously read in from the underlying +// PacketDataSource and written to the returned channel. If the underlying +// PacketDataSource returns an io.EOF error, the channel will be closed. +// If any other error is encountered, it is ignored. +// +// for packet := range packetSource.Packets() { +// handlePacket(packet) // Do something with each packet. +// } +// +// If called more than once, returns the same channel. +func (p *PacketSource) Packets() chan Packet { + if p.c == nil { + p.c = make(chan Packet, 1000) + go p.packetsToChannel() + } + return p.c +} diff --git a/vendor/github.com/google/gopacket/parser.go b/vendor/github.com/google/gopacket/parser.go new file mode 100644 index 00000000..4a4676f1 --- /dev/null +++ b/vendor/github.com/google/gopacket/parser.go @@ -0,0 +1,350 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// A container for single LayerType->DecodingLayer mapping. +type decodingLayerElem struct { + typ LayerType + dec DecodingLayer +} + +// DecodingLayer is an interface for packet layers that can decode themselves. +// +// The important part of DecodingLayer is that they decode themselves in-place. +// Calling DecodeFromBytes on a DecodingLayer totally resets the entire layer to +// the new state defined by the data passed in. A returned error leaves the +// DecodingLayer in an unknown intermediate state, thus its fields should not be +// trusted. +// +// Because the DecodingLayer is resetting its own fields, a call to +// DecodeFromBytes should normally not require any memory allocation. +type DecodingLayer interface { + // DecodeFromBytes resets the internal state of this layer to the state + // defined by the passed-in bytes. Slices in the DecodingLayer may + // reference the passed-in data, so care should be taken to copy it + // first should later modification of data be required before the + // DecodingLayer is discarded. + DecodeFromBytes(data []byte, df DecodeFeedback) error + // CanDecode returns the set of LayerTypes this DecodingLayer can + // decode. For Layers that are also DecodingLayers, this will most + // often be that Layer's LayerType(). + CanDecode() LayerClass + // NextLayerType returns the LayerType which should be used to decode + // the LayerPayload. + NextLayerType() LayerType + // LayerPayload is the set of bytes remaining to decode after a call to + // DecodeFromBytes. + LayerPayload() []byte +} + +// DecodingLayerFunc decodes given packet and stores decoded LayerType +// values into specified slice. Returns either first encountered +// unsupported LayerType value or decoding error. In case of success, +// returns (LayerTypeZero, nil). +type DecodingLayerFunc func([]byte, *[]LayerType) (LayerType, error) + +// DecodingLayerContainer stores all DecodingLayer-s and serves as a +// searching tool for DecodingLayerParser. +type DecodingLayerContainer interface { + // Put adds new DecodingLayer to container. The new instance of + // the same DecodingLayerContainer is returned so it may be + // implemented as a value receiver. + Put(DecodingLayer) DecodingLayerContainer + // Decoder returns DecodingLayer to decode given LayerType and + // true if it was found. If no decoder found, return false. + Decoder(LayerType) (DecodingLayer, bool) + // LayersDecoder returns DecodingLayerFunc which decodes given + // packet, starting with specified LayerType and DecodeFeedback. + LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc +} + +// DecodingLayerSparse is a sparse array-based implementation of +// DecodingLayerContainer. Each DecodingLayer is addressed in an +// allocated slice by LayerType value itself. Though this is the +// fastest container it may be memory-consuming if used with big +// LayerType values. +type DecodingLayerSparse []DecodingLayer + +// Put implements DecodingLayerContainer interface. +func (dl DecodingLayerSparse) Put(d DecodingLayer) DecodingLayerContainer { + maxLayerType := LayerType(len(dl) - 1) + for _, typ := range d.CanDecode().LayerTypes() { + if typ > maxLayerType { + maxLayerType = typ + } + } + + if extra := maxLayerType - LayerType(len(dl)) + 1; extra > 0 { + dl = append(dl, make([]DecodingLayer, extra)...) + } + + for _, typ := range d.CanDecode().LayerTypes() { + dl[typ] = d + } + return dl +} + +// LayersDecoder implements DecodingLayerContainer interface. +func (dl DecodingLayerSparse) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc { + return LayersDecoder(dl, first, df) +} + +// Decoder implements DecodingLayerContainer interface. +func (dl DecodingLayerSparse) Decoder(typ LayerType) (DecodingLayer, bool) { + if int64(typ) < int64(len(dl)) { + decoder := dl[typ] + return decoder, decoder != nil + } + return nil, false +} + +// DecodingLayerArray is an array-based implementation of +// DecodingLayerContainer. Each DecodingLayer is searched linearly in +// an allocated slice in one-by-one fashion. +type DecodingLayerArray []decodingLayerElem + +// Put implements DecodingLayerContainer interface. +func (dl DecodingLayerArray) Put(d DecodingLayer) DecodingLayerContainer { +TYPES: + for _, typ := range d.CanDecode().LayerTypes() { + for i := range dl { + if dl[i].typ == typ { + dl[i].dec = d + continue TYPES + } + } + dl = append(dl, decodingLayerElem{typ, d}) + } + return dl +} + +// Decoder implements DecodingLayerContainer interface. +func (dl DecodingLayerArray) Decoder(typ LayerType) (DecodingLayer, bool) { + for i := range dl { + if dl[i].typ == typ { + return dl[i].dec, true + } + } + return nil, false +} + +// LayersDecoder implements DecodingLayerContainer interface. +func (dl DecodingLayerArray) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc { + return LayersDecoder(dl, first, df) +} + +// DecodingLayerMap is an map-based implementation of +// DecodingLayerContainer. Each DecodingLayer is searched in a map +// hashed by LayerType value. +type DecodingLayerMap map[LayerType]DecodingLayer + +// Put implements DecodingLayerContainer interface. +func (dl DecodingLayerMap) Put(d DecodingLayer) DecodingLayerContainer { + for _, typ := range d.CanDecode().LayerTypes() { + if dl == nil { + dl = make(map[LayerType]DecodingLayer) + } + dl[typ] = d + } + return dl +} + +// Decoder implements DecodingLayerContainer interface. +func (dl DecodingLayerMap) Decoder(typ LayerType) (DecodingLayer, bool) { + d, ok := dl[typ] + return d, ok +} + +// LayersDecoder implements DecodingLayerContainer interface. +func (dl DecodingLayerMap) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc { + return LayersDecoder(dl, first, df) +} + +// Static code check. +var ( + _ = []DecodingLayerContainer{ + DecodingLayerSparse(nil), + DecodingLayerMap(nil), + DecodingLayerArray(nil), + } +) + +// DecodingLayerParser parses a given set of layer types. See DecodeLayers for +// more information on how DecodingLayerParser should be used. +type DecodingLayerParser struct { + // DecodingLayerParserOptions is the set of options available to the + // user to define the parser's behavior. + DecodingLayerParserOptions + dlc DecodingLayerContainer + first LayerType + df DecodeFeedback + + decodeFunc DecodingLayerFunc + + // Truncated is set when a decode layer detects that the packet has been + // truncated. + Truncated bool +} + +// AddDecodingLayer adds a decoding layer to the parser. This adds support for +// the decoding layer's CanDecode layers to the parser... should they be +// encountered, they'll be parsed. +func (l *DecodingLayerParser) AddDecodingLayer(d DecodingLayer) { + l.SetDecodingLayerContainer(l.dlc.Put(d)) +} + +// SetTruncated is used by DecodingLayers to set the Truncated boolean in the +// DecodingLayerParser. Users should simply read Truncated after calling +// DecodeLayers. +func (l *DecodingLayerParser) SetTruncated() { + l.Truncated = true +} + +// NewDecodingLayerParser creates a new DecodingLayerParser and adds in all +// of the given DecodingLayers with AddDecodingLayer. +// +// Each call to DecodeLayers will attempt to decode the given bytes first by +// treating them as a 'first'-type layer, then by using NextLayerType on +// subsequently decoded layers to find the next relevant decoder. Should a +// deoder not be available for the layer type returned by NextLayerType, +// decoding will stop. +// +// NewDecodingLayerParser uses DecodingLayerMap container by +// default. +func NewDecodingLayerParser(first LayerType, decoders ...DecodingLayer) *DecodingLayerParser { + dlp := &DecodingLayerParser{first: first} + dlp.df = dlp // Cast this once to the interface + // default container + dlc := DecodingLayerContainer(DecodingLayerMap(make(map[LayerType]DecodingLayer))) + for _, d := range decoders { + dlc = dlc.Put(d) + } + + dlp.SetDecodingLayerContainer(dlc) + return dlp +} + +// SetDecodingLayerContainer specifies container with decoders. This +// call replaces all decoders already registered in given instance of +// DecodingLayerParser. +func (l *DecodingLayerParser) SetDecodingLayerContainer(dlc DecodingLayerContainer) { + l.dlc = dlc + l.decodeFunc = l.dlc.LayersDecoder(l.first, l.df) +} + +// DecodeLayers decodes as many layers as possible from the given data. It +// initially treats the data as layer type 'typ', then uses NextLayerType on +// each subsequent decoded layer until it gets to a layer type it doesn't know +// how to parse. +// +// For each layer successfully decoded, DecodeLayers appends the layer type to +// the decoded slice. DecodeLayers truncates the 'decoded' slice initially, so +// there's no need to empty it yourself. +// +// This decoding method is about an order of magnitude faster than packet +// decoding, because it only decodes known layers that have already been +// allocated. This means it doesn't need to allocate each layer it returns... +// instead it overwrites the layers that already exist. +// +// Example usage: +// func main() { +// var eth layers.Ethernet +// var ip4 layers.IPv4 +// var ip6 layers.IPv6 +// var tcp layers.TCP +// var udp layers.UDP +// var payload gopacket.Payload +// parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp, &udp, &payload) +// var source gopacket.PacketDataSource = getMyDataSource() +// decodedLayers := make([]gopacket.LayerType, 0, 10) +// for { +// data, _, err := source.ReadPacketData() +// if err != nil { +// fmt.Println("Error reading packet data: ", err) +// continue +// } +// fmt.Println("Decoding packet") +// err = parser.DecodeLayers(data, &decodedLayers) +// for _, typ := range decodedLayers { +// fmt.Println(" Successfully decoded layer type", typ) +// switch typ { +// case layers.LayerTypeEthernet: +// fmt.Println(" Eth ", eth.SrcMAC, eth.DstMAC) +// case layers.LayerTypeIPv4: +// fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP) +// case layers.LayerTypeIPv6: +// fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP) +// case layers.LayerTypeTCP: +// fmt.Println(" TCP ", tcp.SrcPort, tcp.DstPort) +// case layers.LayerTypeUDP: +// fmt.Println(" UDP ", udp.SrcPort, udp.DstPort) +// } +// } +// if decodedLayers.Truncated { +// fmt.Println(" Packet has been truncated") +// } +// if err != nil { +// fmt.Println(" Error encountered:", err) +// } +// } +// } +// +// If DecodeLayers is unable to decode the next layer type, it will return the +// error UnsupportedLayerType. +func (l *DecodingLayerParser) DecodeLayers(data []byte, decoded *[]LayerType) (err error) { + l.Truncated = false + if !l.IgnorePanic { + defer panicToError(&err) + } + typ, err := l.decodeFunc(data, decoded) + if typ != LayerTypeZero { + // no decoder + if l.IgnoreUnsupported { + return nil + } + return UnsupportedLayerType(typ) + } + return err +} + +// UnsupportedLayerType is returned by DecodingLayerParser if DecodeLayers +// encounters a layer type that the DecodingLayerParser has no decoder for. +type UnsupportedLayerType LayerType + +// Error implements the error interface, returning a string to say that the +// given layer type is unsupported. +func (e UnsupportedLayerType) Error() string { + return fmt.Sprintf("No decoder for layer type %v", LayerType(e)) +} + +func panicToError(e *error) { + if r := recover(); r != nil { + *e = fmt.Errorf("panic: %v", r) + } +} + +// DecodingLayerParserOptions provides options to affect the behavior of a given +// DecodingLayerParser. +type DecodingLayerParserOptions struct { + // IgnorePanic determines whether a DecodingLayerParser should stop + // panics on its own (by returning them as an error from DecodeLayers) + // or should allow them to raise up the stack. Handling errors does add + // latency to the process of decoding layers, but is much safer for + // callers. IgnorePanic defaults to false, thus if the caller does + // nothing decode panics will be returned as errors. + IgnorePanic bool + // IgnoreUnsupported will stop parsing and return a nil error when it + // encounters a layer it doesn't have a parser for, instead of returning an + // UnsupportedLayerType error. If this is true, it's up to the caller to make + // sure that all expected layers have been parsed (by checking the decoded + // slice). + IgnoreUnsupported bool +} diff --git a/vendor/github.com/google/gopacket/time.go b/vendor/github.com/google/gopacket/time.go new file mode 100644 index 00000000..6d116cdf --- /dev/null +++ b/vendor/github.com/google/gopacket/time.go @@ -0,0 +1,72 @@ +// Copyright 2018 The GoPacket Authors. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" + "math" + "time" +) + +// TimestampResolution represents the resolution of timestamps in Base^Exponent. +type TimestampResolution struct { + Base, Exponent int +} + +func (t TimestampResolution) String() string { + return fmt.Sprintf("%d^%d", t.Base, t.Exponent) +} + +// ToDuration returns the smallest representable time difference as a time.Duration +func (t TimestampResolution) ToDuration() time.Duration { + if t.Base == 0 { + return 0 + } + if t.Exponent == 0 { + return time.Second + } + switch t.Base { + case 10: + return time.Duration(math.Pow10(t.Exponent + 9)) + case 2: + if t.Exponent < 0 { + return time.Second >> uint(-t.Exponent) + } + return time.Second << uint(t.Exponent) + default: + // this might loose precision + return time.Duration(float64(time.Second) * math.Pow(float64(t.Base), float64(t.Exponent))) + } +} + +// TimestampResolutionInvalid represents an invalid timestamp resolution +var TimestampResolutionInvalid = TimestampResolution{} + +// TimestampResolutionMillisecond is a resolution of 10^-3s +var TimestampResolutionMillisecond = TimestampResolution{10, -3} + +// TimestampResolutionMicrosecond is a resolution of 10^-6s +var TimestampResolutionMicrosecond = TimestampResolution{10, -6} + +// TimestampResolutionNanosecond is a resolution of 10^-9s +var TimestampResolutionNanosecond = TimestampResolution{10, -9} + +// TimestampResolutionNTP is the resolution of NTP timestamps which is 2^-32 ≈ 233 picoseconds +var TimestampResolutionNTP = TimestampResolution{2, -32} + +// TimestampResolutionCaptureInfo is the resolution used in CaptureInfo, which his currently nanosecond +var TimestampResolutionCaptureInfo = TimestampResolutionNanosecond + +// PacketSourceResolution is an interface for packet data sources that +// support reporting the timestamp resolution of the aqcuired timestamps. +// Returned timestamps will always have NanosecondTimestampResolution due +// to the use of time.Time, but scaling might have occured if acquired +// timestamps have a different resolution. +type PacketSourceResolution interface { + // Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution. + Resolution() TimestampResolution +} diff --git a/vendor/github.com/google/gopacket/writer.go b/vendor/github.com/google/gopacket/writer.go new file mode 100644 index 00000000..5d303dc4 --- /dev/null +++ b/vendor/github.com/google/gopacket/writer.go @@ -0,0 +1,232 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +package gopacket + +import ( + "fmt" +) + +// SerializableLayer allows its implementations to be written out as a set of bytes, +// so those bytes may be sent on the wire or otherwise used by the caller. +// SerializableLayer is implemented by certain Layer types, and can be encoded to +// bytes using the LayerWriter object. +type SerializableLayer interface { + // SerializeTo writes this layer to a slice, growing that slice if necessary + // to make it fit the layer's data. + // Args: + // b: SerializeBuffer to write this layer on to. When called, b.Bytes() + // is the payload this layer should wrap, if any. Note that this + // layer can either prepend itself (common), append itself + // (uncommon), or both (sometimes padding or footers are required at + // the end of packet data). It's also possible (though probably very + // rarely needed) to overwrite any bytes in the current payload. + // After this call, b.Bytes() should return the byte encoding of + // this layer wrapping the original b.Bytes() payload. + // opts: options to use while writing out data. + // Returns: + // error if a problem was encountered during encoding. If an error is + // returned, the bytes in data should be considered invalidated, and + // not used. + // + // SerializeTo calls SHOULD entirely ignore LayerContents and + // LayerPayload. It just serializes based on struct fields, neither + // modifying nor using contents/payload. + SerializeTo(b SerializeBuffer, opts SerializeOptions) error + // LayerType returns the type of the layer that is being serialized to the buffer + LayerType() LayerType +} + +// SerializeOptions provides options for behaviors that SerializableLayers may want to +// implement. +type SerializeOptions struct { + // FixLengths determines whether, during serialization, layers should fix + // the values for any length field that depends on the payload. + FixLengths bool + // ComputeChecksums determines whether, during serialization, layers + // should recompute checksums based on their payloads. + ComputeChecksums bool +} + +// SerializeBuffer is a helper used by gopacket for writing out packet layers. +// SerializeBuffer starts off as an empty []byte. Subsequent calls to PrependBytes +// return byte slices before the current Bytes(), AppendBytes returns byte +// slices after. +// +// Byte slices returned by PrependBytes/AppendBytes are NOT zero'd out, so if +// you want to make sure they're all zeros, set them as such. +// +// SerializeBuffer is specifically designed to handle packet writing, where unlike +// with normal writes it's easier to start writing at the inner-most layer and +// work out, meaning that we often need to prepend bytes. This runs counter to +// typical writes to byte slices using append(), where we only write at the end +// of the buffer. +// +// It can be reused via Clear. Note, however, that a Clear call will invalidate the +// byte slices returned by any previous Bytes() call (the same buffer is +// reused). +// +// 1) Reusing a write buffer is generally much faster than creating a new one, +// and with the default implementation it avoids additional memory allocations. +// 2) If a byte slice from a previous Bytes() call will continue to be used, +// it's better to create a new SerializeBuffer. +// +// The Clear method is specifically designed to minimize memory allocations for +// similar later workloads on the SerializeBuffer. IE: if you make a set of +// Prepend/Append calls, then clear, then make the same calls with the same +// sizes, the second round (and all future similar rounds) shouldn't allocate +// any new memory. +type SerializeBuffer interface { + // Bytes returns the contiguous set of bytes collected so far by Prepend/Append + // calls. The slice returned by Bytes will be modified by future Clear calls, + // so if you're planning on clearing this SerializeBuffer, you may want to copy + // Bytes somewhere safe first. + Bytes() []byte + // PrependBytes returns a set of bytes which prepends the current bytes in this + // buffer. These bytes start in an indeterminate state, so they should be + // overwritten by the caller. The caller must only call PrependBytes if they + // know they're going to immediately overwrite all bytes returned. + PrependBytes(num int) ([]byte, error) + // AppendBytes returns a set of bytes which appends the current bytes in this + // buffer. These bytes start in an indeterminate state, so they should be + // overwritten by the caller. The caller must only call AppendBytes if they + // know they're going to immediately overwrite all bytes returned. + AppendBytes(num int) ([]byte, error) + // Clear resets the SerializeBuffer to a new, empty buffer. After a call to clear, + // the byte slice returned by any previous call to Bytes() for this buffer + // should be considered invalidated. + Clear() error + // Layers returns all the Layers that have been successfully serialized into this buffer + // already. + Layers() []LayerType + // PushLayer adds the current Layer to the list of Layers that have been serialized + // into this buffer. + PushLayer(LayerType) +} + +type serializeBuffer struct { + data []byte + start int + prepended, appended int + layers []LayerType +} + +// NewSerializeBuffer creates a new instance of the default implementation of +// the SerializeBuffer interface. +func NewSerializeBuffer() SerializeBuffer { + return &serializeBuffer{} +} + +// NewSerializeBufferExpectedSize creates a new buffer for serialization, optimized for an +// expected number of bytes prepended/appended. This tends to decrease the +// number of memory allocations made by the buffer during writes. +func NewSerializeBufferExpectedSize(expectedPrependLength, expectedAppendLength int) SerializeBuffer { + return &serializeBuffer{ + data: make([]byte, expectedPrependLength, expectedPrependLength+expectedAppendLength), + start: expectedPrependLength, + prepended: expectedPrependLength, + appended: expectedAppendLength, + } +} + +func (w *serializeBuffer) Bytes() []byte { + return w.data[w.start:] +} + +func (w *serializeBuffer) PrependBytes(num int) ([]byte, error) { + if num < 0 { + panic("num < 0") + } + if w.start < num { + toPrepend := w.prepended + if toPrepend < num { + toPrepend = num + } + w.prepended += toPrepend + length := cap(w.data) + toPrepend + newData := make([]byte, length) + newStart := w.start + toPrepend + copy(newData[newStart:], w.data[w.start:]) + w.start = newStart + w.data = newData[:toPrepend+len(w.data)] + } + w.start -= num + return w.data[w.start : w.start+num], nil +} + +func (w *serializeBuffer) AppendBytes(num int) ([]byte, error) { + if num < 0 { + panic("num < 0") + } + initialLength := len(w.data) + if cap(w.data)-initialLength < num { + toAppend := w.appended + if toAppend < num { + toAppend = num + } + w.appended += toAppend + newData := make([]byte, cap(w.data)+toAppend) + copy(newData[w.start:], w.data[w.start:]) + w.data = newData[:initialLength] + } + // Grow the buffer. We know it'll be under capacity given above. + w.data = w.data[:initialLength+num] + return w.data[initialLength:], nil +} + +func (w *serializeBuffer) Clear() error { + w.start = w.prepended + w.data = w.data[:w.start] + w.layers = w.layers[:0] + return nil +} + +func (w *serializeBuffer) Layers() []LayerType { + return w.layers +} + +func (w *serializeBuffer) PushLayer(l LayerType) { + w.layers = append(w.layers, l) +} + +// SerializeLayers clears the given write buffer, then writes all layers into it so +// they correctly wrap each other. Note that by clearing the buffer, it +// invalidates all slices previously returned by w.Bytes() +// +// Example: +// buf := gopacket.NewSerializeBuffer() +// opts := gopacket.SerializeOptions{} +// gopacket.SerializeLayers(buf, opts, a, b, c) +// firstPayload := buf.Bytes() // contains byte representation of a(b(c)) +// gopacket.SerializeLayers(buf, opts, d, e, f) +// secondPayload := buf.Bytes() // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf. +func SerializeLayers(w SerializeBuffer, opts SerializeOptions, layers ...SerializableLayer) error { + w.Clear() + for i := len(layers) - 1; i >= 0; i-- { + layer := layers[i] + err := layer.SerializeTo(w, opts) + if err != nil { + return err + } + w.PushLayer(layer.LayerType()) + } + return nil +} + +// SerializePacket is a convenience function that calls SerializeLayers +// on packet's Layers(). +// It returns an error if one of the packet layers is not a SerializableLayer. +func SerializePacket(buf SerializeBuffer, opts SerializeOptions, packet Packet) error { + sls := []SerializableLayer{} + for _, layer := range packet.Layers() { + sl, ok := layer.(SerializableLayer) + if !ok { + return fmt.Errorf("layer %s is not serializable", layer.LayerType().String()) + } + sls = append(sls, sl) + } + return SerializeLayers(buf, opts, sls...) +} diff --git a/vendor/golang.org/x/net/AUTHORS b/vendor/golang.org/x/net/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/vendor/golang.org/x/net/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/golang.org/x/net/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/vendor/golang.org/x/net/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go index 0178647e..184ac45f 100644 --- a/vendor/golang.org/x/net/http2/frame.go +++ b/vendor/golang.org/x/net/http2/frame.go @@ -23,7 +23,7 @@ const frameHeaderLen = 9 var padZeros = make([]byte, 255) // zeros for padding // A FrameType is a registered frame type as defined in -// http://http2.github.io/http2-spec/#rfc.section.11.2 +// https://httpwg.org/specs/rfc7540.html#rfc.section.11.2 type FrameType uint8 const ( @@ -146,7 +146,7 @@ func typeFrameParser(t FrameType) frameParser { // A FrameHeader is the 9 byte header of all HTTP/2 frames. // -// See http://http2.github.io/http2-spec/#FrameHeader +// See https://httpwg.org/specs/rfc7540.html#FrameHeader type FrameHeader struct { valid bool // caller can access []byte fields in the Frame @@ -575,7 +575,7 @@ func (fr *Framer) checkFrameOrder(f Frame) error { // A DataFrame conveys arbitrary, variable-length sequences of octets // associated with a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.1 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.1 type DataFrame struct { FrameHeader data []byte @@ -698,7 +698,7 @@ func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []by // endpoints communicate, such as preferences and constraints on peer // behavior. // -// See http://http2.github.io/http2-spec/#SETTINGS +// See https://httpwg.org/specs/rfc7540.html#SETTINGS type SettingsFrame struct { FrameHeader p []byte @@ -837,7 +837,7 @@ func (f *Framer) WriteSettingsAck() error { // A PingFrame is a mechanism for measuring a minimal round trip time // from the sender, as well as determining whether an idle connection // is still functional. -// See http://http2.github.io/http2-spec/#rfc.section.6.7 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.7 type PingFrame struct { FrameHeader Data [8]byte @@ -870,7 +870,7 @@ func (f *Framer) WritePing(ack bool, data [8]byte) error { } // A GoAwayFrame informs the remote peer to stop creating streams on this connection. -// See http://http2.github.io/http2-spec/#rfc.section.6.8 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.8 type GoAwayFrame struct { FrameHeader LastStreamID uint32 @@ -934,7 +934,7 @@ func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p } // A WindowUpdateFrame is used to implement flow control. -// See http://http2.github.io/http2-spec/#rfc.section.6.9 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.9 type WindowUpdateFrame struct { FrameHeader Increment uint32 // never read with high bit set @@ -1123,7 +1123,7 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error { } // A PriorityFrame specifies the sender-advised priority of a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.3 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.3 type PriorityFrame struct { FrameHeader PriorityParam @@ -1193,7 +1193,7 @@ func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error { } // A RSTStreamFrame allows for abnormal termination of a stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.4 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4 type RSTStreamFrame struct { FrameHeader ErrCode ErrCode @@ -1225,7 +1225,7 @@ func (f *Framer) WriteRSTStream(streamID uint32, code ErrCode) error { } // A ContinuationFrame is used to continue a sequence of header block fragments. -// See http://http2.github.io/http2-spec/#rfc.section.6.10 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.10 type ContinuationFrame struct { FrameHeader headerFragBuf []byte @@ -1266,7 +1266,7 @@ func (f *Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlock } // A PushPromiseFrame is used to initiate a server stream. -// See http://http2.github.io/http2-spec/#rfc.section.6.6 +// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.6 type PushPromiseFrame struct { FrameHeader PromiseID uint32 diff --git a/vendor/golang.org/x/net/http2/hpack/encode.go b/vendor/golang.org/x/net/http2/hpack/encode.go index 97f17831..6886dc16 100644 --- a/vendor/golang.org/x/net/http2/hpack/encode.go +++ b/vendor/golang.org/x/net/http2/hpack/encode.go @@ -191,7 +191,7 @@ func appendTableSize(dst []byte, v uint32) []byte { // bit prefix, to dst and returns the extended buffer. // // See -// http://http2.github.io/http2-spec/compression.html#integer.representation +// https://httpwg.org/specs/rfc7541.html#integer.representation func appendVarInt(dst []byte, n byte, i uint64) []byte { k := uint64((1 << n) - 1) if i < k { diff --git a/vendor/golang.org/x/net/http2/hpack/hpack.go b/vendor/golang.org/x/net/http2/hpack/hpack.go index 85f18a2b..ebdfbee9 100644 --- a/vendor/golang.org/x/net/http2/hpack/hpack.go +++ b/vendor/golang.org/x/net/http2/hpack/hpack.go @@ -59,7 +59,7 @@ func (hf HeaderField) String() string { // Size returns the size of an entry per RFC 7541 section 4.1. func (hf HeaderField) Size() uint32 { - // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.4.1 // "The size of the dynamic table is the sum of the size of // its entries. The size of an entry is the sum of its name's // length in octets (as defined in Section 5.2), its value's @@ -158,7 +158,7 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) { } type dynamicTable struct { - // http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2 + // https://httpwg.org/specs/rfc7541.html#rfc.section.2.3.2 table headerFieldTable size uint32 // in bytes maxSize uint32 // current maxSize @@ -307,27 +307,27 @@ func (d *Decoder) parseHeaderFieldRepr() error { case b&128 != 0: // Indexed representation. // High bit set? - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.1 return d.parseFieldIndexed() case b&192 == 64: // 6.2.1 Literal Header Field with Incremental Indexing // 0b10xxxxxx: top two bits are 10 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1 return d.parseFieldLiteral(6, indexedTrue) case b&240 == 0: // 6.2.2 Literal Header Field without Indexing // 0b0000xxxx: top four bits are 0000 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2 return d.parseFieldLiteral(4, indexedFalse) case b&240 == 16: // 6.2.3 Literal Header Field never Indexed // 0b0001xxxx: top four bits are 0001 - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3 return d.parseFieldLiteral(4, indexedNever) case b&224 == 32: // 6.3 Dynamic Table Size Update // Top three bits are '001'. - // http://http2.github.io/http2-spec/compression.html#rfc.section.6.3 + // https://httpwg.org/specs/rfc7541.html#rfc.section.6.3 return d.parseDynamicTableSizeUpdate() } @@ -420,7 +420,7 @@ var errVarintOverflow = DecodingError{errors.New("varint integer overflow")} // readVarInt reads an unsigned variable length integer off the // beginning of p. n is the parameter as described in -// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1. +// https://httpwg.org/specs/rfc7541.html#rfc.section.5.1. // // n must always be between 1 and 8. // diff --git a/vendor/golang.org/x/net/http2/http2.go b/vendor/golang.org/x/net/http2/http2.go index 479ba4b2..6f2df281 100644 --- a/vendor/golang.org/x/net/http2/http2.go +++ b/vendor/golang.org/x/net/http2/http2.go @@ -55,14 +55,14 @@ const ( ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // SETTINGS_MAX_FRAME_SIZE default - // http://http2.github.io/http2-spec/#rfc.section.6.5.2 + // https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2 initialMaxFrameSize = 16384 // NextProtoTLS is the NPN/ALPN protocol negotiated during // HTTP/2's TLS setup. NextProtoTLS = "h2" - // http://http2.github.io/http2-spec/#SettingValues + // https://httpwg.org/specs/rfc7540.html#SettingValues initialHeaderTableSize = 4096 initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size @@ -111,7 +111,7 @@ func (st streamState) String() string { // Setting is a setting parameter: which setting it is, and its value. type Setting struct { // ID is which setting is being set. - // See http://http2.github.io/http2-spec/#SettingValues + // See https://httpwg.org/specs/rfc7540.html#SettingFormat ID SettingID // Val is the value. @@ -143,7 +143,7 @@ func (s Setting) Valid() error { } // A SettingID is an HTTP/2 setting as defined in -// http://http2.github.io/http2-spec/#iana-settings +// https://httpwg.org/specs/rfc7540.html#iana-settings type SettingID uint16 const ( diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 47524a61..aa3b0864 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -1747,6 +1747,12 @@ func (sc *serverConn) processData(f *DataFrame) error { // Sender sending more than they'd declared? if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes { + if sc.inflow.available() < int32(f.Length) { + return sc.countError("data_flow", streamError(id, ErrCodeFlowControl)) + } + sc.inflow.take(int32(f.Length)) + sc.sendWindowUpdate(nil, int(f.Length)) // conn-level + st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes)) // RFC 7540, sec 8.1.2.6: A request or response is also malformed if the // value of a content-length header field does not equal the sum of the @@ -2223,6 +2229,9 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler didPanic := true defer func() { rw.rws.stream.cancelCtx() + if req.MultipartForm != nil { + req.MultipartForm.RemoveAll() + } if didPanic { e := recover() sc.writeFrameFromHandler(FrameWriteRequest{ diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 4ded4dfd..90fdc28c 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -67,13 +67,23 @@ const ( // A Transport internally caches connections to servers. It is safe // for concurrent use by multiple goroutines. type Transport struct { - // DialTLS specifies an optional dial function for creating - // TLS connections for requests. + // DialTLSContext specifies an optional dial function with context for + // creating TLS connections for requests. // - // If DialTLS is nil, tls.Dial is used. + // If DialTLSContext and DialTLS is nil, tls.Dial is used. // // If the returned net.Conn has a ConnectionState method like tls.Conn, // it will be used to set http.Response.TLS. + DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) + + // DialTLS specifies an optional dial function for creating + // TLS connections for requests. + // + // If DialTLSContext and DialTLS is nil, tls.Dial is used. + // + // Deprecated: Use DialTLSContext instead, which allows the transport + // to cancel dials as soon as they are no longer needed. + // If both are set, DialTLSContext takes priority. DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error) // TLSClientConfig specifies the TLS configuration to use with @@ -592,7 +602,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b if err != nil { return nil, err } - tconn, err := t.dialTLS(ctx)("tcp", addr, t.newTLSConfig(host)) + tconn, err := t.dialTLS(ctx, "tcp", addr, t.newTLSConfig(host)) if err != nil { return nil, err } @@ -613,24 +623,25 @@ func (t *Transport) newTLSConfig(host string) *tls.Config { return cfg } -func (t *Transport) dialTLS(ctx context.Context) func(string, string, *tls.Config) (net.Conn, error) { - if t.DialTLS != nil { - return t.DialTLS +func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) { + if t.DialTLSContext != nil { + return t.DialTLSContext(ctx, network, addr, tlsCfg) + } else if t.DialTLS != nil { + return t.DialTLS(network, addr, tlsCfg) } - return func(network, addr string, cfg *tls.Config) (net.Conn, error) { - tlsCn, err := t.dialTLSWithContext(ctx, network, addr, cfg) - if err != nil { - return nil, err - } - state := tlsCn.ConnectionState() - if p := state.NegotiatedProtocol; p != NextProtoTLS { - return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS) - } - if !state.NegotiatedProtocolIsMutual { - return nil, errors.New("http2: could not negotiate protocol mutually") - } - return tlsCn, nil + + tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg) + if err != nil { + return nil, err } + state := tlsCn.ConnectionState() + if p := state.NegotiatedProtocol; p != NextProtoTLS { + return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS) + } + if !state.NegotiatedProtocolIsMutual { + return nil, errors.New("http2: could not negotiate protocol mutually") + } + return tlsCn, nil } // disableKeepAlives reports whether connections should be closed as diff --git a/vendor/golang.org/x/net/icmp/dstunreach.go b/vendor/golang.org/x/net/icmp/dstunreach.go new file mode 100644 index 00000000..8615cf54 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/dstunreach.go @@ -0,0 +1,59 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// A DstUnreach represents an ICMP destination unreachable message +// body. +type DstUnreach struct { + Data []byte // data, known as original datagram field + Extensions []Extension // extensions +} + +// Len implements the Len method of MessageBody interface. +func (p *DstUnreach) Len(proto int) int { + if p == nil { + return 0 + } + l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions) + return l +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *DstUnreach) Marshal(proto int) ([]byte, error) { + var typ Type + switch proto { + case iana.ProtocolICMP: + typ = ipv4.ICMPTypeDestinationUnreachable + case iana.ProtocolIPv6ICMP: + typ = ipv6.ICMPTypeDestinationUnreachable + default: + return nil, errInvalidProtocol + } + if !validExtensions(typ, p.Extensions) { + return nil, errInvalidExtension + } + return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions) +} + +// parseDstUnreach parses b as an ICMP destination unreachable message +// body. +func parseDstUnreach(proto int, typ Type, b []byte) (MessageBody, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + p := &DstUnreach{} + var err error + p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b) + if err != nil { + return nil, err + } + return p, nil +} diff --git a/vendor/golang.org/x/net/icmp/echo.go b/vendor/golang.org/x/net/icmp/echo.go new file mode 100644 index 00000000..b5918642 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/echo.go @@ -0,0 +1,173 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "encoding/binary" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// An Echo represents an ICMP echo request or reply message body. +type Echo struct { + ID int // identifier + Seq int // sequence number + Data []byte // data +} + +// Len implements the Len method of MessageBody interface. +func (p *Echo) Len(proto int) int { + if p == nil { + return 0 + } + return 4 + len(p.Data) +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *Echo) Marshal(proto int) ([]byte, error) { + b := make([]byte, 4+len(p.Data)) + binary.BigEndian.PutUint16(b[:2], uint16(p.ID)) + binary.BigEndian.PutUint16(b[2:4], uint16(p.Seq)) + copy(b[4:], p.Data) + return b, nil +} + +// parseEcho parses b as an ICMP echo request or reply message body. +func parseEcho(proto int, _ Type, b []byte) (MessageBody, error) { + bodyLen := len(b) + if bodyLen < 4 { + return nil, errMessageTooShort + } + p := &Echo{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(binary.BigEndian.Uint16(b[2:4]))} + if bodyLen > 4 { + p.Data = make([]byte, bodyLen-4) + copy(p.Data, b[4:]) + } + return p, nil +} + +// An ExtendedEchoRequest represents an ICMP extended echo request +// message body. +type ExtendedEchoRequest struct { + ID int // identifier + Seq int // sequence number + Local bool // must be true when identifying by name or index + Extensions []Extension // extensions +} + +// Len implements the Len method of MessageBody interface. +func (p *ExtendedEchoRequest) Len(proto int) int { + if p == nil { + return 0 + } + l, _ := multipartMessageBodyDataLen(proto, false, nil, p.Extensions) + return l +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *ExtendedEchoRequest) Marshal(proto int) ([]byte, error) { + var typ Type + switch proto { + case iana.ProtocolICMP: + typ = ipv4.ICMPTypeExtendedEchoRequest + case iana.ProtocolIPv6ICMP: + typ = ipv6.ICMPTypeExtendedEchoRequest + default: + return nil, errInvalidProtocol + } + if !validExtensions(typ, p.Extensions) { + return nil, errInvalidExtension + } + b, err := marshalMultipartMessageBody(proto, false, nil, p.Extensions) + if err != nil { + return nil, err + } + binary.BigEndian.PutUint16(b[:2], uint16(p.ID)) + b[2] = byte(p.Seq) + if p.Local { + b[3] |= 0x01 + } + return b, nil +} + +// parseExtendedEchoRequest parses b as an ICMP extended echo request +// message body. +func parseExtendedEchoRequest(proto int, typ Type, b []byte) (MessageBody, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + p := &ExtendedEchoRequest{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(b[2])} + if b[3]&0x01 != 0 { + p.Local = true + } + var err error + _, p.Extensions, err = parseMultipartMessageBody(proto, typ, b) + if err != nil { + return nil, err + } + return p, nil +} + +// An ExtendedEchoReply represents an ICMP extended echo reply message +// body. +type ExtendedEchoReply struct { + ID int // identifier + Seq int // sequence number + State int // 3-bit state working together with Message.Code + Active bool // probed interface is active + IPv4 bool // probed interface runs IPv4 + IPv6 bool // probed interface runs IPv6 +} + +// Len implements the Len method of MessageBody interface. +func (p *ExtendedEchoReply) Len(proto int) int { + if p == nil { + return 0 + } + return 4 +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *ExtendedEchoReply) Marshal(proto int) ([]byte, error) { + b := make([]byte, 4) + binary.BigEndian.PutUint16(b[:2], uint16(p.ID)) + b[2] = byte(p.Seq) + b[3] = byte(p.State<<5) & 0xe0 + if p.Active { + b[3] |= 0x04 + } + if p.IPv4 { + b[3] |= 0x02 + } + if p.IPv6 { + b[3] |= 0x01 + } + return b, nil +} + +// parseExtendedEchoReply parses b as an ICMP extended echo reply +// message body. +func parseExtendedEchoReply(proto int, _ Type, b []byte) (MessageBody, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + p := &ExtendedEchoReply{ + ID: int(binary.BigEndian.Uint16(b[:2])), + Seq: int(b[2]), + State: int(b[3]) >> 5, + } + if b[3]&0x04 != 0 { + p.Active = true + } + if b[3]&0x02 != 0 { + p.IPv4 = true + } + if b[3]&0x01 != 0 { + p.IPv6 = true + } + return p, nil +} diff --git a/vendor/golang.org/x/net/icmp/endpoint.go b/vendor/golang.org/x/net/icmp/endpoint.go new file mode 100644 index 00000000..47f5b698 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/endpoint.go @@ -0,0 +1,113 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "net" + "runtime" + "time" + + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +var _ net.PacketConn = &PacketConn{} + +// A PacketConn represents a packet network endpoint that uses either +// ICMPv4 or ICMPv6. +type PacketConn struct { + c net.PacketConn + p4 *ipv4.PacketConn + p6 *ipv6.PacketConn +} + +func (c *PacketConn) ok() bool { return c != nil && c.c != nil } + +// IPv4PacketConn returns the ipv4.PacketConn of c. +// It returns nil when c is not created as the endpoint for ICMPv4. +func (c *PacketConn) IPv4PacketConn() *ipv4.PacketConn { + if !c.ok() { + return nil + } + return c.p4 +} + +// IPv6PacketConn returns the ipv6.PacketConn of c. +// It returns nil when c is not created as the endpoint for ICMPv6. +func (c *PacketConn) IPv6PacketConn() *ipv6.PacketConn { + if !c.ok() { + return nil + } + return c.p6 +} + +// ReadFrom reads an ICMP message from the connection. +func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + if !c.ok() { + return 0, nil, errInvalidConn + } + // Please be informed that ipv4.NewPacketConn enables + // IP_STRIPHDR option by default on Darwin. + // See golang.org/issue/9395 for further information. + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && c.p4 != nil { + n, _, peer, err := c.p4.ReadFrom(b) + return n, peer, err + } + return c.c.ReadFrom(b) +} + +// WriteTo writes the ICMP message b to dst. +// The provided dst must be net.UDPAddr when c is a non-privileged +// datagram-oriented ICMP endpoint. +// Otherwise it must be net.IPAddr. +func (c *PacketConn) WriteTo(b []byte, dst net.Addr) (int, error) { + if !c.ok() { + return 0, errInvalidConn + } + return c.c.WriteTo(b, dst) +} + +// Close closes the endpoint. +func (c *PacketConn) Close() error { + if !c.ok() { + return errInvalidConn + } + return c.c.Close() +} + +// LocalAddr returns the local network address. +func (c *PacketConn) LocalAddr() net.Addr { + if !c.ok() { + return nil + } + return c.c.LocalAddr() +} + +// SetDeadline sets the read and write deadlines associated with the +// endpoint. +func (c *PacketConn) SetDeadline(t time.Time) error { + if !c.ok() { + return errInvalidConn + } + return c.c.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline associated with the +// endpoint. +func (c *PacketConn) SetReadDeadline(t time.Time) error { + if !c.ok() { + return errInvalidConn + } + return c.c.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline associated with the +// endpoint. +func (c *PacketConn) SetWriteDeadline(t time.Time) error { + if !c.ok() { + return errInvalidConn + } + return c.c.SetWriteDeadline(t) +} diff --git a/vendor/golang.org/x/net/icmp/extension.go b/vendor/golang.org/x/net/icmp/extension.go new file mode 100644 index 00000000..eeb85c3f --- /dev/null +++ b/vendor/golang.org/x/net/icmp/extension.go @@ -0,0 +1,170 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "encoding/binary" + + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// An Extension represents an ICMP extension. +type Extension interface { + // Len returns the length of ICMP extension. + // The provided proto must be either the ICMPv4 or ICMPv6 + // protocol number. + Len(proto int) int + + // Marshal returns the binary encoding of ICMP extension. + // The provided proto must be either the ICMPv4 or ICMPv6 + // protocol number. + Marshal(proto int) ([]byte, error) +} + +const extensionVersion = 2 + +func validExtensionHeader(b []byte) bool { + v := int(b[0]&0xf0) >> 4 + s := binary.BigEndian.Uint16(b[2:4]) + if s != 0 { + s = checksum(b) + } + if v != extensionVersion || s != 0 { + return false + } + return true +} + +// parseExtensions parses b as a list of ICMP extensions. +// The length attribute l must be the length attribute field in +// received icmp messages. +// +// It will return a list of ICMP extensions and an adjusted length +// attribute that represents the length of the padded original +// datagram field. Otherwise, it returns an error. +func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) { + // Still a lot of non-RFC 4884 compliant implementations are + // out there. Set the length attribute l to 128 when it looks + // inappropriate for backwards compatibility. + // + // A minimal extension at least requires 8 octets; 4 octets + // for an extension header, and 4 octets for a single object + // header. + // + // See RFC 4884 for further information. + switch typ { + case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: + if len(b) < 8 || !validExtensionHeader(b) { + return nil, -1, errNoExtension + } + l = 0 + default: + if 128 > l || l+8 > len(b) { + l = 128 + } + if l+8 > len(b) { + return nil, -1, errNoExtension + } + if !validExtensionHeader(b[l:]) { + if l == 128 { + return nil, -1, errNoExtension + } + l = 128 + if !validExtensionHeader(b[l:]) { + return nil, -1, errNoExtension + } + } + } + var exts []Extension + for b = b[l+4:]; len(b) >= 4; { + ol := int(binary.BigEndian.Uint16(b[:2])) + if 4 > ol || ol > len(b) { + break + } + switch b[2] { + case classMPLSLabelStack: + ext, err := parseMPLSLabelStack(b[:ol]) + if err != nil { + return nil, -1, err + } + exts = append(exts, ext) + case classInterfaceInfo: + ext, err := parseInterfaceInfo(b[:ol]) + if err != nil { + return nil, -1, err + } + exts = append(exts, ext) + case classInterfaceIdent: + ext, err := parseInterfaceIdent(b[:ol]) + if err != nil { + return nil, -1, err + } + exts = append(exts, ext) + default: + ext := &RawExtension{Data: make([]byte, ol)} + copy(ext.Data, b[:ol]) + exts = append(exts, ext) + } + b = b[ol:] + } + return exts, l, nil +} + +func validExtensions(typ Type, exts []Extension) bool { + switch typ { + case ipv4.ICMPTypeDestinationUnreachable, ipv4.ICMPTypeTimeExceeded, ipv4.ICMPTypeParameterProblem, + ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeTimeExceeded: + for i := range exts { + switch exts[i].(type) { + case *MPLSLabelStack, *InterfaceInfo, *RawExtension: + default: + return false + } + } + return true + case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: + var n int + for i := range exts { + switch exts[i].(type) { + case *InterfaceIdent: + n++ + case *RawExtension: + default: + return false + } + } + // Not a single InterfaceIdent object or a combo of + // RawExtension and InterfaceIdent objects is not + // allowed. + if n == 1 && len(exts) > 1 { + return false + } + return true + default: + return false + } +} + +// A RawExtension represents a raw extension. +// +// A raw extension is excluded from message processing and can be used +// to construct applications such as protocol conformance testing. +type RawExtension struct { + Data []byte // data +} + +// Len implements the Len method of Extension interface. +func (p *RawExtension) Len(proto int) int { + if p == nil { + return 0 + } + return len(p.Data) +} + +// Marshal implements the Marshal method of Extension interface. +func (p *RawExtension) Marshal(proto int) ([]byte, error) { + return p.Data, nil +} diff --git a/vendor/golang.org/x/net/icmp/helper_posix.go b/vendor/golang.org/x/net/icmp/helper_posix.go new file mode 100644 index 00000000..6c3ebfae --- /dev/null +++ b/vendor/golang.org/x/net/icmp/helper_posix.go @@ -0,0 +1,76 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package icmp + +import ( + "net" + "strconv" + "syscall" +) + +func sockaddr(family int, address string) (syscall.Sockaddr, error) { + switch family { + case syscall.AF_INET: + a, err := net.ResolveIPAddr("ip4", address) + if err != nil { + return nil, err + } + if len(a.IP) == 0 { + a.IP = net.IPv4zero + } + if a.IP = a.IP.To4(); a.IP == nil { + return nil, net.InvalidAddrError("non-ipv4 address") + } + sa := &syscall.SockaddrInet4{} + copy(sa.Addr[:], a.IP) + return sa, nil + case syscall.AF_INET6: + a, err := net.ResolveIPAddr("ip6", address) + if err != nil { + return nil, err + } + if len(a.IP) == 0 { + a.IP = net.IPv6unspecified + } + if a.IP.Equal(net.IPv4zero) { + a.IP = net.IPv6unspecified + } + if a.IP = a.IP.To16(); a.IP == nil || a.IP.To4() != nil { + return nil, net.InvalidAddrError("non-ipv6 address") + } + sa := &syscall.SockaddrInet6{ZoneId: zoneToUint32(a.Zone)} + copy(sa.Addr[:], a.IP) + return sa, nil + default: + return nil, net.InvalidAddrError("unexpected family") + } +} + +func zoneToUint32(zone string) uint32 { + if zone == "" { + return 0 + } + if ifi, err := net.InterfaceByName(zone); err == nil { + return uint32(ifi.Index) + } + n, err := strconv.Atoi(zone) + if err != nil { + return 0 + } + return uint32(n) +} + +func last(s string, b byte) int { + i := len(s) + for i--; i >= 0; i-- { + if s[i] == b { + break + } + } + return i +} diff --git a/vendor/golang.org/x/net/icmp/interface.go b/vendor/golang.org/x/net/icmp/interface.go new file mode 100644 index 00000000..b3dd72fb --- /dev/null +++ b/vendor/golang.org/x/net/icmp/interface.go @@ -0,0 +1,322 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "encoding/binary" + "net" + "strings" + + "golang.org/x/net/internal/iana" +) + +const ( + classInterfaceInfo = 2 +) + +const ( + attrMTU = 1 << iota + attrName + attrIPAddr + attrIfIndex +) + +// An InterfaceInfo represents interface and next-hop identification. +type InterfaceInfo struct { + Class int // extension object class number + Type int // extension object sub-type + Interface *net.Interface + Addr *net.IPAddr +} + +func (ifi *InterfaceInfo) nameLen() int { + if len(ifi.Interface.Name) > 63 { + return 64 + } + l := 1 + len(ifi.Interface.Name) + return (l + 3) &^ 3 +} + +func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) { + l = 4 + if ifi.Interface != nil && ifi.Interface.Index > 0 { + attrs |= attrIfIndex + l += 4 + if len(ifi.Interface.Name) > 0 { + attrs |= attrName + l += ifi.nameLen() + } + if ifi.Interface.MTU > 0 { + attrs |= attrMTU + l += 4 + } + } + if ifi.Addr != nil { + switch proto { + case iana.ProtocolICMP: + if ifi.Addr.IP.To4() != nil { + attrs |= attrIPAddr + l += 4 + net.IPv4len + } + case iana.ProtocolIPv6ICMP: + if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil { + attrs |= attrIPAddr + l += 4 + net.IPv6len + } + } + } + return +} + +// Len implements the Len method of Extension interface. +func (ifi *InterfaceInfo) Len(proto int) int { + _, l := ifi.attrsAndLen(proto) + return l +} + +// Marshal implements the Marshal method of Extension interface. +func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) { + attrs, l := ifi.attrsAndLen(proto) + b := make([]byte, l) + if err := ifi.marshal(proto, b, attrs, l); err != nil { + return nil, err + } + return b, nil +} + +func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error { + binary.BigEndian.PutUint16(b[:2], uint16(l)) + b[2], b[3] = classInterfaceInfo, byte(ifi.Type) + for b = b[4:]; len(b) > 0 && attrs != 0; { + switch { + case attrs&attrIfIndex != 0: + b = ifi.marshalIfIndex(proto, b) + attrs &^= attrIfIndex + case attrs&attrIPAddr != 0: + b = ifi.marshalIPAddr(proto, b) + attrs &^= attrIPAddr + case attrs&attrName != 0: + b = ifi.marshalName(proto, b) + attrs &^= attrName + case attrs&attrMTU != 0: + b = ifi.marshalMTU(proto, b) + attrs &^= attrMTU + } + } + return nil +} + +func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte { + binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.Index)) + return b[4:] +} + +func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + ifi.Interface.Index = int(binary.BigEndian.Uint32(b[:4])) + return b[4:], nil +} + +func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte { + switch proto { + case iana.ProtocolICMP: + binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4)) + copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4()) + b = b[4+net.IPv4len:] + case iana.ProtocolIPv6ICMP: + binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6)) + copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16()) + b = b[4+net.IPv6len:] + } + return b +} + +func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + afi := int(binary.BigEndian.Uint16(b[:2])) + b = b[4:] + switch afi { + case iana.AddrFamilyIPv4: + if len(b) < net.IPv4len { + return nil, errMessageTooShort + } + ifi.Addr.IP = make(net.IP, net.IPv4len) + copy(ifi.Addr.IP, b[:net.IPv4len]) + b = b[net.IPv4len:] + case iana.AddrFamilyIPv6: + if len(b) < net.IPv6len { + return nil, errMessageTooShort + } + ifi.Addr.IP = make(net.IP, net.IPv6len) + copy(ifi.Addr.IP, b[:net.IPv6len]) + b = b[net.IPv6len:] + } + return b, nil +} + +func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte { + l := byte(ifi.nameLen()) + b[0] = l + copy(b[1:], []byte(ifi.Interface.Name)) + return b[l:] +} + +func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) { + if 4 > len(b) || len(b) < int(b[0]) { + return nil, errMessageTooShort + } + l := int(b[0]) + if l%4 != 0 || 4 > l || l > 64 { + return nil, errInvalidExtension + } + var name [63]byte + copy(name[:], b[1:l]) + ifi.Interface.Name = strings.Trim(string(name[:]), "\000") + return b[l:], nil +} + +func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte { + binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.MTU)) + return b[4:] +} + +func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + ifi.Interface.MTU = int(binary.BigEndian.Uint32(b[:4])) + return b[4:], nil +} + +func parseInterfaceInfo(b []byte) (Extension, error) { + ifi := &InterfaceInfo{ + Class: int(b[2]), + Type: int(b[3]), + } + if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 { + ifi.Interface = &net.Interface{} + } + if ifi.Type&attrIPAddr != 0 { + ifi.Addr = &net.IPAddr{} + } + attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU) + for b = b[4:]; len(b) > 0 && attrs != 0; { + var err error + switch { + case attrs&attrIfIndex != 0: + b, err = ifi.parseIfIndex(b) + attrs &^= attrIfIndex + case attrs&attrIPAddr != 0: + b, err = ifi.parseIPAddr(b) + attrs &^= attrIPAddr + case attrs&attrName != 0: + b, err = ifi.parseName(b) + attrs &^= attrName + case attrs&attrMTU != 0: + b, err = ifi.parseMTU(b) + attrs &^= attrMTU + } + if err != nil { + return nil, err + } + } + if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil { + ifi.Addr.Zone = ifi.Interface.Name + } + return ifi, nil +} + +const ( + classInterfaceIdent = 3 + typeInterfaceByName = 1 + typeInterfaceByIndex = 2 + typeInterfaceByAddress = 3 +) + +// An InterfaceIdent represents interface identification. +type InterfaceIdent struct { + Class int // extension object class number + Type int // extension object sub-type + Name string // interface name + Index int // interface index + AFI int // address family identifier; see address family numbers in IANA registry + Addr []byte // address +} + +// Len implements the Len method of Extension interface. +func (ifi *InterfaceIdent) Len(_ int) int { + switch ifi.Type { + case typeInterfaceByName: + l := len(ifi.Name) + if l > 255 { + l = 255 + } + return 4 + (l+3)&^3 + case typeInterfaceByIndex: + return 4 + 4 + case typeInterfaceByAddress: + return 4 + 4 + (len(ifi.Addr)+3)&^3 + default: + return 4 + } +} + +// Marshal implements the Marshal method of Extension interface. +func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) { + b := make([]byte, ifi.Len(proto)) + if err := ifi.marshal(proto, b); err != nil { + return nil, err + } + return b, nil +} + +func (ifi *InterfaceIdent) marshal(proto int, b []byte) error { + l := ifi.Len(proto) + binary.BigEndian.PutUint16(b[:2], uint16(l)) + b[2], b[3] = classInterfaceIdent, byte(ifi.Type) + switch ifi.Type { + case typeInterfaceByName: + copy(b[4:], ifi.Name) + case typeInterfaceByIndex: + binary.BigEndian.PutUint32(b[4:4+4], uint32(ifi.Index)) + case typeInterfaceByAddress: + binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI)) + b[4+2] = byte(len(ifi.Addr)) + copy(b[4+4:], ifi.Addr) + } + return nil +} + +func parseInterfaceIdent(b []byte) (Extension, error) { + ifi := &InterfaceIdent{ + Class: int(b[2]), + Type: int(b[3]), + } + switch ifi.Type { + case typeInterfaceByName: + ifi.Name = strings.Trim(string(b[4:]), "\x00") + case typeInterfaceByIndex: + if len(b[4:]) < 4 { + return nil, errInvalidExtension + } + ifi.Index = int(binary.BigEndian.Uint32(b[4 : 4+4])) + case typeInterfaceByAddress: + if len(b[4:]) < 4 { + return nil, errInvalidExtension + } + ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2])) + l := int(b[4+2]) + if len(b[4+4:]) < l { + return nil, errInvalidExtension + } + ifi.Addr = make([]byte, l) + copy(ifi.Addr, b[4+4:]) + } + return ifi, nil +} diff --git a/vendor/golang.org/x/net/icmp/ipv4.go b/vendor/golang.org/x/net/icmp/ipv4.go new file mode 100644 index 00000000..0ad40fef --- /dev/null +++ b/vendor/golang.org/x/net/icmp/ipv4.go @@ -0,0 +1,69 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "encoding/binary" + "net" + "runtime" + + "golang.org/x/net/internal/socket" + "golang.org/x/net/ipv4" +) + +// freebsdVersion is set in sys_freebsd.go. +// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html. +var freebsdVersion uint32 + +// ParseIPv4Header returns the IPv4 header of the IPv4 packet that +// triggered an ICMP error message. +// This is found in the Data field of the ICMP error message body. +// +// The provided b must be in the format used by a raw ICMP socket on +// the local system. +// This may differ from the wire format, and the format used by a raw +// IP socket, depending on the system. +// +// To parse an IPv6 header, use ipv6.ParseHeader. +func ParseIPv4Header(b []byte) (*ipv4.Header, error) { + if len(b) < ipv4.HeaderLen { + return nil, errHeaderTooShort + } + hdrlen := int(b[0]&0x0f) << 2 + if hdrlen > len(b) { + return nil, errBufferTooShort + } + h := &ipv4.Header{ + Version: int(b[0] >> 4), + Len: hdrlen, + TOS: int(b[1]), + ID: int(binary.BigEndian.Uint16(b[4:6])), + FragOff: int(binary.BigEndian.Uint16(b[6:8])), + TTL: int(b[8]), + Protocol: int(b[9]), + Checksum: int(binary.BigEndian.Uint16(b[10:12])), + Src: net.IPv4(b[12], b[13], b[14], b[15]), + Dst: net.IPv4(b[16], b[17], b[18], b[19]), + } + switch runtime.GOOS { + case "darwin", "ios": + h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + case "freebsd": + if freebsdVersion >= 1000000 { + h.TotalLen = int(binary.BigEndian.Uint16(b[2:4])) + } else { + h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + } + default: + h.TotalLen = int(binary.BigEndian.Uint16(b[2:4])) + } + h.Flags = ipv4.HeaderFlags(h.FragOff&0xe000) >> 13 + h.FragOff = h.FragOff & 0x1fff + if hdrlen-ipv4.HeaderLen > 0 { + h.Options = make([]byte, hdrlen-ipv4.HeaderLen) + copy(h.Options, b[ipv4.HeaderLen:]) + } + return h, nil +} diff --git a/vendor/golang.org/x/net/icmp/ipv6.go b/vendor/golang.org/x/net/icmp/ipv6.go new file mode 100644 index 00000000..2e8cfeb1 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/ipv6.go @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "net" + + "golang.org/x/net/internal/iana" +) + +const ipv6PseudoHeaderLen = 2*net.IPv6len + 8 + +// IPv6PseudoHeader returns an IPv6 pseudo header for checksum +// calculation. +func IPv6PseudoHeader(src, dst net.IP) []byte { + b := make([]byte, ipv6PseudoHeaderLen) + copy(b, src.To16()) + copy(b[net.IPv6len:], dst.To16()) + b[len(b)-1] = byte(iana.ProtocolIPv6ICMP) + return b +} diff --git a/vendor/golang.org/x/net/icmp/listen_posix.go b/vendor/golang.org/x/net/icmp/listen_posix.go new file mode 100644 index 00000000..6aea8047 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/listen_posix.go @@ -0,0 +1,106 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package icmp + +import ( + "net" + "os" + "runtime" + "syscall" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option + +// ListenPacket listens for incoming ICMP packets addressed to +// address. See net.Dial for the syntax of address. +// +// For non-privileged datagram-oriented ICMP endpoints, network must +// be "udp4" or "udp6". The endpoint allows to read, write a few +// limited ICMP messages such as echo request and echo reply. +// Currently only Darwin and Linux support this. +// +// Examples: +// +// ListenPacket("udp4", "192.168.0.1") +// ListenPacket("udp4", "0.0.0.0") +// ListenPacket("udp6", "fe80::1%en0") +// ListenPacket("udp6", "::") +// +// For privileged raw ICMP endpoints, network must be "ip4" or "ip6" +// followed by a colon and an ICMP protocol number or name. +// +// Examples: +// +// ListenPacket("ip4:icmp", "192.168.0.1") +// ListenPacket("ip4:1", "0.0.0.0") +// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0") +// ListenPacket("ip6:58", "::") +func ListenPacket(network, address string) (*PacketConn, error) { + var family, proto int + switch network { + case "udp4": + family, proto = syscall.AF_INET, iana.ProtocolICMP + case "udp6": + family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP + default: + i := last(network, ':') + if i < 0 { + i = len(network) + } + switch network[:i] { + case "ip4": + proto = iana.ProtocolICMP + case "ip6": + proto = iana.ProtocolIPv6ICMP + } + } + var cerr error + var c net.PacketConn + switch family { + case syscall.AF_INET, syscall.AF_INET6: + s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto) + if err != nil { + return nil, os.NewSyscallError("socket", err) + } + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && family == syscall.AF_INET { + if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil { + syscall.Close(s) + return nil, os.NewSyscallError("setsockopt", err) + } + } + sa, err := sockaddr(family, address) + if err != nil { + syscall.Close(s) + return nil, err + } + if err := syscall.Bind(s, sa); err != nil { + syscall.Close(s) + return nil, os.NewSyscallError("bind", err) + } + f := os.NewFile(uintptr(s), "datagram-oriented icmp") + c, cerr = net.FilePacketConn(f) + f.Close() + default: + c, cerr = net.ListenPacket(network, address) + } + if cerr != nil { + return nil, cerr + } + switch proto { + case iana.ProtocolICMP: + return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil + case iana.ProtocolIPv6ICMP: + return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil + default: + return &PacketConn{c: c}, nil + } +} diff --git a/vendor/golang.org/x/net/icmp/listen_stub.go b/vendor/golang.org/x/net/icmp/listen_stub.go new file mode 100644 index 00000000..1acfb74b --- /dev/null +++ b/vendor/golang.org/x/net/icmp/listen_stub.go @@ -0,0 +1,36 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package icmp + +// ListenPacket listens for incoming ICMP packets addressed to +// address. See net.Dial for the syntax of address. +// +// For non-privileged datagram-oriented ICMP endpoints, network must +// be "udp4" or "udp6". The endpoint allows to read, write a few +// limited ICMP messages such as echo request and echo reply. +// Currently only Darwin and Linux support this. +// +// Examples: +// +// ListenPacket("udp4", "192.168.0.1") +// ListenPacket("udp4", "0.0.0.0") +// ListenPacket("udp6", "fe80::1%en0") +// ListenPacket("udp6", "::") +// +// For privileged raw ICMP endpoints, network must be "ip4" or "ip6" +// followed by a colon and an ICMP protocol number or name. +// +// Examples: +// +// ListenPacket("ip4:icmp", "192.168.0.1") +// ListenPacket("ip4:1", "0.0.0.0") +// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0") +// ListenPacket("ip6:58", "::") +func ListenPacket(network, address string) (*PacketConn, error) { + return nil, errNotImplemented +} diff --git a/vendor/golang.org/x/net/icmp/message.go b/vendor/golang.org/x/net/icmp/message.go new file mode 100644 index 00000000..40db65d0 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/message.go @@ -0,0 +1,162 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package icmp provides basic functions for the manipulation of +// messages used in the Internet Control Message Protocols, +// ICMPv4 and ICMPv6. +// +// ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443. +// Multi-part message support for ICMP is defined in RFC 4884. +// ICMP extensions for MPLS are defined in RFC 4950. +// ICMP extensions for interface and next-hop identification are +// defined in RFC 5837. +// PROBE: A utility for probing interfaces is defined in RFC 8335. +package icmp // import "golang.org/x/net/icmp" + +import ( + "encoding/binary" + "errors" + "net" + "runtime" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// BUG(mikio): This package is not implemented on JS, NaCl and Plan 9. + +var ( + errInvalidConn = errors.New("invalid connection") + errInvalidProtocol = errors.New("invalid protocol") + errMessageTooShort = errors.New("message too short") + errHeaderTooShort = errors.New("header too short") + errBufferTooShort = errors.New("buffer too short") + errInvalidBody = errors.New("invalid body") + errNoExtension = errors.New("no extension") + errInvalidExtension = errors.New("invalid extension") + errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH) +) + +func checksum(b []byte) uint16 { + csumcv := len(b) - 1 // checksum coverage + s := uint32(0) + for i := 0; i < csumcv; i += 2 { + s += uint32(b[i+1])<<8 | uint32(b[i]) + } + if csumcv&1 == 0 { + s += uint32(b[csumcv]) + } + s = s>>16 + s&0xffff + s = s + s>>16 + return ^uint16(s) +} + +// A Type represents an ICMP message type. +type Type interface { + Protocol() int +} + +// A Message represents an ICMP message. +type Message struct { + Type Type // type, either ipv4.ICMPType or ipv6.ICMPType + Code int // code + Checksum int // checksum + Body MessageBody // body +} + +// Marshal returns the binary encoding of the ICMP message m. +// +// For an ICMPv4 message, the returned message always contains the +// calculated checksum field. +// +// For an ICMPv6 message, the returned message contains the calculated +// checksum field when psh is not nil, otherwise the kernel will +// compute the checksum field during the message transmission. +// When psh is not nil, it must be the pseudo header for IPv6. +func (m *Message) Marshal(psh []byte) ([]byte, error) { + var mtype byte + switch typ := m.Type.(type) { + case ipv4.ICMPType: + mtype = byte(typ) + case ipv6.ICMPType: + mtype = byte(typ) + default: + return nil, errInvalidProtocol + } + b := []byte{mtype, byte(m.Code), 0, 0} + proto := m.Type.Protocol() + if proto == iana.ProtocolIPv6ICMP && psh != nil { + b = append(psh, b...) + } + if m.Body != nil && m.Body.Len(proto) != 0 { + mb, err := m.Body.Marshal(proto) + if err != nil { + return nil, err + } + b = append(b, mb...) + } + if proto == iana.ProtocolIPv6ICMP { + if psh == nil { // cannot calculate checksum here + return b, nil + } + off, l := 2*net.IPv6len, len(b)-len(psh) + binary.BigEndian.PutUint32(b[off:off+4], uint32(l)) + } + s := checksum(b) + // Place checksum back in header; using ^= avoids the + // assumption the checksum bytes are zero. + b[len(psh)+2] ^= byte(s) + b[len(psh)+3] ^= byte(s >> 8) + return b[len(psh):], nil +} + +var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){ + ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach, + ipv4.ICMPTypeTimeExceeded: parseTimeExceeded, + ipv4.ICMPTypeParameterProblem: parseParamProb, + + ipv4.ICMPTypeEcho: parseEcho, + ipv4.ICMPTypeEchoReply: parseEcho, + ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest, + ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply, + + ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach, + ipv6.ICMPTypePacketTooBig: parsePacketTooBig, + ipv6.ICMPTypeTimeExceeded: parseTimeExceeded, + ipv6.ICMPTypeParameterProblem: parseParamProb, + + ipv6.ICMPTypeEchoRequest: parseEcho, + ipv6.ICMPTypeEchoReply: parseEcho, + ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest, + ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply, +} + +// ParseMessage parses b as an ICMP message. +// The provided proto must be either the ICMPv4 or ICMPv6 protocol +// number. +func ParseMessage(proto int, b []byte) (*Message, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + var err error + m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))} + switch proto { + case iana.ProtocolICMP: + m.Type = ipv4.ICMPType(b[0]) + case iana.ProtocolIPv6ICMP: + m.Type = ipv6.ICMPType(b[0]) + default: + return nil, errInvalidProtocol + } + if fn, ok := parseFns[m.Type]; !ok { + m.Body, err = parseRawBody(proto, b[4:]) + } else { + m.Body, err = fn(proto, m.Type, b[4:]) + } + if err != nil { + return nil, err + } + return m, nil +} diff --git a/vendor/golang.org/x/net/icmp/messagebody.go b/vendor/golang.org/x/net/icmp/messagebody.go new file mode 100644 index 00000000..e2d9bfa0 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/messagebody.go @@ -0,0 +1,52 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +// A MessageBody represents an ICMP message body. +type MessageBody interface { + // Len returns the length of ICMP message body. + // The provided proto must be either the ICMPv4 or ICMPv6 + // protocol number. + Len(proto int) int + + // Marshal returns the binary encoding of ICMP message body. + // The provided proto must be either the ICMPv4 or ICMPv6 + // protocol number. + Marshal(proto int) ([]byte, error) +} + +// A RawBody represents a raw message body. +// +// A raw message body is excluded from message processing and can be +// used to construct applications such as protocol conformance +// testing. +type RawBody struct { + Data []byte // data +} + +// Len implements the Len method of MessageBody interface. +func (p *RawBody) Len(proto int) int { + if p == nil { + return 0 + } + return len(p.Data) +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *RawBody) Marshal(proto int) ([]byte, error) { + return p.Data, nil +} + +// parseRawBody parses b as an ICMP message body. +func parseRawBody(proto int, b []byte) (MessageBody, error) { + p := &RawBody{Data: make([]byte, len(b))} + copy(p.Data, b) + return p, nil +} + +// A DefaultMessageBody represents the default message body. +// +// Deprecated: Use RawBody instead. +type DefaultMessageBody = RawBody diff --git a/vendor/golang.org/x/net/icmp/mpls.go b/vendor/golang.org/x/net/icmp/mpls.go new file mode 100644 index 00000000..f9f4841b --- /dev/null +++ b/vendor/golang.org/x/net/icmp/mpls.go @@ -0,0 +1,77 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import "encoding/binary" + +// MPLSLabel represents an MPLS label stack entry. +type MPLSLabel struct { + Label int // label value + TC int // traffic class; formerly experimental use + S bool // bottom of stack + TTL int // time to live +} + +const ( + classMPLSLabelStack = 1 + typeIncomingMPLSLabelStack = 1 +) + +// MPLSLabelStack represents an MPLS label stack. +type MPLSLabelStack struct { + Class int // extension object class number + Type int // extension object sub-type + Labels []MPLSLabel +} + +// Len implements the Len method of Extension interface. +func (ls *MPLSLabelStack) Len(proto int) int { + return 4 + (4 * len(ls.Labels)) +} + +// Marshal implements the Marshal method of Extension interface. +func (ls *MPLSLabelStack) Marshal(proto int) ([]byte, error) { + b := make([]byte, ls.Len(proto)) + if err := ls.marshal(proto, b); err != nil { + return nil, err + } + return b, nil +} + +func (ls *MPLSLabelStack) marshal(proto int, b []byte) error { + l := ls.Len(proto) + binary.BigEndian.PutUint16(b[:2], uint16(l)) + b[2], b[3] = classMPLSLabelStack, typeIncomingMPLSLabelStack + off := 4 + for _, ll := range ls.Labels { + b[off], b[off+1], b[off+2] = byte(ll.Label>>12), byte(ll.Label>>4&0xff), byte(ll.Label<<4&0xf0) + b[off+2] |= byte(ll.TC << 1 & 0x0e) + if ll.S { + b[off+2] |= 0x1 + } + b[off+3] = byte(ll.TTL) + off += 4 + } + return nil +} + +func parseMPLSLabelStack(b []byte) (Extension, error) { + ls := &MPLSLabelStack{ + Class: int(b[2]), + Type: int(b[3]), + } + for b = b[4:]; len(b) >= 4; b = b[4:] { + ll := MPLSLabel{ + Label: int(b[0])<<12 | int(b[1])<<4 | int(b[2])>>4, + TC: int(b[2]&0x0e) >> 1, + TTL: int(b[3]), + } + if b[2]&0x1 != 0 { + ll.S = true + } + ls.Labels = append(ls.Labels, ll) + } + return ls, nil +} diff --git a/vendor/golang.org/x/net/icmp/multipart.go b/vendor/golang.org/x/net/icmp/multipart.go new file mode 100644 index 00000000..5f366755 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/multipart.go @@ -0,0 +1,129 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import "golang.org/x/net/internal/iana" + +// multipartMessageBodyDataLen takes b as an original datagram and +// exts as extensions, and returns a required length for message body +// and a required length for a padded original datagram in wire +// format. +func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) { + bodyLen = 4 // length of leading octets + var extLen int + var rawExt bool // raw extension may contain an empty object + for _, ext := range exts { + extLen += ext.Len(proto) + if _, ok := ext.(*RawExtension); ok { + rawExt = true + } + } + if extLen > 0 && withOrigDgram { + dataLen = multipartMessageOrigDatagramLen(proto, b) + } else { + dataLen = len(b) + } + if extLen > 0 || rawExt { + bodyLen += 4 // length of extension header + } + bodyLen += dataLen + extLen + return bodyLen, dataLen +} + +// multipartMessageOrigDatagramLen takes b as an original datagram, +// and returns a required length for a padded orignal datagram in wire +// format. +func multipartMessageOrigDatagramLen(proto int, b []byte) int { + roundup := func(b []byte, align int) int { + // According to RFC 4884, the padded original datagram + // field must contain at least 128 octets. + if len(b) < 128 { + return 128 + } + r := len(b) + return (r + align - 1) &^ (align - 1) + } + switch proto { + case iana.ProtocolICMP: + return roundup(b, 4) + case iana.ProtocolIPv6ICMP: + return roundup(b, 8) + default: + return len(b) + } +} + +// marshalMultipartMessageBody takes data as an original datagram and +// exts as extesnsions, and returns a binary encoding of message body. +// It can be used for non-multipart message bodies when exts is nil. +func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) { + bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts) + b := make([]byte, bodyLen) + copy(b[4:], data) + if len(exts) > 0 { + b[4+dataLen] = byte(extensionVersion << 4) + off := 4 + dataLen + 4 // leading octets, data, extension header + for _, ext := range exts { + switch ext := ext.(type) { + case *MPLSLabelStack: + if err := ext.marshal(proto, b[off:]); err != nil { + return nil, err + } + off += ext.Len(proto) + case *InterfaceInfo: + attrs, l := ext.attrsAndLen(proto) + if err := ext.marshal(proto, b[off:], attrs, l); err != nil { + return nil, err + } + off += ext.Len(proto) + case *InterfaceIdent: + if err := ext.marshal(proto, b[off:]); err != nil { + return nil, err + } + off += ext.Len(proto) + case *RawExtension: + copy(b[off:], ext.Data) + off += ext.Len(proto) + } + } + s := checksum(b[4+dataLen:]) + b[4+dataLen+2] ^= byte(s) + b[4+dataLen+3] ^= byte(s >> 8) + if withOrigDgram { + switch proto { + case iana.ProtocolICMP: + b[1] = byte(dataLen / 4) + case iana.ProtocolIPv6ICMP: + b[0] = byte(dataLen / 8) + } + } + } + return b, nil +} + +// parseMultipartMessageBody parses b as either a non-multipart +// message body or a multipart message body. +func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) { + var l int + switch proto { + case iana.ProtocolICMP: + l = 4 * int(b[1]) + case iana.ProtocolIPv6ICMP: + l = 8 * int(b[0]) + } + if len(b) == 4 { + return nil, nil, nil + } + exts, l, err := parseExtensions(typ, b[4:], l) + if err != nil { + l = len(b) - 4 + } + var data []byte + if l > 0 { + data = make([]byte, l) + copy(data, b[4:]) + } + return data, exts, nil +} diff --git a/vendor/golang.org/x/net/icmp/packettoobig.go b/vendor/golang.org/x/net/icmp/packettoobig.go new file mode 100644 index 00000000..afbf24f1 --- /dev/null +++ b/vendor/golang.org/x/net/icmp/packettoobig.go @@ -0,0 +1,43 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import "encoding/binary" + +// A PacketTooBig represents an ICMP packet too big message body. +type PacketTooBig struct { + MTU int // maximum transmission unit of the nexthop link + Data []byte // data, known as original datagram field +} + +// Len implements the Len method of MessageBody interface. +func (p *PacketTooBig) Len(proto int) int { + if p == nil { + return 0 + } + return 4 + len(p.Data) +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *PacketTooBig) Marshal(proto int) ([]byte, error) { + b := make([]byte, 4+len(p.Data)) + binary.BigEndian.PutUint32(b[:4], uint32(p.MTU)) + copy(b[4:], p.Data) + return b, nil +} + +// parsePacketTooBig parses b as an ICMP packet too big message body. +func parsePacketTooBig(proto int, _ Type, b []byte) (MessageBody, error) { + bodyLen := len(b) + if bodyLen < 4 { + return nil, errMessageTooShort + } + p := &PacketTooBig{MTU: int(binary.BigEndian.Uint32(b[:4]))} + if bodyLen > 4 { + p.Data = make([]byte, bodyLen-4) + copy(p.Data, b[4:]) + } + return p, nil +} diff --git a/vendor/golang.org/x/net/icmp/paramprob.go b/vendor/golang.org/x/net/icmp/paramprob.go new file mode 100644 index 00000000..f16fd33e --- /dev/null +++ b/vendor/golang.org/x/net/icmp/paramprob.go @@ -0,0 +1,72 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "encoding/binary" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" +) + +// A ParamProb represents an ICMP parameter problem message body. +type ParamProb struct { + Pointer uintptr // offset within the data where the error was detected + Data []byte // data, known as original datagram field + Extensions []Extension // extensions +} + +// Len implements the Len method of MessageBody interface. +func (p *ParamProb) Len(proto int) int { + if p == nil { + return 0 + } + l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions) + return l +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *ParamProb) Marshal(proto int) ([]byte, error) { + switch proto { + case iana.ProtocolICMP: + if !validExtensions(ipv4.ICMPTypeParameterProblem, p.Extensions) { + return nil, errInvalidExtension + } + b, err := marshalMultipartMessageBody(proto, true, p.Data, p.Extensions) + if err != nil { + return nil, err + } + b[0] = byte(p.Pointer) + return b, nil + case iana.ProtocolIPv6ICMP: + b := make([]byte, p.Len(proto)) + binary.BigEndian.PutUint32(b[:4], uint32(p.Pointer)) + copy(b[4:], p.Data) + return b, nil + default: + return nil, errInvalidProtocol + } +} + +// parseParamProb parses b as an ICMP parameter problem message body. +func parseParamProb(proto int, typ Type, b []byte) (MessageBody, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + p := &ParamProb{} + if proto == iana.ProtocolIPv6ICMP { + p.Pointer = uintptr(binary.BigEndian.Uint32(b[:4])) + p.Data = make([]byte, len(b)-4) + copy(p.Data, b[4:]) + return p, nil + } + p.Pointer = uintptr(b[0]) + var err error + p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b) + if err != nil { + return nil, err + } + return p, nil +} diff --git a/vendor/golang.org/x/net/icmp/sys_freebsd.go b/vendor/golang.org/x/net/icmp/sys_freebsd.go new file mode 100644 index 00000000..c75f3dda --- /dev/null +++ b/vendor/golang.org/x/net/icmp/sys_freebsd.go @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import "syscall" + +func init() { + freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate") +} diff --git a/vendor/golang.org/x/net/icmp/timeexceeded.go b/vendor/golang.org/x/net/icmp/timeexceeded.go new file mode 100644 index 00000000..ffa986fd --- /dev/null +++ b/vendor/golang.org/x/net/icmp/timeexceeded.go @@ -0,0 +1,57 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package icmp + +import ( + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// A TimeExceeded represents an ICMP time exceeded message body. +type TimeExceeded struct { + Data []byte // data, known as original datagram field + Extensions []Extension // extensions +} + +// Len implements the Len method of MessageBody interface. +func (p *TimeExceeded) Len(proto int) int { + if p == nil { + return 0 + } + l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions) + return l +} + +// Marshal implements the Marshal method of MessageBody interface. +func (p *TimeExceeded) Marshal(proto int) ([]byte, error) { + var typ Type + switch proto { + case iana.ProtocolICMP: + typ = ipv4.ICMPTypeTimeExceeded + case iana.ProtocolIPv6ICMP: + typ = ipv6.ICMPTypeTimeExceeded + default: + return nil, errInvalidProtocol + } + if !validExtensions(typ, p.Extensions) { + return nil, errInvalidExtension + } + return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions) +} + +// parseTimeExceeded parses b as an ICMP time exceeded message body. +func parseTimeExceeded(proto int, typ Type, b []byte) (MessageBody, error) { + if len(b) < 4 { + return nil, errMessageTooShort + } + p := &TimeExceeded{} + var err error + p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b) + if err != nil { + return nil, err + } + return p, nil +} diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go index ba53f564..f7d0b0d2 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go @@ -8,22 +8,21 @@ package socket import ( + "net" "os" ) func (c *Conn) recvMsg(m *Message, flags int) error { m.raceWrite() - var h msghdr - vs := make([]iovec, len(m.Buffers)) - var sa []byte - if c.network != "tcp" { - sa = make([]byte, sizeofSockaddrInet6) - } - h.pack(vs, m.Buffers, m.OOB, sa) - var operr error - var n int + var ( + operr error + n int + oobn int + recvflags int + from net.Addr + ) fn := func(s uintptr) bool { - n, operr = recvmsg(s, &h, flags) + n, oobn, recvflags, from, operr = recvmsg(s, m.Buffers, m.OOB, flags, c.network) return ioComplete(flags, operr) } if err := c.c.Read(fn); err != nil { @@ -32,34 +31,21 @@ func (c *Conn) recvMsg(m *Message, flags int) error { if operr != nil { return os.NewSyscallError("recvmsg", operr) } - if c.network != "tcp" { - var err error - m.Addr, err = parseInetAddr(sa[:], c.network) - if err != nil { - return err - } - } + m.Addr = from m.N = n - m.NN = h.controllen() - m.Flags = h.flags() + m.NN = oobn + m.Flags = recvflags return nil } func (c *Conn) sendMsg(m *Message, flags int) error { m.raceRead() - var h msghdr - vs := make([]iovec, len(m.Buffers)) - var sa []byte - if m.Addr != nil { - var a [sizeofSockaddrInet6]byte - n := marshalInetAddr(m.Addr, a[:]) - sa = a[:n] - } - h.pack(vs, m.Buffers, m.OOB, sa) - var operr error - var n int + var ( + operr error + n int + ) fn := func(s uintptr) bool { - n, operr = sendmsg(s, &h, flags) + n, operr = sendmsg(s, m.Buffers, m.OOB, m.Addr, flags) return ioComplete(flags, operr) } if err := c.c.Write(fn); err != nil { diff --git a/vendor/golang.org/x/net/internal/socket/sys_stub.go b/vendor/golang.org/x/net/internal/socket/sys_stub.go index 381e45e1..7cfb349c 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_stub.go +++ b/vendor/golang.org/x/net/internal/socket/sys_stub.go @@ -36,11 +36,11 @@ func setsockopt(s uintptr, level, name int, b []byte) error { return errNotImplemented } -func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { - return 0, errNotImplemented +func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) { + return 0, 0, 0, nil, errNotImplemented } -func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { +func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) { return 0, errNotImplemented } diff --git a/vendor/golang.org/x/net/internal/socket/sys_unix.go b/vendor/golang.org/x/net/internal/socket/sys_unix.go index d203e298..de823932 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_unix.go @@ -8,8 +8,10 @@ package socket import ( - "syscall" + "net" "unsafe" + + "golang.org/x/sys/unix" ) //go:linkname syscall_getsockopt syscall.getsockopt @@ -18,12 +20,6 @@ func syscall_getsockopt(s, level, name int, val unsafe.Pointer, vallen *uint32) //go:linkname syscall_setsockopt syscall.setsockopt func syscall_setsockopt(s, level, name int, val unsafe.Pointer, vallen uintptr) error -//go:linkname syscall_recvmsg syscall.recvmsg -func syscall_recvmsg(s int, msg *syscall.Msghdr, flags int) (int, error) - -//go:linkname syscall_sendmsg syscall.sendmsg -func syscall_sendmsg(s int, msg *syscall.Msghdr, flags int) (int, error) - func getsockopt(s uintptr, level, name int, b []byte) (int, error) { l := uint32(len(b)) err := syscall_getsockopt(int(s), level, name, unsafe.Pointer(&b[0]), &l) @@ -34,10 +30,93 @@ func setsockopt(s uintptr, level, name int, b []byte) error { return syscall_setsockopt(int(s), level, name, unsafe.Pointer(&b[0]), uintptr(len(b))) } -func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { - return syscall_recvmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags) +func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) { + var unixFrom unix.Sockaddr + n, oobn, recvflags, unixFrom, err = unix.RecvmsgBuffers(int(s), buffers, oob, flags) + if unixFrom != nil { + from = sockaddrToAddr(unixFrom, network) + } + return } -func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { - return syscall_sendmsg(int(s), (*syscall.Msghdr)(unsafe.Pointer(h)), flags) +func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) { + var unixTo unix.Sockaddr + if to != nil { + unixTo = addrToSockaddr(to) + } + return unix.SendmsgBuffers(int(s), buffers, oob, unixTo, flags) +} + +// addrToSockaddr converts a net.Addr to a unix.Sockaddr. +func addrToSockaddr(a net.Addr) unix.Sockaddr { + var ( + ip net.IP + port int + zone string + ) + switch a := a.(type) { + case *net.TCPAddr: + ip = a.IP + port = a.Port + zone = a.Zone + case *net.UDPAddr: + ip = a.IP + port = a.Port + zone = a.Zone + case *net.IPAddr: + ip = a.IP + zone = a.Zone + default: + return nil + } + + if ip4 := ip.To4(); ip4 != nil { + sa := unix.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return &sa + } + + if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil { + sa := unix.SockaddrInet6{Port: port} + copy(sa.Addr[:], ip6) + if zone != "" { + sa.ZoneId = uint32(zoneCache.index(zone)) + } + return &sa + } + + return nil +} + +// sockaddrToAddr converts a unix.Sockaddr to a net.Addr. +func sockaddrToAddr(sa unix.Sockaddr, network string) net.Addr { + var ( + ip net.IP + port int + zone string + ) + switch sa := sa.(type) { + case *unix.SockaddrInet4: + ip = make(net.IP, net.IPv4len) + copy(ip, sa.Addr[:]) + port = sa.Port + case *unix.SockaddrInet6: + ip = make(net.IP, net.IPv6len) + copy(ip, sa.Addr[:]) + port = sa.Port + if sa.ZoneId > 0 { + zone = zoneCache.name(int(sa.ZoneId)) + } + default: + return nil + } + + switch network { + case "tcp", "tcp4", "tcp6": + return &net.TCPAddr{IP: ip, Port: port, Zone: zone} + case "udp", "udp4", "udp6": + return &net.UDPAddr{IP: ip, Port: port, Zone: zone} + default: + return &net.IPAddr{IP: ip, Zone: zone} + } } diff --git a/vendor/golang.org/x/net/internal/socket/sys_windows.go b/vendor/golang.org/x/net/internal/socket/sys_windows.go index 2de0d68c..b738b89d 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_windows.go +++ b/vendor/golang.org/x/net/internal/socket/sys_windows.go @@ -5,6 +5,7 @@ package socket import ( + "net" "syscall" "unsafe" @@ -37,11 +38,11 @@ func setsockopt(s uintptr, level, name int, b []byte) error { return syscall.Setsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), int32(len(b))) } -func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { - return 0, errNotImplemented +func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) { + return 0, 0, 0, nil, errNotImplemented } -func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { +func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) { return 0, errNotImplemented } diff --git a/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go index 1e38b922..fc65e62f 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go @@ -27,12 +27,39 @@ func setsockopt(s uintptr, level, name int, b []byte) error { return errnoErr(errno) } -func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { - n, _, errno := syscall_syscall(syscall.SYS___RECVMSG_A, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) - return int(n), errnoErr(errno) +func recvmsg(s uintptr, buffers [][]byte, oob []byte, flags int, network string) (n, oobn int, recvflags int, from net.Addr, err error) { + var h msghdr + vs := make([]iovec, len(buffers)) + var sa []byte + if network != "tcp" { + sa = make([]byte, sizeofSockaddrInet6) + } + h.pack(vs, buffers, oob, sa) + sn, _, errno := syscall_syscall(syscall.SYS___RECVMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags)) + n = int(sn) + oobn = h.controllen() + recvflags = h.flags() + err = errnoErr(errno) + if network != "tcp" { + var err2 error + from, err2 = parseInetAddr(sa, network) + if err2 != nil && err == nil { + err = err2 + } + } + return } -func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { - n, _, errno := syscall_syscall(syscall.SYS___SENDMSG_A, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) +func sendmsg(s uintptr, buffers [][]byte, oob []byte, to net.Addr, flags int) (int, error) { + var h msghdr + vs := make([]iovec, len(buffers)) + var sa []byte + if to != nil { + var a [sizeofSockaddrInet6]byte + n := marshalInetAddr(to, a[:]) + sa = a[:n] + } + h.pack(vs, buffers, oob, sa) + n, _, errno := syscall_syscall(syscall.SYS___SENDMSG_A, s, uintptr(unsafe.Pointer(&h)), uintptr(flags)) return int(n), errnoErr(errno) } diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go b/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go deleted file mode 100644 index 5acf6db6..00000000 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_darwin.go - -package socket - -type iovec struct { - Base *byte - Len uint32 -} - -type msghdr struct { - Name *byte - Namelen uint32 - Iov *iovec - Iovlen int32 - Control *byte - Controllen uint32 - Flags int32 -} - -type cmsghdr struct { - Len uint32 - Level int32 - Type int32 -} - -const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c -) diff --git a/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_riscv64.go similarity index 79% rename from vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go rename to vendor/golang.org/x/net/internal/socket/zsys_freebsd_riscv64.go index 5acf6db6..965c0b28 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_darwin_386.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_freebsd_riscv64.go @@ -1,11 +1,11 @@ // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_darwin.go +// cgo -godefs defs_freebsd.go package socket type iovec struct { Base *byte - Len uint32 + Len uint64 } type msghdr struct { @@ -25,6 +25,6 @@ type cmsghdr struct { } const ( - sizeofIovec = 0x8 - sizeofMsghdr = 0x1c + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 ) diff --git a/vendor/golang.org/x/net/ipv4/zsys_freebsd_riscv64.go b/vendor/golang.org/x/net/ipv4/zsys_freebsd_riscv64.go new file mode 100644 index 00000000..0feb9a75 --- /dev/null +++ b/vendor/golang.org/x/net/ipv4/zsys_freebsd_riscv64.go @@ -0,0 +1,52 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_freebsd.go + +package ipv4 + +const ( + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]uint8 + X__ss_align int64 + X__ss_pad2 [112]uint8 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]uint8 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group sockaddrStorage + Source sockaddrStorage +} diff --git a/vendor/golang.org/x/net/ipv6/zsys_freebsd_riscv64.go b/vendor/golang.org/x/net/ipv6/zsys_freebsd_riscv64.go new file mode 100644 index 00000000..5b39eb8d --- /dev/null +++ b/vendor/golang.org/x/net/ipv6/zsys_freebsd_riscv64.go @@ -0,0 +1,64 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs defs_freebsd.go + +package ipv6 + +const ( + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]uint8 + X__ss_align int64 + X__ss_pad2 [112]uint8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type groupReq struct { + Interface uint32 + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group sockaddrStorage + Source sockaddrStorage +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/golang.org/x/sys/AUTHORS b/vendor/golang.org/x/sys/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/vendor/golang.org/x/sys/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/sys/CONTRIBUTORS b/vendor/golang.org/x/sys/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/vendor/golang.org/x/sys/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c index e363c7d1..a4605e6d 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c +++ b/vendor/golang.org/x/sys/cpu/cpu_gccgo_x86.c @@ -7,6 +7,7 @@ #include #include +#include // Need to wrap __get_cpuid_count because it's declared as static. int @@ -17,27 +18,21 @@ gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf, return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); } +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC push_options +#pragma GCC target("xsave") +#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function) + // xgetbv reads the contents of an XCR (Extended Control Register) // specified in the ECX register into registers EDX:EAX. // Currently, the only supported value for XCR is 0. -// -// TODO: Replace with a better alternative: -// -// #include -// -// #pragma GCC target("xsave") -// -// void gccgoXgetbv(uint32_t *eax, uint32_t *edx) { -// unsigned long long x = _xgetbv(0); -// *eax = x & 0xffffffff; -// *edx = (x >> 32) & 0xffffffff; -// } -// -// Note that _xgetbv is defined starting with GCC 8. void gccgoXgetbv(uint32_t *eax, uint32_t *edx) { - __asm(" xorl %%ecx, %%ecx\n" - " xgetbv" - : "=a"(*eax), "=d"(*edx)); + uint64_t v = _xgetbv(0); + *eax = v & 0xffffffff; + *edx = v >> 32; } + +#pragma clang attribute pop +#pragma GCC pop_options diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go new file mode 100644 index 00000000..dd10eb79 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_other_riscv64.go @@ -0,0 +1,12 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux && riscv64 +// +build !linux,riscv64 + +package cpu + +func archInit() { + Initialized = true +} diff --git a/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s new file mode 100644 index 00000000..d560019e --- /dev/null +++ b/vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s @@ -0,0 +1,29 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (darwin || freebsd || netbsd || openbsd) && gc +// +build darwin freebsd netbsd openbsd +// +build gc + +#include "textflag.h" + +// System call support for RISCV64 BSD + +// Just jump to package syscall's implementation for all these functions. +// The runtime may know about them. + +TEXT ·Syscall(SB),NOSPLIT,$0-56 + JMP syscall·Syscall(SB) + +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + JMP syscall·Syscall6(SB) + +TEXT ·Syscall9(SB),NOSPLIT,$0-104 + JMP syscall·Syscall9(SB) + +TEXT ·RawSyscall(SB),NOSPLIT,$0-56 + JMP syscall·RawSyscall(SB) + +TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 + JMP syscall·RawSyscall6(SB) diff --git a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s index 6abd48ee..56535728 100644 --- a/vendor/golang.org/x/sys/unix/asm_linux_loong64.s +++ b/vendor/golang.org/x/sys/unix/asm_linux_loong64.s @@ -30,7 +30,7 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 MOVV trap+0(FP), R11 // syscall entry SYSCALL MOVV R4, r1+32(FP) - MOVV R5, r2+40(FP) + MOVV R0, r2+40(FP) // r2 is not used. Always set to 0 JAL runtime·exitsyscall(SB) RET @@ -50,5 +50,5 @@ TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 MOVV trap+0(FP), R11 // syscall entry SYSCALL MOVV R4, r1+32(FP) - MOVV R5, r2+40(FP) + MOVV R0, r2+40(FP) // r2 is not used. Always set to 0 RET diff --git a/vendor/golang.org/x/sys/unix/errors_freebsd_386.go b/vendor/golang.org/x/sys/unix/errors_freebsd_386.go deleted file mode 100644 index 761db66e..00000000 --- a/vendor/golang.org/x/sys/unix/errors_freebsd_386.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep -// them here for backwards compatibility. - -package unix - -const ( - DLT_HHDLC = 0x79 - IFF_SMART = 0x20 - IFT_1822 = 0x2 - IFT_A12MPPSWITCH = 0x82 - IFT_AAL2 = 0xbb - IFT_AAL5 = 0x31 - IFT_ADSL = 0x5e - IFT_AFLANE8023 = 0x3b - IFT_AFLANE8025 = 0x3c - IFT_ARAP = 0x58 - IFT_ARCNET = 0x23 - IFT_ARCNETPLUS = 0x24 - IFT_ASYNC = 0x54 - IFT_ATM = 0x25 - IFT_ATMDXI = 0x69 - IFT_ATMFUNI = 0x6a - IFT_ATMIMA = 0x6b - IFT_ATMLOGICAL = 0x50 - IFT_ATMRADIO = 0xbd - IFT_ATMSUBINTERFACE = 0x86 - IFT_ATMVCIENDPT = 0xc2 - IFT_ATMVIRTUAL = 0x95 - IFT_BGPPOLICYACCOUNTING = 0xa2 - IFT_BSC = 0x53 - IFT_CCTEMUL = 0x3d - IFT_CEPT = 0x13 - IFT_CES = 0x85 - IFT_CHANNEL = 0x46 - IFT_CNR = 0x55 - IFT_COFFEE = 0x84 - IFT_COMPOSITELINK = 0x9b - IFT_DCN = 0x8d - IFT_DIGITALPOWERLINE = 0x8a - IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba - IFT_DLSW = 0x4a - IFT_DOCSCABLEDOWNSTREAM = 0x80 - IFT_DOCSCABLEMACLAYER = 0x7f - IFT_DOCSCABLEUPSTREAM = 0x81 - IFT_DS0 = 0x51 - IFT_DS0BUNDLE = 0x52 - IFT_DS1FDL = 0xaa - IFT_DS3 = 0x1e - IFT_DTM = 0x8c - IFT_DVBASILN = 0xac - IFT_DVBASIOUT = 0xad - IFT_DVBRCCDOWNSTREAM = 0x93 - IFT_DVBRCCMACLAYER = 0x92 - IFT_DVBRCCUPSTREAM = 0x94 - IFT_ENC = 0xf4 - IFT_EON = 0x19 - IFT_EPLRS = 0x57 - IFT_ESCON = 0x49 - IFT_ETHER = 0x6 - IFT_FAITH = 0xf2 - IFT_FAST = 0x7d - IFT_FASTETHER = 0x3e - IFT_FASTETHERFX = 0x45 - IFT_FDDI = 0xf - IFT_FIBRECHANNEL = 0x38 - IFT_FRAMERELAYINTERCONNECT = 0x3a - IFT_FRAMERELAYMPI = 0x5c - IFT_FRDLCIENDPT = 0xc1 - IFT_FRELAY = 0x20 - IFT_FRELAYDCE = 0x2c - IFT_FRF16MFRBUNDLE = 0xa3 - IFT_FRFORWARD = 0x9e - IFT_G703AT2MB = 0x43 - IFT_G703AT64K = 0x42 - IFT_GIF = 0xf0 - IFT_GIGABITETHERNET = 0x75 - IFT_GR303IDT = 0xb2 - IFT_GR303RDT = 0xb1 - IFT_H323GATEKEEPER = 0xa4 - IFT_H323PROXY = 0xa5 - IFT_HDH1822 = 0x3 - IFT_HDLC = 0x76 - IFT_HDSL2 = 0xa8 - IFT_HIPERLAN2 = 0xb7 - IFT_HIPPI = 0x2f - IFT_HIPPIINTERFACE = 0x39 - IFT_HOSTPAD = 0x5a - IFT_HSSI = 0x2e - IFT_HY = 0xe - IFT_IBM370PARCHAN = 0x48 - IFT_IDSL = 0x9a - IFT_IEEE80211 = 0x47 - IFT_IEEE80212 = 0x37 - IFT_IEEE8023ADLAG = 0xa1 - IFT_IFGSN = 0x91 - IFT_IMT = 0xbe - IFT_INTERLEAVE = 0x7c - IFT_IP = 0x7e - IFT_IPFORWARD = 0x8e - IFT_IPOVERATM = 0x72 - IFT_IPOVERCDLC = 0x6d - IFT_IPOVERCLAW = 0x6e - IFT_IPSWITCH = 0x4e - IFT_IPXIP = 0xf9 - IFT_ISDN = 0x3f - IFT_ISDNBASIC = 0x14 - IFT_ISDNPRIMARY = 0x15 - IFT_ISDNS = 0x4b - IFT_ISDNU = 0x4c - IFT_ISO88022LLC = 0x29 - IFT_ISO88023 = 0x7 - IFT_ISO88024 = 0x8 - IFT_ISO88025 = 0x9 - IFT_ISO88025CRFPINT = 0x62 - IFT_ISO88025DTR = 0x56 - IFT_ISO88025FIBER = 0x73 - IFT_ISO88026 = 0xa - IFT_ISUP = 0xb3 - IFT_L3IPXVLAN = 0x89 - IFT_LAPB = 0x10 - IFT_LAPD = 0x4d - IFT_LAPF = 0x77 - IFT_LOCALTALK = 0x2a - IFT_LOOP = 0x18 - IFT_MEDIAMAILOVERIP = 0x8b - IFT_MFSIGLINK = 0xa7 - IFT_MIOX25 = 0x26 - IFT_MODEM = 0x30 - IFT_MPC = 0x71 - IFT_MPLS = 0xa6 - IFT_MPLSTUNNEL = 0x96 - IFT_MSDSL = 0x8f - IFT_MVL = 0xbf - IFT_MYRINET = 0x63 - IFT_NFAS = 0xaf - IFT_NSIP = 0x1b - IFT_OPTICALCHANNEL = 0xc3 - IFT_OPTICALTRANSPORT = 0xc4 - IFT_OTHER = 0x1 - IFT_P10 = 0xc - IFT_P80 = 0xd - IFT_PARA = 0x22 - IFT_PFLOG = 0xf6 - IFT_PFSYNC = 0xf7 - IFT_PLC = 0xae - IFT_POS = 0xab - IFT_PPPMULTILINKBUNDLE = 0x6c - IFT_PROPBWAP2MP = 0xb8 - IFT_PROPCNLS = 0x59 - IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 - IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 - IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 - IFT_PROPMUX = 0x36 - IFT_PROPWIRELESSP2P = 0x9d - IFT_PTPSERIAL = 0x16 - IFT_PVC = 0xf1 - IFT_QLLC = 0x44 - IFT_RADIOMAC = 0xbc - IFT_RADSL = 0x5f - IFT_REACHDSL = 0xc0 - IFT_RFC1483 = 0x9f - IFT_RS232 = 0x21 - IFT_RSRB = 0x4f - IFT_SDLC = 0x11 - IFT_SDSL = 0x60 - IFT_SHDSL = 0xa9 - IFT_SIP = 0x1f - IFT_SLIP = 0x1c - IFT_SMDSDXI = 0x2b - IFT_SMDSICIP = 0x34 - IFT_SONET = 0x27 - IFT_SONETOVERHEADCHANNEL = 0xb9 - IFT_SONETPATH = 0x32 - IFT_SONETVT = 0x33 - IFT_SRP = 0x97 - IFT_SS7SIGLINK = 0x9c - IFT_STACKTOSTACK = 0x6f - IFT_STARLAN = 0xb - IFT_STF = 0xd7 - IFT_T1 = 0x12 - IFT_TDLC = 0x74 - IFT_TERMPAD = 0x5b - IFT_TR008 = 0xb0 - IFT_TRANSPHDLC = 0x7b - IFT_TUNNEL = 0x83 - IFT_ULTRA = 0x1d - IFT_USB = 0xa0 - IFT_V11 = 0x40 - IFT_V35 = 0x2d - IFT_V36 = 0x41 - IFT_V37 = 0x78 - IFT_VDSL = 0x61 - IFT_VIRTUALIPADDRESS = 0x70 - IFT_VOICEEM = 0x64 - IFT_VOICEENCAP = 0x67 - IFT_VOICEFXO = 0x65 - IFT_VOICEFXS = 0x66 - IFT_VOICEOVERATM = 0x98 - IFT_VOICEOVERFRAMERELAY = 0x99 - IFT_VOICEOVERIP = 0x68 - IFT_X213 = 0x5d - IFT_X25 = 0x5 - IFT_X25DDN = 0x4 - IFT_X25HUNTGROUP = 0x7a - IFT_X25MLP = 0x79 - IFT_X25PLE = 0x28 - IFT_XETHER = 0x1a - IPPROTO_MAXID = 0x34 - IPV6_FAITH = 0x1d - IPV6_MIN_MEMBERSHIPS = 0x1f - IP_FAITH = 0x16 - IP_MAX_SOURCE_FILTER = 0x400 - IP_MIN_MEMBERSHIPS = 0x1f - MAP_NORESERVE = 0x40 - MAP_RENAME = 0x20 - NET_RT_MAXID = 0x6 - RTF_PRCLONING = 0x10000 - RTM_OLDADD = 0x9 - RTM_OLDDEL = 0xa - RT_CACHING_CONTEXT = 0x1 - RT_NORTREF = 0x2 - SIOCADDRT = 0x8030720a - SIOCALIFADDR = 0x8118691b - SIOCDELRT = 0x8030720b - SIOCDLIFADDR = 0x8118691d - SIOCGLIFADDR = 0xc118691c - SIOCGLIFPHYADDR = 0xc118694b - SIOCSLIFPHYADDR = 0x8118694a -) diff --git a/vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go deleted file mode 100644 index 070f44b6..00000000 --- a/vendor/golang.org/x/sys/unix/errors_freebsd_amd64.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep -// them here for backwards compatibility. - -package unix - -const ( - DLT_HHDLC = 0x79 - IFF_SMART = 0x20 - IFT_1822 = 0x2 - IFT_A12MPPSWITCH = 0x82 - IFT_AAL2 = 0xbb - IFT_AAL5 = 0x31 - IFT_ADSL = 0x5e - IFT_AFLANE8023 = 0x3b - IFT_AFLANE8025 = 0x3c - IFT_ARAP = 0x58 - IFT_ARCNET = 0x23 - IFT_ARCNETPLUS = 0x24 - IFT_ASYNC = 0x54 - IFT_ATM = 0x25 - IFT_ATMDXI = 0x69 - IFT_ATMFUNI = 0x6a - IFT_ATMIMA = 0x6b - IFT_ATMLOGICAL = 0x50 - IFT_ATMRADIO = 0xbd - IFT_ATMSUBINTERFACE = 0x86 - IFT_ATMVCIENDPT = 0xc2 - IFT_ATMVIRTUAL = 0x95 - IFT_BGPPOLICYACCOUNTING = 0xa2 - IFT_BSC = 0x53 - IFT_CCTEMUL = 0x3d - IFT_CEPT = 0x13 - IFT_CES = 0x85 - IFT_CHANNEL = 0x46 - IFT_CNR = 0x55 - IFT_COFFEE = 0x84 - IFT_COMPOSITELINK = 0x9b - IFT_DCN = 0x8d - IFT_DIGITALPOWERLINE = 0x8a - IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba - IFT_DLSW = 0x4a - IFT_DOCSCABLEDOWNSTREAM = 0x80 - IFT_DOCSCABLEMACLAYER = 0x7f - IFT_DOCSCABLEUPSTREAM = 0x81 - IFT_DS0 = 0x51 - IFT_DS0BUNDLE = 0x52 - IFT_DS1FDL = 0xaa - IFT_DS3 = 0x1e - IFT_DTM = 0x8c - IFT_DVBASILN = 0xac - IFT_DVBASIOUT = 0xad - IFT_DVBRCCDOWNSTREAM = 0x93 - IFT_DVBRCCMACLAYER = 0x92 - IFT_DVBRCCUPSTREAM = 0x94 - IFT_ENC = 0xf4 - IFT_EON = 0x19 - IFT_EPLRS = 0x57 - IFT_ESCON = 0x49 - IFT_ETHER = 0x6 - IFT_FAITH = 0xf2 - IFT_FAST = 0x7d - IFT_FASTETHER = 0x3e - IFT_FASTETHERFX = 0x45 - IFT_FDDI = 0xf - IFT_FIBRECHANNEL = 0x38 - IFT_FRAMERELAYINTERCONNECT = 0x3a - IFT_FRAMERELAYMPI = 0x5c - IFT_FRDLCIENDPT = 0xc1 - IFT_FRELAY = 0x20 - IFT_FRELAYDCE = 0x2c - IFT_FRF16MFRBUNDLE = 0xa3 - IFT_FRFORWARD = 0x9e - IFT_G703AT2MB = 0x43 - IFT_G703AT64K = 0x42 - IFT_GIF = 0xf0 - IFT_GIGABITETHERNET = 0x75 - IFT_GR303IDT = 0xb2 - IFT_GR303RDT = 0xb1 - IFT_H323GATEKEEPER = 0xa4 - IFT_H323PROXY = 0xa5 - IFT_HDH1822 = 0x3 - IFT_HDLC = 0x76 - IFT_HDSL2 = 0xa8 - IFT_HIPERLAN2 = 0xb7 - IFT_HIPPI = 0x2f - IFT_HIPPIINTERFACE = 0x39 - IFT_HOSTPAD = 0x5a - IFT_HSSI = 0x2e - IFT_HY = 0xe - IFT_IBM370PARCHAN = 0x48 - IFT_IDSL = 0x9a - IFT_IEEE80211 = 0x47 - IFT_IEEE80212 = 0x37 - IFT_IEEE8023ADLAG = 0xa1 - IFT_IFGSN = 0x91 - IFT_IMT = 0xbe - IFT_INTERLEAVE = 0x7c - IFT_IP = 0x7e - IFT_IPFORWARD = 0x8e - IFT_IPOVERATM = 0x72 - IFT_IPOVERCDLC = 0x6d - IFT_IPOVERCLAW = 0x6e - IFT_IPSWITCH = 0x4e - IFT_IPXIP = 0xf9 - IFT_ISDN = 0x3f - IFT_ISDNBASIC = 0x14 - IFT_ISDNPRIMARY = 0x15 - IFT_ISDNS = 0x4b - IFT_ISDNU = 0x4c - IFT_ISO88022LLC = 0x29 - IFT_ISO88023 = 0x7 - IFT_ISO88024 = 0x8 - IFT_ISO88025 = 0x9 - IFT_ISO88025CRFPINT = 0x62 - IFT_ISO88025DTR = 0x56 - IFT_ISO88025FIBER = 0x73 - IFT_ISO88026 = 0xa - IFT_ISUP = 0xb3 - IFT_L3IPXVLAN = 0x89 - IFT_LAPB = 0x10 - IFT_LAPD = 0x4d - IFT_LAPF = 0x77 - IFT_LOCALTALK = 0x2a - IFT_LOOP = 0x18 - IFT_MEDIAMAILOVERIP = 0x8b - IFT_MFSIGLINK = 0xa7 - IFT_MIOX25 = 0x26 - IFT_MODEM = 0x30 - IFT_MPC = 0x71 - IFT_MPLS = 0xa6 - IFT_MPLSTUNNEL = 0x96 - IFT_MSDSL = 0x8f - IFT_MVL = 0xbf - IFT_MYRINET = 0x63 - IFT_NFAS = 0xaf - IFT_NSIP = 0x1b - IFT_OPTICALCHANNEL = 0xc3 - IFT_OPTICALTRANSPORT = 0xc4 - IFT_OTHER = 0x1 - IFT_P10 = 0xc - IFT_P80 = 0xd - IFT_PARA = 0x22 - IFT_PFLOG = 0xf6 - IFT_PFSYNC = 0xf7 - IFT_PLC = 0xae - IFT_POS = 0xab - IFT_PPPMULTILINKBUNDLE = 0x6c - IFT_PROPBWAP2MP = 0xb8 - IFT_PROPCNLS = 0x59 - IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 - IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 - IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 - IFT_PROPMUX = 0x36 - IFT_PROPWIRELESSP2P = 0x9d - IFT_PTPSERIAL = 0x16 - IFT_PVC = 0xf1 - IFT_QLLC = 0x44 - IFT_RADIOMAC = 0xbc - IFT_RADSL = 0x5f - IFT_REACHDSL = 0xc0 - IFT_RFC1483 = 0x9f - IFT_RS232 = 0x21 - IFT_RSRB = 0x4f - IFT_SDLC = 0x11 - IFT_SDSL = 0x60 - IFT_SHDSL = 0xa9 - IFT_SIP = 0x1f - IFT_SLIP = 0x1c - IFT_SMDSDXI = 0x2b - IFT_SMDSICIP = 0x34 - IFT_SONET = 0x27 - IFT_SONETOVERHEADCHANNEL = 0xb9 - IFT_SONETPATH = 0x32 - IFT_SONETVT = 0x33 - IFT_SRP = 0x97 - IFT_SS7SIGLINK = 0x9c - IFT_STACKTOSTACK = 0x6f - IFT_STARLAN = 0xb - IFT_STF = 0xd7 - IFT_T1 = 0x12 - IFT_TDLC = 0x74 - IFT_TERMPAD = 0x5b - IFT_TR008 = 0xb0 - IFT_TRANSPHDLC = 0x7b - IFT_TUNNEL = 0x83 - IFT_ULTRA = 0x1d - IFT_USB = 0xa0 - IFT_V11 = 0x40 - IFT_V35 = 0x2d - IFT_V36 = 0x41 - IFT_V37 = 0x78 - IFT_VDSL = 0x61 - IFT_VIRTUALIPADDRESS = 0x70 - IFT_VOICEEM = 0x64 - IFT_VOICEENCAP = 0x67 - IFT_VOICEFXO = 0x65 - IFT_VOICEFXS = 0x66 - IFT_VOICEOVERATM = 0x98 - IFT_VOICEOVERFRAMERELAY = 0x99 - IFT_VOICEOVERIP = 0x68 - IFT_X213 = 0x5d - IFT_X25 = 0x5 - IFT_X25DDN = 0x4 - IFT_X25HUNTGROUP = 0x7a - IFT_X25MLP = 0x79 - IFT_X25PLE = 0x28 - IFT_XETHER = 0x1a - IPPROTO_MAXID = 0x34 - IPV6_FAITH = 0x1d - IPV6_MIN_MEMBERSHIPS = 0x1f - IP_FAITH = 0x16 - IP_MAX_SOURCE_FILTER = 0x400 - IP_MIN_MEMBERSHIPS = 0x1f - MAP_NORESERVE = 0x40 - MAP_RENAME = 0x20 - NET_RT_MAXID = 0x6 - RTF_PRCLONING = 0x10000 - RTM_OLDADD = 0x9 - RTM_OLDDEL = 0xa - RT_CACHING_CONTEXT = 0x1 - RT_NORTREF = 0x2 - SIOCADDRT = 0x8040720a - SIOCALIFADDR = 0x8118691b - SIOCDELRT = 0x8040720b - SIOCDLIFADDR = 0x8118691d - SIOCGLIFADDR = 0xc118691c - SIOCGLIFPHYADDR = 0xc118694b - SIOCSLIFPHYADDR = 0x8118694a -) diff --git a/vendor/golang.org/x/sys/unix/errors_freebsd_arm.go b/vendor/golang.org/x/sys/unix/errors_freebsd_arm.go deleted file mode 100644 index 856dca32..00000000 --- a/vendor/golang.org/x/sys/unix/errors_freebsd_arm.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package unix - -const ( - IFT_1822 = 0x2 - IFT_A12MPPSWITCH = 0x82 - IFT_AAL2 = 0xbb - IFT_AAL5 = 0x31 - IFT_ADSL = 0x5e - IFT_AFLANE8023 = 0x3b - IFT_AFLANE8025 = 0x3c - IFT_ARAP = 0x58 - IFT_ARCNET = 0x23 - IFT_ARCNETPLUS = 0x24 - IFT_ASYNC = 0x54 - IFT_ATM = 0x25 - IFT_ATMDXI = 0x69 - IFT_ATMFUNI = 0x6a - IFT_ATMIMA = 0x6b - IFT_ATMLOGICAL = 0x50 - IFT_ATMRADIO = 0xbd - IFT_ATMSUBINTERFACE = 0x86 - IFT_ATMVCIENDPT = 0xc2 - IFT_ATMVIRTUAL = 0x95 - IFT_BGPPOLICYACCOUNTING = 0xa2 - IFT_BSC = 0x53 - IFT_CCTEMUL = 0x3d - IFT_CEPT = 0x13 - IFT_CES = 0x85 - IFT_CHANNEL = 0x46 - IFT_CNR = 0x55 - IFT_COFFEE = 0x84 - IFT_COMPOSITELINK = 0x9b - IFT_DCN = 0x8d - IFT_DIGITALPOWERLINE = 0x8a - IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba - IFT_DLSW = 0x4a - IFT_DOCSCABLEDOWNSTREAM = 0x80 - IFT_DOCSCABLEMACLAYER = 0x7f - IFT_DOCSCABLEUPSTREAM = 0x81 - IFT_DS0 = 0x51 - IFT_DS0BUNDLE = 0x52 - IFT_DS1FDL = 0xaa - IFT_DS3 = 0x1e - IFT_DTM = 0x8c - IFT_DVBASILN = 0xac - IFT_DVBASIOUT = 0xad - IFT_DVBRCCDOWNSTREAM = 0x93 - IFT_DVBRCCMACLAYER = 0x92 - IFT_DVBRCCUPSTREAM = 0x94 - IFT_ENC = 0xf4 - IFT_EON = 0x19 - IFT_EPLRS = 0x57 - IFT_ESCON = 0x49 - IFT_ETHER = 0x6 - IFT_FAST = 0x7d - IFT_FASTETHER = 0x3e - IFT_FASTETHERFX = 0x45 - IFT_FDDI = 0xf - IFT_FIBRECHANNEL = 0x38 - IFT_FRAMERELAYINTERCONNECT = 0x3a - IFT_FRAMERELAYMPI = 0x5c - IFT_FRDLCIENDPT = 0xc1 - IFT_FRELAY = 0x20 - IFT_FRELAYDCE = 0x2c - IFT_FRF16MFRBUNDLE = 0xa3 - IFT_FRFORWARD = 0x9e - IFT_G703AT2MB = 0x43 - IFT_G703AT64K = 0x42 - IFT_GIF = 0xf0 - IFT_GIGABITETHERNET = 0x75 - IFT_GR303IDT = 0xb2 - IFT_GR303RDT = 0xb1 - IFT_H323GATEKEEPER = 0xa4 - IFT_H323PROXY = 0xa5 - IFT_HDH1822 = 0x3 - IFT_HDLC = 0x76 - IFT_HDSL2 = 0xa8 - IFT_HIPERLAN2 = 0xb7 - IFT_HIPPI = 0x2f - IFT_HIPPIINTERFACE = 0x39 - IFT_HOSTPAD = 0x5a - IFT_HSSI = 0x2e - IFT_HY = 0xe - IFT_IBM370PARCHAN = 0x48 - IFT_IDSL = 0x9a - IFT_IEEE80211 = 0x47 - IFT_IEEE80212 = 0x37 - IFT_IEEE8023ADLAG = 0xa1 - IFT_IFGSN = 0x91 - IFT_IMT = 0xbe - IFT_INTERLEAVE = 0x7c - IFT_IP = 0x7e - IFT_IPFORWARD = 0x8e - IFT_IPOVERATM = 0x72 - IFT_IPOVERCDLC = 0x6d - IFT_IPOVERCLAW = 0x6e - IFT_IPSWITCH = 0x4e - IFT_ISDN = 0x3f - IFT_ISDNBASIC = 0x14 - IFT_ISDNPRIMARY = 0x15 - IFT_ISDNS = 0x4b - IFT_ISDNU = 0x4c - IFT_ISO88022LLC = 0x29 - IFT_ISO88023 = 0x7 - IFT_ISO88024 = 0x8 - IFT_ISO88025 = 0x9 - IFT_ISO88025CRFPINT = 0x62 - IFT_ISO88025DTR = 0x56 - IFT_ISO88025FIBER = 0x73 - IFT_ISO88026 = 0xa - IFT_ISUP = 0xb3 - IFT_L3IPXVLAN = 0x89 - IFT_LAPB = 0x10 - IFT_LAPD = 0x4d - IFT_LAPF = 0x77 - IFT_LOCALTALK = 0x2a - IFT_LOOP = 0x18 - IFT_MEDIAMAILOVERIP = 0x8b - IFT_MFSIGLINK = 0xa7 - IFT_MIOX25 = 0x26 - IFT_MODEM = 0x30 - IFT_MPC = 0x71 - IFT_MPLS = 0xa6 - IFT_MPLSTUNNEL = 0x96 - IFT_MSDSL = 0x8f - IFT_MVL = 0xbf - IFT_MYRINET = 0x63 - IFT_NFAS = 0xaf - IFT_NSIP = 0x1b - IFT_OPTICALCHANNEL = 0xc3 - IFT_OPTICALTRANSPORT = 0xc4 - IFT_OTHER = 0x1 - IFT_P10 = 0xc - IFT_P80 = 0xd - IFT_PARA = 0x22 - IFT_PFLOG = 0xf6 - IFT_PFSYNC = 0xf7 - IFT_PLC = 0xae - IFT_POS = 0xab - IFT_PPPMULTILINKBUNDLE = 0x6c - IFT_PROPBWAP2MP = 0xb8 - IFT_PROPCNLS = 0x59 - IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5 - IFT_PROPDOCSWIRELESSMACLAYER = 0xb4 - IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6 - IFT_PROPMUX = 0x36 - IFT_PROPWIRELESSP2P = 0x9d - IFT_PTPSERIAL = 0x16 - IFT_PVC = 0xf1 - IFT_QLLC = 0x44 - IFT_RADIOMAC = 0xbc - IFT_RADSL = 0x5f - IFT_REACHDSL = 0xc0 - IFT_RFC1483 = 0x9f - IFT_RS232 = 0x21 - IFT_RSRB = 0x4f - IFT_SDLC = 0x11 - IFT_SDSL = 0x60 - IFT_SHDSL = 0xa9 - IFT_SIP = 0x1f - IFT_SLIP = 0x1c - IFT_SMDSDXI = 0x2b - IFT_SMDSICIP = 0x34 - IFT_SONET = 0x27 - IFT_SONETOVERHEADCHANNEL = 0xb9 - IFT_SONETPATH = 0x32 - IFT_SONETVT = 0x33 - IFT_SRP = 0x97 - IFT_SS7SIGLINK = 0x9c - IFT_STACKTOSTACK = 0x6f - IFT_STARLAN = 0xb - IFT_STF = 0xd7 - IFT_T1 = 0x12 - IFT_TDLC = 0x74 - IFT_TERMPAD = 0x5b - IFT_TR008 = 0xb0 - IFT_TRANSPHDLC = 0x7b - IFT_TUNNEL = 0x83 - IFT_ULTRA = 0x1d - IFT_USB = 0xa0 - IFT_V11 = 0x40 - IFT_V35 = 0x2d - IFT_V36 = 0x41 - IFT_V37 = 0x78 - IFT_VDSL = 0x61 - IFT_VIRTUALIPADDRESS = 0x70 - IFT_VOICEEM = 0x64 - IFT_VOICEENCAP = 0x67 - IFT_VOICEFXO = 0x65 - IFT_VOICEFXS = 0x66 - IFT_VOICEOVERATM = 0x98 - IFT_VOICEOVERFRAMERELAY = 0x99 - IFT_VOICEOVERIP = 0x68 - IFT_X213 = 0x5d - IFT_X25 = 0x5 - IFT_X25DDN = 0x4 - IFT_X25HUNTGROUP = 0x7a - IFT_X25MLP = 0x79 - IFT_X25PLE = 0x28 - IFT_XETHER = 0x1a - - // missing constants on FreeBSD-11.1-RELEASE, copied from old values in ztypes_freebsd_arm.go - IFF_SMART = 0x20 - IFT_FAITH = 0xf2 - IFT_IPXIP = 0xf9 - IPPROTO_MAXID = 0x34 - IPV6_FAITH = 0x1d - IP_FAITH = 0x16 - MAP_NORESERVE = 0x40 - MAP_RENAME = 0x20 - NET_RT_MAXID = 0x6 - RTF_PRCLONING = 0x10000 - RTM_OLDADD = 0x9 - RTM_OLDDEL = 0xa - SIOCADDRT = 0x8030720a - SIOCALIFADDR = 0x8118691b - SIOCDELRT = 0x8030720b - SIOCDLIFADDR = 0x8118691d - SIOCGLIFADDR = 0xc118691c - SIOCGLIFPHYADDR = 0xc118694b - SIOCSLIFPHYADDR = 0x8118694a -) diff --git a/vendor/golang.org/x/sys/unix/errors_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/errors_freebsd_arm64.go deleted file mode 100644 index 946dcf3f..00000000 --- a/vendor/golang.org/x/sys/unix/errors_freebsd_arm64.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Constants that were deprecated or moved to enums in the FreeBSD headers. Keep -// them here for backwards compatibility. - -package unix - -const ( - DLT_HHDLC = 0x79 - IPV6_MIN_MEMBERSHIPS = 0x1f - IP_MAX_SOURCE_FILTER = 0x400 - IP_MIN_MEMBERSHIPS = 0x1f - RT_CACHING_CONTEXT = 0x1 - RT_NORTREF = 0x2 -) diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index ee736234..dcef4de6 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -89,25 +89,30 @@ dragonfly_amd64) freebsd_386) mkerrors="$mkerrors -m32" mksyscall="go run mksyscall.go -l32" - mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'" + mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; freebsd_amd64) mkerrors="$mkerrors -m64" - mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'" + mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; freebsd_arm) mkerrors="$mkerrors" mksyscall="go run mksyscall.go -l32 -arm" - mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'" + mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" # Let the type of C char be signed for making the bare syscall # API consistent across platforms. mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" ;; freebsd_arm64) mkerrors="$mkerrors -m64" - mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'" + mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" + mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" + ;; +freebsd_riscv64) + mkerrors="$mkerrors -m64" + mksysnum="go run mksysnum.go 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12'" mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" ;; netbsd_386) diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index d888fb77..2ab44aa6 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -128,6 +128,7 @@ includes_FreeBSD=' #include #include #include +#include #include #include #include @@ -202,6 +203,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -295,6 +297,10 @@ struct ltchars { #define SOL_NETLINK 270 #endif +#ifndef SOL_SMC +#define SOL_SMC 286 +#endif + #ifdef SOL_BLUETOOTH // SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h // but it is already in bluetooth_linux.go @@ -529,7 +535,7 @@ ccflags="$@" $2 ~ /^(MS|MNT|MOUNT|UMOUNT)_/ || $2 ~ /^NS_GET_/ || $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || - $2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT|TFD)_/ || + $2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT|PIOD|TFD)_/ || $2 ~ /^KEXEC_/ || $2 ~ /^LINUX_REBOOT_CMD_/ || $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || @@ -553,6 +559,7 @@ ccflags="$@" $2 ~ /^CLONE_[A-Z_]+/ || $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && $2 ~ /^(BPF|DLT)_/ || + $2 ~ /^AUDIT_/ || $2 ~ /^(CLOCK|TIMER)_/ || $2 ~ /^CAN_/ || $2 ~ /^CAP_/ || @@ -575,7 +582,6 @@ ccflags="$@" $2 ~ /^SEEK_/ || $2 ~ /^SPLICE_/ || $2 ~ /^SYNC_FILE_RANGE_/ || - $2 !~ /^AUDIT_RECORD_MAGIC/ && $2 !~ /IOC_MAGIC/ && $2 ~ /^[A-Z][A-Z0-9_]+_MAGIC2?$/ || $2 ~ /^(VM|VMADDR)_/ || diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index ad22c33d..e2a30e88 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -217,14 +217,63 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { return } -func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { - // Recvmsg not implemented on AIX - return -1, -1, -1, ENOSYS +func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { + var msg Msghdr + msg.Name = (*byte)(unsafe.Pointer(rsa)) + msg.Namelen = uint32(SizeofSockaddrAny) + var dummy byte + if len(oob) > 0 { + // receive at least one normal byte + if emptyIovecs(iov) { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] + } + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + } + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } + if n, err = recvmsg(fd, &msg, flags); n == -1 { + return + } + oobn = int(msg.Controllen) + recvflags = int(msg.Flags) + return } -func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { - // SendmsgN not implemented on AIX - return -1, ENOSYS +func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { + var msg Msghdr + msg.Name = (*byte)(unsafe.Pointer(ptr)) + msg.Namelen = uint32(salen) + var dummy byte + var empty bool + if len(oob) > 0 { + // send at least one normal byte + empty := emptyIovecs(iov) + if empty { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] + } + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + } + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } + if n, err = sendmsg(fd, &msg, flags); err != nil { + return 0, err + } + if len(oob) > 0 && empty { + n = 0 + } + return n, nil } func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index 9c87c5f0..c437fc5d 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -325,27 +325,26 @@ func GetsockoptString(fd, level, opt int) (string, error) { //sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) -func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { +func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) - var iov Iovec - if len(p) > 0 { - iov.Base = (*byte)(unsafe.Pointer(&p[0])) - iov.SetLen(len(p)) - } var dummy byte if len(oob) > 0 { // receive at least one normal byte - if len(p) == 0 { - iov.Base = &dummy - iov.SetLen(1) + if emptyIovecs(iov) { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] } msg.Control = (*byte)(unsafe.Pointer(&oob[0])) msg.SetControllen(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = recvmsg(fd, &msg, flags); err != nil { return } @@ -356,31 +355,32 @@ func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) -func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { +func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) - var iov Iovec - if len(p) > 0 { - iov.Base = (*byte)(unsafe.Pointer(&p[0])) - iov.SetLen(len(p)) - } var dummy byte + var empty bool if len(oob) > 0 { // send at least one normal byte - if len(p) == 0 { - iov.Base = &dummy - iov.SetLen(1) + empty := emptyIovecs(iov) + if empty { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] } msg.Control = (*byte)(unsafe.Pointer(&oob[0])) msg.SetControllen(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = sendmsg(fd, &msg, flags); err != nil { return 0, err } - if len(oob) > 0 && len(p) == 0 { + if len(oob) > 0 && empty { n = 0 } return n, nil diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index e5448cc9..4f87f16e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -393,6 +393,13 @@ func GetsockoptXucred(fd, level, opt int) (*Xucred, error) { return x, err } +func GetsockoptTCPConnectionInfo(fd, level, opt int) (*TCPConnectionInfo, error) { + var value TCPConnectionInfo + vallen := _Socklen(SizeofTCPConnectionInfo) + err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen) + return &value, err +} + func SysctlKinfoProc(name string, args ...int) (*KinfoProc, error) { mib, err := sysctlmib(name, args...) if err != nil { diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go index 6f6c510f..de7c23e0 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go @@ -17,25 +17,12 @@ import ( "unsafe" ) -const ( - SYS_FSTAT_FREEBSD12 = 551 // { int fstat(int fd, _Out_ struct stat *sb); } - SYS_FSTATAT_FREEBSD12 = 552 // { int fstatat(int fd, _In_z_ char *path, \ - SYS_GETDIRENTRIES_FREEBSD12 = 554 // { ssize_t getdirentries(int fd, \ - SYS_STATFS_FREEBSD12 = 555 // { int statfs(_In_z_ char *path, \ - SYS_FSTATFS_FREEBSD12 = 556 // { int fstatfs(int fd, \ - SYS_GETFSSTAT_FREEBSD12 = 557 // { int getfsstat( \ - SYS_MKNODAT_FREEBSD12 = 559 // { int mknodat(int fd, _In_z_ char *path, \ -) - // See https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html. var ( osreldateOnce sync.Once osreldate uint32 ) -// INO64_FIRST from /usr/src/lib/libc/sys/compat-ino64.h -const _ino64First = 1200031 - func supportsABI(ver uint32) bool { osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") }) return osreldate >= ver @@ -159,38 +146,18 @@ func Accept4(fd, flags int) (nfd int, sa Sockaddr, err error) { func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { var ( - _p0 unsafe.Pointer - bufsize uintptr - oldBuf []statfs_freebsd11_t - needsConvert bool + _p0 unsafe.Pointer + bufsize uintptr ) - if len(buf) > 0 { - if supportsABI(_ino64First) { - _p0 = unsafe.Pointer(&buf[0]) - bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) - } else { - n := len(buf) - oldBuf = make([]statfs_freebsd11_t, n) - _p0 = unsafe.Pointer(&oldBuf[0]) - bufsize = unsafe.Sizeof(statfs_freebsd11_t{}) * uintptr(n) - needsConvert = true - } + _p0 = unsafe.Pointer(&buf[0]) + bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) } - var sysno uintptr = SYS_GETFSSTAT - if supportsABI(_ino64First) { - sysno = SYS_GETFSSTAT_FREEBSD12 - } - r0, _, e1 := Syscall(sysno, uintptr(_p0), bufsize, uintptr(flags)) + r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) n = int(r0) if e1 != 0 { err = e1 } - if e1 == 0 && needsConvert { - for i := range oldBuf { - buf[i].convertFrom(&oldBuf[i]) - } - } return } @@ -245,87 +212,11 @@ func Uname(uname *Utsname) error { } func Stat(path string, st *Stat_t) (err error) { - var oldStat stat_freebsd11_t - if supportsABI(_ino64First) { - return fstatat_freebsd12(AT_FDCWD, path, st, 0) - } - err = stat(path, &oldStat) - if err != nil { - return err - } - - st.convertFrom(&oldStat) - return nil + return Fstatat(AT_FDCWD, path, st, 0) } func Lstat(path string, st *Stat_t) (err error) { - var oldStat stat_freebsd11_t - if supportsABI(_ino64First) { - return fstatat_freebsd12(AT_FDCWD, path, st, AT_SYMLINK_NOFOLLOW) - } - err = lstat(path, &oldStat) - if err != nil { - return err - } - - st.convertFrom(&oldStat) - return nil -} - -func Fstat(fd int, st *Stat_t) (err error) { - var oldStat stat_freebsd11_t - if supportsABI(_ino64First) { - return fstat_freebsd12(fd, st) - } - err = fstat(fd, &oldStat) - if err != nil { - return err - } - - st.convertFrom(&oldStat) - return nil -} - -func Fstatat(fd int, path string, st *Stat_t, flags int) (err error) { - var oldStat stat_freebsd11_t - if supportsABI(_ino64First) { - return fstatat_freebsd12(fd, path, st, flags) - } - err = fstatat(fd, path, &oldStat, flags) - if err != nil { - return err - } - - st.convertFrom(&oldStat) - return nil -} - -func Statfs(path string, st *Statfs_t) (err error) { - var oldStatfs statfs_freebsd11_t - if supportsABI(_ino64First) { - return statfs_freebsd12(path, st) - } - err = statfs(path, &oldStatfs) - if err != nil { - return err - } - - st.convertFrom(&oldStatfs) - return nil -} - -func Fstatfs(fd int, st *Statfs_t) (err error) { - var oldStatfs statfs_freebsd11_t - if supportsABI(_ino64First) { - return fstatfs_freebsd12(fd, st) - } - err = fstatfs(fd, &oldStatfs) - if err != nil { - return err - } - - st.convertFrom(&oldStatfs) - return nil + return Fstatat(AT_FDCWD, path, st, AT_SYMLINK_NOFOLLOW) } func Getdents(fd int, buf []byte) (n int, err error) { @@ -333,162 +224,25 @@ func Getdents(fd int, buf []byte) (n int, err error) { } func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { - if supportsABI(_ino64First) { - if basep == nil || unsafe.Sizeof(*basep) == 8 { - return getdirentries_freebsd12(fd, buf, (*uint64)(unsafe.Pointer(basep))) - } - // The freebsd12 syscall needs a 64-bit base. On 32-bit machines - // we can't just use the basep passed in. See #32498. - var base uint64 = uint64(*basep) - n, err = getdirentries_freebsd12(fd, buf, &base) - *basep = uintptr(base) - if base>>32 != 0 { - // We can't stuff the base back into a uintptr, so any - // future calls would be suspect. Generate an error. - // EIO is allowed by getdirentries. - err = EIO - } - return + if basep == nil || unsafe.Sizeof(*basep) == 8 { + return getdirentries(fd, buf, (*uint64)(unsafe.Pointer(basep))) } - - // The old syscall entries are smaller than the new. Use 1/4 of the original - // buffer size rounded up to DIRBLKSIZ (see /usr/src/lib/libc/sys/getdirentries.c). - oldBufLen := roundup(len(buf)/4, _dirblksiz) - oldBuf := make([]byte, oldBufLen) - n, err = getdirentries(fd, oldBuf, basep) - if err == nil && n > 0 { - n = convertFromDirents11(buf, oldBuf[:n]) + // The syscall needs a 64-bit base. On 32-bit machines + // we can't just use the basep passed in. See #32498. + var base uint64 = uint64(*basep) + n, err = getdirentries(fd, buf, &base) + *basep = uintptr(base) + if base>>32 != 0 { + // We can't stuff the base back into a uintptr, so any + // future calls would be suspect. Generate an error. + // EIO is allowed by getdirentries. + err = EIO } return } func Mknod(path string, mode uint32, dev uint64) (err error) { - var oldDev int - if supportsABI(_ino64First) { - return mknodat_freebsd12(AT_FDCWD, path, mode, dev) - } - oldDev = int(dev) - return mknod(path, mode, oldDev) -} - -func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { - var oldDev int - if supportsABI(_ino64First) { - return mknodat_freebsd12(fd, path, mode, dev) - } - oldDev = int(dev) - return mknodat(fd, path, mode, oldDev) -} - -// round x to the nearest multiple of y, larger or equal to x. -// -// from /usr/include/sys/param.h Macros for counting and rounding. -// #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) -func roundup(x, y int) int { - return ((x + y - 1) / y) * y -} - -func (s *Stat_t) convertFrom(old *stat_freebsd11_t) { - *s = Stat_t{ - Dev: uint64(old.Dev), - Ino: uint64(old.Ino), - Nlink: uint64(old.Nlink), - Mode: old.Mode, - Uid: old.Uid, - Gid: old.Gid, - Rdev: uint64(old.Rdev), - Atim: old.Atim, - Mtim: old.Mtim, - Ctim: old.Ctim, - Btim: old.Btim, - Size: old.Size, - Blocks: old.Blocks, - Blksize: old.Blksize, - Flags: old.Flags, - Gen: uint64(old.Gen), - } -} - -func (s *Statfs_t) convertFrom(old *statfs_freebsd11_t) { - *s = Statfs_t{ - Version: _statfsVersion, - Type: old.Type, - Flags: old.Flags, - Bsize: old.Bsize, - Iosize: old.Iosize, - Blocks: old.Blocks, - Bfree: old.Bfree, - Bavail: old.Bavail, - Files: old.Files, - Ffree: old.Ffree, - Syncwrites: old.Syncwrites, - Asyncwrites: old.Asyncwrites, - Syncreads: old.Syncreads, - Asyncreads: old.Asyncreads, - // Spare - Namemax: old.Namemax, - Owner: old.Owner, - Fsid: old.Fsid, - // Charspare - // Fstypename - // Mntfromname - // Mntonname - } - - sl := old.Fstypename[:] - n := clen(*(*[]byte)(unsafe.Pointer(&sl))) - copy(s.Fstypename[:], old.Fstypename[:n]) - - sl = old.Mntfromname[:] - n = clen(*(*[]byte)(unsafe.Pointer(&sl))) - copy(s.Mntfromname[:], old.Mntfromname[:n]) - - sl = old.Mntonname[:] - n = clen(*(*[]byte)(unsafe.Pointer(&sl))) - copy(s.Mntonname[:], old.Mntonname[:n]) -} - -func convertFromDirents11(buf []byte, old []byte) int { - const ( - fixedSize = int(unsafe.Offsetof(Dirent{}.Name)) - oldFixedSize = int(unsafe.Offsetof(dirent_freebsd11{}.Name)) - ) - - dstPos := 0 - srcPos := 0 - for dstPos+fixedSize < len(buf) && srcPos+oldFixedSize < len(old) { - var dstDirent Dirent - var srcDirent dirent_freebsd11 - - // If multiple direntries are written, sometimes when we reach the final one, - // we may have cap of old less than size of dirent_freebsd11. - copy((*[unsafe.Sizeof(srcDirent)]byte)(unsafe.Pointer(&srcDirent))[:], old[srcPos:]) - - reclen := roundup(fixedSize+int(srcDirent.Namlen)+1, 8) - if dstPos+reclen > len(buf) { - break - } - - dstDirent.Fileno = uint64(srcDirent.Fileno) - dstDirent.Off = 0 - dstDirent.Reclen = uint16(reclen) - dstDirent.Type = srcDirent.Type - dstDirent.Pad0 = 0 - dstDirent.Namlen = uint16(srcDirent.Namlen) - dstDirent.Pad1 = 0 - - copy(dstDirent.Name[:], srcDirent.Name[:srcDirent.Namlen]) - copy(buf[dstPos:], (*[unsafe.Sizeof(dstDirent)]byte)(unsafe.Pointer(&dstDirent))[:]) - padding := buf[dstPos+fixedSize+int(dstDirent.Namlen) : dstPos+reclen] - for i := range padding { - padding[i] = 0 - } - - dstPos += int(dstDirent.Reclen) - srcPos += int(srcDirent.Reclen) - } - - return dstPos + return Mknodat(AT_FDCWD, path, mode, dev) } func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { @@ -501,31 +255,31 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys ptrace(request int, pid int, addr uintptr, data int) (err error) func PtraceAttach(pid int) (err error) { - return ptrace(PTRACE_ATTACH, pid, 0, 0) + return ptrace(PT_ATTACH, pid, 0, 0) } func PtraceCont(pid int, signal int) (err error) { - return ptrace(PTRACE_CONT, pid, 1, signal) + return ptrace(PT_CONTINUE, pid, 1, signal) } func PtraceDetach(pid int) (err error) { - return ptrace(PTRACE_DETACH, pid, 1, 0) + return ptrace(PT_DETACH, pid, 1, 0) } func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) { - return ptrace(PTRACE_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0) + return ptrace(PT_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0) } func PtraceGetRegs(pid int, regsout *Reg) (err error) { - return ptrace(PTRACE_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0) + return ptrace(PT_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0) } func PtraceLwpEvents(pid int, enable int) (err error) { - return ptrace(PTRACE_LWPEVENTS, pid, 0, enable) + return ptrace(PT_LWP_EVENTS, pid, 0, enable) } func PtraceLwpInfo(pid int, info uintptr) (err error) { - return ptrace(PTRACE_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{}))) + return ptrace(PT_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{}))) } func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { @@ -545,11 +299,11 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) { } func PtraceSetRegs(pid int, regs *Reg) (err error) { - return ptrace(PTRACE_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0) + return ptrace(PT_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0) } func PtraceSingleStep(pid int) (err error) { - return ptrace(PTRACE_SINGLESTEP, pid, 1, 0) + return ptrace(PT_STEP, pid, 1, 0) } /* @@ -591,16 +345,12 @@ func PtraceSingleStep(pid int) (err error) { //sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //sys Flock(fd int, how int) (err error) //sys Fpathconf(fd int, name int) (val int, err error) -//sys fstat(fd int, stat *stat_freebsd11_t) (err error) -//sys fstat_freebsd12(fd int, stat *Stat_t) (err error) -//sys fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) -//sys fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error) -//sys fstatfs(fd int, stat *statfs_freebsd11_t) (err error) -//sys fstatfs_freebsd12(fd int, stat *Statfs_t) (err error) +//sys Fstat(fd int, stat *Stat_t) (err error) +//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) +//sys Fstatfs(fd int, stat *Statfs_t) (err error) //sys Fsync(fd int) (err error) //sys Ftruncate(fd int, length int64) (err error) -//sys getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) -//sys getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error) +//sys getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) //sys Getdtablesize() (size int) //sysnb Getegid() (egid int) //sysnb Geteuid() (uid int) @@ -622,13 +372,10 @@ func PtraceSingleStep(pid int) (err error) { //sys Link(path string, link string) (err error) //sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error) //sys Listen(s int, backlog int) (err error) -//sys lstat(path string, stat *stat_freebsd11_t) (err error) //sys Mkdir(path string, mode uint32) (err error) //sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error) -//sys mknod(path string, mode uint32, dev int) (err error) -//sys mknodat(fd int, path string, mode uint32, dev int) (err error) -//sys mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error) +//sys Mknodat(fd int, path string, mode uint32, dev uint64) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error) @@ -658,9 +405,7 @@ func PtraceSingleStep(pid int) (err error) { //sysnb Setsid() (pid int, err error) //sysnb Settimeofday(tp *Timeval) (err error) //sysnb Setuid(uid int) (err error) -//sys stat(path string, stat *stat_freebsd11_t) (err error) -//sys statfs(path string, stat *statfs_freebsd11_t) (err error) -//sys statfs_freebsd12(path string, stat *Statfs_t) (err error) +//sys Statfs(path string, stat *Statfs_t) (err error) //sys Symlink(path string, link string) (err error) //sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error) //sys Sync() (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go index 342fc32b..c3c4c698 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_386.go @@ -57,11 +57,11 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func PtraceGetFsBase(pid int, fsbase *int64) (err error) { - return ptrace(PTRACE_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) + return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) } func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)} - err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) + err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) return int(ioDesc.Len), err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go index a32d5aa4..82be61a2 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go @@ -57,11 +57,11 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) func PtraceGetFsBase(pid int, fsbase *int64) (err error) { - return ptrace(PTRACE_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) + return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0) } func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)} - err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) + err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) return int(ioDesc.Len), err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go index 1e36d39a..cd58f102 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go @@ -58,6 +58,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)} - err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) + err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) return int(ioDesc.Len), err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go index a09a1537..d6f538f9 100644 --- a/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go @@ -58,6 +58,6 @@ func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)} - err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) + err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) return int(ioDesc.Len), err } diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go new file mode 100644 index 00000000..8ea6e961 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go @@ -0,0 +1,63 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build riscv64 && freebsd +// +build riscv64,freebsd + +package unix + +import ( + "syscall" + "unsafe" +) + +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} +} + +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint64(fd) + k.Filter = int16(mode) + k.Flags = uint16(flags) +} + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (msghdr *Msghdr) SetIovlen(length int) { + msghdr.Iovlen = int32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + var writtenOut uint64 = 0 + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0) + + written = int(writtenOut) + + if e1 != 0 { + err = e1 + } + return +} + +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) + +func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) { + ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)} + err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0) + return int(ioDesc.Len), err +} diff --git a/vendor/golang.org/x/sys/unix/syscall_illumos.go b/vendor/golang.org/x/sys/unix/syscall_illumos.go index 8d5f294c..e48244a9 100644 --- a/vendor/golang.org/x/sys/unix/syscall_illumos.go +++ b/vendor/golang.org/x/sys/unix/syscall_illumos.go @@ -20,10 +20,9 @@ func bytes2iovec(bs [][]byte) []Iovec { for i, b := range bs { iovecs[i].SetLen(len(b)) if len(b) > 0 { - // somehow Iovec.Base on illumos is (*int8), not (*byte) - iovecs[i].Base = (*int8)(unsafe.Pointer(&b[0])) + iovecs[i].Base = &b[0] } else { - iovecs[i].Base = (*int8)(unsafe.Pointer(&_zero)) + iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero)) } } return iovecs diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index c8d20321..5e4a94f7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1499,18 +1499,13 @@ func KeyctlRestrictKeyring(ringid int, keyType string, restriction string) error //sys keyctlRestrictKeyringByType(cmd int, arg2 int, keyType string, restriction string) (err error) = SYS_KEYCTL //sys keyctlRestrictKeyring(cmd int, arg2 int) (err error) = SYS_KEYCTL -func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { +func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) - var iov Iovec - if len(p) > 0 { - iov.Base = &p[0] - iov.SetLen(len(p)) - } var dummy byte if len(oob) > 0 { - if len(p) == 0 { + if emptyIovecs(iov) { var sockType int sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE) if err != nil { @@ -1518,15 +1513,19 @@ func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn } // receive at least one normal byte if sockType != SOCK_DGRAM { - iov.Base = &dummy - iov.SetLen(1) + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] } } msg.Control = &oob[0] msg.SetControllen(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = recvmsg(fd, &msg, flags); err != nil { return } @@ -1535,18 +1534,15 @@ func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn return } -func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { +func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(ptr) msg.Namelen = uint32(salen) - var iov Iovec - if len(p) > 0 { - iov.Base = &p[0] - iov.SetLen(len(p)) - } var dummy byte + var empty bool if len(oob) > 0 { - if len(p) == 0 { + empty := emptyIovecs(iov) + if empty { var sockType int sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE) if err != nil { @@ -1554,19 +1550,22 @@ func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags i } // send at least one normal byte if sockType != SOCK_DGRAM { - iov.Base = &dummy - iov.SetLen(1) + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) } } msg.Control = &oob[0] msg.SetControllen(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = sendmsg(fd, &msg, flags); err != nil { return 0, err } - if len(oob) > 0 && len(p) == 0 { + if len(oob) > 0 && empty { n = 0 } return n, nil diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go index 28ba7b8c..0b69c3ef 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go @@ -12,8 +12,6 @@ import "unsafe" //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT //sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64 //sys Fchown(fd int, uid int, gid int) (err error) -//sys Fstat(fd int, stat *Stat_t) (err error) -//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) //sys Ftruncate(fd int, length int64) (err error) //sysnb Getegid() (egid int) @@ -43,6 +41,43 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) +func timespecFromStatxTimestamp(x StatxTimestamp) Timespec { + return Timespec{ + Sec: x.Sec, + Nsec: int64(x.Nsec), + } +} + +func Fstatat(fd int, path string, stat *Stat_t, flags int) error { + var r Statx_t + // Do it the glibc way, add AT_NO_AUTOMOUNT. + if err := Statx(fd, path, AT_NO_AUTOMOUNT|flags, STATX_BASIC_STATS, &r); err != nil { + return err + } + + stat.Dev = Mkdev(r.Dev_major, r.Dev_minor) + stat.Ino = r.Ino + stat.Mode = uint32(r.Mode) + stat.Nlink = r.Nlink + stat.Uid = r.Uid + stat.Gid = r.Gid + stat.Rdev = Mkdev(r.Rdev_major, r.Rdev_minor) + // hope we don't get to process files so large to overflow these size + // fields... + stat.Size = int64(r.Size) + stat.Blksize = int32(r.Blksize) + stat.Blocks = int64(r.Blocks) + stat.Atim = timespecFromStatxTimestamp(r.Atime) + stat.Mtim = timespecFromStatxTimestamp(r.Mtime) + stat.Ctim = timespecFromStatxTimestamp(r.Ctime) + + return nil +} + +func Fstat(fd int, stat *Stat_t) (err error) { + return Fstatat(fd, "", stat, AT_EMPTY_PATH) +} + func Stat(path string, stat *Stat_t) (err error) { return Fstatat(AT_FDCWD, path, stat, 0) } diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go index 8ff7adba..925a748a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go @@ -22,6 +22,7 @@ import "unsafe" //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) //sys Listen(s int, n int) (err error) +//sys MemfdSecret(flags int) (fd int, err error) //sys pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64 //sys pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64 //sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go index 30f28534..1378489f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go @@ -26,6 +26,10 @@ func (msghdr *Msghdr) SetControllen(length int) { msghdr.Controllen = uint32(length) } +func (msghdr *Msghdr) SetIovlen(length int) { + msghdr.Iovlen = uint32(length) +} + func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint32(length) } diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index 5c2003ce..b5ec457c 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -451,26 +451,25 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) { //sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg -func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { +func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(rsa)) msg.Namelen = uint32(SizeofSockaddrAny) - var iov Iovec - if len(p) > 0 { - iov.Base = (*int8)(unsafe.Pointer(&p[0])) - iov.SetLen(len(p)) - } - var dummy int8 + var dummy byte if len(oob) > 0 { // receive at least one normal byte - if len(p) == 0 { - iov.Base = &dummy - iov.SetLen(1) + if emptyIovecs(iov) { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] } msg.Accrightslen = int32(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = recvmsg(fd, &msg, flags); n == -1 { return } @@ -480,30 +479,31 @@ func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn //sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg -func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { +func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) { var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) msg.Namelen = uint32(salen) - var iov Iovec - if len(p) > 0 { - iov.Base = (*int8)(unsafe.Pointer(&p[0])) - iov.SetLen(len(p)) - } - var dummy int8 + var dummy byte + var empty bool if len(oob) > 0 { // send at least one normal byte - if len(p) == 0 { - iov.Base = &dummy - iov.SetLen(1) + empty = emptyIovecs(iov) + if empty { + var iova [1]Iovec + iova[0].Base = &dummy + iova[0].SetLen(1) + iov = iova[:] } msg.Accrightslen = int32(len(oob)) } - msg.Iov = &iov - msg.Iovlen = 1 + if len(iov) > 0 { + msg.Iov = &iov[0] + msg.SetIovlen(len(iov)) + } if n, err = sendmsg(fd, &msg, flags); err != nil { return 0, err } - if len(oob) > 0 && len(p) == 0 { + if len(oob) > 0 && empty { n = 0 } return n, nil @@ -618,6 +618,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys Getpriority(which int, who int) (n int, err error) //sysnb Getrlimit(which int, lim *Rlimit) (err error) //sysnb Getrusage(who int, rusage *Rusage) (err error) +//sysnb Getsid(pid int) (sid int, err error) //sysnb Gettimeofday(tv *Timeval) (err error) //sysnb Getuid() (uid int) //sys Kill(pid int, signum syscall.Signal) (err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_unix.go b/vendor/golang.org/x/sys/unix/syscall_unix.go index 70508afc..1ff5060b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -338,8 +338,13 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { } func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { + var iov [1]Iovec + if len(p) > 0 { + iov[0].Base = &p[0] + iov[0].SetLen(len(p)) + } var rsa RawSockaddrAny - n, oobn, recvflags, err = recvmsgRaw(fd, p, oob, flags, &rsa) + n, oobn, recvflags, err = recvmsgRaw(fd, iov[:], oob, flags, &rsa) // source address is only specified if the socket is unconnected if rsa.Addr.Family != AF_UNSPEC { from, err = anyToSockaddr(fd, &rsa) @@ -347,12 +352,42 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from return } +// RecvmsgBuffers receives a message from a socket using the recvmsg +// system call. The flags are passed to recvmsg. Any non-control data +// read is scattered into the buffers slices. The results are: +// - n is the number of non-control data read into bufs +// - oobn is the number of control data read into oob; this may be interpreted using [ParseSocketControlMessage] +// - recvflags is flags returned by recvmsg +// - from is the address of the sender +func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { + iov := make([]Iovec, len(buffers)) + for i := range buffers { + if len(buffers[i]) > 0 { + iov[i].Base = &buffers[i][0] + iov[i].SetLen(len(buffers[i])) + } else { + iov[i].Base = (*byte)(unsafe.Pointer(&_zero)) + } + } + var rsa RawSockaddrAny + n, oobn, recvflags, err = recvmsgRaw(fd, iov, oob, flags, &rsa) + if err == nil && rsa.Addr.Family != AF_UNSPEC { + from, err = anyToSockaddr(fd, &rsa) + } + return +} + func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { _, err = SendmsgN(fd, p, oob, to, flags) return } func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) { + var iov [1]Iovec + if len(p) > 0 { + iov[0].Base = &p[0] + iov[0].SetLen(len(p)) + } var ptr unsafe.Pointer var salen _Socklen if to != nil { @@ -361,7 +396,32 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) return 0, err } } - return sendmsgN(fd, p, oob, ptr, salen, flags) + return sendmsgN(fd, iov[:], oob, ptr, salen, flags) +} + +// SendmsgBuffers sends a message on a socket to an address using the sendmsg +// system call. The flags are passed to sendmsg. Any non-control data written +// is gathered from buffers. The function returns the number of bytes written +// to the socket. +func SendmsgBuffers(fd int, buffers [][]byte, oob []byte, to Sockaddr, flags int) (n int, err error) { + iov := make([]Iovec, len(buffers)) + for i := range buffers { + if len(buffers[i]) > 0 { + iov[i].Base = &buffers[i][0] + iov[i].SetLen(len(buffers[i])) + } else { + iov[i].Base = (*byte)(unsafe.Pointer(&_zero)) + } + } + var ptr unsafe.Pointer + var salen _Socklen + if to != nil { + ptr, salen, err = to.sockaddr() + if err != nil { + return 0, err + } + } + return sendmsgN(fd, iov, oob, ptr, salen, flags) } func Send(s int, buf []byte, flags int) (err error) { @@ -484,3 +544,13 @@ func Lutimes(path string, tv []Timeval) error { } return UtimesNanoAt(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW) } + +// emptyIovec reports whether there are no bytes in the slice of Iovec. +func emptyIovecs(iov []Iovec) bool { + for i := range iov { + if iov[i].Len > 0 { + return false + } + } + return true +} diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go index 44090011..f8c2c513 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go @@ -151,6 +151,7 @@ const ( BIOCSETF = 0x80084267 BIOCSETFNR = 0x80084282 BIOCSETIF = 0x8020426c + BIOCSETVLANPCP = 0x80044285 BIOCSETWF = 0x8008427b BIOCSETZBUF = 0x800c4281 BIOCSHDRCMPLT = 0x80044275 @@ -447,7 +448,7 @@ const ( DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 DLT_INFINIBAND = 0xf7 DLT_IPFILTER = 0x74 - DLT_IPMB = 0xc7 + DLT_IPMB_KONTRON = 0xc7 DLT_IPMB_LINUX = 0xd1 DLT_IPMI_HPM_2 = 0x104 DLT_IPNET = 0xe2 @@ -487,10 +488,11 @@ const ( DLT_LINUX_LAPD = 0xb1 DLT_LINUX_PPP_WITHDIRECTION = 0xa6 DLT_LINUX_SLL = 0x71 + DLT_LINUX_SLL2 = 0x114 DLT_LOOP = 0x6c DLT_LORATAP = 0x10e DLT_LTALK = 0x72 - DLT_MATCHING_MAX = 0x113 + DLT_MATCHING_MAX = 0x114 DLT_MATCHING_MIN = 0x68 DLT_MFR = 0xb6 DLT_MOST = 0xd3 @@ -734,6 +736,7 @@ const ( IPPROTO_CMTP = 0x26 IPPROTO_CPHB = 0x49 IPPROTO_CPNX = 0x48 + IPPROTO_DCCP = 0x21 IPPROTO_DDP = 0x25 IPPROTO_DGP = 0x56 IPPROTO_DIVERT = 0x102 @@ -814,7 +817,6 @@ const ( IPPROTO_SCTP = 0x84 IPPROTO_SDRP = 0x2a IPPROTO_SEND = 0x103 - IPPROTO_SEP = 0x21 IPPROTO_SHIM6 = 0x8c IPPROTO_SKIP = 0x39 IPPROTO_SPACER = 0x7fff @@ -911,6 +913,7 @@ const ( IPV6_V6ONLY = 0x1b IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 + IPV6_VLAN_PCP = 0x4b IP_ADD_MEMBERSHIP = 0xc IP_ADD_SOURCE_MEMBERSHIP = 0x46 IP_BINDANY = 0x18 @@ -989,8 +992,12 @@ const ( IP_TOS = 0x3 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + IP_VLAN_PCP = 0x4b ISIG = 0x80 ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 IXANY = 0x800 IXOFF = 0x400 IXON = 0x200 @@ -1000,7 +1007,6 @@ const ( KERN_VERSION = 0x4 LOCAL_CONNWAIT = 0x4 LOCAL_CREDS = 0x2 - LOCAL_CREDS_PERSISTENT = 0x3 LOCAL_PEERCRED = 0x1 LOCAL_VENDOR = 0x80000000 LOCK_EX = 0x2 @@ -1179,6 +1185,8 @@ const ( O_NONBLOCK = 0x4 O_RDONLY = 0x0 O_RDWR = 0x2 + O_RESOLVE_BENEATH = 0x800000 + O_SEARCH = 0x40000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 @@ -1189,6 +1197,10 @@ const ( PARMRK = 0x8 PARODD = 0x2000 PENDIN = 0x20000000 + PIOD_READ_D = 0x1 + PIOD_READ_I = 0x3 + PIOD_WRITE_D = 0x2 + PIOD_WRITE_I = 0x4 PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1196,6 +1208,60 @@ const ( PROT_NONE = 0x0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PTRACE_DEFAULT = 0x1 + PTRACE_EXEC = 0x1 + PTRACE_FORK = 0x8 + PTRACE_LWP = 0x10 + PTRACE_SCE = 0x2 + PTRACE_SCX = 0x4 + PTRACE_SYSCALL = 0x6 + PTRACE_VFORK = 0x20 + PT_ATTACH = 0xa + PT_CLEARSTEP = 0x10 + PT_CONTINUE = 0x7 + PT_DETACH = 0xb + PT_FIRSTMACH = 0x40 + PT_FOLLOW_FORK = 0x17 + PT_GETDBREGS = 0x25 + PT_GETFPREGS = 0x23 + PT_GETFSBASE = 0x47 + PT_GETGSBASE = 0x49 + PT_GETLWPLIST = 0xf + PT_GETNUMLWPS = 0xe + PT_GETREGS = 0x21 + PT_GETXMMREGS = 0x40 + PT_GETXSTATE = 0x45 + PT_GETXSTATE_INFO = 0x44 + PT_GET_EVENT_MASK = 0x19 + PT_GET_SC_ARGS = 0x1b + PT_GET_SC_RET = 0x1c + PT_IO = 0xc + PT_KILL = 0x8 + PT_LWPINFO = 0xd + PT_LWP_EVENTS = 0x18 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_RESUME = 0x13 + PT_SETDBREGS = 0x26 + PT_SETFPREGS = 0x24 + PT_SETFSBASE = 0x48 + PT_SETGSBASE = 0x4a + PT_SETREGS = 0x22 + PT_SETSTEP = 0x11 + PT_SETXMMREGS = 0x41 + PT_SETXSTATE = 0x46 + PT_SET_EVENT_MASK = 0x1a + PT_STEP = 0x9 + PT_SUSPEND = 0x12 + PT_SYSCALL = 0x16 + PT_TO_SCE = 0x14 + PT_TO_SCX = 0x15 + PT_TRACE_ME = 0x0 + PT_VM_ENTRY = 0x29 + PT_VM_TIMESTAMP = 0x28 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + P_ZONEID = 0xc RLIMIT_AS = 0xa RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1320,10 +1386,12 @@ const ( SIOCGHWADDR = 0xc020693e SIOCGI2C = 0xc020693d SIOCGIFADDR = 0xc0206921 + SIOCGIFALIAS = 0xc044692d SIOCGIFBRDADDR = 0xc0206923 SIOCGIFCAP = 0xc020691f SIOCGIFCONF = 0xc0086924 SIOCGIFDESCR = 0xc020692a + SIOCGIFDOWNREASON = 0xc058699a SIOCGIFDSTADDR = 0xc0206922 SIOCGIFFIB = 0xc020695c SIOCGIFFLAGS = 0xc0206911 @@ -1414,6 +1482,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 SO_RCVTIMEO = 0x1006 + SO_RERROR = 0x20000 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_REUSEPORT_LB = 0x10000 @@ -1472,22 +1541,40 @@ const ( TCOFLUSH = 0x2 TCOOFF = 0x1 TCOON = 0x2 + TCPOPT_EOL = 0x0 + TCPOPT_FAST_OPEN = 0x22 + TCPOPT_MAXSEG = 0x2 + TCPOPT_NOP = 0x1 + TCPOPT_PAD = 0x0 + TCPOPT_SACK = 0x5 + TCPOPT_SACK_PERMITTED = 0x4 + TCPOPT_SIGNATURE = 0x13 + TCPOPT_TIMESTAMP = 0x8 + TCPOPT_WINDOW = 0x3 TCP_BBR_ACK_COMP_ALG = 0x448 + TCP_BBR_ALGORITHM = 0x43b TCP_BBR_DRAIN_INC_EXTRA = 0x43c TCP_BBR_DRAIN_PG = 0x42e TCP_BBR_EXTRA_GAIN = 0x449 + TCP_BBR_EXTRA_STATE = 0x453 + TCP_BBR_FLOOR_MIN_TSO = 0x454 + TCP_BBR_HDWR_PACE = 0x451 + TCP_BBR_HOLD_TARGET = 0x436 TCP_BBR_IWINTSO = 0x42b TCP_BBR_LOWGAIN_FD = 0x436 TCP_BBR_LOWGAIN_HALF = 0x435 TCP_BBR_LOWGAIN_THRESH = 0x434 TCP_BBR_MAX_RTO = 0x439 TCP_BBR_MIN_RTO = 0x438 + TCP_BBR_MIN_TOPACEOUT = 0x455 TCP_BBR_ONE_RETRAN = 0x431 TCP_BBR_PACE_CROSS = 0x442 TCP_BBR_PACE_DEL_TAR = 0x43f + TCP_BBR_PACE_OH = 0x435 TCP_BBR_PACE_PER_SEC = 0x43e TCP_BBR_PACE_SEG_MAX = 0x440 TCP_BBR_PACE_SEG_MIN = 0x441 + TCP_BBR_POLICER_DETECT = 0x457 TCP_BBR_PROBE_RTT_GAIN = 0x44d TCP_BBR_PROBE_RTT_INT = 0x430 TCP_BBR_PROBE_RTT_LEN = 0x44e @@ -1496,12 +1583,18 @@ const ( TCP_BBR_REC_OVER_HPTS = 0x43a TCP_BBR_RETRAN_WTSO = 0x44b TCP_BBR_RWND_IS_APP = 0x42f + TCP_BBR_SEND_IWND_IN_TSO = 0x44f TCP_BBR_STARTUP_EXIT_EPOCH = 0x43d TCP_BBR_STARTUP_LOSS_EXIT = 0x432 TCP_BBR_STARTUP_PG = 0x42d + TCP_BBR_TMR_PACE_OH = 0x448 + TCP_BBR_TSLIMITS = 0x434 + TCP_BBR_TSTMP_RAISES = 0x456 TCP_BBR_UNLIMITED = 0x43b TCP_BBR_USEDEL_RATE = 0x437 TCP_BBR_USE_LOWGAIN = 0x433 + TCP_BBR_USE_RACK_CHEAT = 0x450 + TCP_BBR_UTTER_MAX_TSO = 0x452 TCP_CA_NAME_MAX = 0x10 TCP_CCALGOOPT = 0x41 TCP_CONGESTION = 0x40 @@ -1541,6 +1634,7 @@ const ( TCP_PCAP_OUT = 0x800 TCP_RACK_EARLY_RECOV = 0x423 TCP_RACK_EARLY_SEG = 0x424 + TCP_RACK_GP_INCREASE = 0x446 TCP_RACK_IDLE_REDUCE_HIGH = 0x444 TCP_RACK_MIN_PACE = 0x445 TCP_RACK_MIN_PACE_SEG = 0x446 @@ -1554,7 +1648,6 @@ const ( TCP_RACK_PRR_SENDALOT = 0x421 TCP_RACK_REORD_FADE = 0x426 TCP_RACK_REORD_THRESH = 0x425 - TCP_RACK_SESS_CWV = 0x42a TCP_RACK_TLP_INC_VAR = 0x429 TCP_RACK_TLP_REDUCE = 0x41c TCP_RACK_TLP_THRESH = 0x427 @@ -1694,12 +1787,13 @@ const ( EIDRM = syscall.Errno(0x52) EILSEQ = syscall.Errno(0x56) EINPROGRESS = syscall.Errno(0x24) + EINTEGRITY = syscall.Errno(0x61) EINTR = syscall.Errno(0x4) EINVAL = syscall.Errno(0x16) EIO = syscall.Errno(0x5) EISCONN = syscall.Errno(0x38) EISDIR = syscall.Errno(0x15) - ELAST = syscall.Errno(0x60) + ELAST = syscall.Errno(0x61) ELOOP = syscall.Errno(0x3e) EMFILE = syscall.Errno(0x18) EMLINK = syscall.Errno(0x1f) @@ -1842,7 +1936,7 @@ var errorList = [...]struct { {32, "EPIPE", "broken pipe"}, {33, "EDOM", "numerical argument out of domain"}, {34, "ERANGE", "result too large"}, - {35, "EAGAIN", "resource temporarily unavailable"}, + {35, "EWOULDBLOCK", "resource temporarily unavailable"}, {36, "EINPROGRESS", "operation now in progress"}, {37, "EALREADY", "operation already in progress"}, {38, "ENOTSOCK", "socket operation on non-socket"}, @@ -1904,6 +1998,7 @@ var errorList = [...]struct { {94, "ECAPMODE", "not permitted in capability mode"}, {95, "ENOTRECOVERABLE", "state not recoverable"}, {96, "EOWNERDEAD", "previous owner died"}, + {97, "EINTEGRITY", "integrity check failed"}, } // Signal table diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go index 64520d31..96310c3b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go @@ -151,6 +151,7 @@ const ( BIOCSETF = 0x80104267 BIOCSETFNR = 0x80104282 BIOCSETIF = 0x8020426c + BIOCSETVLANPCP = 0x80044285 BIOCSETWF = 0x8010427b BIOCSETZBUF = 0x80184281 BIOCSHDRCMPLT = 0x80044275 @@ -447,7 +448,7 @@ const ( DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 DLT_INFINIBAND = 0xf7 DLT_IPFILTER = 0x74 - DLT_IPMB = 0xc7 + DLT_IPMB_KONTRON = 0xc7 DLT_IPMB_LINUX = 0xd1 DLT_IPMI_HPM_2 = 0x104 DLT_IPNET = 0xe2 @@ -487,10 +488,11 @@ const ( DLT_LINUX_LAPD = 0xb1 DLT_LINUX_PPP_WITHDIRECTION = 0xa6 DLT_LINUX_SLL = 0x71 + DLT_LINUX_SLL2 = 0x114 DLT_LOOP = 0x6c DLT_LORATAP = 0x10e DLT_LTALK = 0x72 - DLT_MATCHING_MAX = 0x113 + DLT_MATCHING_MAX = 0x114 DLT_MATCHING_MIN = 0x68 DLT_MFR = 0xb6 DLT_MOST = 0xd3 @@ -734,6 +736,7 @@ const ( IPPROTO_CMTP = 0x26 IPPROTO_CPHB = 0x49 IPPROTO_CPNX = 0x48 + IPPROTO_DCCP = 0x21 IPPROTO_DDP = 0x25 IPPROTO_DGP = 0x56 IPPROTO_DIVERT = 0x102 @@ -814,7 +817,6 @@ const ( IPPROTO_SCTP = 0x84 IPPROTO_SDRP = 0x2a IPPROTO_SEND = 0x103 - IPPROTO_SEP = 0x21 IPPROTO_SHIM6 = 0x8c IPPROTO_SKIP = 0x39 IPPROTO_SPACER = 0x7fff @@ -911,6 +913,7 @@ const ( IPV6_V6ONLY = 0x1b IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 + IPV6_VLAN_PCP = 0x4b IP_ADD_MEMBERSHIP = 0xc IP_ADD_SOURCE_MEMBERSHIP = 0x46 IP_BINDANY = 0x18 @@ -989,8 +992,12 @@ const ( IP_TOS = 0x3 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + IP_VLAN_PCP = 0x4b ISIG = 0x80 ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 IXANY = 0x800 IXOFF = 0x400 IXON = 0x200 @@ -1000,7 +1007,6 @@ const ( KERN_VERSION = 0x4 LOCAL_CONNWAIT = 0x4 LOCAL_CREDS = 0x2 - LOCAL_CREDS_PERSISTENT = 0x3 LOCAL_PEERCRED = 0x1 LOCAL_VENDOR = 0x80000000 LOCK_EX = 0x2 @@ -1180,6 +1186,8 @@ const ( O_NONBLOCK = 0x4 O_RDONLY = 0x0 O_RDWR = 0x2 + O_RESOLVE_BENEATH = 0x800000 + O_SEARCH = 0x40000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 @@ -1190,6 +1198,10 @@ const ( PARMRK = 0x8 PARODD = 0x2000 PENDIN = 0x20000000 + PIOD_READ_D = 0x1 + PIOD_READ_I = 0x3 + PIOD_WRITE_D = 0x2 + PIOD_WRITE_I = 0x4 PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1197,6 +1209,58 @@ const ( PROT_NONE = 0x0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PTRACE_DEFAULT = 0x1 + PTRACE_EXEC = 0x1 + PTRACE_FORK = 0x8 + PTRACE_LWP = 0x10 + PTRACE_SCE = 0x2 + PTRACE_SCX = 0x4 + PTRACE_SYSCALL = 0x6 + PTRACE_VFORK = 0x20 + PT_ATTACH = 0xa + PT_CLEARSTEP = 0x10 + PT_CONTINUE = 0x7 + PT_DETACH = 0xb + PT_FIRSTMACH = 0x40 + PT_FOLLOW_FORK = 0x17 + PT_GETDBREGS = 0x25 + PT_GETFPREGS = 0x23 + PT_GETFSBASE = 0x47 + PT_GETGSBASE = 0x49 + PT_GETLWPLIST = 0xf + PT_GETNUMLWPS = 0xe + PT_GETREGS = 0x21 + PT_GETXSTATE = 0x45 + PT_GETXSTATE_INFO = 0x44 + PT_GET_EVENT_MASK = 0x19 + PT_GET_SC_ARGS = 0x1b + PT_GET_SC_RET = 0x1c + PT_IO = 0xc + PT_KILL = 0x8 + PT_LWPINFO = 0xd + PT_LWP_EVENTS = 0x18 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_RESUME = 0x13 + PT_SETDBREGS = 0x26 + PT_SETFPREGS = 0x24 + PT_SETFSBASE = 0x48 + PT_SETGSBASE = 0x4a + PT_SETREGS = 0x22 + PT_SETSTEP = 0x11 + PT_SETXSTATE = 0x46 + PT_SET_EVENT_MASK = 0x1a + PT_STEP = 0x9 + PT_SUSPEND = 0x12 + PT_SYSCALL = 0x16 + PT_TO_SCE = 0x14 + PT_TO_SCX = 0x15 + PT_TRACE_ME = 0x0 + PT_VM_ENTRY = 0x29 + PT_VM_TIMESTAMP = 0x28 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + P_ZONEID = 0xc RLIMIT_AS = 0xa RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1321,10 +1385,12 @@ const ( SIOCGHWADDR = 0xc020693e SIOCGI2C = 0xc020693d SIOCGIFADDR = 0xc0206921 + SIOCGIFALIAS = 0xc044692d SIOCGIFBRDADDR = 0xc0206923 SIOCGIFCAP = 0xc020691f SIOCGIFCONF = 0xc0106924 SIOCGIFDESCR = 0xc020692a + SIOCGIFDOWNREASON = 0xc058699a SIOCGIFDSTADDR = 0xc0206922 SIOCGIFFIB = 0xc020695c SIOCGIFFLAGS = 0xc0206911 @@ -1415,6 +1481,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 SO_RCVTIMEO = 0x1006 + SO_RERROR = 0x20000 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_REUSEPORT_LB = 0x10000 @@ -1473,22 +1540,40 @@ const ( TCOFLUSH = 0x2 TCOOFF = 0x1 TCOON = 0x2 + TCPOPT_EOL = 0x0 + TCPOPT_FAST_OPEN = 0x22 + TCPOPT_MAXSEG = 0x2 + TCPOPT_NOP = 0x1 + TCPOPT_PAD = 0x0 + TCPOPT_SACK = 0x5 + TCPOPT_SACK_PERMITTED = 0x4 + TCPOPT_SIGNATURE = 0x13 + TCPOPT_TIMESTAMP = 0x8 + TCPOPT_WINDOW = 0x3 TCP_BBR_ACK_COMP_ALG = 0x448 + TCP_BBR_ALGORITHM = 0x43b TCP_BBR_DRAIN_INC_EXTRA = 0x43c TCP_BBR_DRAIN_PG = 0x42e TCP_BBR_EXTRA_GAIN = 0x449 + TCP_BBR_EXTRA_STATE = 0x453 + TCP_BBR_FLOOR_MIN_TSO = 0x454 + TCP_BBR_HDWR_PACE = 0x451 + TCP_BBR_HOLD_TARGET = 0x436 TCP_BBR_IWINTSO = 0x42b TCP_BBR_LOWGAIN_FD = 0x436 TCP_BBR_LOWGAIN_HALF = 0x435 TCP_BBR_LOWGAIN_THRESH = 0x434 TCP_BBR_MAX_RTO = 0x439 TCP_BBR_MIN_RTO = 0x438 + TCP_BBR_MIN_TOPACEOUT = 0x455 TCP_BBR_ONE_RETRAN = 0x431 TCP_BBR_PACE_CROSS = 0x442 TCP_BBR_PACE_DEL_TAR = 0x43f + TCP_BBR_PACE_OH = 0x435 TCP_BBR_PACE_PER_SEC = 0x43e TCP_BBR_PACE_SEG_MAX = 0x440 TCP_BBR_PACE_SEG_MIN = 0x441 + TCP_BBR_POLICER_DETECT = 0x457 TCP_BBR_PROBE_RTT_GAIN = 0x44d TCP_BBR_PROBE_RTT_INT = 0x430 TCP_BBR_PROBE_RTT_LEN = 0x44e @@ -1497,12 +1582,18 @@ const ( TCP_BBR_REC_OVER_HPTS = 0x43a TCP_BBR_RETRAN_WTSO = 0x44b TCP_BBR_RWND_IS_APP = 0x42f + TCP_BBR_SEND_IWND_IN_TSO = 0x44f TCP_BBR_STARTUP_EXIT_EPOCH = 0x43d TCP_BBR_STARTUP_LOSS_EXIT = 0x432 TCP_BBR_STARTUP_PG = 0x42d + TCP_BBR_TMR_PACE_OH = 0x448 + TCP_BBR_TSLIMITS = 0x434 + TCP_BBR_TSTMP_RAISES = 0x456 TCP_BBR_UNLIMITED = 0x43b TCP_BBR_USEDEL_RATE = 0x437 TCP_BBR_USE_LOWGAIN = 0x433 + TCP_BBR_USE_RACK_CHEAT = 0x450 + TCP_BBR_UTTER_MAX_TSO = 0x452 TCP_CA_NAME_MAX = 0x10 TCP_CCALGOOPT = 0x41 TCP_CONGESTION = 0x40 @@ -1542,6 +1633,7 @@ const ( TCP_PCAP_OUT = 0x800 TCP_RACK_EARLY_RECOV = 0x423 TCP_RACK_EARLY_SEG = 0x424 + TCP_RACK_GP_INCREASE = 0x446 TCP_RACK_IDLE_REDUCE_HIGH = 0x444 TCP_RACK_MIN_PACE = 0x445 TCP_RACK_MIN_PACE_SEG = 0x446 @@ -1555,7 +1647,6 @@ const ( TCP_RACK_PRR_SENDALOT = 0x421 TCP_RACK_REORD_FADE = 0x426 TCP_RACK_REORD_THRESH = 0x425 - TCP_RACK_SESS_CWV = 0x42a TCP_RACK_TLP_INC_VAR = 0x429 TCP_RACK_TLP_REDUCE = 0x41c TCP_RACK_TLP_THRESH = 0x427 @@ -1693,12 +1784,13 @@ const ( EIDRM = syscall.Errno(0x52) EILSEQ = syscall.Errno(0x56) EINPROGRESS = syscall.Errno(0x24) + EINTEGRITY = syscall.Errno(0x61) EINTR = syscall.Errno(0x4) EINVAL = syscall.Errno(0x16) EIO = syscall.Errno(0x5) EISCONN = syscall.Errno(0x38) EISDIR = syscall.Errno(0x15) - ELAST = syscall.Errno(0x60) + ELAST = syscall.Errno(0x61) ELOOP = syscall.Errno(0x3e) EMFILE = syscall.Errno(0x18) EMLINK = syscall.Errno(0x1f) @@ -1841,7 +1933,7 @@ var errorList = [...]struct { {32, "EPIPE", "broken pipe"}, {33, "EDOM", "numerical argument out of domain"}, {34, "ERANGE", "result too large"}, - {35, "EAGAIN", "resource temporarily unavailable"}, + {35, "EWOULDBLOCK", "resource temporarily unavailable"}, {36, "EINPROGRESS", "operation now in progress"}, {37, "EALREADY", "operation already in progress"}, {38, "ENOTSOCK", "socket operation on non-socket"}, @@ -1903,6 +1995,7 @@ var errorList = [...]struct { {94, "ECAPMODE", "not permitted in capability mode"}, {95, "ENOTRECOVERABLE", "state not recoverable"}, {96, "EOWNERDEAD", "previous owner died"}, + {97, "EINTEGRITY", "integrity check failed"}, } // Signal table diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go index 99e9a0e0..777b69de 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go @@ -151,6 +151,7 @@ const ( BIOCSETF = 0x80084267 BIOCSETFNR = 0x80084282 BIOCSETIF = 0x8020426c + BIOCSETVLANPCP = 0x80044285 BIOCSETWF = 0x8008427b BIOCSETZBUF = 0x800c4281 BIOCSHDRCMPLT = 0x80044275 @@ -362,7 +363,7 @@ const ( CTL_KERN = 0x1 CTL_MAXNAME = 0x18 CTL_NET = 0x4 - DIOCGATTR = 0xc144648e + DIOCGATTR = 0xc148648e DIOCGDELETE = 0x80106488 DIOCGFLUSH = 0x20006487 DIOCGFRONTSTUFF = 0x40086486 @@ -377,7 +378,7 @@ const ( DIOCGSTRIPESIZE = 0x4008648b DIOCSKERNELDUMP = 0x804c6490 DIOCSKERNELDUMP_FREEBSD11 = 0x80046485 - DIOCZONECMD = 0xc06c648f + DIOCZONECMD = 0xc078648f DLT_A429 = 0xb8 DLT_A653_ICM = 0xb9 DLT_AIRONET_HEADER = 0x78 @@ -407,7 +408,9 @@ const ( DLT_C_HDLC_WITH_DIR = 0xcd DLT_DBUS = 0xe7 DLT_DECT = 0xdd + DLT_DISPLAYPORT_AUX = 0x113 DLT_DOCSIS = 0x8f + DLT_DOCSIS31_XRA31 = 0x111 DLT_DVB_CI = 0xeb DLT_ECONET = 0x73 DLT_EN10MB = 0x1 @@ -417,6 +420,7 @@ const ( DLT_ERF = 0xc5 DLT_ERF_ETH = 0xaf DLT_ERF_POS = 0xb0 + DLT_ETHERNET_MPACKET = 0x112 DLT_FC_2 = 0xe0 DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 DLT_FDDI = 0xa @@ -444,7 +448,7 @@ const ( DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 DLT_INFINIBAND = 0xf7 DLT_IPFILTER = 0x74 - DLT_IPMB = 0xc7 + DLT_IPMB_KONTRON = 0xc7 DLT_IPMB_LINUX = 0xd1 DLT_IPMI_HPM_2 = 0x104 DLT_IPNET = 0xe2 @@ -484,9 +488,11 @@ const ( DLT_LINUX_LAPD = 0xb1 DLT_LINUX_PPP_WITHDIRECTION = 0xa6 DLT_LINUX_SLL = 0x71 + DLT_LINUX_SLL2 = 0x114 DLT_LOOP = 0x6c + DLT_LORATAP = 0x10e DLT_LTALK = 0x72 - DLT_MATCHING_MAX = 0x109 + DLT_MATCHING_MAX = 0x114 DLT_MATCHING_MIN = 0x68 DLT_MFR = 0xb6 DLT_MOST = 0xd3 @@ -502,7 +508,9 @@ const ( DLT_NFC_LLCP = 0xf5 DLT_NFLOG = 0xef DLT_NG40 = 0xf4 + DLT_NORDIC_BLE = 0x110 DLT_NULL = 0x0 + DLT_OPENFLOW = 0x10b DLT_PCI_EXP = 0x7d DLT_PFLOG = 0x75 DLT_PFSYNC = 0x79 @@ -526,15 +534,18 @@ const ( DLT_RTAC_SERIAL = 0xfa DLT_SCCP = 0x8e DLT_SCTP = 0xf8 + DLT_SDLC = 0x10c DLT_SITA = 0xc4 DLT_SLIP = 0x8 DLT_SLIP_BSDOS = 0xd DLT_STANAG_5066_D_PDU = 0xed DLT_SUNATM = 0x7b DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TI_LLN_SNIFFER = 0x10d DLT_TZSP = 0x80 DLT_USB = 0xba DLT_USBPCAP = 0xf9 + DLT_USB_DARWIN = 0x10a DLT_USB_FREEBSD = 0xba DLT_USB_LINUX = 0xbd DLT_USB_LINUX_MMAPPED = 0xdc @@ -554,6 +565,7 @@ const ( DLT_USER7 = 0x9a DLT_USER8 = 0x9b DLT_USER9 = 0x9c + DLT_VSOCK = 0x10f DLT_WATTSTOPPER_DLM = 0x107 DLT_WIHART = 0xdf DLT_WIRESHARK_UPPER_PDU = 0xfc @@ -578,6 +590,7 @@ const ( ECHONL = 0x10 ECHOPRT = 0x20 EVFILT_AIO = -0x3 + EVFILT_EMPTY = -0xd EVFILT_FS = -0x9 EVFILT_LIO = -0xa EVFILT_PROC = -0x5 @@ -585,11 +598,12 @@ const ( EVFILT_READ = -0x1 EVFILT_SENDFILE = -0xc EVFILT_SIGNAL = -0x6 - EVFILT_SYSCOUNT = 0xc + EVFILT_SYSCOUNT = 0xd EVFILT_TIMER = -0x7 EVFILT_USER = -0xb EVFILT_VNODE = -0x4 EVFILT_WRITE = -0x2 + EVNAMEMAP_NAME_SIZE = 0x40 EV_ADD = 0x1 EV_CLEAR = 0x20 EV_DELETE = 0x2 @@ -606,6 +620,7 @@ const ( EV_RECEIPT = 0x40 EV_SYSFLAGS = 0xf000 EXTA = 0x4b00 + EXTATTR_MAXNAMELEN = 0xff EXTATTR_NAMESPACE_EMPTY = 0x0 EXTATTR_NAMESPACE_SYSTEM = 0x2 EXTATTR_NAMESPACE_USER = 0x1 @@ -647,6 +662,7 @@ const ( IEXTEN = 0x400 IFAN_ARRIVAL = 0x0 IFAN_DEPARTURE = 0x1 + IFCAP_WOL_MAGIC = 0x2000 IFF_ALLMULTI = 0x200 IFF_ALTPHYS = 0x4000 IFF_BROADCAST = 0x2 @@ -663,6 +679,7 @@ const ( IFF_MONITOR = 0x40000 IFF_MULTICAST = 0x8000 IFF_NOARP = 0x80 + IFF_NOGROUP = 0x800000 IFF_OACTIVE = 0x400 IFF_POINTOPOINT = 0x10 IFF_PPROMISC = 0x20000 @@ -719,6 +736,7 @@ const ( IPPROTO_CMTP = 0x26 IPPROTO_CPHB = 0x49 IPPROTO_CPNX = 0x48 + IPPROTO_DCCP = 0x21 IPPROTO_DDP = 0x25 IPPROTO_DGP = 0x56 IPPROTO_DIVERT = 0x102 @@ -799,7 +817,6 @@ const ( IPPROTO_SCTP = 0x84 IPPROTO_SDRP = 0x2a IPPROTO_SEND = 0x103 - IPPROTO_SEP = 0x21 IPPROTO_SHIM6 = 0x8c IPPROTO_SKIP = 0x39 IPPROTO_SPACER = 0x7fff @@ -837,6 +854,7 @@ const ( IPV6_DSTOPTS = 0x32 IPV6_FLOWID = 0x43 IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_LEN = 0x14 IPV6_FLOWLABEL_MASK = 0xffff0f00 IPV6_FLOWTYPE = 0x44 IPV6_FRAGTTL = 0x78 @@ -857,13 +875,13 @@ const ( IPV6_MAX_GROUP_SRC_FILTER = 0x200 IPV6_MAX_MEMBERSHIPS = 0xfff IPV6_MAX_SOCK_SRC_FILTER = 0x80 - IPV6_MIN_MEMBERSHIPS = 0x1f IPV6_MMTU = 0x500 IPV6_MSFILTER = 0x4a IPV6_MULTICAST_HOPS = 0xa IPV6_MULTICAST_IF = 0x9 IPV6_MULTICAST_LOOP = 0xb IPV6_NEXTHOP = 0x30 + IPV6_ORIGDSTADDR = 0x48 IPV6_PATHMTU = 0x2c IPV6_PKTINFO = 0x2e IPV6_PORTRANGE = 0xe @@ -875,6 +893,7 @@ const ( IPV6_RECVFLOWID = 0x46 IPV6_RECVHOPLIMIT = 0x25 IPV6_RECVHOPOPTS = 0x27 + IPV6_RECVORIGDSTADDR = 0x48 IPV6_RECVPATHMTU = 0x2b IPV6_RECVPKTINFO = 0x24 IPV6_RECVRSSBUCKETID = 0x47 @@ -894,6 +913,7 @@ const ( IPV6_V6ONLY = 0x1b IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 + IPV6_VLAN_PCP = 0x4b IP_ADD_MEMBERSHIP = 0xc IP_ADD_SOURCE_MEMBERSHIP = 0x46 IP_BINDANY = 0x18 @@ -935,10 +955,8 @@ const ( IP_MAX_MEMBERSHIPS = 0xfff IP_MAX_SOCK_MUTE_FILTER = 0x80 IP_MAX_SOCK_SRC_FILTER = 0x80 - IP_MAX_SOURCE_FILTER = 0x400 IP_MF = 0x2000 IP_MINTTL = 0x42 - IP_MIN_MEMBERSHIPS = 0x1f IP_MSFILTER = 0x4a IP_MSS = 0x240 IP_MULTICAST_IF = 0x9 @@ -948,6 +966,7 @@ const ( IP_OFFMASK = 0x1fff IP_ONESBCAST = 0x17 IP_OPTIONS = 0x1 + IP_ORIGDSTADDR = 0x1b IP_PORTRANGE = 0x13 IP_PORTRANGE_DEFAULT = 0x0 IP_PORTRANGE_HIGH = 0x1 @@ -956,6 +975,7 @@ const ( IP_RECVFLOWID = 0x5d IP_RECVIF = 0x14 IP_RECVOPTS = 0x5 + IP_RECVORIGDSTADDR = 0x1b IP_RECVRETOPTS = 0x6 IP_RECVRSSBUCKETID = 0x5e IP_RECVTOS = 0x44 @@ -972,8 +992,12 @@ const ( IP_TOS = 0x3 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + IP_VLAN_PCP = 0x4b ISIG = 0x80 ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 IXANY = 0x800 IXOFF = 0x400 IXON = 0x200 @@ -983,7 +1007,6 @@ const ( KERN_VERSION = 0x4 LOCAL_CONNWAIT = 0x4 LOCAL_CREDS = 0x2 - LOCAL_CREDS_PERSISTENT = 0x3 LOCAL_PEERCRED = 0x1 LOCAL_VENDOR = 0x80000000 LOCK_EX = 0x2 @@ -1071,10 +1094,12 @@ const ( MNT_SUSPEND = 0x4 MNT_SYNCHRONOUS = 0x2 MNT_UNION = 0x20 + MNT_UNTRUSTED = 0x800000000 MNT_UPDATE = 0x10000 - MNT_UPDATEMASK = 0x2d8d0807e + MNT_UPDATEMASK = 0xad8d0807e MNT_USER = 0x8000 - MNT_VISFLAGMASK = 0x3fef0ffff + MNT_VERIFIED = 0x400000000 + MNT_VISFLAGMASK = 0xffef0ffff MNT_WAIT = 0x1 MSG_CMSG_CLOEXEC = 0x40000 MSG_COMPAT = 0x8000 @@ -1103,6 +1128,7 @@ const ( NFDBITS = 0x20 NOFLSH = 0x80000000 NOKERNINFO = 0x2000000 + NOTE_ABSTIME = 0x10 NOTE_ATTRIB = 0x8 NOTE_CHILD = 0x4 NOTE_CLOSE = 0x100 @@ -1159,6 +1185,8 @@ const ( O_NONBLOCK = 0x4 O_RDONLY = 0x0 O_RDWR = 0x2 + O_RESOLVE_BENEATH = 0x800000 + O_SEARCH = 0x40000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 @@ -1169,6 +1197,10 @@ const ( PARMRK = 0x8 PARODD = 0x2000 PENDIN = 0x20000000 + PIOD_READ_D = 0x1 + PIOD_READ_I = 0x3 + PIOD_WRITE_D = 0x2 + PIOD_WRITE_I = 0x4 PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1176,6 +1208,53 @@ const ( PROT_NONE = 0x0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PTRACE_DEFAULT = 0x1 + PTRACE_EXEC = 0x1 + PTRACE_FORK = 0x8 + PTRACE_LWP = 0x10 + PTRACE_SCE = 0x2 + PTRACE_SCX = 0x4 + PTRACE_SYSCALL = 0x6 + PTRACE_VFORK = 0x20 + PT_ATTACH = 0xa + PT_CLEARSTEP = 0x10 + PT_CONTINUE = 0x7 + PT_DETACH = 0xb + PT_FIRSTMACH = 0x40 + PT_FOLLOW_FORK = 0x17 + PT_GETDBREGS = 0x25 + PT_GETFPREGS = 0x23 + PT_GETLWPLIST = 0xf + PT_GETNUMLWPS = 0xe + PT_GETREGS = 0x21 + PT_GETVFPREGS = 0x40 + PT_GET_EVENT_MASK = 0x19 + PT_GET_SC_ARGS = 0x1b + PT_GET_SC_RET = 0x1c + PT_IO = 0xc + PT_KILL = 0x8 + PT_LWPINFO = 0xd + PT_LWP_EVENTS = 0x18 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_RESUME = 0x13 + PT_SETDBREGS = 0x26 + PT_SETFPREGS = 0x24 + PT_SETREGS = 0x22 + PT_SETSTEP = 0x11 + PT_SETVFPREGS = 0x41 + PT_SET_EVENT_MASK = 0x1a + PT_STEP = 0x9 + PT_SUSPEND = 0x12 + PT_SYSCALL = 0x16 + PT_TO_SCE = 0x14 + PT_TO_SCX = 0x15 + PT_TRACE_ME = 0x0 + PT_VM_ENTRY = 0x29 + PT_VM_TIMESTAMP = 0x28 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + P_ZONEID = 0xc RLIMIT_AS = 0xa RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1257,7 +1336,6 @@ const ( RTV_WEIGHT = 0x100 RT_ALL_FIBS = -0x1 RT_BLACKHOLE = 0x40 - RT_CACHING_CONTEXT = 0x1 RT_DEFAULT_FIB = 0x0 RT_HAS_GW = 0x80 RT_HAS_HEADER = 0x10 @@ -1267,15 +1345,17 @@ const ( RT_LLE_CACHE = 0x100 RT_MAY_LOOP = 0x8 RT_MAY_LOOP_BIT = 0x3 - RT_NORTREF = 0x2 RT_REJECT = 0x20 RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 RUSAGE_THREAD = 0x1 SCM_BINTIME = 0x4 SCM_CREDS = 0x3 + SCM_MONOTONIC = 0x6 + SCM_REALTIME = 0x5 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 + SCM_TIME_INFO = 0x7 SEEK_CUR = 0x1 SEEK_DATA = 0x3 SEEK_END = 0x2 @@ -1299,10 +1379,12 @@ const ( SIOCGHWADDR = 0xc020693e SIOCGI2C = 0xc020693d SIOCGIFADDR = 0xc0206921 + SIOCGIFALIAS = 0xc044692d SIOCGIFBRDADDR = 0xc0206923 SIOCGIFCAP = 0xc020691f SIOCGIFCONF = 0xc0086924 SIOCGIFDESCR = 0xc020692a + SIOCGIFDOWNREASON = 0xc058699a SIOCGIFDSTADDR = 0xc0206922 SIOCGIFFIB = 0xc020695c SIOCGIFFLAGS = 0xc0206911 @@ -1318,8 +1400,11 @@ const ( SIOCGIFPDSTADDR = 0xc0206948 SIOCGIFPHYS = 0xc0206935 SIOCGIFPSRCADDR = 0xc0206947 + SIOCGIFRSSHASH = 0xc0186997 + SIOCGIFRSSKEY = 0xc0946996 SIOCGIFSTATUS = 0xc331693b SIOCGIFXMEDIA = 0xc028698b + SIOCGLANPCP = 0xc0206998 SIOCGLOWAT = 0x40047303 SIOCGPGRP = 0x40047309 SIOCGPRIVATE_0 = 0xc0206950 @@ -1350,6 +1435,7 @@ const ( SIOCSIFPHYS = 0x80206936 SIOCSIFRVNET = 0xc020695b SIOCSIFVNET = 0xc020695a + SIOCSLANPCP = 0x80206999 SIOCSLOWAT = 0x80047302 SIOCSPGRP = 0x80047308 SIOCSTUNFIB = 0x8020695f @@ -1369,6 +1455,7 @@ const ( SO_BINTIME = 0x2000 SO_BROADCAST = 0x20 SO_DEBUG = 0x1 + SO_DOMAIN = 0x1019 SO_DONTROUTE = 0x10 SO_ERROR = 0x1007 SO_KEEPALIVE = 0x8 @@ -1377,6 +1464,7 @@ const ( SO_LISTENINCQLEN = 0x1013 SO_LISTENQLEN = 0x1012 SO_LISTENQLIMIT = 0x1011 + SO_MAX_PACING_RATE = 0x1018 SO_NOSIGPIPE = 0x800 SO_NO_DDP = 0x8000 SO_NO_OFFLOAD = 0x4000 @@ -1387,13 +1475,22 @@ const ( SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 SO_RCVTIMEO = 0x1006 + SO_RERROR = 0x20000 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 + SO_REUSEPORT_LB = 0x10000 SO_SETFIB = 0x1014 SO_SNDBUF = 0x1001 SO_SNDLOWAT = 0x1003 SO_SNDTIMEO = 0x1005 SO_TIMESTAMP = 0x400 + SO_TS_BINTIME = 0x1 + SO_TS_CLOCK = 0x1017 + SO_TS_CLOCK_MAX = 0x3 + SO_TS_DEFAULT = 0x0 + SO_TS_MONOTONIC = 0x3 + SO_TS_REALTIME = 0x2 + SO_TS_REALTIME_MICRO = 0x0 SO_TYPE = 0x1008 SO_USELOOPBACK = 0x40 SO_USER_COOKIE = 0x1015 @@ -1437,10 +1534,69 @@ const ( TCOFLUSH = 0x2 TCOOFF = 0x1 TCOON = 0x2 + TCPOPT_EOL = 0x0 + TCPOPT_FAST_OPEN = 0x22 + TCPOPT_MAXSEG = 0x2 + TCPOPT_NOP = 0x1 + TCPOPT_PAD = 0x0 + TCPOPT_SACK = 0x5 + TCPOPT_SACK_PERMITTED = 0x4 + TCPOPT_SIGNATURE = 0x13 + TCPOPT_TIMESTAMP = 0x8 + TCPOPT_WINDOW = 0x3 + TCP_BBR_ACK_COMP_ALG = 0x448 + TCP_BBR_ALGORITHM = 0x43b + TCP_BBR_DRAIN_INC_EXTRA = 0x43c + TCP_BBR_DRAIN_PG = 0x42e + TCP_BBR_EXTRA_GAIN = 0x449 + TCP_BBR_EXTRA_STATE = 0x453 + TCP_BBR_FLOOR_MIN_TSO = 0x454 + TCP_BBR_HDWR_PACE = 0x451 + TCP_BBR_HOLD_TARGET = 0x436 + TCP_BBR_IWINTSO = 0x42b + TCP_BBR_LOWGAIN_FD = 0x436 + TCP_BBR_LOWGAIN_HALF = 0x435 + TCP_BBR_LOWGAIN_THRESH = 0x434 + TCP_BBR_MAX_RTO = 0x439 + TCP_BBR_MIN_RTO = 0x438 + TCP_BBR_MIN_TOPACEOUT = 0x455 + TCP_BBR_ONE_RETRAN = 0x431 + TCP_BBR_PACE_CROSS = 0x442 + TCP_BBR_PACE_DEL_TAR = 0x43f + TCP_BBR_PACE_OH = 0x435 + TCP_BBR_PACE_PER_SEC = 0x43e + TCP_BBR_PACE_SEG_MAX = 0x440 + TCP_BBR_PACE_SEG_MIN = 0x441 + TCP_BBR_POLICER_DETECT = 0x457 + TCP_BBR_PROBE_RTT_GAIN = 0x44d + TCP_BBR_PROBE_RTT_INT = 0x430 + TCP_BBR_PROBE_RTT_LEN = 0x44e + TCP_BBR_RACK_RTT_USE = 0x44a + TCP_BBR_RECFORCE = 0x42c + TCP_BBR_REC_OVER_HPTS = 0x43a + TCP_BBR_RETRAN_WTSO = 0x44b + TCP_BBR_RWND_IS_APP = 0x42f + TCP_BBR_SEND_IWND_IN_TSO = 0x44f + TCP_BBR_STARTUP_EXIT_EPOCH = 0x43d + TCP_BBR_STARTUP_LOSS_EXIT = 0x432 + TCP_BBR_STARTUP_PG = 0x42d + TCP_BBR_TMR_PACE_OH = 0x448 + TCP_BBR_TSLIMITS = 0x434 + TCP_BBR_TSTMP_RAISES = 0x456 + TCP_BBR_UNLIMITED = 0x43b + TCP_BBR_USEDEL_RATE = 0x437 + TCP_BBR_USE_LOWGAIN = 0x433 + TCP_BBR_USE_RACK_CHEAT = 0x450 + TCP_BBR_UTTER_MAX_TSO = 0x452 TCP_CA_NAME_MAX = 0x10 TCP_CCALGOOPT = 0x41 TCP_CONGESTION = 0x40 + TCP_DATA_AFTER_CLOSE = 0x44c + TCP_DELACK = 0x48 TCP_FASTOPEN = 0x401 + TCP_FASTOPEN_MAX_COOKIE_LEN = 0x10 + TCP_FASTOPEN_MIN_COOKIE_LEN = 0x4 + TCP_FASTOPEN_PSK_LEN = 0x10 TCP_FUNCTION_BLK = 0x2000 TCP_FUNCTION_NAME_LEN_MAX = 0x20 TCP_INFO = 0x20 @@ -1448,6 +1604,12 @@ const ( TCP_KEEPIDLE = 0x100 TCP_KEEPINIT = 0x80 TCP_KEEPINTVL = 0x200 + TCP_LOG = 0x22 + TCP_LOGBUF = 0x23 + TCP_LOGDUMP = 0x25 + TCP_LOGDUMPID = 0x26 + TCP_LOGID = 0x24 + TCP_LOG_ID_LEN = 0x40 TCP_MAXBURST = 0x4 TCP_MAXHLEN = 0x3c TCP_MAXOLEN = 0x28 @@ -1463,8 +1625,30 @@ const ( TCP_NOPUSH = 0x4 TCP_PCAP_IN = 0x1000 TCP_PCAP_OUT = 0x800 + TCP_RACK_EARLY_RECOV = 0x423 + TCP_RACK_EARLY_SEG = 0x424 + TCP_RACK_GP_INCREASE = 0x446 + TCP_RACK_IDLE_REDUCE_HIGH = 0x444 + TCP_RACK_MIN_PACE = 0x445 + TCP_RACK_MIN_PACE_SEG = 0x446 + TCP_RACK_MIN_TO = 0x422 + TCP_RACK_PACE_ALWAYS = 0x41f + TCP_RACK_PACE_MAX_SEG = 0x41e + TCP_RACK_PACE_REDUCE = 0x41d + TCP_RACK_PKT_DELAY = 0x428 + TCP_RACK_PROP = 0x41b + TCP_RACK_PROP_RATE = 0x420 + TCP_RACK_PRR_SENDALOT = 0x421 + TCP_RACK_REORD_FADE = 0x426 + TCP_RACK_REORD_THRESH = 0x425 + TCP_RACK_TLP_INC_VAR = 0x429 + TCP_RACK_TLP_REDUCE = 0x41c + TCP_RACK_TLP_THRESH = 0x427 + TCP_RACK_TLP_USE = 0x447 TCP_VENDOR = 0x80000000 TCSAFLUSH = 0x2 + TIMER_ABSTIME = 0x1 + TIMER_RELTIME = 0x0 TIOCCBRK = 0x2000747a TIOCCDTR = 0x20007478 TIOCCONS = 0x80047462 @@ -1528,6 +1712,8 @@ const ( TIOCTIMESTAMP = 0x40107459 TIOCUCNTL = 0x80047466 TOSTOP = 0x400000 + UTIME_NOW = -0x1 + UTIME_OMIT = -0x2 VDISCARD = 0xf VDSUSP = 0xb VEOF = 0x0 @@ -1592,12 +1778,13 @@ const ( EIDRM = syscall.Errno(0x52) EILSEQ = syscall.Errno(0x56) EINPROGRESS = syscall.Errno(0x24) + EINTEGRITY = syscall.Errno(0x61) EINTR = syscall.Errno(0x4) EINVAL = syscall.Errno(0x16) EIO = syscall.Errno(0x5) EISCONN = syscall.Errno(0x38) EISDIR = syscall.Errno(0x15) - ELAST = syscall.Errno(0x60) + ELAST = syscall.Errno(0x61) ELOOP = syscall.Errno(0x3e) EMFILE = syscall.Errno(0x18) EMLINK = syscall.Errno(0x1f) @@ -1740,7 +1927,7 @@ var errorList = [...]struct { {32, "EPIPE", "broken pipe"}, {33, "EDOM", "numerical argument out of domain"}, {34, "ERANGE", "result too large"}, - {35, "EAGAIN", "resource temporarily unavailable"}, + {35, "EWOULDBLOCK", "resource temporarily unavailable"}, {36, "EINPROGRESS", "operation now in progress"}, {37, "EALREADY", "operation already in progress"}, {38, "ENOTSOCK", "socket operation on non-socket"}, @@ -1802,6 +1989,7 @@ var errorList = [...]struct { {94, "ECAPMODE", "not permitted in capability mode"}, {95, "ENOTRECOVERABLE", "state not recoverable"}, {96, "EOWNERDEAD", "previous owner died"}, + {97, "EINTEGRITY", "integrity check failed"}, } // Signal table diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go index 4c837711..c557ac2d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go @@ -151,6 +151,7 @@ const ( BIOCSETF = 0x80104267 BIOCSETFNR = 0x80104282 BIOCSETIF = 0x8020426c + BIOCSETVLANPCP = 0x80044285 BIOCSETWF = 0x8010427b BIOCSETZBUF = 0x80184281 BIOCSHDRCMPLT = 0x80044275 @@ -447,7 +448,7 @@ const ( DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 DLT_INFINIBAND = 0xf7 DLT_IPFILTER = 0x74 - DLT_IPMB = 0xc7 + DLT_IPMB_KONTRON = 0xc7 DLT_IPMB_LINUX = 0xd1 DLT_IPMI_HPM_2 = 0x104 DLT_IPNET = 0xe2 @@ -487,10 +488,11 @@ const ( DLT_LINUX_LAPD = 0xb1 DLT_LINUX_PPP_WITHDIRECTION = 0xa6 DLT_LINUX_SLL = 0x71 + DLT_LINUX_SLL2 = 0x114 DLT_LOOP = 0x6c DLT_LORATAP = 0x10e DLT_LTALK = 0x72 - DLT_MATCHING_MAX = 0x113 + DLT_MATCHING_MAX = 0x114 DLT_MATCHING_MIN = 0x68 DLT_MFR = 0xb6 DLT_MOST = 0xd3 @@ -734,6 +736,7 @@ const ( IPPROTO_CMTP = 0x26 IPPROTO_CPHB = 0x49 IPPROTO_CPNX = 0x48 + IPPROTO_DCCP = 0x21 IPPROTO_DDP = 0x25 IPPROTO_DGP = 0x56 IPPROTO_DIVERT = 0x102 @@ -814,7 +817,6 @@ const ( IPPROTO_SCTP = 0x84 IPPROTO_SDRP = 0x2a IPPROTO_SEND = 0x103 - IPPROTO_SEP = 0x21 IPPROTO_SHIM6 = 0x8c IPPROTO_SKIP = 0x39 IPPROTO_SPACER = 0x7fff @@ -911,6 +913,7 @@ const ( IPV6_V6ONLY = 0x1b IPV6_VERSION = 0x60 IPV6_VERSION_MASK = 0xf0 + IPV6_VLAN_PCP = 0x4b IP_ADD_MEMBERSHIP = 0xc IP_ADD_SOURCE_MEMBERSHIP = 0x46 IP_BINDANY = 0x18 @@ -989,8 +992,12 @@ const ( IP_TOS = 0x3 IP_TTL = 0x4 IP_UNBLOCK_SOURCE = 0x49 + IP_VLAN_PCP = 0x4b ISIG = 0x80 ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 IXANY = 0x800 IXOFF = 0x400 IXON = 0x200 @@ -1000,7 +1007,6 @@ const ( KERN_VERSION = 0x4 LOCAL_CONNWAIT = 0x4 LOCAL_CREDS = 0x2 - LOCAL_CREDS_PERSISTENT = 0x3 LOCAL_PEERCRED = 0x1 LOCAL_VENDOR = 0x80000000 LOCK_EX = 0x2 @@ -1180,6 +1186,8 @@ const ( O_NONBLOCK = 0x4 O_RDONLY = 0x0 O_RDWR = 0x2 + O_RESOLVE_BENEATH = 0x800000 + O_SEARCH = 0x40000 O_SHLOCK = 0x10 O_SYNC = 0x80 O_TRUNC = 0x400 @@ -1190,6 +1198,10 @@ const ( PARMRK = 0x8 PARODD = 0x2000 PENDIN = 0x20000000 + PIOD_READ_D = 0x1 + PIOD_READ_I = 0x3 + PIOD_WRITE_D = 0x2 + PIOD_WRITE_I = 0x4 PRIO_PGRP = 0x1 PRIO_PROCESS = 0x0 PRIO_USER = 0x2 @@ -1197,6 +1209,51 @@ const ( PROT_NONE = 0x0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PTRACE_DEFAULT = 0x1 + PTRACE_EXEC = 0x1 + PTRACE_FORK = 0x8 + PTRACE_LWP = 0x10 + PTRACE_SCE = 0x2 + PTRACE_SCX = 0x4 + PTRACE_SYSCALL = 0x6 + PTRACE_VFORK = 0x20 + PT_ATTACH = 0xa + PT_CLEARSTEP = 0x10 + PT_CONTINUE = 0x7 + PT_DETACH = 0xb + PT_FIRSTMACH = 0x40 + PT_FOLLOW_FORK = 0x17 + PT_GETDBREGS = 0x25 + PT_GETFPREGS = 0x23 + PT_GETLWPLIST = 0xf + PT_GETNUMLWPS = 0xe + PT_GETREGS = 0x21 + PT_GET_EVENT_MASK = 0x19 + PT_GET_SC_ARGS = 0x1b + PT_GET_SC_RET = 0x1c + PT_IO = 0xc + PT_KILL = 0x8 + PT_LWPINFO = 0xd + PT_LWP_EVENTS = 0x18 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_RESUME = 0x13 + PT_SETDBREGS = 0x26 + PT_SETFPREGS = 0x24 + PT_SETREGS = 0x22 + PT_SETSTEP = 0x11 + PT_SET_EVENT_MASK = 0x1a + PT_STEP = 0x9 + PT_SUSPEND = 0x12 + PT_SYSCALL = 0x16 + PT_TO_SCE = 0x14 + PT_TO_SCX = 0x15 + PT_TRACE_ME = 0x0 + PT_VM_ENTRY = 0x29 + PT_VM_TIMESTAMP = 0x28 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + P_ZONEID = 0xc RLIMIT_AS = 0xa RLIMIT_CORE = 0x4 RLIMIT_CPU = 0x0 @@ -1321,10 +1378,12 @@ const ( SIOCGHWADDR = 0xc020693e SIOCGI2C = 0xc020693d SIOCGIFADDR = 0xc0206921 + SIOCGIFALIAS = 0xc044692d SIOCGIFBRDADDR = 0xc0206923 SIOCGIFCAP = 0xc020691f SIOCGIFCONF = 0xc0106924 SIOCGIFDESCR = 0xc020692a + SIOCGIFDOWNREASON = 0xc058699a SIOCGIFDSTADDR = 0xc0206922 SIOCGIFFIB = 0xc020695c SIOCGIFFLAGS = 0xc0206911 @@ -1415,6 +1474,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVLOWAT = 0x1004 SO_RCVTIMEO = 0x1006 + SO_RERROR = 0x20000 SO_REUSEADDR = 0x4 SO_REUSEPORT = 0x200 SO_REUSEPORT_LB = 0x10000 @@ -1473,22 +1533,40 @@ const ( TCOFLUSH = 0x2 TCOOFF = 0x1 TCOON = 0x2 + TCPOPT_EOL = 0x0 + TCPOPT_FAST_OPEN = 0x22 + TCPOPT_MAXSEG = 0x2 + TCPOPT_NOP = 0x1 + TCPOPT_PAD = 0x0 + TCPOPT_SACK = 0x5 + TCPOPT_SACK_PERMITTED = 0x4 + TCPOPT_SIGNATURE = 0x13 + TCPOPT_TIMESTAMP = 0x8 + TCPOPT_WINDOW = 0x3 TCP_BBR_ACK_COMP_ALG = 0x448 + TCP_BBR_ALGORITHM = 0x43b TCP_BBR_DRAIN_INC_EXTRA = 0x43c TCP_BBR_DRAIN_PG = 0x42e TCP_BBR_EXTRA_GAIN = 0x449 + TCP_BBR_EXTRA_STATE = 0x453 + TCP_BBR_FLOOR_MIN_TSO = 0x454 + TCP_BBR_HDWR_PACE = 0x451 + TCP_BBR_HOLD_TARGET = 0x436 TCP_BBR_IWINTSO = 0x42b TCP_BBR_LOWGAIN_FD = 0x436 TCP_BBR_LOWGAIN_HALF = 0x435 TCP_BBR_LOWGAIN_THRESH = 0x434 TCP_BBR_MAX_RTO = 0x439 TCP_BBR_MIN_RTO = 0x438 + TCP_BBR_MIN_TOPACEOUT = 0x455 TCP_BBR_ONE_RETRAN = 0x431 TCP_BBR_PACE_CROSS = 0x442 TCP_BBR_PACE_DEL_TAR = 0x43f + TCP_BBR_PACE_OH = 0x435 TCP_BBR_PACE_PER_SEC = 0x43e TCP_BBR_PACE_SEG_MAX = 0x440 TCP_BBR_PACE_SEG_MIN = 0x441 + TCP_BBR_POLICER_DETECT = 0x457 TCP_BBR_PROBE_RTT_GAIN = 0x44d TCP_BBR_PROBE_RTT_INT = 0x430 TCP_BBR_PROBE_RTT_LEN = 0x44e @@ -1497,12 +1575,18 @@ const ( TCP_BBR_REC_OVER_HPTS = 0x43a TCP_BBR_RETRAN_WTSO = 0x44b TCP_BBR_RWND_IS_APP = 0x42f + TCP_BBR_SEND_IWND_IN_TSO = 0x44f TCP_BBR_STARTUP_EXIT_EPOCH = 0x43d TCP_BBR_STARTUP_LOSS_EXIT = 0x432 TCP_BBR_STARTUP_PG = 0x42d + TCP_BBR_TMR_PACE_OH = 0x448 + TCP_BBR_TSLIMITS = 0x434 + TCP_BBR_TSTMP_RAISES = 0x456 TCP_BBR_UNLIMITED = 0x43b TCP_BBR_USEDEL_RATE = 0x437 TCP_BBR_USE_LOWGAIN = 0x433 + TCP_BBR_USE_RACK_CHEAT = 0x450 + TCP_BBR_UTTER_MAX_TSO = 0x452 TCP_CA_NAME_MAX = 0x10 TCP_CCALGOOPT = 0x41 TCP_CONGESTION = 0x40 @@ -1542,6 +1626,7 @@ const ( TCP_PCAP_OUT = 0x800 TCP_RACK_EARLY_RECOV = 0x423 TCP_RACK_EARLY_SEG = 0x424 + TCP_RACK_GP_INCREASE = 0x446 TCP_RACK_IDLE_REDUCE_HIGH = 0x444 TCP_RACK_MIN_PACE = 0x445 TCP_RACK_MIN_PACE_SEG = 0x446 @@ -1555,7 +1640,6 @@ const ( TCP_RACK_PRR_SENDALOT = 0x421 TCP_RACK_REORD_FADE = 0x426 TCP_RACK_REORD_THRESH = 0x425 - TCP_RACK_SESS_CWV = 0x42a TCP_RACK_TLP_INC_VAR = 0x429 TCP_RACK_TLP_REDUCE = 0x41c TCP_RACK_TLP_THRESH = 0x427 @@ -1694,12 +1778,13 @@ const ( EIDRM = syscall.Errno(0x52) EILSEQ = syscall.Errno(0x56) EINPROGRESS = syscall.Errno(0x24) + EINTEGRITY = syscall.Errno(0x61) EINTR = syscall.Errno(0x4) EINVAL = syscall.Errno(0x16) EIO = syscall.Errno(0x5) EISCONN = syscall.Errno(0x38) EISDIR = syscall.Errno(0x15) - ELAST = syscall.Errno(0x60) + ELAST = syscall.Errno(0x61) ELOOP = syscall.Errno(0x3e) EMFILE = syscall.Errno(0x18) EMLINK = syscall.Errno(0x1f) @@ -1842,7 +1927,7 @@ var errorList = [...]struct { {32, "EPIPE", "broken pipe"}, {33, "EDOM", "numerical argument out of domain"}, {34, "ERANGE", "result too large"}, - {35, "EAGAIN", "resource temporarily unavailable"}, + {35, "EWOULDBLOCK", "resource temporarily unavailable"}, {36, "EINPROGRESS", "operation now in progress"}, {37, "EALREADY", "operation already in progress"}, {38, "ENOTSOCK", "socket operation on non-socket"}, @@ -1904,6 +1989,7 @@ var errorList = [...]struct { {94, "ECAPMODE", "not permitted in capability mode"}, {95, "ENOTRECOVERABLE", "state not recoverable"}, {96, "EOWNERDEAD", "previous owner died"}, + {97, "EINTEGRITY", "integrity check failed"}, } // Signal table diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go new file mode 100644 index 00000000..341b4d96 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go @@ -0,0 +1,2148 @@ +// mkerrors.sh -m64 +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build riscv64 && freebsd +// +build riscv64,freebsd + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs -- -m64 _const.go + +package unix + +import "syscall" + +const ( + AF_APPLETALK = 0x10 + AF_ARP = 0x23 + AF_ATM = 0x1e + AF_BLUETOOTH = 0x24 + AF_CCITT = 0xa + AF_CHAOS = 0x5 + AF_CNT = 0x15 + AF_COIP = 0x14 + AF_DATAKIT = 0x9 + AF_DECnet = 0xc + AF_DLI = 0xd + AF_E164 = 0x1a + AF_ECMA = 0x8 + AF_HYLINK = 0xf + AF_HYPERV = 0x2b + AF_IEEE80211 = 0x25 + AF_IMPLINK = 0x3 + AF_INET = 0x2 + AF_INET6 = 0x1c + AF_INET6_SDP = 0x2a + AF_INET_SDP = 0x28 + AF_IPX = 0x17 + AF_ISDN = 0x1a + AF_ISO = 0x7 + AF_LAT = 0xe + AF_LINK = 0x12 + AF_LOCAL = 0x1 + AF_MAX = 0x2b + AF_NATM = 0x1d + AF_NETBIOS = 0x6 + AF_NETGRAPH = 0x20 + AF_OSI = 0x7 + AF_PUP = 0x4 + AF_ROUTE = 0x11 + AF_SCLUSTER = 0x22 + AF_SIP = 0x18 + AF_SLOW = 0x21 + AF_SNA = 0xb + AF_UNIX = 0x1 + AF_UNSPEC = 0x0 + AF_VENDOR00 = 0x27 + AF_VENDOR01 = 0x29 + AF_VENDOR03 = 0x2d + AF_VENDOR04 = 0x2f + AF_VENDOR05 = 0x31 + AF_VENDOR06 = 0x33 + AF_VENDOR07 = 0x35 + AF_VENDOR08 = 0x37 + AF_VENDOR09 = 0x39 + AF_VENDOR10 = 0x3b + AF_VENDOR11 = 0x3d + AF_VENDOR12 = 0x3f + AF_VENDOR13 = 0x41 + AF_VENDOR14 = 0x43 + AF_VENDOR15 = 0x45 + AF_VENDOR16 = 0x47 + AF_VENDOR17 = 0x49 + AF_VENDOR18 = 0x4b + AF_VENDOR19 = 0x4d + AF_VENDOR20 = 0x4f + AF_VENDOR21 = 0x51 + AF_VENDOR22 = 0x53 + AF_VENDOR23 = 0x55 + AF_VENDOR24 = 0x57 + AF_VENDOR25 = 0x59 + AF_VENDOR26 = 0x5b + AF_VENDOR27 = 0x5d + AF_VENDOR28 = 0x5f + AF_VENDOR29 = 0x61 + AF_VENDOR30 = 0x63 + AF_VENDOR31 = 0x65 + AF_VENDOR32 = 0x67 + AF_VENDOR33 = 0x69 + AF_VENDOR34 = 0x6b + AF_VENDOR35 = 0x6d + AF_VENDOR36 = 0x6f + AF_VENDOR37 = 0x71 + AF_VENDOR38 = 0x73 + AF_VENDOR39 = 0x75 + AF_VENDOR40 = 0x77 + AF_VENDOR41 = 0x79 + AF_VENDOR42 = 0x7b + AF_VENDOR43 = 0x7d + AF_VENDOR44 = 0x7f + AF_VENDOR45 = 0x81 + AF_VENDOR46 = 0x83 + AF_VENDOR47 = 0x85 + ALTWERASE = 0x200 + B0 = 0x0 + B1000000 = 0xf4240 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1500000 = 0x16e360 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B2000000 = 0x1e8480 + B230400 = 0x38400 + B2400 = 0x960 + B2500000 = 0x2625a0 + B28800 = 0x7080 + B300 = 0x12c + B3000000 = 0x2dc6c0 + B3500000 = 0x3567e0 + B38400 = 0x9600 + B4000000 = 0x3d0900 + B460800 = 0x70800 + B4800 = 0x12c0 + B50 = 0x32 + B500000 = 0x7a120 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B921600 = 0xe1000 + B9600 = 0x2580 + BIOCFEEDBACK = 0x8004427c + BIOCFLUSH = 0x20004268 + BIOCGBLEN = 0x40044266 + BIOCGDIRECTION = 0x40044276 + BIOCGDLT = 0x4004426a + BIOCGDLTLIST = 0xc0104279 + BIOCGETBUFMODE = 0x4004427d + BIOCGETIF = 0x4020426b + BIOCGETZMAX = 0x4008427f + BIOCGHDRCMPLT = 0x40044274 + BIOCGRSIG = 0x40044272 + BIOCGRTIMEOUT = 0x4010426e + BIOCGSEESENT = 0x40044276 + BIOCGSTATS = 0x4008426f + BIOCGTSTAMP = 0x40044283 + BIOCIMMEDIATE = 0x80044270 + BIOCLOCK = 0x2000427a + BIOCPROMISC = 0x20004269 + BIOCROTZBUF = 0x40184280 + BIOCSBLEN = 0xc0044266 + BIOCSDIRECTION = 0x80044277 + BIOCSDLT = 0x80044278 + BIOCSETBUFMODE = 0x8004427e + BIOCSETF = 0x80104267 + BIOCSETFNR = 0x80104282 + BIOCSETIF = 0x8020426c + BIOCSETVLANPCP = 0x80044285 + BIOCSETWF = 0x8010427b + BIOCSETZBUF = 0x80184281 + BIOCSHDRCMPLT = 0x80044275 + BIOCSRSIG = 0x80044273 + BIOCSRTIMEOUT = 0x8010426d + BIOCSSEESENT = 0x80044277 + BIOCSTSTAMP = 0x80044284 + BIOCVERSION = 0x40044271 + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0x0 + BPF_ALIGNMENT = 0x8 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_BUFMODE_BUFFER = 0x1 + BPF_BUFMODE_ZBUF = 0x2 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0x0 + BPF_IND = 0x40 + BPF_JA = 0x0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0x0 + BPF_LD = 0x0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXBUFSIZE = 0x80000 + BPF_MAXINSNS = 0x200 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINBUFSIZE = 0x20 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MOD = 0x90 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RELEASE = 0x30bb6 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0x0 + BPF_TXA = 0x80 + BPF_T_BINTIME = 0x2 + BPF_T_BINTIME_FAST = 0x102 + BPF_T_BINTIME_MONOTONIC = 0x202 + BPF_T_BINTIME_MONOTONIC_FAST = 0x302 + BPF_T_FAST = 0x100 + BPF_T_FLAG_MASK = 0x300 + BPF_T_FORMAT_MASK = 0x3 + BPF_T_MICROTIME = 0x0 + BPF_T_MICROTIME_FAST = 0x100 + BPF_T_MICROTIME_MONOTONIC = 0x200 + BPF_T_MICROTIME_MONOTONIC_FAST = 0x300 + BPF_T_MONOTONIC = 0x200 + BPF_T_MONOTONIC_FAST = 0x300 + BPF_T_NANOTIME = 0x1 + BPF_T_NANOTIME_FAST = 0x101 + BPF_T_NANOTIME_MONOTONIC = 0x201 + BPF_T_NANOTIME_MONOTONIC_FAST = 0x301 + BPF_T_NONE = 0x3 + BPF_T_NORMAL = 0x0 + BPF_W = 0x0 + BPF_X = 0x8 + BPF_XOR = 0xa0 + BRKINT = 0x2 + CAP_ACCEPT = 0x200000020000000 + CAP_ACL_CHECK = 0x400000000010000 + CAP_ACL_DELETE = 0x400000000020000 + CAP_ACL_GET = 0x400000000040000 + CAP_ACL_SET = 0x400000000080000 + CAP_ALL0 = 0x20007ffffffffff + CAP_ALL1 = 0x4000000001fffff + CAP_BIND = 0x200000040000000 + CAP_BINDAT = 0x200008000000400 + CAP_CHFLAGSAT = 0x200000000001400 + CAP_CONNECT = 0x200000080000000 + CAP_CONNECTAT = 0x200010000000400 + CAP_CREATE = 0x200000000000040 + CAP_EVENT = 0x400000000000020 + CAP_EXTATTR_DELETE = 0x400000000001000 + CAP_EXTATTR_GET = 0x400000000002000 + CAP_EXTATTR_LIST = 0x400000000004000 + CAP_EXTATTR_SET = 0x400000000008000 + CAP_FCHDIR = 0x200000000000800 + CAP_FCHFLAGS = 0x200000000001000 + CAP_FCHMOD = 0x200000000002000 + CAP_FCHMODAT = 0x200000000002400 + CAP_FCHOWN = 0x200000000004000 + CAP_FCHOWNAT = 0x200000000004400 + CAP_FCNTL = 0x200000000008000 + CAP_FCNTL_ALL = 0x78 + CAP_FCNTL_GETFL = 0x8 + CAP_FCNTL_GETOWN = 0x20 + CAP_FCNTL_SETFL = 0x10 + CAP_FCNTL_SETOWN = 0x40 + CAP_FEXECVE = 0x200000000000080 + CAP_FLOCK = 0x200000000010000 + CAP_FPATHCONF = 0x200000000020000 + CAP_FSCK = 0x200000000040000 + CAP_FSTAT = 0x200000000080000 + CAP_FSTATAT = 0x200000000080400 + CAP_FSTATFS = 0x200000000100000 + CAP_FSYNC = 0x200000000000100 + CAP_FTRUNCATE = 0x200000000000200 + CAP_FUTIMES = 0x200000000200000 + CAP_FUTIMESAT = 0x200000000200400 + CAP_GETPEERNAME = 0x200000100000000 + CAP_GETSOCKNAME = 0x200000200000000 + CAP_GETSOCKOPT = 0x200000400000000 + CAP_IOCTL = 0x400000000000080 + CAP_IOCTLS_ALL = 0x7fffffffffffffff + CAP_KQUEUE = 0x400000000100040 + CAP_KQUEUE_CHANGE = 0x400000000100000 + CAP_KQUEUE_EVENT = 0x400000000000040 + CAP_LINKAT_SOURCE = 0x200020000000400 + CAP_LINKAT_TARGET = 0x200000000400400 + CAP_LISTEN = 0x200000800000000 + CAP_LOOKUP = 0x200000000000400 + CAP_MAC_GET = 0x400000000000001 + CAP_MAC_SET = 0x400000000000002 + CAP_MKDIRAT = 0x200000000800400 + CAP_MKFIFOAT = 0x200000001000400 + CAP_MKNODAT = 0x200000002000400 + CAP_MMAP = 0x200000000000010 + CAP_MMAP_R = 0x20000000000001d + CAP_MMAP_RW = 0x20000000000001f + CAP_MMAP_RWX = 0x20000000000003f + CAP_MMAP_RX = 0x20000000000003d + CAP_MMAP_W = 0x20000000000001e + CAP_MMAP_WX = 0x20000000000003e + CAP_MMAP_X = 0x20000000000003c + CAP_PDGETPID = 0x400000000000200 + CAP_PDKILL = 0x400000000000800 + CAP_PDWAIT = 0x400000000000400 + CAP_PEELOFF = 0x200001000000000 + CAP_POLL_EVENT = 0x400000000000020 + CAP_PREAD = 0x20000000000000d + CAP_PWRITE = 0x20000000000000e + CAP_READ = 0x200000000000001 + CAP_RECV = 0x200000000000001 + CAP_RENAMEAT_SOURCE = 0x200000004000400 + CAP_RENAMEAT_TARGET = 0x200040000000400 + CAP_RIGHTS_VERSION = 0x0 + CAP_RIGHTS_VERSION_00 = 0x0 + CAP_SEEK = 0x20000000000000c + CAP_SEEK_TELL = 0x200000000000004 + CAP_SEM_GETVALUE = 0x400000000000004 + CAP_SEM_POST = 0x400000000000008 + CAP_SEM_WAIT = 0x400000000000010 + CAP_SEND = 0x200000000000002 + CAP_SETSOCKOPT = 0x200002000000000 + CAP_SHUTDOWN = 0x200004000000000 + CAP_SOCK_CLIENT = 0x200007780000003 + CAP_SOCK_SERVER = 0x200007f60000003 + CAP_SYMLINKAT = 0x200000008000400 + CAP_TTYHOOK = 0x400000000000100 + CAP_UNLINKAT = 0x200000010000400 + CAP_UNUSED0_44 = 0x200080000000000 + CAP_UNUSED0_57 = 0x300000000000000 + CAP_UNUSED1_22 = 0x400000000200000 + CAP_UNUSED1_57 = 0x500000000000000 + CAP_WRITE = 0x200000000000002 + CFLUSH = 0xf + CLOCAL = 0x8000 + CLOCK_BOOTTIME = 0x5 + CLOCK_MONOTONIC = 0x4 + CLOCK_MONOTONIC_COARSE = 0xc + CLOCK_MONOTONIC_FAST = 0xc + CLOCK_MONOTONIC_PRECISE = 0xb + CLOCK_PROCESS_CPUTIME_ID = 0xf + CLOCK_PROF = 0x2 + CLOCK_REALTIME = 0x0 + CLOCK_REALTIME_COARSE = 0xa + CLOCK_REALTIME_FAST = 0xa + CLOCK_REALTIME_PRECISE = 0x9 + CLOCK_SECOND = 0xd + CLOCK_THREAD_CPUTIME_ID = 0xe + CLOCK_UPTIME = 0x5 + CLOCK_UPTIME_FAST = 0x8 + CLOCK_UPTIME_PRECISE = 0x7 + CLOCK_VIRTUAL = 0x1 + CPUSTATES = 0x5 + CP_IDLE = 0x4 + CP_INTR = 0x3 + CP_NICE = 0x1 + CP_SYS = 0x2 + CP_USER = 0x0 + CREAD = 0x800 + CRTSCTS = 0x30000 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a + CTL_HW = 0x6 + CTL_KERN = 0x1 + CTL_MAXNAME = 0x18 + CTL_NET = 0x4 + DIOCGATTR = 0xc148648e + DIOCGDELETE = 0x80106488 + DIOCGFLUSH = 0x20006487 + DIOCGFWHEADS = 0x40046483 + DIOCGFWSECTORS = 0x40046482 + DIOCGIDENT = 0x41006489 + DIOCGKERNELDUMP = 0xc0986492 + DIOCGMEDIASIZE = 0x40086481 + DIOCGPHYSPATH = 0x4400648d + DIOCGPROVIDERNAME = 0x4400648a + DIOCGSECTORSIZE = 0x40046480 + DIOCGSTRIPEOFFSET = 0x4008648c + DIOCGSTRIPESIZE = 0x4008648b + DIOCSKERNELDUMP = 0x80986491 + DIOCSKERNELDUMP_FREEBSD11 = 0x80046485 + DIOCSKERNELDUMP_FREEBSD12 = 0x80506490 + DIOCZONECMD = 0xc080648f + DLT_A429 = 0xb8 + DLT_A653_ICM = 0xb9 + DLT_AIRONET_HEADER = 0x78 + DLT_AOS = 0xde + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a + DLT_ARCNET = 0x7 + DLT_ARCNET_LINUX = 0x81 + DLT_ATM_CLIP = 0x13 + DLT_ATM_RFC1483 = 0xb + DLT_AURORA = 0x7e + DLT_AX25 = 0x3 + DLT_AX25_KISS = 0xca + DLT_BACNET_MS_TP = 0xa5 + DLT_BLUETOOTH_BREDR_BB = 0xff + DLT_BLUETOOTH_HCI_H4 = 0xbb + DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9 + DLT_BLUETOOTH_LE_LL = 0xfb + DLT_BLUETOOTH_LE_LL_WITH_PHDR = 0x100 + DLT_BLUETOOTH_LINUX_MONITOR = 0xfe + DLT_CAN20B = 0xbe + DLT_CAN_SOCKETCAN = 0xe3 + DLT_CHAOS = 0x5 + DLT_CHDLC = 0x68 + DLT_CISCO_IOS = 0x76 + DLT_CLASS_NETBSD_RAWAF = 0x2240000 + DLT_C_HDLC = 0x68 + DLT_C_HDLC_WITH_DIR = 0xcd + DLT_DBUS = 0xe7 + DLT_DECT = 0xdd + DLT_DISPLAYPORT_AUX = 0x113 + DLT_DOCSIS = 0x8f + DLT_DOCSIS31_XRA31 = 0x111 + DLT_DVB_CI = 0xeb + DLT_ECONET = 0x73 + DLT_EN10MB = 0x1 + DLT_EN3MB = 0x2 + DLT_ENC = 0x6d + DLT_EPON = 0x103 + DLT_ERF = 0xc5 + DLT_ERF_ETH = 0xaf + DLT_ERF_POS = 0xb0 + DLT_ETHERNET_MPACKET = 0x112 + DLT_FC_2 = 0xe0 + DLT_FC_2_WITH_FRAME_DELIMS = 0xe1 + DLT_FDDI = 0xa + DLT_FLEXRAY = 0xd2 + DLT_FRELAY = 0x6b + DLT_FRELAY_WITH_DIR = 0xce + DLT_GCOM_SERIAL = 0xad + DLT_GCOM_T1E1 = 0xac + DLT_GPF_F = 0xab + DLT_GPF_T = 0xaa + DLT_GPRS_LLC = 0xa9 + DLT_GSMTAP_ABIS = 0xda + DLT_GSMTAP_UM = 0xd9 + DLT_IBM_SN = 0x92 + DLT_IBM_SP = 0x91 + DLT_IEEE802 = 0x6 + DLT_IEEE802_11 = 0x69 + DLT_IEEE802_11_RADIO = 0x7f + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_IEEE802_15_4 = 0xc3 + DLT_IEEE802_15_4_LINUX = 0xbf + DLT_IEEE802_15_4_NOFCS = 0xe6 + DLT_IEEE802_15_4_NONASK_PHY = 0xd7 + DLT_IEEE802_16_MAC_CPS = 0xbc + DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1 + DLT_INFINIBAND = 0xf7 + DLT_IPFILTER = 0x74 + DLT_IPMB_KONTRON = 0xc7 + DLT_IPMB_LINUX = 0xd1 + DLT_IPMI_HPM_2 = 0x104 + DLT_IPNET = 0xe2 + DLT_IPOIB = 0xf2 + DLT_IPV4 = 0xe4 + DLT_IPV6 = 0xe5 + DLT_IP_OVER_FC = 0x7a + DLT_ISO_14443 = 0x108 + DLT_JUNIPER_ATM1 = 0x89 + DLT_JUNIPER_ATM2 = 0x87 + DLT_JUNIPER_ATM_CEMIC = 0xee + DLT_JUNIPER_CHDLC = 0xb5 + DLT_JUNIPER_ES = 0x84 + DLT_JUNIPER_ETHER = 0xb2 + DLT_JUNIPER_FIBRECHANNEL = 0xea + DLT_JUNIPER_FRELAY = 0xb4 + DLT_JUNIPER_GGSN = 0x85 + DLT_JUNIPER_ISM = 0xc2 + DLT_JUNIPER_MFR = 0x86 + DLT_JUNIPER_MLFR = 0x83 + DLT_JUNIPER_MLPPP = 0x82 + DLT_JUNIPER_MONITOR = 0xa4 + DLT_JUNIPER_PIC_PEER = 0xae + DLT_JUNIPER_PPP = 0xb3 + DLT_JUNIPER_PPPOE = 0xa7 + DLT_JUNIPER_PPPOE_ATM = 0xa8 + DLT_JUNIPER_SERVICES = 0x88 + DLT_JUNIPER_SRX_E2E = 0xe9 + DLT_JUNIPER_ST = 0xc8 + DLT_JUNIPER_VP = 0xb7 + DLT_JUNIPER_VS = 0xe8 + DLT_LAPB_WITH_DIR = 0xcf + DLT_LAPD = 0xcb + DLT_LIN = 0xd4 + DLT_LINUX_EVDEV = 0xd8 + DLT_LINUX_IRDA = 0x90 + DLT_LINUX_LAPD = 0xb1 + DLT_LINUX_PPP_WITHDIRECTION = 0xa6 + DLT_LINUX_SLL = 0x71 + DLT_LINUX_SLL2 = 0x114 + DLT_LOOP = 0x6c + DLT_LORATAP = 0x10e + DLT_LTALK = 0x72 + DLT_MATCHING_MAX = 0x114 + DLT_MATCHING_MIN = 0x68 + DLT_MFR = 0xb6 + DLT_MOST = 0xd3 + DLT_MPEG_2_TS = 0xf3 + DLT_MPLS = 0xdb + DLT_MTP2 = 0x8c + DLT_MTP2_WITH_PHDR = 0x8b + DLT_MTP3 = 0x8d + DLT_MUX27010 = 0xec + DLT_NETANALYZER = 0xf0 + DLT_NETANALYZER_TRANSPARENT = 0xf1 + DLT_NETLINK = 0xfd + DLT_NFC_LLCP = 0xf5 + DLT_NFLOG = 0xef + DLT_NG40 = 0xf4 + DLT_NORDIC_BLE = 0x110 + DLT_NULL = 0x0 + DLT_OPENFLOW = 0x10b + DLT_PCI_EXP = 0x7d + DLT_PFLOG = 0x75 + DLT_PFSYNC = 0x79 + DLT_PKTAP = 0x102 + DLT_PPI = 0xc0 + DLT_PPP = 0x9 + DLT_PPP_BSDOS = 0xe + DLT_PPP_ETHER = 0x33 + DLT_PPP_PPPD = 0xa6 + DLT_PPP_SERIAL = 0x32 + DLT_PPP_WITH_DIR = 0xcc + DLT_PPP_WITH_DIRECTION = 0xa6 + DLT_PRISM_HEADER = 0x77 + DLT_PROFIBUS_DL = 0x101 + DLT_PRONET = 0x4 + DLT_RAIF1 = 0xc6 + DLT_RAW = 0xc + DLT_RDS = 0x109 + DLT_REDBACK_SMARTEDGE = 0x20 + DLT_RIO = 0x7c + DLT_RTAC_SERIAL = 0xfa + DLT_SCCP = 0x8e + DLT_SCTP = 0xf8 + DLT_SDLC = 0x10c + DLT_SITA = 0xc4 + DLT_SLIP = 0x8 + DLT_SLIP_BSDOS = 0xd + DLT_STANAG_5066_D_PDU = 0xed + DLT_SUNATM = 0x7b + DLT_SYMANTEC_FIREWALL = 0x63 + DLT_TI_LLN_SNIFFER = 0x10d + DLT_TZSP = 0x80 + DLT_USB = 0xba + DLT_USBPCAP = 0xf9 + DLT_USB_DARWIN = 0x10a + DLT_USB_FREEBSD = 0xba + DLT_USB_LINUX = 0xbd + DLT_USB_LINUX_MMAPPED = 0xdc + DLT_USER0 = 0x93 + DLT_USER1 = 0x94 + DLT_USER10 = 0x9d + DLT_USER11 = 0x9e + DLT_USER12 = 0x9f + DLT_USER13 = 0xa0 + DLT_USER14 = 0xa1 + DLT_USER15 = 0xa2 + DLT_USER2 = 0x95 + DLT_USER3 = 0x96 + DLT_USER4 = 0x97 + DLT_USER5 = 0x98 + DLT_USER6 = 0x99 + DLT_USER7 = 0x9a + DLT_USER8 = 0x9b + DLT_USER9 = 0x9c + DLT_VSOCK = 0x10f + DLT_WATTSTOPPER_DLM = 0x107 + DLT_WIHART = 0xdf + DLT_WIRESHARK_UPPER_PDU = 0xfc + DLT_X2E_SERIAL = 0xd5 + DLT_X2E_XORAYA = 0xd6 + DLT_ZWAVE_R1_R2 = 0x105 + DLT_ZWAVE_R3 = 0x106 + DT_BLK = 0x6 + DT_CHR = 0x2 + DT_DIR = 0x4 + DT_FIFO = 0x1 + DT_LNK = 0xa + DT_REG = 0x8 + DT_SOCK = 0xc + DT_UNKNOWN = 0x0 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EHE_DEAD_PRIORITY = -0x1 + EVFILT_AIO = -0x3 + EVFILT_EMPTY = -0xd + EVFILT_FS = -0x9 + EVFILT_LIO = -0xa + EVFILT_PROC = -0x5 + EVFILT_PROCDESC = -0x8 + EVFILT_READ = -0x1 + EVFILT_SENDFILE = -0xc + EVFILT_SIGNAL = -0x6 + EVFILT_SYSCOUNT = 0xd + EVFILT_TIMER = -0x7 + EVFILT_USER = -0xb + EVFILT_VNODE = -0x4 + EVFILT_WRITE = -0x2 + EVNAMEMAP_NAME_SIZE = 0x40 + EV_ADD = 0x1 + EV_CLEAR = 0x20 + EV_DELETE = 0x2 + EV_DISABLE = 0x8 + EV_DISPATCH = 0x80 + EV_DROP = 0x1000 + EV_ENABLE = 0x4 + EV_EOF = 0x8000 + EV_ERROR = 0x4000 + EV_FLAG1 = 0x2000 + EV_FLAG2 = 0x4000 + EV_FORCEONESHOT = 0x100 + EV_ONESHOT = 0x10 + EV_RECEIPT = 0x40 + EV_SYSFLAGS = 0xf000 + EXTA = 0x4b00 + EXTATTR_MAXNAMELEN = 0xff + EXTATTR_NAMESPACE_EMPTY = 0x0 + EXTATTR_NAMESPACE_SYSTEM = 0x2 + EXTATTR_NAMESPACE_USER = 0x1 + EXTB = 0x9600 + EXTPROC = 0x800 + FD_CLOEXEC = 0x1 + FD_NONE = -0xc8 + FD_SETSIZE = 0x400 + FLUSHO = 0x800000 + F_ADD_SEALS = 0x13 + F_CANCEL = 0x5 + F_DUP2FD = 0xa + F_DUP2FD_CLOEXEC = 0x12 + F_DUPFD = 0x0 + F_DUPFD_CLOEXEC = 0x11 + F_GETFD = 0x1 + F_GETFL = 0x3 + F_GETLK = 0xb + F_GETOWN = 0x5 + F_GET_SEALS = 0x14 + F_ISUNIONSTACK = 0x15 + F_KINFO = 0x16 + F_OGETLK = 0x7 + F_OK = 0x0 + F_OSETLK = 0x8 + F_OSETLKW = 0x9 + F_RDAHEAD = 0x10 + F_RDLCK = 0x1 + F_READAHEAD = 0xf + F_SEAL_GROW = 0x4 + F_SEAL_SEAL = 0x1 + F_SEAL_SHRINK = 0x2 + F_SEAL_WRITE = 0x8 + F_SETFD = 0x2 + F_SETFL = 0x4 + F_SETLK = 0xc + F_SETLKW = 0xd + F_SETLK_REMOTE = 0xe + F_SETOWN = 0x6 + F_UNLCK = 0x2 + F_UNLCKSYS = 0x4 + F_WRLCK = 0x3 + HUPCL = 0x4000 + HW_MACHINE = 0x1 + ICANON = 0x100 + ICMP6_FILTER = 0x12 + ICRNL = 0x100 + IEXTEN = 0x400 + IFAN_ARRIVAL = 0x0 + IFAN_DEPARTURE = 0x1 + IFCAP_WOL_MAGIC = 0x2000 + IFF_ALLMULTI = 0x200 + IFF_ALTPHYS = 0x4000 + IFF_BROADCAST = 0x2 + IFF_CANTCHANGE = 0x218f72 + IFF_CANTCONFIG = 0x10000 + IFF_DEBUG = 0x4 + IFF_DRV_OACTIVE = 0x400 + IFF_DRV_RUNNING = 0x40 + IFF_DYING = 0x200000 + IFF_KNOWSEPOCH = 0x20 + IFF_LINK0 = 0x1000 + IFF_LINK1 = 0x2000 + IFF_LINK2 = 0x4000 + IFF_LOOPBACK = 0x8 + IFF_MONITOR = 0x40000 + IFF_MULTICAST = 0x8000 + IFF_NOARP = 0x80 + IFF_NOGROUP = 0x800000 + IFF_OACTIVE = 0x400 + IFF_POINTOPOINT = 0x10 + IFF_PPROMISC = 0x20000 + IFF_PROMISC = 0x100 + IFF_RENAMING = 0x400000 + IFF_RUNNING = 0x40 + IFF_SIMPLEX = 0x800 + IFF_STATICARP = 0x80000 + IFF_UP = 0x1 + IFNAMSIZ = 0x10 + IFT_BRIDGE = 0xd1 + IFT_CARP = 0xf8 + IFT_IEEE1394 = 0x90 + IFT_INFINIBAND = 0xc7 + IFT_L2VLAN = 0x87 + IFT_L3IPVLAN = 0x88 + IFT_PPP = 0x17 + IFT_PROPVIRTUAL = 0x35 + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 + IN_CLASSA_HOST = 0xffffff + IN_CLASSA_MAX = 0x80 + IN_CLASSA_NET = 0xff000000 + IN_CLASSA_NSHIFT = 0x18 + IN_CLASSB_HOST = 0xffff + IN_CLASSB_MAX = 0x10000 + IN_CLASSB_NET = 0xffff0000 + IN_CLASSB_NSHIFT = 0x10 + IN_CLASSC_HOST = 0xff + IN_CLASSC_NET = 0xffffff00 + IN_CLASSC_NSHIFT = 0x8 + IN_CLASSD_HOST = 0xfffffff + IN_CLASSD_NET = 0xf0000000 + IN_CLASSD_NSHIFT = 0x1c + IN_LOOPBACKNET = 0x7f + IN_NETMASK_DEFAULT = 0xffffff00 + IN_RFC3021_MASK = 0xfffffffe + IPPROTO_3PC = 0x22 + IPPROTO_ADFS = 0x44 + IPPROTO_AH = 0x33 + IPPROTO_AHIP = 0x3d + IPPROTO_APES = 0x63 + IPPROTO_ARGUS = 0xd + IPPROTO_AX25 = 0x5d + IPPROTO_BHA = 0x31 + IPPROTO_BLT = 0x1e + IPPROTO_BRSATMON = 0x4c + IPPROTO_CARP = 0x70 + IPPROTO_CFTP = 0x3e + IPPROTO_CHAOS = 0x10 + IPPROTO_CMTP = 0x26 + IPPROTO_CPHB = 0x49 + IPPROTO_CPNX = 0x48 + IPPROTO_DCCP = 0x21 + IPPROTO_DDP = 0x25 + IPPROTO_DGP = 0x56 + IPPROTO_DIVERT = 0x102 + IPPROTO_DONE = 0x101 + IPPROTO_DSTOPTS = 0x3c + IPPROTO_EGP = 0x8 + IPPROTO_EMCON = 0xe + IPPROTO_ENCAP = 0x62 + IPPROTO_EON = 0x50 + IPPROTO_ESP = 0x32 + IPPROTO_ETHERIP = 0x61 + IPPROTO_FRAGMENT = 0x2c + IPPROTO_GGP = 0x3 + IPPROTO_GMTP = 0x64 + IPPROTO_GRE = 0x2f + IPPROTO_HELLO = 0x3f + IPPROTO_HIP = 0x8b + IPPROTO_HMP = 0x14 + IPPROTO_HOPOPTS = 0x0 + IPPROTO_ICMP = 0x1 + IPPROTO_ICMPV6 = 0x3a + IPPROTO_IDP = 0x16 + IPPROTO_IDPR = 0x23 + IPPROTO_IDRP = 0x2d + IPPROTO_IGMP = 0x2 + IPPROTO_IGP = 0x55 + IPPROTO_IGRP = 0x58 + IPPROTO_IL = 0x28 + IPPROTO_INLSP = 0x34 + IPPROTO_INP = 0x20 + IPPROTO_IP = 0x0 + IPPROTO_IPCOMP = 0x6c + IPPROTO_IPCV = 0x47 + IPPROTO_IPEIP = 0x5e + IPPROTO_IPIP = 0x4 + IPPROTO_IPPC = 0x43 + IPPROTO_IPV4 = 0x4 + IPPROTO_IPV6 = 0x29 + IPPROTO_IRTP = 0x1c + IPPROTO_KRYPTOLAN = 0x41 + IPPROTO_LARP = 0x5b + IPPROTO_LEAF1 = 0x19 + IPPROTO_LEAF2 = 0x1a + IPPROTO_MAX = 0x100 + IPPROTO_MEAS = 0x13 + IPPROTO_MH = 0x87 + IPPROTO_MHRP = 0x30 + IPPROTO_MICP = 0x5f + IPPROTO_MOBILE = 0x37 + IPPROTO_MPLS = 0x89 + IPPROTO_MTP = 0x5c + IPPROTO_MUX = 0x12 + IPPROTO_ND = 0x4d + IPPROTO_NHRP = 0x36 + IPPROTO_NONE = 0x3b + IPPROTO_NSP = 0x1f + IPPROTO_NVPII = 0xb + IPPROTO_OLD_DIVERT = 0xfe + IPPROTO_OSPFIGP = 0x59 + IPPROTO_PFSYNC = 0xf0 + IPPROTO_PGM = 0x71 + IPPROTO_PIGP = 0x9 + IPPROTO_PIM = 0x67 + IPPROTO_PRM = 0x15 + IPPROTO_PUP = 0xc + IPPROTO_PVP = 0x4b + IPPROTO_RAW = 0xff + IPPROTO_RCCMON = 0xa + IPPROTO_RDP = 0x1b + IPPROTO_RESERVED_253 = 0xfd + IPPROTO_RESERVED_254 = 0xfe + IPPROTO_ROUTING = 0x2b + IPPROTO_RSVP = 0x2e + IPPROTO_RVD = 0x42 + IPPROTO_SATEXPAK = 0x40 + IPPROTO_SATMON = 0x45 + IPPROTO_SCCSP = 0x60 + IPPROTO_SCTP = 0x84 + IPPROTO_SDRP = 0x2a + IPPROTO_SEND = 0x103 + IPPROTO_SHIM6 = 0x8c + IPPROTO_SKIP = 0x39 + IPPROTO_SPACER = 0x7fff + IPPROTO_SRPC = 0x5a + IPPROTO_ST = 0x7 + IPPROTO_SVMTP = 0x52 + IPPROTO_SWIPE = 0x35 + IPPROTO_TCF = 0x57 + IPPROTO_TCP = 0x6 + IPPROTO_TLSP = 0x38 + IPPROTO_TP = 0x1d + IPPROTO_TPXX = 0x27 + IPPROTO_TRUNK1 = 0x17 + IPPROTO_TRUNK2 = 0x18 + IPPROTO_TTP = 0x54 + IPPROTO_UDP = 0x11 + IPPROTO_UDPLITE = 0x88 + IPPROTO_VINES = 0x53 + IPPROTO_VISA = 0x46 + IPPROTO_VMTP = 0x51 + IPPROTO_WBEXPAK = 0x4f + IPPROTO_WBMON = 0x4e + IPPROTO_WSN = 0x4a + IPPROTO_XNET = 0xf + IPPROTO_XTP = 0x24 + IPV6_AUTOFLOWLABEL = 0x3b + IPV6_BINDANY = 0x40 + IPV6_BINDMULTI = 0x41 + IPV6_BINDV6ONLY = 0x1b + IPV6_CHECKSUM = 0x1a + IPV6_DEFAULT_MULTICAST_HOPS = 0x1 + IPV6_DEFAULT_MULTICAST_LOOP = 0x1 + IPV6_DEFHLIM = 0x40 + IPV6_DONTFRAG = 0x3e + IPV6_DSTOPTS = 0x32 + IPV6_FLOWID = 0x43 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_LEN = 0x14 + IPV6_FLOWLABEL_MASK = 0xffff0f00 + IPV6_FLOWTYPE = 0x44 + IPV6_FRAGTTL = 0x78 + IPV6_FW_ADD = 0x1e + IPV6_FW_DEL = 0x1f + IPV6_FW_FLUSH = 0x20 + IPV6_FW_GET = 0x22 + IPV6_FW_ZERO = 0x21 + IPV6_HLIMDEC = 0x1 + IPV6_HOPLIMIT = 0x2f + IPV6_HOPOPTS = 0x31 + IPV6_IPSEC_POLICY = 0x1c + IPV6_JOIN_GROUP = 0xc + IPV6_LEAVE_GROUP = 0xd + IPV6_MAXHLIM = 0xff + IPV6_MAXOPTHDR = 0x800 + IPV6_MAXPACKET = 0xffff + IPV6_MAX_GROUP_SRC_FILTER = 0x200 + IPV6_MAX_MEMBERSHIPS = 0xfff + IPV6_MAX_SOCK_SRC_FILTER = 0x80 + IPV6_MMTU = 0x500 + IPV6_MSFILTER = 0x4a + IPV6_MULTICAST_HOPS = 0xa + IPV6_MULTICAST_IF = 0x9 + IPV6_MULTICAST_LOOP = 0xb + IPV6_NEXTHOP = 0x30 + IPV6_ORIGDSTADDR = 0x48 + IPV6_PATHMTU = 0x2c + IPV6_PKTINFO = 0x2e + IPV6_PORTRANGE = 0xe + IPV6_PORTRANGE_DEFAULT = 0x0 + IPV6_PORTRANGE_HIGH = 0x1 + IPV6_PORTRANGE_LOW = 0x2 + IPV6_PREFER_TEMPADDR = 0x3f + IPV6_RECVDSTOPTS = 0x28 + IPV6_RECVFLOWID = 0x46 + IPV6_RECVHOPLIMIT = 0x25 + IPV6_RECVHOPOPTS = 0x27 + IPV6_RECVORIGDSTADDR = 0x48 + IPV6_RECVPATHMTU = 0x2b + IPV6_RECVPKTINFO = 0x24 + IPV6_RECVRSSBUCKETID = 0x47 + IPV6_RECVRTHDR = 0x26 + IPV6_RECVTCLASS = 0x39 + IPV6_RSSBUCKETID = 0x45 + IPV6_RSS_LISTEN_BUCKET = 0x42 + IPV6_RTHDR = 0x33 + IPV6_RTHDRDSTOPTS = 0x23 + IPV6_RTHDR_LOOSE = 0x0 + IPV6_RTHDR_STRICT = 0x1 + IPV6_RTHDR_TYPE_0 = 0x0 + IPV6_SOCKOPT_RESERVED1 = 0x3 + IPV6_TCLASS = 0x3d + IPV6_UNICAST_HOPS = 0x4 + IPV6_USE_MIN_MTU = 0x2a + IPV6_V6ONLY = 0x1b + IPV6_VERSION = 0x60 + IPV6_VERSION_MASK = 0xf0 + IPV6_VLAN_PCP = 0x4b + IP_ADD_MEMBERSHIP = 0xc + IP_ADD_SOURCE_MEMBERSHIP = 0x46 + IP_BINDANY = 0x18 + IP_BINDMULTI = 0x19 + IP_BLOCK_SOURCE = 0x48 + IP_DEFAULT_MULTICAST_LOOP = 0x1 + IP_DEFAULT_MULTICAST_TTL = 0x1 + IP_DF = 0x4000 + IP_DONTFRAG = 0x43 + IP_DROP_MEMBERSHIP = 0xd + IP_DROP_SOURCE_MEMBERSHIP = 0x47 + IP_DUMMYNET3 = 0x31 + IP_DUMMYNET_CONFIGURE = 0x3c + IP_DUMMYNET_DEL = 0x3d + IP_DUMMYNET_FLUSH = 0x3e + IP_DUMMYNET_GET = 0x40 + IP_FLOWID = 0x5a + IP_FLOWTYPE = 0x5b + IP_FW3 = 0x30 + IP_FW_ADD = 0x32 + IP_FW_DEL = 0x33 + IP_FW_FLUSH = 0x34 + IP_FW_GET = 0x36 + IP_FW_NAT_CFG = 0x38 + IP_FW_NAT_DEL = 0x39 + IP_FW_NAT_GET_CONFIG = 0x3a + IP_FW_NAT_GET_LOG = 0x3b + IP_FW_RESETLOG = 0x37 + IP_FW_TABLE_ADD = 0x28 + IP_FW_TABLE_DEL = 0x29 + IP_FW_TABLE_FLUSH = 0x2a + IP_FW_TABLE_GETSIZE = 0x2b + IP_FW_TABLE_LIST = 0x2c + IP_FW_ZERO = 0x35 + IP_HDRINCL = 0x2 + IP_IPSEC_POLICY = 0x15 + IP_MAXPACKET = 0xffff + IP_MAX_GROUP_SRC_FILTER = 0x200 + IP_MAX_MEMBERSHIPS = 0xfff + IP_MAX_SOCK_MUTE_FILTER = 0x80 + IP_MAX_SOCK_SRC_FILTER = 0x80 + IP_MF = 0x2000 + IP_MINTTL = 0x42 + IP_MSFILTER = 0x4a + IP_MSS = 0x240 + IP_MULTICAST_IF = 0x9 + IP_MULTICAST_LOOP = 0xb + IP_MULTICAST_TTL = 0xa + IP_MULTICAST_VIF = 0xe + IP_OFFMASK = 0x1fff + IP_ONESBCAST = 0x17 + IP_OPTIONS = 0x1 + IP_ORIGDSTADDR = 0x1b + IP_PORTRANGE = 0x13 + IP_PORTRANGE_DEFAULT = 0x0 + IP_PORTRANGE_HIGH = 0x1 + IP_PORTRANGE_LOW = 0x2 + IP_RECVDSTADDR = 0x7 + IP_RECVFLOWID = 0x5d + IP_RECVIF = 0x14 + IP_RECVOPTS = 0x5 + IP_RECVORIGDSTADDR = 0x1b + IP_RECVRETOPTS = 0x6 + IP_RECVRSSBUCKETID = 0x5e + IP_RECVTOS = 0x44 + IP_RECVTTL = 0x41 + IP_RETOPTS = 0x8 + IP_RF = 0x8000 + IP_RSSBUCKETID = 0x5c + IP_RSS_LISTEN_BUCKET = 0x1a + IP_RSVP_OFF = 0x10 + IP_RSVP_ON = 0xf + IP_RSVP_VIF_OFF = 0x12 + IP_RSVP_VIF_ON = 0x11 + IP_SENDSRCADDR = 0x7 + IP_TOS = 0x3 + IP_TTL = 0x4 + IP_UNBLOCK_SOURCE = 0x49 + IP_VLAN_PCP = 0x4b + ISIG = 0x80 + ISTRIP = 0x20 + ITIMER_PROF = 0x2 + ITIMER_REAL = 0x0 + ITIMER_VIRTUAL = 0x1 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + KERN_HOSTNAME = 0xa + KERN_OSRELEASE = 0x2 + KERN_OSTYPE = 0x1 + KERN_VERSION = 0x4 + LOCAL_CONNWAIT = 0x4 + LOCAL_CREDS = 0x2 + LOCAL_CREDS_PERSISTENT = 0x3 + LOCAL_PEERCRED = 0x1 + LOCAL_VENDOR = 0x80000000 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 + MADV_AUTOSYNC = 0x7 + MADV_CORE = 0x9 + MADV_DONTNEED = 0x4 + MADV_FREE = 0x5 + MADV_NOCORE = 0x8 + MADV_NORMAL = 0x0 + MADV_NOSYNC = 0x6 + MADV_PROTECT = 0xa + MADV_RANDOM = 0x1 + MADV_SEQUENTIAL = 0x2 + MADV_WILLNEED = 0x3 + MAP_32BIT = 0x80000 + MAP_ALIGNED_SUPER = 0x1000000 + MAP_ALIGNMENT_MASK = -0x1000000 + MAP_ALIGNMENT_SHIFT = 0x18 + MAP_ANON = 0x1000 + MAP_ANONYMOUS = 0x1000 + MAP_COPY = 0x2 + MAP_EXCL = 0x4000 + MAP_FILE = 0x0 + MAP_FIXED = 0x10 + MAP_GUARD = 0x2000 + MAP_HASSEMAPHORE = 0x200 + MAP_NOCORE = 0x20000 + MAP_NOSYNC = 0x800 + MAP_PREFAULT_READ = 0x40000 + MAP_PRIVATE = 0x2 + MAP_RESERVED0020 = 0x20 + MAP_RESERVED0040 = 0x40 + MAP_RESERVED0080 = 0x80 + MAP_RESERVED0100 = 0x100 + MAP_SHARED = 0x1 + MAP_STACK = 0x400 + MCAST_BLOCK_SOURCE = 0x54 + MCAST_EXCLUDE = 0x2 + MCAST_INCLUDE = 0x1 + MCAST_JOIN_GROUP = 0x50 + MCAST_JOIN_SOURCE_GROUP = 0x52 + MCAST_LEAVE_GROUP = 0x51 + MCAST_LEAVE_SOURCE_GROUP = 0x53 + MCAST_UNBLOCK_SOURCE = 0x55 + MCAST_UNDEFINED = 0x0 + MCL_CURRENT = 0x1 + MCL_FUTURE = 0x2 + MFD_ALLOW_SEALING = 0x2 + MFD_CLOEXEC = 0x1 + MFD_HUGETLB = 0x4 + MFD_HUGE_16GB = -0x78000000 + MFD_HUGE_16MB = 0x60000000 + MFD_HUGE_1GB = 0x78000000 + MFD_HUGE_1MB = 0x50000000 + MFD_HUGE_256MB = 0x70000000 + MFD_HUGE_2GB = 0x7c000000 + MFD_HUGE_2MB = 0x54000000 + MFD_HUGE_32MB = 0x64000000 + MFD_HUGE_512KB = 0x4c000000 + MFD_HUGE_512MB = 0x74000000 + MFD_HUGE_64KB = 0x40000000 + MFD_HUGE_8MB = 0x5c000000 + MFD_HUGE_MASK = 0xfc000000 + MFD_HUGE_SHIFT = 0x1a + MNT_ACLS = 0x8000000 + MNT_ASYNC = 0x40 + MNT_AUTOMOUNTED = 0x200000000 + MNT_BYFSID = 0x8000000 + MNT_CMDFLAGS = 0x300d0f0000 + MNT_DEFEXPORTED = 0x200 + MNT_DELEXPORT = 0x20000 + MNT_EMPTYDIR = 0x2000000000 + MNT_EXKERB = 0x800 + MNT_EXPORTANON = 0x400 + MNT_EXPORTED = 0x100 + MNT_EXPUBLIC = 0x20000000 + MNT_EXRDONLY = 0x80 + MNT_EXTLS = 0x4000000000 + MNT_EXTLSCERT = 0x8000000000 + MNT_EXTLSCERTUSER = 0x10000000000 + MNT_FORCE = 0x80000 + MNT_GJOURNAL = 0x2000000 + MNT_IGNORE = 0x800000 + MNT_LAZY = 0x3 + MNT_LOCAL = 0x1000 + MNT_MULTILABEL = 0x4000000 + MNT_NFS4ACLS = 0x10 + MNT_NOATIME = 0x10000000 + MNT_NOCLUSTERR = 0x40000000 + MNT_NOCLUSTERW = 0x80000000 + MNT_NOCOVER = 0x1000000000 + MNT_NOEXEC = 0x4 + MNT_NONBUSY = 0x4000000 + MNT_NOSUID = 0x8 + MNT_NOSYMFOLLOW = 0x400000 + MNT_NOWAIT = 0x2 + MNT_QUOTA = 0x2000 + MNT_RDONLY = 0x1 + MNT_RELOAD = 0x40000 + MNT_ROOTFS = 0x4000 + MNT_SNAPSHOT = 0x1000000 + MNT_SOFTDEP = 0x200000 + MNT_SUIDDIR = 0x100000 + MNT_SUJ = 0x100000000 + MNT_SUSPEND = 0x4 + MNT_SYNCHRONOUS = 0x2 + MNT_UNION = 0x20 + MNT_UNTRUSTED = 0x800000000 + MNT_UPDATE = 0x10000 + MNT_UPDATEMASK = 0xad8d0807e + MNT_USER = 0x8000 + MNT_VERIFIED = 0x400000000 + MNT_VISFLAGMASK = 0xffef0ffff + MNT_WAIT = 0x1 + MSG_CMSG_CLOEXEC = 0x40000 + MSG_COMPAT = 0x8000 + MSG_CTRUNC = 0x20 + MSG_DONTROUTE = 0x4 + MSG_DONTWAIT = 0x80 + MSG_EOF = 0x100 + MSG_EOR = 0x8 + MSG_NBIO = 0x4000 + MSG_NOSIGNAL = 0x20000 + MSG_NOTIFICATION = 0x2000 + MSG_OOB = 0x1 + MSG_PEEK = 0x2 + MSG_TRUNC = 0x10 + MSG_WAITALL = 0x40 + MSG_WAITFORONE = 0x80000 + MS_ASYNC = 0x1 + MS_INVALIDATE = 0x2 + MS_SYNC = 0x0 + NAME_MAX = 0xff + NET_RT_DUMP = 0x1 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFLISTL = 0x5 + NET_RT_IFMALIST = 0x4 + NET_RT_NHGRP = 0x7 + NET_RT_NHOP = 0x6 + NFDBITS = 0x40 + NOFLSH = 0x80000000 + NOKERNINFO = 0x2000000 + NOTE_ABSTIME = 0x10 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_CLOSE = 0x100 + NOTE_CLOSE_WRITE = 0x200 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FFAND = 0x40000000 + NOTE_FFCOPY = 0xc0000000 + NOTE_FFCTRLMASK = 0xc0000000 + NOTE_FFLAGSMASK = 0xffffff + NOTE_FFNOP = 0x0 + NOTE_FFOR = 0x80000000 + NOTE_FILE_POLL = 0x2 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_MSECONDS = 0x2 + NOTE_NSECONDS = 0x8 + NOTE_OPEN = 0x80 + NOTE_PCTRLMASK = 0xf0000000 + NOTE_PDATAMASK = 0xfffff + NOTE_READ = 0x400 + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_SECONDS = 0x1 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_TRIGGER = 0x1000000 + NOTE_USECONDS = 0x4 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 + OXTABS = 0x4 + O_ACCMODE = 0x3 + O_APPEND = 0x8 + O_ASYNC = 0x40 + O_CLOEXEC = 0x100000 + O_CREAT = 0x200 + O_DIRECT = 0x10000 + O_DIRECTORY = 0x20000 + O_DSYNC = 0x1000000 + O_EMPTY_PATH = 0x2000000 + O_EXCL = 0x800 + O_EXEC = 0x40000 + O_EXLOCK = 0x20 + O_FSYNC = 0x80 + O_NDELAY = 0x4 + O_NOCTTY = 0x8000 + O_NOFOLLOW = 0x100 + O_NONBLOCK = 0x4 + O_PATH = 0x400000 + O_RDONLY = 0x0 + O_RDWR = 0x2 + O_RESOLVE_BENEATH = 0x800000 + O_SEARCH = 0x40000 + O_SHLOCK = 0x10 + O_SYNC = 0x80 + O_TRUNC = 0x400 + O_TTY_INIT = 0x80000 + O_VERIFY = 0x200000 + O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PIOD_READ_D = 0x1 + PIOD_READ_I = 0x3 + PIOD_WRITE_D = 0x2 + PIOD_WRITE_I = 0x4 + PRIO_PGRP = 0x1 + PRIO_PROCESS = 0x0 + PRIO_USER = 0x2 + PROT_EXEC = 0x4 + PROT_NONE = 0x0 + PROT_READ = 0x1 + PROT_WRITE = 0x2 + PTRACE_DEFAULT = 0x1 + PTRACE_EXEC = 0x1 + PTRACE_FORK = 0x8 + PTRACE_LWP = 0x10 + PTRACE_SCE = 0x2 + PTRACE_SCX = 0x4 + PTRACE_SYSCALL = 0x6 + PTRACE_VFORK = 0x20 + PT_ATTACH = 0xa + PT_CLEARSTEP = 0x10 + PT_CONTINUE = 0x7 + PT_COREDUMP = 0x1d + PT_DETACH = 0xb + PT_FIRSTMACH = 0x40 + PT_FOLLOW_FORK = 0x17 + PT_GETDBREGS = 0x25 + PT_GETFPREGS = 0x23 + PT_GETLWPLIST = 0xf + PT_GETNUMLWPS = 0xe + PT_GETREGS = 0x21 + PT_GET_EVENT_MASK = 0x19 + PT_GET_SC_ARGS = 0x1b + PT_GET_SC_RET = 0x1c + PT_IO = 0xc + PT_KILL = 0x8 + PT_LWPINFO = 0xd + PT_LWP_EVENTS = 0x18 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_RESUME = 0x13 + PT_SETDBREGS = 0x26 + PT_SETFPREGS = 0x24 + PT_SETREGS = 0x22 + PT_SETSTEP = 0x11 + PT_SET_EVENT_MASK = 0x1a + PT_STEP = 0x9 + PT_SUSPEND = 0x12 + PT_SYSCALL = 0x16 + PT_TO_SCE = 0x14 + PT_TO_SCX = 0x15 + PT_TRACE_ME = 0x0 + PT_VM_ENTRY = 0x29 + PT_VM_TIMESTAMP = 0x28 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + P_ZONEID = 0xc + RLIMIT_AS = 0xa + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_MEMLOCK = 0x6 + RLIMIT_NOFILE = 0x8 + RLIMIT_NPROC = 0x7 + RLIMIT_RSS = 0x5 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0x0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_FIXEDMTU = 0x80000 + RTF_FMASK = 0x1004d808 + RTF_GATEWAY = 0x2 + RTF_GWFLAG_COMPAT = 0x80000000 + RTF_HOST = 0x4 + RTF_LLDATA = 0x400 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_STATIC = 0x800 + RTF_STICKY = 0x10000000 + RTF_UP = 0x1 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_IEEE80211 = 0x12 + RTM_IFANNOUNCE = 0x11 + RTM_IFINFO = 0xe + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RTV_WEIGHT = 0x100 + RT_ALL_FIBS = -0x1 + RT_BLACKHOLE = 0x40 + RT_DEFAULT_FIB = 0x0 + RT_DEFAULT_WEIGHT = 0x1 + RT_HAS_GW = 0x80 + RT_HAS_HEADER = 0x10 + RT_HAS_HEADER_BIT = 0x4 + RT_L2_ME = 0x4 + RT_L2_ME_BIT = 0x2 + RT_LLE_CACHE = 0x100 + RT_MAX_WEIGHT = 0xffffff + RT_MAY_LOOP = 0x8 + RT_MAY_LOOP_BIT = 0x3 + RT_REJECT = 0x20 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + RUSAGE_THREAD = 0x1 + SCM_BINTIME = 0x4 + SCM_CREDS = 0x3 + SCM_CREDS2 = 0x8 + SCM_MONOTONIC = 0x6 + SCM_REALTIME = 0x5 + SCM_RIGHTS = 0x1 + SCM_TIMESTAMP = 0x2 + SCM_TIME_INFO = 0x7 + SEEK_CUR = 0x1 + SEEK_DATA = 0x3 + SEEK_END = 0x2 + SEEK_HOLE = 0x4 + SEEK_SET = 0x0 + SHUT_RD = 0x0 + SHUT_RDWR = 0x2 + SHUT_WR = 0x1 + SIOCADDMULTI = 0x80206931 + SIOCAIFADDR = 0x8040691a + SIOCAIFGROUP = 0x80286987 + SIOCATMARK = 0x40047307 + SIOCDELMULTI = 0x80206932 + SIOCDIFADDR = 0x80206919 + SIOCDIFGROUP = 0x80286989 + SIOCDIFPHYADDR = 0x80206949 + SIOCGDRVSPEC = 0xc028697b + SIOCGETSGCNT = 0xc0207210 + SIOCGETVIFCNT = 0xc028720f + SIOCGHIWAT = 0x40047301 + SIOCGHWADDR = 0xc020693e + SIOCGI2C = 0xc020693d + SIOCGIFADDR = 0xc0206921 + SIOCGIFALIAS = 0xc044692d + SIOCGIFBRDADDR = 0xc0206923 + SIOCGIFCAP = 0xc020691f + SIOCGIFCONF = 0xc0106924 + SIOCGIFDATA = 0x8020692c + SIOCGIFDESCR = 0xc020692a + SIOCGIFDOWNREASON = 0xc058699a + SIOCGIFDSTADDR = 0xc0206922 + SIOCGIFFIB = 0xc020695c + SIOCGIFFLAGS = 0xc0206911 + SIOCGIFGENERIC = 0xc020693a + SIOCGIFGMEMB = 0xc028698a + SIOCGIFGROUP = 0xc0286988 + SIOCGIFINDEX = 0xc0206920 + SIOCGIFMAC = 0xc0206926 + SIOCGIFMEDIA = 0xc0306938 + SIOCGIFMETRIC = 0xc0206917 + SIOCGIFMTU = 0xc0206933 + SIOCGIFNETMASK = 0xc0206925 + SIOCGIFPDSTADDR = 0xc0206948 + SIOCGIFPHYS = 0xc0206935 + SIOCGIFPSRCADDR = 0xc0206947 + SIOCGIFRSSHASH = 0xc0186997 + SIOCGIFRSSKEY = 0xc0946996 + SIOCGIFSTATUS = 0xc331693b + SIOCGIFXMEDIA = 0xc030698b + SIOCGLANPCP = 0xc0206998 + SIOCGLOWAT = 0x40047303 + SIOCGPGRP = 0x40047309 + SIOCGPRIVATE_0 = 0xc0206950 + SIOCGPRIVATE_1 = 0xc0206951 + SIOCGTUNFIB = 0xc020695e + SIOCIFCREATE = 0xc020697a + SIOCIFCREATE2 = 0xc020697c + SIOCIFDESTROY = 0x80206979 + SIOCIFGCLONERS = 0xc0106978 + SIOCSDRVSPEC = 0x8028697b + SIOCSHIWAT = 0x80047300 + SIOCSIFADDR = 0x8020690c + SIOCSIFBRDADDR = 0x80206913 + SIOCSIFCAP = 0x8020691e + SIOCSIFDESCR = 0x80206929 + SIOCSIFDSTADDR = 0x8020690e + SIOCSIFFIB = 0x8020695d + SIOCSIFFLAGS = 0x80206910 + SIOCSIFGENERIC = 0x80206939 + SIOCSIFLLADDR = 0x8020693c + SIOCSIFMAC = 0x80206927 + SIOCSIFMEDIA = 0xc0206937 + SIOCSIFMETRIC = 0x80206918 + SIOCSIFMTU = 0x80206934 + SIOCSIFNAME = 0x80206928 + SIOCSIFNETMASK = 0x80206916 + SIOCSIFPHYADDR = 0x80406946 + SIOCSIFPHYS = 0x80206936 + SIOCSIFRVNET = 0xc020695b + SIOCSIFVNET = 0xc020695a + SIOCSLANPCP = 0x80206999 + SIOCSLOWAT = 0x80047302 + SIOCSPGRP = 0x80047308 + SIOCSTUNFIB = 0x8020695f + SOCK_CLOEXEC = 0x10000000 + SOCK_DGRAM = 0x2 + SOCK_MAXADDRLEN = 0xff + SOCK_NONBLOCK = 0x20000000 + SOCK_RAW = 0x3 + SOCK_RDM = 0x4 + SOCK_SEQPACKET = 0x5 + SOCK_STREAM = 0x1 + SOL_LOCAL = 0x0 + SOL_SOCKET = 0xffff + SOMAXCONN = 0x80 + SO_ACCEPTCONN = 0x2 + SO_ACCEPTFILTER = 0x1000 + SO_BINTIME = 0x2000 + SO_BROADCAST = 0x20 + SO_DEBUG = 0x1 + SO_DOMAIN = 0x1019 + SO_DONTROUTE = 0x10 + SO_ERROR = 0x1007 + SO_KEEPALIVE = 0x8 + SO_LABEL = 0x1009 + SO_LINGER = 0x80 + SO_LISTENINCQLEN = 0x1013 + SO_LISTENQLEN = 0x1012 + SO_LISTENQLIMIT = 0x1011 + SO_MAX_PACING_RATE = 0x1018 + SO_NOSIGPIPE = 0x800 + SO_NO_DDP = 0x8000 + SO_NO_OFFLOAD = 0x4000 + SO_OOBINLINE = 0x100 + SO_PEERLABEL = 0x1010 + SO_PROTOCOL = 0x1016 + SO_PROTOTYPE = 0x1016 + SO_RCVBUF = 0x1002 + SO_RCVLOWAT = 0x1004 + SO_RCVTIMEO = 0x1006 + SO_RERROR = 0x20000 + SO_REUSEADDR = 0x4 + SO_REUSEPORT = 0x200 + SO_REUSEPORT_LB = 0x10000 + SO_SETFIB = 0x1014 + SO_SNDBUF = 0x1001 + SO_SNDLOWAT = 0x1003 + SO_SNDTIMEO = 0x1005 + SO_TIMESTAMP = 0x400 + SO_TS_BINTIME = 0x1 + SO_TS_CLOCK = 0x1017 + SO_TS_CLOCK_MAX = 0x3 + SO_TS_DEFAULT = 0x0 + SO_TS_MONOTONIC = 0x3 + SO_TS_REALTIME = 0x2 + SO_TS_REALTIME_MICRO = 0x0 + SO_TYPE = 0x1008 + SO_USELOOPBACK = 0x40 + SO_USER_COOKIE = 0x1015 + SO_VENDOR = 0x80000000 + S_BLKSIZE = 0x200 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + TAB0 = 0x0 + TAB3 = 0x4 + TABDLY = 0x4 + TCIFLUSH = 0x1 + TCIOFF = 0x3 + TCIOFLUSH = 0x3 + TCION = 0x4 + TCOFLUSH = 0x2 + TCOOFF = 0x1 + TCOON = 0x2 + TCPOPT_EOL = 0x0 + TCPOPT_FAST_OPEN = 0x22 + TCPOPT_MAXSEG = 0x2 + TCPOPT_NOP = 0x1 + TCPOPT_PAD = 0x0 + TCPOPT_SACK = 0x5 + TCPOPT_SACK_PERMITTED = 0x4 + TCPOPT_SIGNATURE = 0x13 + TCPOPT_TIMESTAMP = 0x8 + TCPOPT_WINDOW = 0x3 + TCP_BBR_ACK_COMP_ALG = 0x448 + TCP_BBR_ALGORITHM = 0x43b + TCP_BBR_DRAIN_INC_EXTRA = 0x43c + TCP_BBR_DRAIN_PG = 0x42e + TCP_BBR_EXTRA_GAIN = 0x449 + TCP_BBR_EXTRA_STATE = 0x453 + TCP_BBR_FLOOR_MIN_TSO = 0x454 + TCP_BBR_HDWR_PACE = 0x451 + TCP_BBR_HOLD_TARGET = 0x436 + TCP_BBR_IWINTSO = 0x42b + TCP_BBR_LOWGAIN_FD = 0x436 + TCP_BBR_LOWGAIN_HALF = 0x435 + TCP_BBR_LOWGAIN_THRESH = 0x434 + TCP_BBR_MAX_RTO = 0x439 + TCP_BBR_MIN_RTO = 0x438 + TCP_BBR_MIN_TOPACEOUT = 0x455 + TCP_BBR_ONE_RETRAN = 0x431 + TCP_BBR_PACE_CROSS = 0x442 + TCP_BBR_PACE_DEL_TAR = 0x43f + TCP_BBR_PACE_OH = 0x435 + TCP_BBR_PACE_PER_SEC = 0x43e + TCP_BBR_PACE_SEG_MAX = 0x440 + TCP_BBR_PACE_SEG_MIN = 0x441 + TCP_BBR_POLICER_DETECT = 0x457 + TCP_BBR_PROBE_RTT_GAIN = 0x44d + TCP_BBR_PROBE_RTT_INT = 0x430 + TCP_BBR_PROBE_RTT_LEN = 0x44e + TCP_BBR_RACK_INIT_RATE = 0x458 + TCP_BBR_RACK_RTT_USE = 0x44a + TCP_BBR_RECFORCE = 0x42c + TCP_BBR_REC_OVER_HPTS = 0x43a + TCP_BBR_RETRAN_WTSO = 0x44b + TCP_BBR_RWND_IS_APP = 0x42f + TCP_BBR_SEND_IWND_IN_TSO = 0x44f + TCP_BBR_STARTUP_EXIT_EPOCH = 0x43d + TCP_BBR_STARTUP_LOSS_EXIT = 0x432 + TCP_BBR_STARTUP_PG = 0x42d + TCP_BBR_TMR_PACE_OH = 0x448 + TCP_BBR_TSLIMITS = 0x434 + TCP_BBR_TSTMP_RAISES = 0x456 + TCP_BBR_UNLIMITED = 0x43b + TCP_BBR_USEDEL_RATE = 0x437 + TCP_BBR_USE_LOWGAIN = 0x433 + TCP_BBR_USE_RACK_CHEAT = 0x450 + TCP_BBR_USE_RACK_RR = 0x450 + TCP_BBR_UTTER_MAX_TSO = 0x452 + TCP_CA_NAME_MAX = 0x10 + TCP_CCALGOOPT = 0x41 + TCP_CONGESTION = 0x40 + TCP_DATA_AFTER_CLOSE = 0x44c + TCP_DEFER_OPTIONS = 0x470 + TCP_DELACK = 0x48 + TCP_FASTOPEN = 0x401 + TCP_FASTOPEN_MAX_COOKIE_LEN = 0x10 + TCP_FASTOPEN_MIN_COOKIE_LEN = 0x4 + TCP_FASTOPEN_PSK_LEN = 0x10 + TCP_FAST_RSM_HACK = 0x471 + TCP_FIN_IS_RST = 0x49 + TCP_FUNCTION_BLK = 0x2000 + TCP_FUNCTION_NAME_LEN_MAX = 0x20 + TCP_HDWR_RATE_CAP = 0x46a + TCP_HDWR_UP_ONLY = 0x46c + TCP_IDLE_REDUCE = 0x46 + TCP_INFO = 0x20 + TCP_IWND_NB = 0x2b + TCP_IWND_NSEG = 0x2c + TCP_KEEPCNT = 0x400 + TCP_KEEPIDLE = 0x100 + TCP_KEEPINIT = 0x80 + TCP_KEEPINTVL = 0x200 + TCP_LOG = 0x22 + TCP_LOGBUF = 0x23 + TCP_LOGDUMP = 0x25 + TCP_LOGDUMPID = 0x26 + TCP_LOGID = 0x24 + TCP_LOGID_CNT = 0x2e + TCP_LOG_ID_LEN = 0x40 + TCP_LOG_LIMIT = 0x4a + TCP_LOG_TAG = 0x2f + TCP_MAXBURST = 0x4 + TCP_MAXHLEN = 0x3c + TCP_MAXOLEN = 0x28 + TCP_MAXPEAKRATE = 0x45 + TCP_MAXSEG = 0x2 + TCP_MAXUNACKTIME = 0x44 + TCP_MAXWIN = 0xffff + TCP_MAX_SACK = 0x4 + TCP_MAX_WINSHIFT = 0xe + TCP_MD5SIG = 0x10 + TCP_MINMSS = 0xd8 + TCP_MSS = 0x218 + TCP_NODELAY = 0x1 + TCP_NOOPT = 0x8 + TCP_NOPUSH = 0x4 + TCP_NO_PRR = 0x462 + TCP_PACING_RATE_CAP = 0x46b + TCP_PCAP_IN = 0x1000 + TCP_PCAP_OUT = 0x800 + TCP_PERF_INFO = 0x4e + TCP_PROC_ACCOUNTING = 0x4c + TCP_RACK_ABC_VAL = 0x46d + TCP_RACK_CHEAT_NOT_CONF_RATE = 0x459 + TCP_RACK_DO_DETECTION = 0x449 + TCP_RACK_EARLY_RECOV = 0x423 + TCP_RACK_EARLY_SEG = 0x424 + TCP_RACK_FORCE_MSEG = 0x45d + TCP_RACK_GP_INCREASE = 0x446 + TCP_RACK_GP_INCREASE_CA = 0x45a + TCP_RACK_GP_INCREASE_REC = 0x45c + TCP_RACK_GP_INCREASE_SS = 0x45b + TCP_RACK_IDLE_REDUCE_HIGH = 0x444 + TCP_RACK_MBUF_QUEUE = 0x41a + TCP_RACK_MEASURE_CNT = 0x46f + TCP_RACK_MIN_PACE = 0x445 + TCP_RACK_MIN_PACE_SEG = 0x446 + TCP_RACK_MIN_TO = 0x422 + TCP_RACK_NONRXT_CFG_RATE = 0x463 + TCP_RACK_NO_PUSH_AT_MAX = 0x466 + TCP_RACK_PACE_ALWAYS = 0x41f + TCP_RACK_PACE_MAX_SEG = 0x41e + TCP_RACK_PACE_RATE_CA = 0x45e + TCP_RACK_PACE_RATE_REC = 0x460 + TCP_RACK_PACE_RATE_SS = 0x45f + TCP_RACK_PACE_REDUCE = 0x41d + TCP_RACK_PACE_TO_FILL = 0x467 + TCP_RACK_PACING_BETA = 0x472 + TCP_RACK_PACING_BETA_ECN = 0x473 + TCP_RACK_PKT_DELAY = 0x428 + TCP_RACK_PROFILE = 0x469 + TCP_RACK_PROP = 0x41b + TCP_RACK_PROP_RATE = 0x420 + TCP_RACK_PRR_SENDALOT = 0x421 + TCP_RACK_REORD_FADE = 0x426 + TCP_RACK_REORD_THRESH = 0x425 + TCP_RACK_RR_CONF = 0x459 + TCP_RACK_TIMER_SLOP = 0x474 + TCP_RACK_TLP_INC_VAR = 0x429 + TCP_RACK_TLP_REDUCE = 0x41c + TCP_RACK_TLP_THRESH = 0x427 + TCP_RACK_TLP_USE = 0x447 + TCP_REC_ABC_VAL = 0x46e + TCP_REMOTE_UDP_ENCAPS_PORT = 0x47 + TCP_REUSPORT_LB_NUMA = 0x402 + TCP_REUSPORT_LB_NUMA_CURDOM = -0x1 + TCP_REUSPORT_LB_NUMA_NODOM = -0x2 + TCP_RXTLS_ENABLE = 0x29 + TCP_RXTLS_MODE = 0x2a + TCP_SHARED_CWND_ALLOWED = 0x4b + TCP_SHARED_CWND_ENABLE = 0x464 + TCP_SHARED_CWND_TIME_LIMIT = 0x468 + TCP_STATS = 0x21 + TCP_TIMELY_DYN_ADJ = 0x465 + TCP_TLS_MODE_IFNET = 0x2 + TCP_TLS_MODE_NONE = 0x0 + TCP_TLS_MODE_SW = 0x1 + TCP_TLS_MODE_TOE = 0x3 + TCP_TXTLS_ENABLE = 0x27 + TCP_TXTLS_MODE = 0x28 + TCP_USER_LOG = 0x30 + TCP_USE_CMP_ACKS = 0x4d + TCP_VENDOR = 0x80000000 + TCSAFLUSH = 0x2 + TIMER_ABSTIME = 0x1 + TIMER_RELTIME = 0x0 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDRAIN = 0x2000745e + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGPTN = 0x4004740f + TIOCGSID = 0x40047463 + TIOCGWINSZ = 0x40087468 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DCD = 0x40 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0x0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTMASTER = 0x2000741c + TIOCSBRK = 0x2000747b + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2004745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40107459 + TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + UTIME_NOW = -0x1 + UTIME_OMIT = -0x2 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VERASE2 = 0x7 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VTIME = 0x11 + VWERASE = 0x4 + WCONTINUED = 0x4 + WCOREFLAG = 0x80 + WEXITED = 0x10 + WLINUXCLONE = 0x80000000 + WNOHANG = 0x1 + WNOWAIT = 0x8 + WSTOPPED = 0x2 + WTRAPPED = 0x20 + WUNTRACED = 0x2 +) + +// Errors +const ( + E2BIG = syscall.Errno(0x7) + EACCES = syscall.Errno(0xd) + EADDRINUSE = syscall.Errno(0x30) + EADDRNOTAVAIL = syscall.Errno(0x31) + EAFNOSUPPORT = syscall.Errno(0x2f) + EAGAIN = syscall.Errno(0x23) + EALREADY = syscall.Errno(0x25) + EAUTH = syscall.Errno(0x50) + EBADF = syscall.Errno(0x9) + EBADMSG = syscall.Errno(0x59) + EBADRPC = syscall.Errno(0x48) + EBUSY = syscall.Errno(0x10) + ECANCELED = syscall.Errno(0x55) + ECAPMODE = syscall.Errno(0x5e) + ECHILD = syscall.Errno(0xa) + ECONNABORTED = syscall.Errno(0x35) + ECONNREFUSED = syscall.Errno(0x3d) + ECONNRESET = syscall.Errno(0x36) + EDEADLK = syscall.Errno(0xb) + EDESTADDRREQ = syscall.Errno(0x27) + EDOM = syscall.Errno(0x21) + EDOOFUS = syscall.Errno(0x58) + EDQUOT = syscall.Errno(0x45) + EEXIST = syscall.Errno(0x11) + EFAULT = syscall.Errno(0xe) + EFBIG = syscall.Errno(0x1b) + EFTYPE = syscall.Errno(0x4f) + EHOSTDOWN = syscall.Errno(0x40) + EHOSTUNREACH = syscall.Errno(0x41) + EIDRM = syscall.Errno(0x52) + EILSEQ = syscall.Errno(0x56) + EINPROGRESS = syscall.Errno(0x24) + EINTEGRITY = syscall.Errno(0x61) + EINTR = syscall.Errno(0x4) + EINVAL = syscall.Errno(0x16) + EIO = syscall.Errno(0x5) + EISCONN = syscall.Errno(0x38) + EISDIR = syscall.Errno(0x15) + ELAST = syscall.Errno(0x61) + ELOOP = syscall.Errno(0x3e) + EMFILE = syscall.Errno(0x18) + EMLINK = syscall.Errno(0x1f) + EMSGSIZE = syscall.Errno(0x28) + EMULTIHOP = syscall.Errno(0x5a) + ENAMETOOLONG = syscall.Errno(0x3f) + ENEEDAUTH = syscall.Errno(0x51) + ENETDOWN = syscall.Errno(0x32) + ENETRESET = syscall.Errno(0x34) + ENETUNREACH = syscall.Errno(0x33) + ENFILE = syscall.Errno(0x17) + ENOATTR = syscall.Errno(0x57) + ENOBUFS = syscall.Errno(0x37) + ENODEV = syscall.Errno(0x13) + ENOENT = syscall.Errno(0x2) + ENOEXEC = syscall.Errno(0x8) + ENOLCK = syscall.Errno(0x4d) + ENOLINK = syscall.Errno(0x5b) + ENOMEM = syscall.Errno(0xc) + ENOMSG = syscall.Errno(0x53) + ENOPROTOOPT = syscall.Errno(0x2a) + ENOSPC = syscall.Errno(0x1c) + ENOSYS = syscall.Errno(0x4e) + ENOTBLK = syscall.Errno(0xf) + ENOTCAPABLE = syscall.Errno(0x5d) + ENOTCONN = syscall.Errno(0x39) + ENOTDIR = syscall.Errno(0x14) + ENOTEMPTY = syscall.Errno(0x42) + ENOTRECOVERABLE = syscall.Errno(0x5f) + ENOTSOCK = syscall.Errno(0x26) + ENOTSUP = syscall.Errno(0x2d) + ENOTTY = syscall.Errno(0x19) + ENXIO = syscall.Errno(0x6) + EOPNOTSUPP = syscall.Errno(0x2d) + EOVERFLOW = syscall.Errno(0x54) + EOWNERDEAD = syscall.Errno(0x60) + EPERM = syscall.Errno(0x1) + EPFNOSUPPORT = syscall.Errno(0x2e) + EPIPE = syscall.Errno(0x20) + EPROCLIM = syscall.Errno(0x43) + EPROCUNAVAIL = syscall.Errno(0x4c) + EPROGMISMATCH = syscall.Errno(0x4b) + EPROGUNAVAIL = syscall.Errno(0x4a) + EPROTO = syscall.Errno(0x5c) + EPROTONOSUPPORT = syscall.Errno(0x2b) + EPROTOTYPE = syscall.Errno(0x29) + ERANGE = syscall.Errno(0x22) + EREMOTE = syscall.Errno(0x47) + EROFS = syscall.Errno(0x1e) + ERPCMISMATCH = syscall.Errno(0x49) + ESHUTDOWN = syscall.Errno(0x3a) + ESOCKTNOSUPPORT = syscall.Errno(0x2c) + ESPIPE = syscall.Errno(0x1d) + ESRCH = syscall.Errno(0x3) + ESTALE = syscall.Errno(0x46) + ETIMEDOUT = syscall.Errno(0x3c) + ETOOMANYREFS = syscall.Errno(0x3b) + ETXTBSY = syscall.Errno(0x1a) + EUSERS = syscall.Errno(0x44) + EWOULDBLOCK = syscall.Errno(0x23) + EXDEV = syscall.Errno(0x12) +) + +// Signals +const ( + SIGABRT = syscall.Signal(0x6) + SIGALRM = syscall.Signal(0xe) + SIGBUS = syscall.Signal(0xa) + SIGCHLD = syscall.Signal(0x14) + SIGCONT = syscall.Signal(0x13) + SIGEMT = syscall.Signal(0x7) + SIGFPE = syscall.Signal(0x8) + SIGHUP = syscall.Signal(0x1) + SIGILL = syscall.Signal(0x4) + SIGINFO = syscall.Signal(0x1d) + SIGINT = syscall.Signal(0x2) + SIGIO = syscall.Signal(0x17) + SIGIOT = syscall.Signal(0x6) + SIGKILL = syscall.Signal(0x9) + SIGLIBRT = syscall.Signal(0x21) + SIGLWP = syscall.Signal(0x20) + SIGPIPE = syscall.Signal(0xd) + SIGPROF = syscall.Signal(0x1b) + SIGQUIT = syscall.Signal(0x3) + SIGSEGV = syscall.Signal(0xb) + SIGSTOP = syscall.Signal(0x11) + SIGSYS = syscall.Signal(0xc) + SIGTERM = syscall.Signal(0xf) + SIGTHR = syscall.Signal(0x20) + SIGTRAP = syscall.Signal(0x5) + SIGTSTP = syscall.Signal(0x12) + SIGTTIN = syscall.Signal(0x15) + SIGTTOU = syscall.Signal(0x16) + SIGURG = syscall.Signal(0x10) + SIGUSR1 = syscall.Signal(0x1e) + SIGUSR2 = syscall.Signal(0x1f) + SIGVTALRM = syscall.Signal(0x1a) + SIGWINCH = syscall.Signal(0x1c) + SIGXCPU = syscall.Signal(0x18) + SIGXFSZ = syscall.Signal(0x19) +) + +// Error table +var errorList = [...]struct { + num syscall.Errno + name string + desc string +}{ + {1, "EPERM", "operation not permitted"}, + {2, "ENOENT", "no such file or directory"}, + {3, "ESRCH", "no such process"}, + {4, "EINTR", "interrupted system call"}, + {5, "EIO", "input/output error"}, + {6, "ENXIO", "device not configured"}, + {7, "E2BIG", "argument list too long"}, + {8, "ENOEXEC", "exec format error"}, + {9, "EBADF", "bad file descriptor"}, + {10, "ECHILD", "no child processes"}, + {11, "EDEADLK", "resource deadlock avoided"}, + {12, "ENOMEM", "cannot allocate memory"}, + {13, "EACCES", "permission denied"}, + {14, "EFAULT", "bad address"}, + {15, "ENOTBLK", "block device required"}, + {16, "EBUSY", "device busy"}, + {17, "EEXIST", "file exists"}, + {18, "EXDEV", "cross-device link"}, + {19, "ENODEV", "operation not supported by device"}, + {20, "ENOTDIR", "not a directory"}, + {21, "EISDIR", "is a directory"}, + {22, "EINVAL", "invalid argument"}, + {23, "ENFILE", "too many open files in system"}, + {24, "EMFILE", "too many open files"}, + {25, "ENOTTY", "inappropriate ioctl for device"}, + {26, "ETXTBSY", "text file busy"}, + {27, "EFBIG", "file too large"}, + {28, "ENOSPC", "no space left on device"}, + {29, "ESPIPE", "illegal seek"}, + {30, "EROFS", "read-only file system"}, + {31, "EMLINK", "too many links"}, + {32, "EPIPE", "broken pipe"}, + {33, "EDOM", "numerical argument out of domain"}, + {34, "ERANGE", "result too large"}, + {35, "EWOULDBLOCK", "resource temporarily unavailable"}, + {36, "EINPROGRESS", "operation now in progress"}, + {37, "EALREADY", "operation already in progress"}, + {38, "ENOTSOCK", "socket operation on non-socket"}, + {39, "EDESTADDRREQ", "destination address required"}, + {40, "EMSGSIZE", "message too long"}, + {41, "EPROTOTYPE", "protocol wrong type for socket"}, + {42, "ENOPROTOOPT", "protocol not available"}, + {43, "EPROTONOSUPPORT", "protocol not supported"}, + {44, "ESOCKTNOSUPPORT", "socket type not supported"}, + {45, "EOPNOTSUPP", "operation not supported"}, + {46, "EPFNOSUPPORT", "protocol family not supported"}, + {47, "EAFNOSUPPORT", "address family not supported by protocol family"}, + {48, "EADDRINUSE", "address already in use"}, + {49, "EADDRNOTAVAIL", "can't assign requested address"}, + {50, "ENETDOWN", "network is down"}, + {51, "ENETUNREACH", "network is unreachable"}, + {52, "ENETRESET", "network dropped connection on reset"}, + {53, "ECONNABORTED", "software caused connection abort"}, + {54, "ECONNRESET", "connection reset by peer"}, + {55, "ENOBUFS", "no buffer space available"}, + {56, "EISCONN", "socket is already connected"}, + {57, "ENOTCONN", "socket is not connected"}, + {58, "ESHUTDOWN", "can't send after socket shutdown"}, + {59, "ETOOMANYREFS", "too many references: can't splice"}, + {60, "ETIMEDOUT", "operation timed out"}, + {61, "ECONNREFUSED", "connection refused"}, + {62, "ELOOP", "too many levels of symbolic links"}, + {63, "ENAMETOOLONG", "file name too long"}, + {64, "EHOSTDOWN", "host is down"}, + {65, "EHOSTUNREACH", "no route to host"}, + {66, "ENOTEMPTY", "directory not empty"}, + {67, "EPROCLIM", "too many processes"}, + {68, "EUSERS", "too many users"}, + {69, "EDQUOT", "disc quota exceeded"}, + {70, "ESTALE", "stale NFS file handle"}, + {71, "EREMOTE", "too many levels of remote in path"}, + {72, "EBADRPC", "RPC struct is bad"}, + {73, "ERPCMISMATCH", "RPC version wrong"}, + {74, "EPROGUNAVAIL", "RPC prog. not avail"}, + {75, "EPROGMISMATCH", "program version wrong"}, + {76, "EPROCUNAVAIL", "bad procedure for program"}, + {77, "ENOLCK", "no locks available"}, + {78, "ENOSYS", "function not implemented"}, + {79, "EFTYPE", "inappropriate file type or format"}, + {80, "EAUTH", "authentication error"}, + {81, "ENEEDAUTH", "need authenticator"}, + {82, "EIDRM", "identifier removed"}, + {83, "ENOMSG", "no message of desired type"}, + {84, "EOVERFLOW", "value too large to be stored in data type"}, + {85, "ECANCELED", "operation canceled"}, + {86, "EILSEQ", "illegal byte sequence"}, + {87, "ENOATTR", "attribute not found"}, + {88, "EDOOFUS", "programming error"}, + {89, "EBADMSG", "bad message"}, + {90, "EMULTIHOP", "multihop attempted"}, + {91, "ENOLINK", "link has been severed"}, + {92, "EPROTO", "protocol error"}, + {93, "ENOTCAPABLE", "capabilities insufficient"}, + {94, "ECAPMODE", "not permitted in capability mode"}, + {95, "ENOTRECOVERABLE", "state not recoverable"}, + {96, "EOWNERDEAD", "previous owner died"}, + {97, "EINTEGRITY", "integrity check failed"}, +} + +// Signal table +var signalList = [...]struct { + num syscall.Signal + name string + desc string +}{ + {1, "SIGHUP", "hangup"}, + {2, "SIGINT", "interrupt"}, + {3, "SIGQUIT", "quit"}, + {4, "SIGILL", "illegal instruction"}, + {5, "SIGTRAP", "trace/BPT trap"}, + {6, "SIGIOT", "abort trap"}, + {7, "SIGEMT", "EMT trap"}, + {8, "SIGFPE", "floating point exception"}, + {9, "SIGKILL", "killed"}, + {10, "SIGBUS", "bus error"}, + {11, "SIGSEGV", "segmentation fault"}, + {12, "SIGSYS", "bad system call"}, + {13, "SIGPIPE", "broken pipe"}, + {14, "SIGALRM", "alarm clock"}, + {15, "SIGTERM", "terminated"}, + {16, "SIGURG", "urgent I/O condition"}, + {17, "SIGSTOP", "suspended (signal)"}, + {18, "SIGTSTP", "suspended"}, + {19, "SIGCONT", "continued"}, + {20, "SIGCHLD", "child exited"}, + {21, "SIGTTIN", "stopped (tty input)"}, + {22, "SIGTTOU", "stopped (tty output)"}, + {23, "SIGIO", "I/O possible"}, + {24, "SIGXCPU", "cputime limit exceeded"}, + {25, "SIGXFSZ", "filesize limit exceeded"}, + {26, "SIGVTALRM", "virtual timer expired"}, + {27, "SIGPROF", "profiling timer expired"}, + {28, "SIGWINCH", "window size changes"}, + {29, "SIGINFO", "information request"}, + {30, "SIGUSR1", "user defined signal 1"}, + {31, "SIGUSR2", "user defined signal 2"}, + {32, "SIGTHR", "unknown signal"}, + {33, "SIGLIBRT", "unknown signal"}, +} diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index c0a43f8b..785d693e 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -140,6 +140,306 @@ const ( ARPHRD_VOID = 0xffff ARPHRD_VSOCKMON = 0x33a ARPHRD_X25 = 0x10f + AUDIT_ADD = 0x3eb + AUDIT_ADD_RULE = 0x3f3 + AUDIT_ALWAYS = 0x2 + AUDIT_ANOM_ABEND = 0x6a5 + AUDIT_ANOM_CREAT = 0x6a7 + AUDIT_ANOM_LINK = 0x6a6 + AUDIT_ANOM_PROMISCUOUS = 0x6a4 + AUDIT_ARCH = 0xb + AUDIT_ARCH_AARCH64 = 0xc00000b7 + AUDIT_ARCH_ALPHA = 0xc0009026 + AUDIT_ARCH_ARCOMPACT = 0x4000005d + AUDIT_ARCH_ARCOMPACTBE = 0x5d + AUDIT_ARCH_ARCV2 = 0x400000c3 + AUDIT_ARCH_ARCV2BE = 0xc3 + AUDIT_ARCH_ARM = 0x40000028 + AUDIT_ARCH_ARMEB = 0x28 + AUDIT_ARCH_C6X = 0x4000008c + AUDIT_ARCH_C6XBE = 0x8c + AUDIT_ARCH_CRIS = 0x4000004c + AUDIT_ARCH_CSKY = 0x400000fc + AUDIT_ARCH_FRV = 0x5441 + AUDIT_ARCH_H8300 = 0x2e + AUDIT_ARCH_HEXAGON = 0xa4 + AUDIT_ARCH_I386 = 0x40000003 + AUDIT_ARCH_IA64 = 0xc0000032 + AUDIT_ARCH_LOONGARCH32 = 0x40000102 + AUDIT_ARCH_LOONGARCH64 = 0xc0000102 + AUDIT_ARCH_M32R = 0x58 + AUDIT_ARCH_M68K = 0x4 + AUDIT_ARCH_MICROBLAZE = 0xbd + AUDIT_ARCH_MIPS = 0x8 + AUDIT_ARCH_MIPS64 = 0x80000008 + AUDIT_ARCH_MIPS64N32 = 0xa0000008 + AUDIT_ARCH_MIPSEL = 0x40000008 + AUDIT_ARCH_MIPSEL64 = 0xc0000008 + AUDIT_ARCH_MIPSEL64N32 = 0xe0000008 + AUDIT_ARCH_NDS32 = 0x400000a7 + AUDIT_ARCH_NDS32BE = 0xa7 + AUDIT_ARCH_NIOS2 = 0x40000071 + AUDIT_ARCH_OPENRISC = 0x5c + AUDIT_ARCH_PARISC = 0xf + AUDIT_ARCH_PARISC64 = 0x8000000f + AUDIT_ARCH_PPC = 0x14 + AUDIT_ARCH_PPC64 = 0x80000015 + AUDIT_ARCH_PPC64LE = 0xc0000015 + AUDIT_ARCH_RISCV32 = 0x400000f3 + AUDIT_ARCH_RISCV64 = 0xc00000f3 + AUDIT_ARCH_S390 = 0x16 + AUDIT_ARCH_S390X = 0x80000016 + AUDIT_ARCH_SH = 0x2a + AUDIT_ARCH_SH64 = 0x8000002a + AUDIT_ARCH_SHEL = 0x4000002a + AUDIT_ARCH_SHEL64 = 0xc000002a + AUDIT_ARCH_SPARC = 0x2 + AUDIT_ARCH_SPARC64 = 0x8000002b + AUDIT_ARCH_TILEGX = 0xc00000bf + AUDIT_ARCH_TILEGX32 = 0x400000bf + AUDIT_ARCH_TILEPRO = 0x400000bc + AUDIT_ARCH_UNICORE = 0x4000006e + AUDIT_ARCH_X86_64 = 0xc000003e + AUDIT_ARCH_XTENSA = 0x5e + AUDIT_ARG0 = 0xc8 + AUDIT_ARG1 = 0xc9 + AUDIT_ARG2 = 0xca + AUDIT_ARG3 = 0xcb + AUDIT_AVC = 0x578 + AUDIT_AVC_PATH = 0x57a + AUDIT_BITMASK_SIZE = 0x40 + AUDIT_BIT_MASK = 0x8000000 + AUDIT_BIT_TEST = 0x48000000 + AUDIT_BPF = 0x536 + AUDIT_BPRM_FCAPS = 0x529 + AUDIT_CAPSET = 0x52a + AUDIT_CLASS_CHATTR = 0x2 + AUDIT_CLASS_CHATTR_32 = 0x3 + AUDIT_CLASS_DIR_WRITE = 0x0 + AUDIT_CLASS_DIR_WRITE_32 = 0x1 + AUDIT_CLASS_READ = 0x4 + AUDIT_CLASS_READ_32 = 0x5 + AUDIT_CLASS_SIGNAL = 0x8 + AUDIT_CLASS_SIGNAL_32 = 0x9 + AUDIT_CLASS_WRITE = 0x6 + AUDIT_CLASS_WRITE_32 = 0x7 + AUDIT_COMPARE_AUID_TO_EUID = 0x10 + AUDIT_COMPARE_AUID_TO_FSUID = 0xe + AUDIT_COMPARE_AUID_TO_OBJ_UID = 0x5 + AUDIT_COMPARE_AUID_TO_SUID = 0xf + AUDIT_COMPARE_EGID_TO_FSGID = 0x17 + AUDIT_COMPARE_EGID_TO_OBJ_GID = 0x4 + AUDIT_COMPARE_EGID_TO_SGID = 0x18 + AUDIT_COMPARE_EUID_TO_FSUID = 0x12 + AUDIT_COMPARE_EUID_TO_OBJ_UID = 0x3 + AUDIT_COMPARE_EUID_TO_SUID = 0x11 + AUDIT_COMPARE_FSGID_TO_OBJ_GID = 0x9 + AUDIT_COMPARE_FSUID_TO_OBJ_UID = 0x8 + AUDIT_COMPARE_GID_TO_EGID = 0x14 + AUDIT_COMPARE_GID_TO_FSGID = 0x15 + AUDIT_COMPARE_GID_TO_OBJ_GID = 0x2 + AUDIT_COMPARE_GID_TO_SGID = 0x16 + AUDIT_COMPARE_SGID_TO_FSGID = 0x19 + AUDIT_COMPARE_SGID_TO_OBJ_GID = 0x7 + AUDIT_COMPARE_SUID_TO_FSUID = 0x13 + AUDIT_COMPARE_SUID_TO_OBJ_UID = 0x6 + AUDIT_COMPARE_UID_TO_AUID = 0xa + AUDIT_COMPARE_UID_TO_EUID = 0xb + AUDIT_COMPARE_UID_TO_FSUID = 0xc + AUDIT_COMPARE_UID_TO_OBJ_UID = 0x1 + AUDIT_COMPARE_UID_TO_SUID = 0xd + AUDIT_CONFIG_CHANGE = 0x519 + AUDIT_CWD = 0x51b + AUDIT_DAEMON_ABORT = 0x4b2 + AUDIT_DAEMON_CONFIG = 0x4b3 + AUDIT_DAEMON_END = 0x4b1 + AUDIT_DAEMON_START = 0x4b0 + AUDIT_DEL = 0x3ec + AUDIT_DEL_RULE = 0x3f4 + AUDIT_DEVMAJOR = 0x64 + AUDIT_DEVMINOR = 0x65 + AUDIT_DIR = 0x6b + AUDIT_DM_CTRL = 0x53a + AUDIT_DM_EVENT = 0x53b + AUDIT_EGID = 0x6 + AUDIT_EOE = 0x528 + AUDIT_EQUAL = 0x40000000 + AUDIT_EUID = 0x2 + AUDIT_EVENT_LISTENER = 0x537 + AUDIT_EXE = 0x70 + AUDIT_EXECVE = 0x51d + AUDIT_EXIT = 0x67 + AUDIT_FAIL_PANIC = 0x2 + AUDIT_FAIL_PRINTK = 0x1 + AUDIT_FAIL_SILENT = 0x0 + AUDIT_FANOTIFY = 0x533 + AUDIT_FD_PAIR = 0x525 + AUDIT_FEATURE_BITMAP_ALL = 0x7f + AUDIT_FEATURE_BITMAP_BACKLOG_LIMIT = 0x1 + AUDIT_FEATURE_BITMAP_BACKLOG_WAIT_TIME = 0x2 + AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND = 0x8 + AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH = 0x4 + AUDIT_FEATURE_BITMAP_FILTER_FS = 0x40 + AUDIT_FEATURE_BITMAP_LOST_RESET = 0x20 + AUDIT_FEATURE_BITMAP_SESSIONID_FILTER = 0x10 + AUDIT_FEATURE_CHANGE = 0x530 + AUDIT_FEATURE_LOGINUID_IMMUTABLE = 0x1 + AUDIT_FEATURE_ONLY_UNSET_LOGINUID = 0x0 + AUDIT_FEATURE_VERSION = 0x1 + AUDIT_FIELD_COMPARE = 0x6f + AUDIT_FILETYPE = 0x6c + AUDIT_FILTERKEY = 0xd2 + AUDIT_FILTER_ENTRY = 0x2 + AUDIT_FILTER_EXCLUDE = 0x5 + AUDIT_FILTER_EXIT = 0x4 + AUDIT_FILTER_FS = 0x6 + AUDIT_FILTER_PREPEND = 0x10 + AUDIT_FILTER_TASK = 0x1 + AUDIT_FILTER_TYPE = 0x5 + AUDIT_FILTER_URING_EXIT = 0x7 + AUDIT_FILTER_USER = 0x0 + AUDIT_FILTER_WATCH = 0x3 + AUDIT_FIRST_KERN_ANOM_MSG = 0x6a4 + AUDIT_FIRST_USER_MSG = 0x44c + AUDIT_FIRST_USER_MSG2 = 0x834 + AUDIT_FSGID = 0x8 + AUDIT_FSTYPE = 0x1a + AUDIT_FSUID = 0x4 + AUDIT_GET = 0x3e8 + AUDIT_GET_FEATURE = 0x3fb + AUDIT_GID = 0x5 + AUDIT_GREATER_THAN = 0x20000000 + AUDIT_GREATER_THAN_OR_EQUAL = 0x60000000 + AUDIT_INODE = 0x66 + AUDIT_INTEGRITY_DATA = 0x708 + AUDIT_INTEGRITY_EVM_XATTR = 0x70e + AUDIT_INTEGRITY_HASH = 0x70b + AUDIT_INTEGRITY_METADATA = 0x709 + AUDIT_INTEGRITY_PCR = 0x70c + AUDIT_INTEGRITY_POLICY_RULE = 0x70f + AUDIT_INTEGRITY_RULE = 0x70d + AUDIT_INTEGRITY_STATUS = 0x70a + AUDIT_IPC = 0x517 + AUDIT_IPC_SET_PERM = 0x51f + AUDIT_KERNEL = 0x7d0 + AUDIT_KERNEL_OTHER = 0x524 + AUDIT_KERN_MODULE = 0x532 + AUDIT_LAST_FEATURE = 0x1 + AUDIT_LAST_KERN_ANOM_MSG = 0x707 + AUDIT_LAST_USER_MSG = 0x4af + AUDIT_LAST_USER_MSG2 = 0xbb7 + AUDIT_LESS_THAN = 0x10000000 + AUDIT_LESS_THAN_OR_EQUAL = 0x50000000 + AUDIT_LIST = 0x3ea + AUDIT_LIST_RULES = 0x3f5 + AUDIT_LOGIN = 0x3ee + AUDIT_LOGINUID = 0x9 + AUDIT_LOGINUID_SET = 0x18 + AUDIT_MAC_CALIPSO_ADD = 0x58a + AUDIT_MAC_CALIPSO_DEL = 0x58b + AUDIT_MAC_CIPSOV4_ADD = 0x57f + AUDIT_MAC_CIPSOV4_DEL = 0x580 + AUDIT_MAC_CONFIG_CHANGE = 0x57d + AUDIT_MAC_IPSEC_ADDSA = 0x583 + AUDIT_MAC_IPSEC_ADDSPD = 0x585 + AUDIT_MAC_IPSEC_DELSA = 0x584 + AUDIT_MAC_IPSEC_DELSPD = 0x586 + AUDIT_MAC_IPSEC_EVENT = 0x587 + AUDIT_MAC_MAP_ADD = 0x581 + AUDIT_MAC_MAP_DEL = 0x582 + AUDIT_MAC_POLICY_LOAD = 0x57b + AUDIT_MAC_STATUS = 0x57c + AUDIT_MAC_UNLBL_ALLOW = 0x57e + AUDIT_MAC_UNLBL_STCADD = 0x588 + AUDIT_MAC_UNLBL_STCDEL = 0x589 + AUDIT_MAKE_EQUIV = 0x3f7 + AUDIT_MAX_FIELDS = 0x40 + AUDIT_MAX_FIELD_COMPARE = 0x19 + AUDIT_MAX_KEY_LEN = 0x100 + AUDIT_MESSAGE_TEXT_MAX = 0x2170 + AUDIT_MMAP = 0x52b + AUDIT_MQ_GETSETATTR = 0x523 + AUDIT_MQ_NOTIFY = 0x522 + AUDIT_MQ_OPEN = 0x520 + AUDIT_MQ_SENDRECV = 0x521 + AUDIT_MSGTYPE = 0xc + AUDIT_NEGATE = 0x80000000 + AUDIT_NETFILTER_CFG = 0x52d + AUDIT_NETFILTER_PKT = 0x52c + AUDIT_NEVER = 0x0 + AUDIT_NLGRP_MAX = 0x1 + AUDIT_NOT_EQUAL = 0x30000000 + AUDIT_NR_FILTERS = 0x8 + AUDIT_OBJ_GID = 0x6e + AUDIT_OBJ_LEV_HIGH = 0x17 + AUDIT_OBJ_LEV_LOW = 0x16 + AUDIT_OBJ_PID = 0x526 + AUDIT_OBJ_ROLE = 0x14 + AUDIT_OBJ_TYPE = 0x15 + AUDIT_OBJ_UID = 0x6d + AUDIT_OBJ_USER = 0x13 + AUDIT_OPENAT2 = 0x539 + AUDIT_OPERATORS = 0x78000000 + AUDIT_PATH = 0x516 + AUDIT_PERM = 0x6a + AUDIT_PERM_ATTR = 0x8 + AUDIT_PERM_EXEC = 0x1 + AUDIT_PERM_READ = 0x4 + AUDIT_PERM_WRITE = 0x2 + AUDIT_PERS = 0xa + AUDIT_PID = 0x0 + AUDIT_POSSIBLE = 0x1 + AUDIT_PPID = 0x12 + AUDIT_PROCTITLE = 0x52f + AUDIT_REPLACE = 0x531 + AUDIT_SADDR_FAM = 0x71 + AUDIT_SECCOMP = 0x52e + AUDIT_SELINUX_ERR = 0x579 + AUDIT_SESSIONID = 0x19 + AUDIT_SET = 0x3e9 + AUDIT_SET_FEATURE = 0x3fa + AUDIT_SGID = 0x7 + AUDIT_SID_UNSET = 0xffffffff + AUDIT_SIGNAL_INFO = 0x3f2 + AUDIT_SOCKADDR = 0x51a + AUDIT_SOCKETCALL = 0x518 + AUDIT_STATUS_BACKLOG_LIMIT = 0x10 + AUDIT_STATUS_BACKLOG_WAIT_TIME = 0x20 + AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL = 0x80 + AUDIT_STATUS_ENABLED = 0x1 + AUDIT_STATUS_FAILURE = 0x2 + AUDIT_STATUS_LOST = 0x40 + AUDIT_STATUS_PID = 0x4 + AUDIT_STATUS_RATE_LIMIT = 0x8 + AUDIT_SUBJ_CLR = 0x11 + AUDIT_SUBJ_ROLE = 0xe + AUDIT_SUBJ_SEN = 0x10 + AUDIT_SUBJ_TYPE = 0xf + AUDIT_SUBJ_USER = 0xd + AUDIT_SUCCESS = 0x68 + AUDIT_SUID = 0x3 + AUDIT_SYSCALL = 0x514 + AUDIT_SYSCALL_CLASSES = 0x10 + AUDIT_TIME_ADJNTPVAL = 0x535 + AUDIT_TIME_INJOFFSET = 0x534 + AUDIT_TRIM = 0x3f6 + AUDIT_TTY = 0x527 + AUDIT_TTY_GET = 0x3f8 + AUDIT_TTY_SET = 0x3f9 + AUDIT_UID = 0x1 + AUDIT_UID_UNSET = 0xffffffff + AUDIT_UNUSED_BITS = 0x7fffc00 + AUDIT_URINGOP = 0x538 + AUDIT_USER = 0x3ed + AUDIT_USER_AVC = 0x453 + AUDIT_USER_TTY = 0x464 + AUDIT_VERSION_BACKLOG_LIMIT = 0x1 + AUDIT_VERSION_BACKLOG_WAIT_TIME = 0x2 + AUDIT_VERSION_LATEST = 0x7f + AUDIT_WATCH = 0x69 + AUDIT_WATCH_INS = 0x3ef + AUDIT_WATCH_LIST = 0x3f1 + AUDIT_WATCH_REM = 0x3f0 AUTOFS_SUPER_MAGIC = 0x187 B0 = 0x0 B110 = 0x3 @@ -184,6 +484,7 @@ const ( BPF_F_ALLOW_MULTI = 0x2 BPF_F_ALLOW_OVERRIDE = 0x1 BPF_F_ANY_ALIGNMENT = 0x2 + BPF_F_KPROBE_MULTI_RETURN = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 @@ -191,6 +492,8 @@ const ( BPF_F_TEST_RND_HI32 = 0x4 BPF_F_TEST_RUN_ON_CPU = 0x1 BPF_F_TEST_STATE_FREQ = 0x8 + BPF_F_TEST_XDP_LIVE_FRAMES = 0x2 + BPF_F_XDP_HAS_FRAGS = 0x20 BPF_H = 0x8 BPF_IMM = 0x0 BPF_IND = 0x40 @@ -517,9 +820,9 @@ const ( DM_UUID_FLAG = 0x4000 DM_UUID_LEN = 0x81 DM_VERSION = 0xc138fd00 - DM_VERSION_EXTRA = "-ioctl (2021-03-22)" + DM_VERSION_EXTRA = "-ioctl (2022-02-22)" DM_VERSION_MAJOR = 0x4 - DM_VERSION_MINOR = 0x2d + DM_VERSION_MINOR = 0x2e DM_VERSION_PATCHLEVEL = 0x0 DT_BLK = 0x6 DT_CHR = 0x2 @@ -535,6 +838,55 @@ const ( EFD_SEMAPHORE = 0x1 EFIVARFS_MAGIC = 0xde5e81e4 EFS_SUPER_MAGIC = 0x414a53 + EM_386 = 0x3 + EM_486 = 0x6 + EM_68K = 0x4 + EM_860 = 0x7 + EM_88K = 0x5 + EM_AARCH64 = 0xb7 + EM_ALPHA = 0x9026 + EM_ALTERA_NIOS2 = 0x71 + EM_ARCOMPACT = 0x5d + EM_ARCV2 = 0xc3 + EM_ARM = 0x28 + EM_BLACKFIN = 0x6a + EM_BPF = 0xf7 + EM_CRIS = 0x4c + EM_CSKY = 0xfc + EM_CYGNUS_M32R = 0x9041 + EM_CYGNUS_MN10300 = 0xbeef + EM_FRV = 0x5441 + EM_H8_300 = 0x2e + EM_HEXAGON = 0xa4 + EM_IA_64 = 0x32 + EM_LOONGARCH = 0x102 + EM_M32 = 0x1 + EM_M32R = 0x58 + EM_MICROBLAZE = 0xbd + EM_MIPS = 0x8 + EM_MIPS_RS3_LE = 0xa + EM_MIPS_RS4_BE = 0xa + EM_MN10300 = 0x59 + EM_NDS32 = 0xa7 + EM_NONE = 0x0 + EM_OPENRISC = 0x5c + EM_PARISC = 0xf + EM_PPC = 0x14 + EM_PPC64 = 0x15 + EM_RISCV = 0xf3 + EM_S390 = 0x16 + EM_S390_OLD = 0xa390 + EM_SH = 0x2a + EM_SPARC = 0x2 + EM_SPARC32PLUS = 0x12 + EM_SPARCV9 = 0x2b + EM_SPU = 0x17 + EM_TILEGX = 0xbf + EM_TILEPRO = 0xbc + EM_TI_C6000 = 0x8c + EM_UNICORE = 0x6e + EM_X86_64 = 0x3e + EM_XTENSA = 0x5e ENCODING_DEFAULT = 0x0 ENCODING_FM_MARK = 0x3 ENCODING_FM_SPACE = 0x4 @@ -712,6 +1064,7 @@ const ( ETH_P_EDSA = 0xdada ETH_P_ERSPAN = 0x88be ETH_P_ERSPAN2 = 0x22eb + ETH_P_ETHERCAT = 0x88a4 ETH_P_FCOE = 0x8906 ETH_P_FIP = 0x8914 ETH_P_HDLC = 0x19 @@ -749,6 +1102,7 @@ const ( ETH_P_PPP_MP = 0x8 ETH_P_PPP_SES = 0x8864 ETH_P_PREAUTH = 0x88c7 + ETH_P_PROFINET = 0x8892 ETH_P_PRP = 0x88fb ETH_P_PUP = 0x200 ETH_P_PUPAT = 0x201 @@ -837,6 +1191,7 @@ const ( FAN_FS_ERROR = 0x8000 FAN_MARK_ADD = 0x1 FAN_MARK_DONT_FOLLOW = 0x4 + FAN_MARK_EVICTABLE = 0x200 FAN_MARK_FILESYSTEM = 0x100 FAN_MARK_FLUSH = 0x80 FAN_MARK_IGNORED_MASK = 0x20 @@ -1055,7 +1410,7 @@ const ( IFA_F_STABLE_PRIVACY = 0x800 IFA_F_TEMPORARY = 0x1 IFA_F_TENTATIVE = 0x40 - IFA_MAX = 0xa + IFA_MAX = 0xb IFF_ALLMULTI = 0x200 IFF_ATTACH_QUEUE = 0x200 IFF_AUTOMEDIA = 0x4000 @@ -1403,6 +1758,7 @@ const ( LANDLOCK_ACCESS_FS_MAKE_SYM = 0x1000 LANDLOCK_ACCESS_FS_READ_DIR = 0x8 LANDLOCK_ACCESS_FS_READ_FILE = 0x4 + LANDLOCK_ACCESS_FS_REFER = 0x2000 LANDLOCK_ACCESS_FS_REMOVE_DIR = 0x10 LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20 LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2 @@ -1758,6 +2114,7 @@ const ( NLM_F_ACK_TLVS = 0x200 NLM_F_APPEND = 0x800 NLM_F_ATOMIC = 0x400 + NLM_F_BULK = 0x200 NLM_F_CAPPED = 0x100 NLM_F_CREATE = 0x400 NLM_F_DUMP = 0x300 @@ -2075,6 +2432,11 @@ const ( PR_SET_UNALIGN = 0x6 PR_SET_VMA = 0x53564d41 PR_SET_VMA_ANON_NAME = 0x0 + PR_SME_GET_VL = 0x40 + PR_SME_SET_VL = 0x3f + PR_SME_SET_VL_ONEXEC = 0x40000 + PR_SME_VL_INHERIT = 0x20000 + PR_SME_VL_LEN_MASK = 0xffff PR_SPEC_DISABLE = 0x4 PR_SPEC_DISABLE_NOEXEC = 0x10 PR_SPEC_ENABLE = 0x2 @@ -2227,8 +2589,9 @@ const ( RTC_FEATURE_ALARM = 0x0 RTC_FEATURE_ALARM_RES_2S = 0x3 RTC_FEATURE_ALARM_RES_MINUTE = 0x1 + RTC_FEATURE_ALARM_WAKEUP_ONLY = 0x7 RTC_FEATURE_BACKUP_SWITCH_MODE = 0x6 - RTC_FEATURE_CNT = 0x7 + RTC_FEATURE_CNT = 0x8 RTC_FEATURE_CORRECTION = 0x5 RTC_FEATURE_NEED_WEEK_DAY = 0x2 RTC_FEATURE_UPDATE_INTERRUPT = 0x4 @@ -2302,6 +2665,7 @@ const ( RTM_DELRULE = 0x21 RTM_DELTCLASS = 0x29 RTM_DELTFILTER = 0x2d + RTM_DELTUNNEL = 0x79 RTM_DELVLAN = 0x71 RTM_F_CLONED = 0x200 RTM_F_EQUALIZE = 0x400 @@ -2334,8 +2698,9 @@ const ( RTM_GETSTATS = 0x5e RTM_GETTCLASS = 0x2a RTM_GETTFILTER = 0x2e + RTM_GETTUNNEL = 0x7a RTM_GETVLAN = 0x72 - RTM_MAX = 0x77 + RTM_MAX = 0x7b RTM_NEWACTION = 0x30 RTM_NEWADDR = 0x14 RTM_NEWADDRLABEL = 0x48 @@ -2359,11 +2724,13 @@ const ( RTM_NEWSTATS = 0x5c RTM_NEWTCLASS = 0x28 RTM_NEWTFILTER = 0x2c - RTM_NR_FAMILIES = 0x1a - RTM_NR_MSGTYPES = 0x68 + RTM_NEWTUNNEL = 0x78 + RTM_NR_FAMILIES = 0x1b + RTM_NR_MSGTYPES = 0x6c RTM_SETDCB = 0x4f RTM_SETLINK = 0x13 RTM_SETNEIGHTBL = 0x43 + RTM_SETSTATS = 0x5f RTNH_ALIGNTO = 0x4 RTNH_COMPARE_MASK = 0x59 RTNH_F_DEAD = 0x1 @@ -2544,6 +2911,9 @@ const ( SOCK_RDM = 0x4 SOCK_SEQPACKET = 0x5 SOCK_SNDBUF_LOCK = 0x1 + SOCK_TXREHASH_DEFAULT = 0xff + SOCK_TXREHASH_DISABLED = 0x0 + SOCK_TXREHASH_ENABLED = 0x1 SOL_AAL = 0x109 SOL_ALG = 0x117 SOL_ATM = 0x108 @@ -2559,6 +2929,8 @@ const ( SOL_IUCV = 0x115 SOL_KCM = 0x119 SOL_LLC = 0x10c + SOL_MCTP = 0x11d + SOL_MPTCP = 0x11c SOL_NETBEUI = 0x10b SOL_NETLINK = 0x10e SOL_NFC = 0x118 @@ -2568,6 +2940,7 @@ const ( SOL_RAW = 0xff SOL_RDS = 0x114 SOL_RXRPC = 0x110 + SOL_SMC = 0x11e SOL_TCP = 0x6 SOL_TIPC = 0x10f SOL_TLS = 0x11a @@ -2674,7 +3047,7 @@ const ( TASKSTATS_GENL_NAME = "TASKSTATS" TASKSTATS_GENL_VERSION = 0x1 TASKSTATS_TYPE_MAX = 0x6 - TASKSTATS_VERSION = 0xb + TASKSTATS_VERSION = 0xd TCIFLUSH = 0x0 TCIOFF = 0x2 TCIOFLUSH = 0x2 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 1b305fab..274e2dab 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -326,6 +326,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -350,6 +351,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 6bcdef5d..95b6eeed 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -327,6 +327,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -351,6 +352,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index e65df0f8..918cd130 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -333,6 +333,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -357,6 +358,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index c7021115..3907dc5a 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -323,6 +323,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -347,6 +348,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 @@ -511,6 +513,7 @@ const ( WORDSIZE = 0x40 XCASE = 0x4 XTABS = 0x1800 + ZA_MAGIC = 0x54366345 _HIDIOCGRAWNAME = 0x80804804 _HIDIOCGRAWPHYS = 0x80404805 _HIDIOCGRAWUNIQ = 0x80404808 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 0d83a1cd..03d5c105 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -109,8 +109,6 @@ const ( IUCLC = 0x200 IXOFF = 0x1000 IXON = 0x400 - LASX_CTX_MAGIC = 0x41535801 - LSX_CTX_MAGIC = 0x53580001 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 MAP_DENYWRITE = 0x800 @@ -319,6 +317,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -343,6 +342,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 7f44a495..bd794e01 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -326,6 +326,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x1004 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 @@ -351,6 +352,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x1008 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 2f92b4e4..6c741b05 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -326,6 +326,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x1004 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 @@ -351,6 +352,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x1008 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index f5367a96..807b8cd2 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -326,6 +326,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x1004 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 @@ -351,6 +352,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x1008 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 2e22337d..a39e4f5c 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -326,6 +326,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x1004 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x1006 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x1006 @@ -351,6 +352,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x1008 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 858c4f30..c0fcda86 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -381,6 +381,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x10 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 @@ -405,6 +406,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index af2a7ba6..f3b72407 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -385,6 +385,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x10 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 @@ -409,6 +410,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index eaa2eb8e..72f2a45d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -385,6 +385,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x10 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x12 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x12 @@ -409,6 +410,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index faaa9f06..45b214b4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -314,6 +314,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -338,6 +339,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 0d161f0b..1897f207 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -389,6 +389,7 @@ const ( SO_RCVBUF = 0x8 SO_RCVBUFFORCE = 0x21 SO_RCVLOWAT = 0x12 + SO_RCVMARK = 0x4b SO_RCVTIMEO = 0x14 SO_RCVTIMEO_NEW = 0x42 SO_RCVTIMEO_OLD = 0x14 @@ -413,6 +414,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPNS_OLD = 0x23 SO_TIMESTAMP_NEW = 0x3f + SO_TXREHASH = 0x4a SO_TXTIME = 0x3d SO_TYPE = 0x3 SO_WIFI_STATUS = 0x29 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 4fd497a3..1fb7a395 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -380,6 +380,7 @@ const ( SO_RCVBUF = 0x1002 SO_RCVBUFFORCE = 0x100b SO_RCVLOWAT = 0x800 + SO_RCVMARK = 0x54 SO_RCVTIMEO = 0x2000 SO_RCVTIMEO_NEW = 0x44 SO_RCVTIMEO_OLD = 0x2000 @@ -404,6 +405,7 @@ const ( SO_TIMESTAMPNS_NEW = 0x42 SO_TIMESTAMPNS_OLD = 0x21 SO_TIMESTAMP_NEW = 0x46 + SO_TXREHASH = 0x53 SO_TXTIME = 0x3f SO_TYPE = 0x1008 SO_WIFI_STATUS = 0x25 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go index e9d9997e..039c4aa0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go @@ -912,7 +912,7 @@ func Fpathconf(fd int, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat(fd int, stat *stat_freebsd11_t) (err error) { +func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -922,17 +922,7 @@ func fstat(fd int, stat *stat_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat_freebsd12(fd int, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_FSTAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) { +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -947,22 +937,7 @@ func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { +func Fstatfs(fd int, stat *Statfs_t) (err error) { _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -972,16 +947,6 @@ func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatfs_freebsd12(fd int, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_FSTATFS_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fsync(fd int) (err error) { _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) if e1 != 0 { @@ -1002,7 +967,7 @@ func Ftruncate(fd int, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { +func getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) @@ -1019,23 +984,6 @@ func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error) { - var _p0 unsafe.Pointer - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_GETDIRENTRIES_FREEBSD12, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getdtablesize() (size int) { r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) size = int(r0) @@ -1257,21 +1205,6 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func lstat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Mkdir(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1317,43 +1250,13 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknod(path string, mode uint32, dev int) (err error) { +func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { return } - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat(fd int, path string, mode uint32, dev int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), uintptr(dev>>32), 0) + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), uintptr(dev>>32), 0) if e1 != 0 { err = errnoErr(e1) } @@ -1753,22 +1656,7 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func stat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func statfs(path string, stat *statfs_freebsd11_t) (err error) { +func Statfs(path string, stat *Statfs_t) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1783,21 +1671,6 @@ func statfs(path string, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func statfs_freebsd12(path string, stat *Statfs_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STATFS_FREEBSD12, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Symlink(path string, link string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go index edd373b1..0535d3cf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go @@ -912,7 +912,7 @@ func Fpathconf(fd int, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat(fd int, stat *stat_freebsd11_t) (err error) { +func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -922,17 +922,7 @@ func fstat(fd int, stat *stat_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat_freebsd12(fd int, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_FSTAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) { +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -947,22 +937,7 @@ func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { +func Fstatfs(fd int, stat *Statfs_t) (err error) { _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -972,16 +947,6 @@ func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatfs_freebsd12(fd int, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_FSTATFS_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fsync(fd int) (err error) { _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) if e1 != 0 { @@ -1002,7 +967,7 @@ func Ftruncate(fd int, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { +func getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) @@ -1019,23 +984,6 @@ func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error) { - var _p0 unsafe.Pointer - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_GETDIRENTRIES_FREEBSD12, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getdtablesize() (size int) { r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) size = int(r0) @@ -1257,21 +1205,6 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func lstat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Mkdir(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1317,22 +1250,7 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknod(path string, mode uint32, dev int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat(fd int, path string, mode uint32, dev int) (err error) { +func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1347,21 +1265,6 @@ func mknodat(fd int, path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { @@ -1753,22 +1656,7 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func stat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func statfs(path string, stat *statfs_freebsd11_t) (err error) { +func Statfs(path string, stat *Statfs_t) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1783,21 +1671,6 @@ func statfs(path string, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func statfs_freebsd12(path string, stat *Statfs_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STATFS_FREEBSD12, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Symlink(path string, link string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go index 82e9764b..1018b522 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go @@ -351,22 +351,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { - var _p0 unsafe.Pointer - if len(mib) > 0 { - _p0 = unsafe.Pointer(&mib[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func pipe2(p *[2]_C_int, flags int) (err error) { _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) if e1 != 0 { @@ -404,6 +388,22 @@ func ioctl(fd int, req uint, arg uintptr) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ptrace(request int, pid int, addr uintptr, data int) (err error) { _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) if e1 != 0 { @@ -912,7 +912,7 @@ func Fpathconf(fd int, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat(fd int, stat *stat_freebsd11_t) (err error) { +func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -922,17 +922,7 @@ func fstat(fd int, stat *stat_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat_freebsd12(fd int, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_FSTAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) { +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -947,22 +937,7 @@ func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { +func Fstatfs(fd int, stat *Statfs_t) (err error) { _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -972,16 +947,6 @@ func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatfs_freebsd12(fd int, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_FSTATFS_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fsync(fd int) (err error) { _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) if e1 != 0 { @@ -1002,7 +967,7 @@ func Ftruncate(fd int, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { +func getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) @@ -1019,23 +984,6 @@ func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error) { - var _p0 unsafe.Pointer - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_GETDIRENTRIES_FREEBSD12, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getdtablesize() (size int) { r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) size = int(r0) @@ -1257,21 +1205,6 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func lstat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Mkdir(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1317,43 +1250,13 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknod(path string, mode uint32, dev int) (err error) { +func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { return } - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat(fd int, path string, mode uint32, dev int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0, uintptr(dev), uintptr(dev>>32)) if e1 != 0 { err = errnoErr(e1) } @@ -1753,22 +1656,7 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func stat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func statfs(path string, stat *statfs_freebsd11_t) (err error) { +func Statfs(path string, stat *Statfs_t) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1783,21 +1671,6 @@ func statfs(path string, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func statfs_freebsd12(path string, stat *Statfs_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STATFS_FREEBSD12, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Symlink(path string, link string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go index a6479acd..3802f4b3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go @@ -912,7 +912,7 @@ func Fpathconf(fd int, name int) (val int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat(fd int, stat *stat_freebsd11_t) (err error) { +func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -922,17 +922,7 @@ func fstat(fd int, stat *stat_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstat_freebsd12(fd int, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_FSTAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) { +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -947,22 +937,7 @@ func fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { +func Fstatfs(fd int, stat *Statfs_t) (err error) { _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { err = errnoErr(e1) @@ -972,16 +947,6 @@ func fstatfs(fd int, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatfs_freebsd12(fd int, stat *Statfs_t) (err error) { - _, _, e1 := Syscall(SYS_FSTATFS_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fsync(fd int) (err error) { _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) if e1 != 0 { @@ -1002,7 +967,7 @@ func Ftruncate(fd int, length int64) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { +func getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) { var _p0 unsafe.Pointer if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) @@ -1019,23 +984,6 @@ func getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error) { - var _p0 unsafe.Pointer - if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) - } else { - _p0 = unsafe.Pointer(&_zero) - } - r0, _, e1 := Syscall6(SYS_GETDIRENTRIES_FREEBSD12, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) - n = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Getdtablesize() (size int) { r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) size = int(r0) @@ -1257,21 +1205,6 @@ func Listen(s int, backlog int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func lstat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Mkdir(path string, mode uint32) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1317,22 +1250,7 @@ func Mkfifo(path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknod(path string, mode uint32, dev int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func mknodat(fd int, path string, mode uint32, dev int) (err error) { +func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1347,21 +1265,6 @@ func mknodat(fd int, path string, mode uint32, dev int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_MKNODAT_FREEBSD12, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Nanosleep(time *Timespec, leftover *Timespec) (err error) { _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) if e1 != 0 { @@ -1753,22 +1656,7 @@ func Setuid(uid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func stat(path string, stat *stat_freebsd11_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func statfs(path string, stat *statfs_freebsd11_t) (err error) { +func Statfs(path string, stat *Statfs_t) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) if err != nil { @@ -1783,21 +1671,6 @@ func statfs(path string, stat *statfs_freebsd11_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func statfs_freebsd12(path string, stat *Statfs_t) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall(SYS_STATFS_FREEBSD12, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Symlink(path string, link string) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go new file mode 100644 index 00000000..8a2db7da --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go @@ -0,0 +1,1889 @@ +// go run mksyscall.go -tags freebsd,riscv64 syscall_bsd.go syscall_freebsd.go syscall_freebsd_riscv64.go +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build freebsd && riscv64 +// +build freebsd,riscv64 + +package unix + +import ( + "syscall" + "unsafe" +) + +var _ syscall.Errno + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(ngid int, gid *_Gid_t) (n int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setgroups(ngid int, gid *_Gid_t) (err error) { + _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) { + r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0) + wpid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { + r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(s int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, nevent int, timeout *Timespec) (n int, err error) { + r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimes(path string, timeval *[2]Timeval) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func futimes(fd int, timeval *[2]Timeval) (err error) { + _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func poll(fds *PollFd, nfds int, timeout int) (n int, err error) { + r0, _, e1 := Syscall(SYS_POLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Madvise(b []byte, behav int) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(behav)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mlock(b []byte) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MLOCK, uintptr(_p0), uintptr(len(b)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mlockall(flags int) (err error) { + _, _, e1 := Syscall(SYS_MLOCKALL, uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mprotect(b []byte, prot int) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MPROTECT, uintptr(_p0), uintptr(len(b)), uintptr(prot)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Msync(b []byte, flags int) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MSYNC, uintptr(_p0), uintptr(len(b)), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Munlock(b []byte) (err error) { + var _p0 unsafe.Pointer + if len(b) > 0 { + _p0 = unsafe.Pointer(&b[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall(SYS_MUNLOCK, uintptr(_p0), uintptr(len(b)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Munlockall() (err error) { + _, _, e1 := Syscall(SYS_MUNLOCKALL, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getcwd(buf []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS___GETCWD, uintptr(_p0), uintptr(len(buf)), 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ioctl(fd int, req uint, arg uintptr) (err error) { + _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ptrace(request int, pid int, addr uintptr, data int) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Access(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { + _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func CapEnter() (err error) { + _, _, e1 := Syscall(SYS_CAP_ENTER, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func capRightsGet(version int, fd int, rightsp *CapRights) (err error) { + _, _, e1 := Syscall(SYS___CAP_RIGHTS_GET, uintptr(version), uintptr(fd), uintptr(unsafe.Pointer(rightsp))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func capRightsLimit(fd int, rightsp *CapRights) (err error) { + _, _, e1 := Syscall(SYS_CAP_RIGHTS_LIMIT, uintptr(fd), uintptr(unsafe.Pointer(rightsp)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chmod(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chroot(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(fd int) (nfd int, err error) { + r0, _, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0) + nfd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup2(from int, to int) (err error) { + _, _, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Exit(code int) { + Syscall(SYS_EXIT, uintptr(code), 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrGetFd(fd int, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_GET_FD, uintptr(fd), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p0)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrSetFd(fd int, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_SET_FD, uintptr(fd), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p0)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrDeleteFd(fd int, attrnamespace int, attrname string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(attrname) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXTATTR_DELETE_FD, uintptr(fd), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p0))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrListFd(fd int, attrnamespace int, data uintptr, nbytes int) (ret int, err error) { + r0, _, e1 := Syscall6(SYS_EXTATTR_LIST_FD, uintptr(fd), uintptr(attrnamespace), uintptr(data), uintptr(nbytes), 0, 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrGetFile(file string, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(file) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_GET_FILE, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrSetFile(file string, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(file) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_SET_FILE, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrDeleteFile(file string, attrnamespace int, attrname string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(file) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXTATTR_DELETE_FILE, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrListFile(file string, attrnamespace int, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(file) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_LIST_FILE, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(data), uintptr(nbytes), 0, 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrGetLink(link string, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(link) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_GET_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrSetLink(link string, attrnamespace int, attrname string, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(link) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_SET_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1)), uintptr(data), uintptr(nbytes), 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrDeleteLink(link string, attrnamespace int, attrname string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(link) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(attrname) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXTATTR_DELETE_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(unsafe.Pointer(_p1))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ExtattrListLink(link string, attrnamespace int, data uintptr, nbytes int) (ret int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(link) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_EXTATTR_LIST_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(attrnamespace), uintptr(data), uintptr(nbytes), 0, 0) + ret = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fadvise(fd int, offset int64, length int64, advice int) (err error) { + _, _, e1 := Syscall6(SYS_POSIX_FADVISE, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchdir(fd int) (err error) { + _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchflags(fd int, flags int) (err error) { + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(fd), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmod(fd int, mode uint32) (err error) { + _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flock(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fpathconf(fd int, name int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, stat *Stat_t) (err error) { + _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatfs(fd int, stat *Statfs_t) (err error) { + _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getdirentries(fd int, buf []byte, basep *uint64) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdtablesize() (size int) { + r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) + size = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0) + pgid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgrp() (pgrp int) { + r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0) + pgrp = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpid() (pid int) { + r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getppid() (ppid int) { + r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0) + ppid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpriority(which int, who int) (prio int, err error) { + r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0) + prio = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getsid(pid int) (sid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0) + sid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Gettimeofday(tv *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Issetugid() (tainted bool) { + r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0) + tainted = bool(r0 != 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kill(pid int, signum syscall.Signal) (err error) { + _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kqueue() (fd int, err error) { + r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lchown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Link(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_LINKAT, uintptr(pathfd), uintptr(unsafe.Pointer(_p0)), uintptr(linkfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, backlog int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdir(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdirat(dirfd int, path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkfifo(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Nanosleep(time *Timespec, leftover *Timespec) (err error) { + _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Open(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Openat(fdat int, path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(fdat), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pathconf(path string, name int) (val int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Readlink(path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Readlinkat(dirfd int, path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_READLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rename(from string, to string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Renameat(fromfd int, from string, tofd int, to string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(fromfd), uintptr(unsafe.Pointer(_p0)), uintptr(tofd), uintptr(unsafe.Pointer(_p1)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Revoke(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rmdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { + r0, _, e1 := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence)) + newoffset = int64(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) { + r0, _, e1 := Syscall6(SYS_SELECT, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setegid(egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seteuid(euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setgid(gid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setlogin(name string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpgid(pid int, pgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpriority(which int, who int, prio int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setregid(rgid int, egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setreuid(ruid int, euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setresgid(rgid int, egid int, sgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setresuid(ruid int, euid int, suid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Settimeofday(tp *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Statfs(path string, stat *Statfs_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Symlink(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(oldpath) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(newpath) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINKAT, uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1))) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sync() (err error) { + _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Umask(newmask int) (oldmask int) { + r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0) + oldmask = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Undelete(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unlink(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unlinkat(dirfd int, path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unmount(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func write(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) { + r0, _, e1 := Syscall6(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos)) + ret = uintptr(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func munmap(addr uintptr, length uintptr) (err error) { + _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept4(fd int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (nfd int, err error) { + r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0) + nfd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go index 8cdfbe71..523f2ba0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go @@ -83,31 +83,6 @@ func Fchown(fd int, uid int, gid int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Fstat(fd int, stat *Stat_t) (err error) { - _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_FSTATAT, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Fstatfs(fd int, buf *Statfs_t) (err error) { _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go index a1a9bcbb..1239cc2d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go @@ -180,6 +180,17 @@ func Listen(s int, n int) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func MemfdSecret(flags int) (fd int, err error) { + r0, _, e1 := Syscall(SYS_MEMFD_SECRET, uintptr(flags), 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func pread(fd int, p []byte, offset int64) (n int, err error) { var _p0 unsafe.Pointer if len(p) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index d12f4fbf..fdf53f8d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -66,6 +66,7 @@ import ( //go:cgo_import_dynamic libc_getpriority getpriority "libc.so" //go:cgo_import_dynamic libc_getrlimit getrlimit "libc.so" //go:cgo_import_dynamic libc_getrusage getrusage "libc.so" +//go:cgo_import_dynamic libc_getsid getsid "libc.so" //go:cgo_import_dynamic libc_gettimeofday gettimeofday "libc.so" //go:cgo_import_dynamic libc_getuid getuid "libc.so" //go:cgo_import_dynamic libc_kill kill "libc.so" @@ -202,6 +203,7 @@ import ( //go:linkname procGetpriority libc_getpriority //go:linkname procGetrlimit libc_getrlimit //go:linkname procGetrusage libc_getrusage +//go:linkname procGetsid libc_getsid //go:linkname procGettimeofday libc_gettimeofday //go:linkname procGetuid libc_getuid //go:linkname procKill libc_kill @@ -339,6 +341,7 @@ var ( procGetpriority, procGetrlimit, procGetrusage, + procGetsid, procGettimeofday, procGetuid, procKill, @@ -1044,6 +1047,17 @@ func Getrusage(who int, rusage *Rusage) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func Getsid(pid int) (sid int, err error) { + r0, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&procGetsid)), 1, uintptr(pid), 0, 0, 0, 0, 0) + sid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Gettimeofday(tv *Timeval) (err error) { _, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&procGettimeofday)), 1, uintptr(unsafe.Pointer(tv)), 0, 0, 0, 0, 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go index 59d5dfc2..4e0d9610 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go @@ -1,4 +1,4 @@ -// go run mksysnum.go https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master +// go run mksysnum.go https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && freebsd @@ -19,10 +19,9 @@ const ( SYS_UNLINK = 10 // { int unlink(char *path); } SYS_CHDIR = 12 // { int chdir(char *path); } SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); } SYS_CHMOD = 15 // { int chmod(char *path, int mode); } SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } - SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int + SYS_BREAK = 17 // { caddr_t break(char *nsize); } SYS_GETPID = 20 // { pid_t getpid(void); } SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, caddr_t data); } SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } @@ -43,7 +42,6 @@ const ( SYS_KILL = 37 // { int kill(int pid, int signum); } SYS_GETPPID = 39 // { pid_t getppid(void); } SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } SYS_GETEGID = 43 // { gid_t getegid(void); } SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, size_t offset, u_int scale); } SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, int pid); } @@ -58,15 +56,14 @@ const ( SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, size_t count); } SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); } - SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int + SYS_UMASK = 60 // { int umask(int newmask); } SYS_CHROOT = 61 // { int chroot(char *path); } SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); } SYS_VFORK = 66 // { int vfork(void); } SYS_SBRK = 69 // { int sbrk(int incr); } SYS_SSTK = 70 // { int sstk(int incr); } - SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise ovadvise_args int SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, int prot); } + SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); } SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); } SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, char *vec); } SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } @@ -124,14 +121,10 @@ const ( SYS_SETGID = 181 // { int setgid(gid_t gid); } SYS_SETEGID = 182 // { int setegid(gid_t egid); } SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_STAT = 188 // { int stat(char *path, struct stat *ub); } - SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); } - SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); } SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } getrlimit __getrlimit_args int SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } setrlimit __setrlimit_args int - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } __sysctl sysctl_args int SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } @@ -143,12 +136,12 @@ const ( SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, size_t nsops); } SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } SYS_SHMAT = 228 // { int shmat(int shmid, const void *shmaddr, int shmflg); } SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); } SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); } SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, struct timespec *tp); } - SYS_CLOCK_SETTIME = 233 // { int clock_settime( clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, const struct timespec *tp); } SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, struct timespec *tp); } SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, struct sigevent *evp, int *timerid); } SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } @@ -157,50 +150,44 @@ const ( SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } - SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( struct ffclock_estimate *cest); } - SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( struct ffclock_estimate *cest); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate(struct ffclock_estimate *cest); } + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate(struct ffclock_estimate *cest); } SYS_CLOCK_NANOSLEEP = 244 // { int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); } - SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id,int which, clockid_t *clock_id); } + SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id, int which, clockid_t *clock_id); } SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } SYS_RFORK = 251 // { int rfork(int flags); } - SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, int timeout); } SYS_ISSETUGID = 253 // { int issetugid(void); } SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } SYS_AIO_READ = 255 // { int aio_read(struct aiocb *aiocbp); } SYS_AIO_WRITE = 256 // { int aio_write(struct aiocb *aiocbp); } - SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); } - SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, size_t count); } + SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb* const *acb_list, int nent, struct sigevent *sig); } SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); } - SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); } - SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); } - SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); } SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); } - SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } SYS_MODNEXT = 300 // { int modnext(int modid); } - SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat *stat); } + SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); } SYS_MODFNEXT = 302 // { int modfnext(int modid); } SYS_MODFIND = 303 // { int modfind(const char *name); } SYS_KLDLOAD = 304 // { int kldload(const char *file); } SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } SYS_KLDFIND = 306 // { int kldfind(const char *file); } SYS_KLDNEXT = 307 // { int kldnext(int fileid); } - SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat *stat); } SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } SYS_GETSID = 310 // { int getsid(pid_t pid); } SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } SYS_AIO_RETURN = 314 // { ssize_t aio_return(struct aiocb *aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend( struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } + SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); } SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); } SYS_YIELD = 321 // { int yield(void); } SYS_MLOCKALL = 324 // { int mlockall(int how); } SYS_MUNLOCKALL = 325 // { int munlockall(void); } - SYS___GETCWD = 326 // { int __getcwd(char *buf, u_int buflen); } + SYS___GETCWD = 326 // { int __getcwd(char *buf, size_t buflen); } SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); } SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); } SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } @@ -226,14 +213,13 @@ const ( SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); } SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); } SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } - SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } - SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete( struct aiocb **aiocbp, struct timespec *timeout); } + SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } SYS_EXTATTR_SET_FD = 371 // { ssize_t extattr_set_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } @@ -251,10 +237,6 @@ const ( SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); } SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); } SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, int call, void *arg); } - SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } - SYS_STATFS = 396 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); } - SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } SYS_KSEM_CLOSE = 400 // { int ksem_close(semid_t id); } SYS_KSEM_POST = 401 // { int ksem_post(semid_t id); } SYS_KSEM_WAIT = 402 // { int ksem_wait(semid_t id); } @@ -267,14 +249,14 @@ const ( SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, struct mac *mac_p); } SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, struct mac *mac_p); } - SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, char **envv, struct mac *mac_p); } SYS_SIGACTION = 416 // { int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); } - SYS_SIGRETURN = 417 // { int sigreturn( const struct __ucontext *sigcntxp); } + SYS_SIGRETURN = 417 // { int sigreturn(const struct __ucontext *sigcntxp); } SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } - SYS_SETCONTEXT = 422 // { int setcontext( const struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext(const struct __ucontext *ucp); } SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, const struct __ucontext *ucp); } SYS_SWAPOFF = 424 // { int swapoff(const char *name); } SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); } @@ -288,10 +270,10 @@ const ( SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( const char *path, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } SYS_KSEM_TIMEDWAIT = 441 // { int ksem_timedwait(semid_t id, const struct timespec *abstime); } - SYS_THR_SUSPEND = 442 // { int thr_suspend( const struct timespec *timeout); } + SYS_THR_SUSPEND = 442 // { int thr_suspend(const struct timespec *timeout); } SYS_THR_WAKE = 443 // { int thr_wake(long id); } SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } SYS_AUDIT = 445 // { int audit(const void *record, u_int length); } @@ -300,17 +282,17 @@ const ( SYS_SETAUID = 448 // { int setauid(uid_t *auid); } SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } - SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } SYS_AUDITCTL = 453 // { int auditctl(char *path); } SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, u_long val, void *uaddr1, void *uaddr2); } SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, int param_size); } SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } SYS_KMQ_OPEN = 457 // { int kmq_open(const char *path, int flags, mode_t mode, const struct mq_attr *attr); } - SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } - SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } - SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len,unsigned msg_prio, const struct timespec *abs_timeout);} - SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } + SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } + SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } SYS_KMQ_UNLINK = 462 // { int kmq_unlink(const char *path); } SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } @@ -319,7 +301,7 @@ const ( SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } - SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr * from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); } SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); } SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } @@ -338,14 +320,12 @@ const ( SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, int flag); } SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag); } SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, char **envv); } - SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, struct timeval *times); } SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, char *path2, int flag); } SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } - SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, mode_t mode); } - SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, size_t bufsize); } + SYS_READLINKAT = 500 // { ssize_t readlinkat(int fd, char *path, char *buf, size_t bufsize); } SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, char *new); } SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, char *path2); } SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } @@ -391,7 +371,24 @@ const ( SYS_PPOLL = 545 // { int ppoll(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *set); } SYS_FUTIMENS = 546 // { int futimens(int fd, struct timespec *times); } SYS_UTIMENSAT = 547 // { int utimensat(int fd, char *path, struct timespec *times, int flag); } - SYS_NUMA_GETAFFINITY = 548 // { int numa_getaffinity(cpuwhich_t which, id_t id, struct vm_domain_policy_entry *policy); } - SYS_NUMA_SETAFFINITY = 549 // { int numa_setaffinity(cpuwhich_t which, id_t id, const struct vm_domain_policy_entry *policy); } SYS_FDATASYNC = 550 // { int fdatasync(int fd); } + SYS_FSTAT = 551 // { int fstat(int fd, struct stat *sb); } + SYS_FSTATAT = 552 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } + SYS_FHSTAT = 553 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } + SYS_GETDIRENTRIES = 554 // { ssize_t getdirentries(int fd, char *buf, size_t count, off_t *basep); } + SYS_STATFS = 555 // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 556 // { int fstatfs(int fd, struct statfs *buf); } + SYS_GETFSSTAT = 557 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } + SYS_FHSTATFS = 558 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } + SYS_MKNODAT = 559 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } + SYS_KEVENT = 560 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_CPUSET_GETDOMAIN = 561 // { int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int *policy); } + SYS_CPUSET_SETDOMAIN = 562 // { int cpuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int policy); } + SYS_GETRANDOM = 563 // { int getrandom(void *buf, size_t buflen, unsigned int flags); } + SYS_GETFHAT = 564 // { int getfhat(int fd, char *path, struct fhandle *fhp, int flags); } + SYS_FHLINK = 565 // { int fhlink(struct fhandle *fhp, const char *to); } + SYS_FHLINKAT = 566 // { int fhlinkat(struct fhandle *fhp, int tofd, const char *to,); } + SYS_FHREADLINK = 567 // { int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); } + SYS___SYSCTLBYNAME = 570 // { int __sysctlbyname(const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_CLOSE_RANGE = 575 // { int close_range(u_int lowfd, u_int highfd, int flags); } ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go index 342d471d..01636b83 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go @@ -1,4 +1,4 @@ -// go run mksysnum.go https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master +// go run mksysnum.go https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && freebsd @@ -19,10 +19,9 @@ const ( SYS_UNLINK = 10 // { int unlink(char *path); } SYS_CHDIR = 12 // { int chdir(char *path); } SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); } SYS_CHMOD = 15 // { int chmod(char *path, int mode); } SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } - SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int + SYS_BREAK = 17 // { caddr_t break(char *nsize); } SYS_GETPID = 20 // { pid_t getpid(void); } SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, caddr_t data); } SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } @@ -43,7 +42,6 @@ const ( SYS_KILL = 37 // { int kill(int pid, int signum); } SYS_GETPPID = 39 // { pid_t getppid(void); } SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } SYS_GETEGID = 43 // { gid_t getegid(void); } SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, size_t offset, u_int scale); } SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, int pid); } @@ -58,15 +56,14 @@ const ( SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, size_t count); } SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); } - SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int + SYS_UMASK = 60 // { int umask(int newmask); } SYS_CHROOT = 61 // { int chroot(char *path); } SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); } SYS_VFORK = 66 // { int vfork(void); } SYS_SBRK = 69 // { int sbrk(int incr); } SYS_SSTK = 70 // { int sstk(int incr); } - SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise ovadvise_args int SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, int prot); } + SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); } SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); } SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, char *vec); } SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } @@ -124,14 +121,10 @@ const ( SYS_SETGID = 181 // { int setgid(gid_t gid); } SYS_SETEGID = 182 // { int setegid(gid_t egid); } SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_STAT = 188 // { int stat(char *path, struct stat *ub); } - SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); } - SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); } SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } getrlimit __getrlimit_args int SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } setrlimit __setrlimit_args int - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } __sysctl sysctl_args int SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } @@ -143,12 +136,12 @@ const ( SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, size_t nsops); } SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } SYS_SHMAT = 228 // { int shmat(int shmid, const void *shmaddr, int shmflg); } SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); } SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); } SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, struct timespec *tp); } - SYS_CLOCK_SETTIME = 233 // { int clock_settime( clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, const struct timespec *tp); } SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, struct timespec *tp); } SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, struct sigevent *evp, int *timerid); } SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } @@ -157,50 +150,44 @@ const ( SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } - SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( struct ffclock_estimate *cest); } - SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( struct ffclock_estimate *cest); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate(struct ffclock_estimate *cest); } + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate(struct ffclock_estimate *cest); } SYS_CLOCK_NANOSLEEP = 244 // { int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); } - SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id,int which, clockid_t *clock_id); } + SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id, int which, clockid_t *clock_id); } SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } SYS_RFORK = 251 // { int rfork(int flags); } - SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, int timeout); } SYS_ISSETUGID = 253 // { int issetugid(void); } SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } SYS_AIO_READ = 255 // { int aio_read(struct aiocb *aiocbp); } SYS_AIO_WRITE = 256 // { int aio_write(struct aiocb *aiocbp); } - SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); } - SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, size_t count); } + SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb* const *acb_list, int nent, struct sigevent *sig); } SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); } - SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); } - SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); } - SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); } SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); } - SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } SYS_MODNEXT = 300 // { int modnext(int modid); } - SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat *stat); } + SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); } SYS_MODFNEXT = 302 // { int modfnext(int modid); } SYS_MODFIND = 303 // { int modfind(const char *name); } SYS_KLDLOAD = 304 // { int kldload(const char *file); } SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } SYS_KLDFIND = 306 // { int kldfind(const char *file); } SYS_KLDNEXT = 307 // { int kldnext(int fileid); } - SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat *stat); } SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } SYS_GETSID = 310 // { int getsid(pid_t pid); } SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } SYS_AIO_RETURN = 314 // { ssize_t aio_return(struct aiocb *aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend( struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } + SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); } SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); } SYS_YIELD = 321 // { int yield(void); } SYS_MLOCKALL = 324 // { int mlockall(int how); } SYS_MUNLOCKALL = 325 // { int munlockall(void); } - SYS___GETCWD = 326 // { int __getcwd(char *buf, u_int buflen); } + SYS___GETCWD = 326 // { int __getcwd(char *buf, size_t buflen); } SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); } SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); } SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } @@ -226,14 +213,13 @@ const ( SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); } SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); } SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } - SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } - SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete( struct aiocb **aiocbp, struct timespec *timeout); } + SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } SYS_EXTATTR_SET_FD = 371 // { ssize_t extattr_set_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } @@ -251,10 +237,6 @@ const ( SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); } SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); } SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, int call, void *arg); } - SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } - SYS_STATFS = 396 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); } - SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } SYS_KSEM_CLOSE = 400 // { int ksem_close(semid_t id); } SYS_KSEM_POST = 401 // { int ksem_post(semid_t id); } SYS_KSEM_WAIT = 402 // { int ksem_wait(semid_t id); } @@ -267,14 +249,14 @@ const ( SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, struct mac *mac_p); } SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, struct mac *mac_p); } - SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, char **envv, struct mac *mac_p); } SYS_SIGACTION = 416 // { int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); } - SYS_SIGRETURN = 417 // { int sigreturn( const struct __ucontext *sigcntxp); } + SYS_SIGRETURN = 417 // { int sigreturn(const struct __ucontext *sigcntxp); } SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } - SYS_SETCONTEXT = 422 // { int setcontext( const struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext(const struct __ucontext *ucp); } SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, const struct __ucontext *ucp); } SYS_SWAPOFF = 424 // { int swapoff(const char *name); } SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); } @@ -288,10 +270,10 @@ const ( SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( const char *path, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } SYS_KSEM_TIMEDWAIT = 441 // { int ksem_timedwait(semid_t id, const struct timespec *abstime); } - SYS_THR_SUSPEND = 442 // { int thr_suspend( const struct timespec *timeout); } + SYS_THR_SUSPEND = 442 // { int thr_suspend(const struct timespec *timeout); } SYS_THR_WAKE = 443 // { int thr_wake(long id); } SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } SYS_AUDIT = 445 // { int audit(const void *record, u_int length); } @@ -300,17 +282,17 @@ const ( SYS_SETAUID = 448 // { int setauid(uid_t *auid); } SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } - SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } SYS_AUDITCTL = 453 // { int auditctl(char *path); } SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, u_long val, void *uaddr1, void *uaddr2); } SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, int param_size); } SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } SYS_KMQ_OPEN = 457 // { int kmq_open(const char *path, int flags, mode_t mode, const struct mq_attr *attr); } - SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } - SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } - SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len,unsigned msg_prio, const struct timespec *abs_timeout);} - SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } + SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } + SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } SYS_KMQ_UNLINK = 462 // { int kmq_unlink(const char *path); } SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } @@ -319,7 +301,7 @@ const ( SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } - SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr * from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); } SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); } SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } @@ -338,14 +320,12 @@ const ( SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, int flag); } SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag); } SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, char **envv); } - SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, struct timeval *times); } SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, char *path2, int flag); } SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } - SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, mode_t mode); } - SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, size_t bufsize); } + SYS_READLINKAT = 500 // { ssize_t readlinkat(int fd, char *path, char *buf, size_t bufsize); } SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, char *new); } SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, char *path2); } SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } @@ -391,7 +371,24 @@ const ( SYS_PPOLL = 545 // { int ppoll(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *set); } SYS_FUTIMENS = 546 // { int futimens(int fd, struct timespec *times); } SYS_UTIMENSAT = 547 // { int utimensat(int fd, char *path, struct timespec *times, int flag); } - SYS_NUMA_GETAFFINITY = 548 // { int numa_getaffinity(cpuwhich_t which, id_t id, struct vm_domain_policy_entry *policy); } - SYS_NUMA_SETAFFINITY = 549 // { int numa_setaffinity(cpuwhich_t which, id_t id, const struct vm_domain_policy_entry *policy); } SYS_FDATASYNC = 550 // { int fdatasync(int fd); } + SYS_FSTAT = 551 // { int fstat(int fd, struct stat *sb); } + SYS_FSTATAT = 552 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } + SYS_FHSTAT = 553 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } + SYS_GETDIRENTRIES = 554 // { ssize_t getdirentries(int fd, char *buf, size_t count, off_t *basep); } + SYS_STATFS = 555 // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 556 // { int fstatfs(int fd, struct statfs *buf); } + SYS_GETFSSTAT = 557 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } + SYS_FHSTATFS = 558 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } + SYS_MKNODAT = 559 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } + SYS_KEVENT = 560 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_CPUSET_GETDOMAIN = 561 // { int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int *policy); } + SYS_CPUSET_SETDOMAIN = 562 // { int cpuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int policy); } + SYS_GETRANDOM = 563 // { int getrandom(void *buf, size_t buflen, unsigned int flags); } + SYS_GETFHAT = 564 // { int getfhat(int fd, char *path, struct fhandle *fhp, int flags); } + SYS_FHLINK = 565 // { int fhlink(struct fhandle *fhp, const char *to); } + SYS_FHLINKAT = 566 // { int fhlinkat(struct fhandle *fhp, int tofd, const char *to,); } + SYS_FHREADLINK = 567 // { int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); } + SYS___SYSCTLBYNAME = 570 // { int __sysctlbyname(const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_CLOSE_RANGE = 575 // { int close_range(u_int lowfd, u_int highfd, int flags); } ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go index e2e3d72c..ad99bc10 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go @@ -1,4 +1,4 @@ -// go run mksysnum.go https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master +// go run mksysnum.go https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && freebsd @@ -19,10 +19,9 @@ const ( SYS_UNLINK = 10 // { int unlink(char *path); } SYS_CHDIR = 12 // { int chdir(char *path); } SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); } SYS_CHMOD = 15 // { int chmod(char *path, int mode); } SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } - SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int + SYS_BREAK = 17 // { caddr_t break(char *nsize); } SYS_GETPID = 20 // { pid_t getpid(void); } SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, caddr_t data); } SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } @@ -43,7 +42,6 @@ const ( SYS_KILL = 37 // { int kill(int pid, int signum); } SYS_GETPPID = 39 // { pid_t getppid(void); } SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } SYS_GETEGID = 43 // { gid_t getegid(void); } SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, size_t offset, u_int scale); } SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, int pid); } @@ -58,15 +56,14 @@ const ( SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, size_t count); } SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); } - SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int + SYS_UMASK = 60 // { int umask(int newmask); } SYS_CHROOT = 61 // { int chroot(char *path); } SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); } SYS_VFORK = 66 // { int vfork(void); } SYS_SBRK = 69 // { int sbrk(int incr); } SYS_SSTK = 70 // { int sstk(int incr); } - SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise ovadvise_args int SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, int prot); } + SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); } SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); } SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, char *vec); } SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } @@ -124,14 +121,10 @@ const ( SYS_SETGID = 181 // { int setgid(gid_t gid); } SYS_SETEGID = 182 // { int setegid(gid_t egid); } SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_STAT = 188 // { int stat(char *path, struct stat *ub); } - SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); } - SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); } SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } getrlimit __getrlimit_args int SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } setrlimit __setrlimit_args int - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } __sysctl sysctl_args int SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } @@ -143,12 +136,12 @@ const ( SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, size_t nsops); } SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } SYS_SHMAT = 228 // { int shmat(int shmid, const void *shmaddr, int shmflg); } SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); } SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); } SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, struct timespec *tp); } - SYS_CLOCK_SETTIME = 233 // { int clock_settime( clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, const struct timespec *tp); } SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, struct timespec *tp); } SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, struct sigevent *evp, int *timerid); } SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } @@ -157,50 +150,44 @@ const ( SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } - SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( struct ffclock_estimate *cest); } - SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( struct ffclock_estimate *cest); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate(struct ffclock_estimate *cest); } + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate(struct ffclock_estimate *cest); } SYS_CLOCK_NANOSLEEP = 244 // { int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); } - SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id,int which, clockid_t *clock_id); } + SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id, int which, clockid_t *clock_id); } SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } SYS_RFORK = 251 // { int rfork(int flags); } - SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, int timeout); } SYS_ISSETUGID = 253 // { int issetugid(void); } SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } SYS_AIO_READ = 255 // { int aio_read(struct aiocb *aiocbp); } SYS_AIO_WRITE = 256 // { int aio_write(struct aiocb *aiocbp); } - SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); } - SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, size_t count); } + SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb* const *acb_list, int nent, struct sigevent *sig); } SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); } - SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); } - SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); } - SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); } SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); } - SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } SYS_MODNEXT = 300 // { int modnext(int modid); } - SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat *stat); } + SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); } SYS_MODFNEXT = 302 // { int modfnext(int modid); } SYS_MODFIND = 303 // { int modfind(const char *name); } SYS_KLDLOAD = 304 // { int kldload(const char *file); } SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } SYS_KLDFIND = 306 // { int kldfind(const char *file); } SYS_KLDNEXT = 307 // { int kldnext(int fileid); } - SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat *stat); } SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } SYS_GETSID = 310 // { int getsid(pid_t pid); } SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } SYS_AIO_RETURN = 314 // { ssize_t aio_return(struct aiocb *aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend( struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } + SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); } SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); } SYS_YIELD = 321 // { int yield(void); } SYS_MLOCKALL = 324 // { int mlockall(int how); } SYS_MUNLOCKALL = 325 // { int munlockall(void); } - SYS___GETCWD = 326 // { int __getcwd(char *buf, u_int buflen); } + SYS___GETCWD = 326 // { int __getcwd(char *buf, size_t buflen); } SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); } SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); } SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } @@ -226,14 +213,13 @@ const ( SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); } SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); } SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } - SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } - SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete( struct aiocb **aiocbp, struct timespec *timeout); } + SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } SYS_EXTATTR_SET_FD = 371 // { ssize_t extattr_set_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } @@ -251,10 +237,6 @@ const ( SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); } SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); } SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, int call, void *arg); } - SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } - SYS_STATFS = 396 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); } - SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } SYS_KSEM_CLOSE = 400 // { int ksem_close(semid_t id); } SYS_KSEM_POST = 401 // { int ksem_post(semid_t id); } SYS_KSEM_WAIT = 402 // { int ksem_wait(semid_t id); } @@ -267,14 +249,14 @@ const ( SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, struct mac *mac_p); } SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, struct mac *mac_p); } - SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, char **envv, struct mac *mac_p); } SYS_SIGACTION = 416 // { int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); } - SYS_SIGRETURN = 417 // { int sigreturn( const struct __ucontext *sigcntxp); } + SYS_SIGRETURN = 417 // { int sigreturn(const struct __ucontext *sigcntxp); } SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } - SYS_SETCONTEXT = 422 // { int setcontext( const struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext(const struct __ucontext *ucp); } SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, const struct __ucontext *ucp); } SYS_SWAPOFF = 424 // { int swapoff(const char *name); } SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); } @@ -288,10 +270,10 @@ const ( SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( const char *path, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } SYS_KSEM_TIMEDWAIT = 441 // { int ksem_timedwait(semid_t id, const struct timespec *abstime); } - SYS_THR_SUSPEND = 442 // { int thr_suspend( const struct timespec *timeout); } + SYS_THR_SUSPEND = 442 // { int thr_suspend(const struct timespec *timeout); } SYS_THR_WAKE = 443 // { int thr_wake(long id); } SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } SYS_AUDIT = 445 // { int audit(const void *record, u_int length); } @@ -300,17 +282,17 @@ const ( SYS_SETAUID = 448 // { int setauid(uid_t *auid); } SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } - SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } SYS_AUDITCTL = 453 // { int auditctl(char *path); } SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, u_long val, void *uaddr1, void *uaddr2); } SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, int param_size); } SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } SYS_KMQ_OPEN = 457 // { int kmq_open(const char *path, int flags, mode_t mode, const struct mq_attr *attr); } - SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } - SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } - SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len,unsigned msg_prio, const struct timespec *abs_timeout);} - SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } + SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } + SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } SYS_KMQ_UNLINK = 462 // { int kmq_unlink(const char *path); } SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } @@ -319,7 +301,7 @@ const ( SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } - SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr * from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); } SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); } SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } @@ -338,14 +320,12 @@ const ( SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, int flag); } SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag); } SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, char **envv); } - SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, struct timeval *times); } SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, char *path2, int flag); } SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } - SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, mode_t mode); } - SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, size_t bufsize); } + SYS_READLINKAT = 500 // { ssize_t readlinkat(int fd, char *path, char *buf, size_t bufsize); } SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, char *new); } SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, char *path2); } SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } @@ -391,7 +371,24 @@ const ( SYS_PPOLL = 545 // { int ppoll(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *set); } SYS_FUTIMENS = 546 // { int futimens(int fd, struct timespec *times); } SYS_UTIMENSAT = 547 // { int utimensat(int fd, char *path, struct timespec *times, int flag); } - SYS_NUMA_GETAFFINITY = 548 // { int numa_getaffinity(cpuwhich_t which, id_t id, struct vm_domain_policy_entry *policy); } - SYS_NUMA_SETAFFINITY = 549 // { int numa_setaffinity(cpuwhich_t which, id_t id, const struct vm_domain_policy_entry *policy); } SYS_FDATASYNC = 550 // { int fdatasync(int fd); } + SYS_FSTAT = 551 // { int fstat(int fd, struct stat *sb); } + SYS_FSTATAT = 552 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } + SYS_FHSTAT = 553 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } + SYS_GETDIRENTRIES = 554 // { ssize_t getdirentries(int fd, char *buf, size_t count, off_t *basep); } + SYS_STATFS = 555 // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 556 // { int fstatfs(int fd, struct statfs *buf); } + SYS_GETFSSTAT = 557 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } + SYS_FHSTATFS = 558 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } + SYS_MKNODAT = 559 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } + SYS_KEVENT = 560 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_CPUSET_GETDOMAIN = 561 // { int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int *policy); } + SYS_CPUSET_SETDOMAIN = 562 // { int cpuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int policy); } + SYS_GETRANDOM = 563 // { int getrandom(void *buf, size_t buflen, unsigned int flags); } + SYS_GETFHAT = 564 // { int getfhat(int fd, char *path, struct fhandle *fhp, int flags); } + SYS_FHLINK = 565 // { int fhlink(struct fhandle *fhp, const char *to); } + SYS_FHLINKAT = 566 // { int fhlinkat(struct fhandle *fhp, int tofd, const char *to,); } + SYS_FHREADLINK = 567 // { int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); } + SYS___SYSCTLBYNAME = 570 // { int __sysctlbyname(const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_CLOSE_RANGE = 575 // { int close_range(u_int lowfd, u_int highfd, int flags); } ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go index 61ad5ca3..89dcc427 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go @@ -1,4 +1,4 @@ -// go run mksysnum.go https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master +// go run mksysnum.go https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && freebsd @@ -19,10 +19,9 @@ const ( SYS_UNLINK = 10 // { int unlink(char *path); } SYS_CHDIR = 12 // { int chdir(char *path); } SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); } SYS_CHMOD = 15 // { int chmod(char *path, int mode); } SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } - SYS_OBREAK = 17 // { int obreak(char *nsize); } break obreak_args int + SYS_BREAK = 17 // { caddr_t break(char *nsize); } SYS_GETPID = 20 // { pid_t getpid(void); } SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, caddr_t data); } SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } @@ -43,7 +42,6 @@ const ( SYS_KILL = 37 // { int kill(int pid, int signum); } SYS_GETPPID = 39 // { pid_t getppid(void); } SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } SYS_GETEGID = 43 // { gid_t getegid(void); } SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, size_t offset, u_int scale); } SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, int pid); } @@ -58,15 +56,14 @@ const ( SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, size_t count); } SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); } - SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args int + SYS_UMASK = 60 // { int umask(int newmask); } SYS_CHROOT = 61 // { int chroot(char *path); } SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); } SYS_VFORK = 66 // { int vfork(void); } SYS_SBRK = 69 // { int sbrk(int incr); } SYS_SSTK = 70 // { int sstk(int incr); } - SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise ovadvise_args int SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, int prot); } + SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); } SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); } SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, char *vec); } SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } @@ -124,14 +121,10 @@ const ( SYS_SETGID = 181 // { int setgid(gid_t gid); } SYS_SETEGID = 182 // { int setegid(gid_t egid); } SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_STAT = 188 // { int stat(char *path, struct stat *ub); } - SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); } - SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); } SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } getrlimit __getrlimit_args int SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } setrlimit __setrlimit_args int - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } __sysctl sysctl_args int SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } @@ -143,12 +136,12 @@ const ( SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, size_t nsops); } SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); } SYS_MSGSND = 226 // { int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 227 // { int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } SYS_SHMAT = 228 // { int shmat(int shmid, const void *shmaddr, int shmflg); } SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); } SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); } SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, struct timespec *tp); } - SYS_CLOCK_SETTIME = 233 // { int clock_settime( clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, const struct timespec *tp); } SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, struct timespec *tp); } SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, struct sigevent *evp, int *timerid); } SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } @@ -157,50 +150,44 @@ const ( SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } - SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( struct ffclock_estimate *cest); } - SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( struct ffclock_estimate *cest); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate(struct ffclock_estimate *cest); } + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate(struct ffclock_estimate *cest); } SYS_CLOCK_NANOSLEEP = 244 // { int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); } - SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id,int which, clockid_t *clock_id); } + SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id, int which, clockid_t *clock_id); } SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } SYS_RFORK = 251 // { int rfork(int flags); } - SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, u_int nfds, int timeout); } SYS_ISSETUGID = 253 // { int issetugid(void); } SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } SYS_AIO_READ = 255 // { int aio_read(struct aiocb *aiocbp); } SYS_AIO_WRITE = 256 // { int aio_write(struct aiocb *aiocbp); } - SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); } - SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, size_t count); } + SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb* const *acb_list, int nent, struct sigevent *sig); } SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); } - SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); } - SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); } - SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); } SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); } - SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } SYS_MODNEXT = 300 // { int modnext(int modid); } - SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat *stat); } + SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); } SYS_MODFNEXT = 302 // { int modfnext(int modid); } SYS_MODFIND = 303 // { int modfind(const char *name); } SYS_KLDLOAD = 304 // { int kldload(const char *file); } SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } SYS_KLDFIND = 306 // { int kldfind(const char *file); } SYS_KLDNEXT = 307 // { int kldnext(int fileid); } - SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat* stat); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat *stat); } SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } SYS_GETSID = 310 // { int getsid(pid_t pid); } SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } SYS_AIO_RETURN = 314 // { ssize_t aio_return(struct aiocb *aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend( struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } + SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); } SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); } SYS_YIELD = 321 // { int yield(void); } SYS_MLOCKALL = 324 // { int mlockall(int how); } SYS_MUNLOCKALL = 325 // { int munlockall(void); } - SYS___GETCWD = 326 // { int __getcwd(char *buf, u_int buflen); } + SYS___GETCWD = 326 // { int __getcwd(char *buf, size_t buflen); } SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); } SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); } SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } @@ -226,14 +213,13 @@ const ( SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); } SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); } SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } - SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } - SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete( struct aiocb **aiocbp, struct timespec *timeout); } + SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } SYS_EXTATTR_SET_FD = 371 // { ssize_t extattr_set_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } @@ -251,10 +237,6 @@ const ( SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); } SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); } SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, int call, void *arg); } - SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } - SYS_STATFS = 396 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); } - SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } SYS_KSEM_CLOSE = 400 // { int ksem_close(semid_t id); } SYS_KSEM_POST = 401 // { int ksem_post(semid_t id); } SYS_KSEM_WAIT = 402 // { int ksem_wait(semid_t id); } @@ -267,14 +249,14 @@ const ( SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, struct mac *mac_p); } SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, struct mac *mac_p); } - SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } - SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( const char *path, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, char **envv, struct mac *mac_p); } SYS_SIGACTION = 416 // { int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); } - SYS_SIGRETURN = 417 // { int sigreturn( const struct __ucontext *sigcntxp); } + SYS_SIGRETURN = 417 // { int sigreturn(const struct __ucontext *sigcntxp); } SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } - SYS_SETCONTEXT = 422 // { int setcontext( const struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext(const struct __ucontext *ucp); } SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, const struct __ucontext *ucp); } SYS_SWAPOFF = 424 // { int swapoff(const char *name); } SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); } @@ -288,10 +270,10 @@ const ( SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( const char *path, int attrnamespace, void *data, size_t nbytes); } - SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } SYS_KSEM_TIMEDWAIT = 441 // { int ksem_timedwait(semid_t id, const struct timespec *abstime); } - SYS_THR_SUSPEND = 442 // { int thr_suspend( const struct timespec *timeout); } + SYS_THR_SUSPEND = 442 // { int thr_suspend(const struct timespec *timeout); } SYS_THR_WAKE = 443 // { int thr_wake(long id); } SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } SYS_AUDIT = 445 // { int audit(const void *record, u_int length); } @@ -300,17 +282,17 @@ const ( SYS_SETAUID = 448 // { int setauid(uid_t *auid); } SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } - SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } SYS_AUDITCTL = 453 // { int auditctl(char *path); } SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, u_long val, void *uaddr1, void *uaddr2); } SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, int param_size); } SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } SYS_KMQ_OPEN = 457 // { int kmq_open(const char *path, int flags, mode_t mode, const struct mq_attr *attr); } - SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } - SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } - SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len,unsigned msg_prio, const struct timespec *abs_timeout);} - SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } + SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } + SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } SYS_KMQ_UNLINK = 462 // { int kmq_unlink(const char *path); } SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } @@ -319,7 +301,7 @@ const ( SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } - SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr * from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); } SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); } SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } @@ -338,14 +320,12 @@ const ( SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, int flag); } SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag); } SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, char **envv); } - SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, struct timeval *times); } SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, char *path2, int flag); } SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } - SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, mode_t mode); } - SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, size_t bufsize); } + SYS_READLINKAT = 500 // { ssize_t readlinkat(int fd, char *path, char *buf, size_t bufsize); } SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, char *new); } SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, char *path2); } SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } @@ -391,7 +371,24 @@ const ( SYS_PPOLL = 545 // { int ppoll(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *set); } SYS_FUTIMENS = 546 // { int futimens(int fd, struct timespec *times); } SYS_UTIMENSAT = 547 // { int utimensat(int fd, char *path, struct timespec *times, int flag); } - SYS_NUMA_GETAFFINITY = 548 // { int numa_getaffinity(cpuwhich_t which, id_t id, struct vm_domain_policy_entry *policy); } - SYS_NUMA_SETAFFINITY = 549 // { int numa_setaffinity(cpuwhich_t which, id_t id, const struct vm_domain_policy_entry *policy); } SYS_FDATASYNC = 550 // { int fdatasync(int fd); } + SYS_FSTAT = 551 // { int fstat(int fd, struct stat *sb); } + SYS_FSTATAT = 552 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } + SYS_FHSTAT = 553 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } + SYS_GETDIRENTRIES = 554 // { ssize_t getdirentries(int fd, char *buf, size_t count, off_t *basep); } + SYS_STATFS = 555 // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 556 // { int fstatfs(int fd, struct statfs *buf); } + SYS_GETFSSTAT = 557 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } + SYS_FHSTATFS = 558 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } + SYS_MKNODAT = 559 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } + SYS_KEVENT = 560 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_CPUSET_GETDOMAIN = 561 // { int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int *policy); } + SYS_CPUSET_SETDOMAIN = 562 // { int cpuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int policy); } + SYS_GETRANDOM = 563 // { int getrandom(void *buf, size_t buflen, unsigned int flags); } + SYS_GETFHAT = 564 // { int getfhat(int fd, char *path, struct fhandle *fhp, int flags); } + SYS_FHLINK = 565 // { int fhlink(struct fhandle *fhp, const char *to); } + SYS_FHLINKAT = 566 // { int fhlinkat(struct fhandle *fhp, int tofd, const char *to,); } + SYS_FHREADLINK = 567 // { int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); } + SYS___SYSCTLBYNAME = 570 // { int __sysctlbyname(const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_CLOSE_RANGE = 575 // { int close_range(u_int lowfd, u_int highfd, int flags); } ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go new file mode 100644 index 00000000..ee37aaa0 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go @@ -0,0 +1,394 @@ +// go run mksysnum.go https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12 +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build riscv64 && freebsd +// +build riscv64,freebsd + +package unix + +const ( + // SYS_NOSYS = 0; // { int nosys(void); } syscall nosys_args int + SYS_EXIT = 1 // { void sys_exit(int rval); } exit sys_exit_args void + SYS_FORK = 2 // { int fork(void); } + SYS_READ = 3 // { ssize_t read(int fd, void *buf, size_t nbyte); } + SYS_WRITE = 4 // { ssize_t write(int fd, const void *buf, size_t nbyte); } + SYS_OPEN = 5 // { int open(char *path, int flags, int mode); } + SYS_CLOSE = 6 // { int close(int fd); } + SYS_WAIT4 = 7 // { int wait4(int pid, int *status, int options, struct rusage *rusage); } + SYS_LINK = 9 // { int link(char *path, char *link); } + SYS_UNLINK = 10 // { int unlink(char *path); } + SYS_CHDIR = 12 // { int chdir(char *path); } + SYS_FCHDIR = 13 // { int fchdir(int fd); } + SYS_CHMOD = 15 // { int chmod(char *path, int mode); } + SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); } + SYS_BREAK = 17 // { caddr_t break(char *nsize); } + SYS_GETPID = 20 // { pid_t getpid(void); } + SYS_MOUNT = 21 // { int mount(char *type, char *path, int flags, caddr_t data); } + SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); } + SYS_SETUID = 23 // { int setuid(uid_t uid); } + SYS_GETUID = 24 // { uid_t getuid(void); } + SYS_GETEUID = 25 // { uid_t geteuid(void); } + SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, caddr_t addr, int data); } + SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, int flags); } + SYS_SENDMSG = 28 // { int sendmsg(int s, struct msghdr *msg, int flags); } + SYS_RECVFROM = 29 // { int recvfrom(int s, caddr_t buf, size_t len, int flags, struct sockaddr * __restrict from, __socklen_t * __restrict fromlenaddr); } + SYS_ACCEPT = 30 // { int accept(int s, struct sockaddr * __restrict name, __socklen_t * __restrict anamelen); } + SYS_GETPEERNAME = 31 // { int getpeername(int fdes, struct sockaddr * __restrict asa, __socklen_t * __restrict alen); } + SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, struct sockaddr * __restrict asa, __socklen_t * __restrict alen); } + SYS_ACCESS = 33 // { int access(char *path, int amode); } + SYS_CHFLAGS = 34 // { int chflags(const char *path, u_long flags); } + SYS_FCHFLAGS = 35 // { int fchflags(int fd, u_long flags); } + SYS_SYNC = 36 // { int sync(void); } + SYS_KILL = 37 // { int kill(int pid, int signum); } + SYS_GETPPID = 39 // { pid_t getppid(void); } + SYS_DUP = 41 // { int dup(u_int fd); } + SYS_GETEGID = 43 // { gid_t getegid(void); } + SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, size_t offset, u_int scale); } + SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, int facs, int pid); } + SYS_GETGID = 47 // { gid_t getgid(void); } + SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int namelen); } + SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); } + SYS_ACCT = 51 // { int acct(char *path); } + SYS_SIGALTSTACK = 53 // { int sigaltstack(stack_t *ss, stack_t *oss); } + SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, caddr_t data); } + SYS_REBOOT = 55 // { int reboot(int opt); } + SYS_REVOKE = 56 // { int revoke(char *path); } + SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } + SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, size_t count); } + SYS_EXECVE = 59 // { int execve(char *fname, char **argv, char **envv); } + SYS_UMASK = 60 // { int umask(int newmask); } + SYS_CHROOT = 61 // { int chroot(char *path); } + SYS_MSYNC = 65 // { int msync(void *addr, size_t len, int flags); } + SYS_VFORK = 66 // { int vfork(void); } + SYS_SBRK = 69 // { int sbrk(int incr); } + SYS_SSTK = 70 // { int sstk(int incr); } + SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); } + SYS_MPROTECT = 74 // { int mprotect(void *addr, size_t len, int prot); } + SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, int behav); } + SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, char *vec); } + SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } + SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, gid_t *gidset); } + SYS_GETPGRP = 81 // { int getpgrp(void); } + SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); } + SYS_SETITIMER = 83 // { int setitimer(u_int which, struct itimerval *itv, struct itimerval *oitv); } + SYS_SWAPON = 85 // { int swapon(char *name); } + SYS_GETITIMER = 86 // { int getitimer(u_int which, struct itimerval *itv); } + SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); } + SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); } + SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); } + SYS_SELECT = 93 // { int select(int nd, fd_set *in, fd_set *ou, fd_set *ex, struct timeval *tv); } + SYS_FSYNC = 95 // { int fsync(int fd); } + SYS_SETPRIORITY = 96 // { int setpriority(int which, int who, int prio); } + SYS_SOCKET = 97 // { int socket(int domain, int type, int protocol); } + SYS_CONNECT = 98 // { int connect(int s, caddr_t name, int namelen); } + SYS_GETPRIORITY = 100 // { int getpriority(int which, int who); } + SYS_BIND = 104 // { int bind(int s, caddr_t name, int namelen); } + SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, caddr_t val, int valsize); } + SYS_LISTEN = 106 // { int listen(int s, int backlog); } + SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, struct timezone *tzp); } + SYS_GETRUSAGE = 117 // { int getrusage(int who, struct rusage *rusage); } + SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, caddr_t val, int *avalsize); } + SYS_READV = 120 // { int readv(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_WRITEV = 121 // { int writev(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, struct timezone *tzp); } + SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); } + SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); } + SYS_SETREUID = 126 // { int setreuid(int ruid, int euid); } + SYS_SETREGID = 127 // { int setregid(int rgid, int egid); } + SYS_RENAME = 128 // { int rename(char *from, char *to); } + SYS_FLOCK = 131 // { int flock(int fd, int how); } + SYS_MKFIFO = 132 // { int mkfifo(char *path, int mode); } + SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, int flags, caddr_t to, int tolen); } + SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); } + SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, int protocol, int *rsv); } + SYS_MKDIR = 136 // { int mkdir(char *path, int mode); } + SYS_RMDIR = 137 // { int rmdir(char *path); } + SYS_UTIMES = 138 // { int utimes(char *path, struct timeval *tptr); } + SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, struct timeval *olddelta); } + SYS_SETSID = 147 // { int setsid(void); } + SYS_QUOTACTL = 148 // { int quotactl(char *path, int cmd, int uid, caddr_t arg); } + SYS_NLM_SYSCALL = 154 // { int nlm_syscall(int debug_level, int grace_period, int addr_count, char **addrs); } + SYS_NFSSVC = 155 // { int nfssvc(int flag, caddr_t argp); } + SYS_LGETFH = 160 // { int lgetfh(char *fname, struct fhandle *fhp); } + SYS_GETFH = 161 // { int getfh(char *fname, struct fhandle *fhp); } + SYS_SYSARCH = 165 // { int sysarch(int op, char *parms); } + SYS_RTPRIO = 166 // { int rtprio(int function, pid_t pid, struct rtprio *rtp); } + SYS_SEMSYS = 169 // { int semsys(int which, int a2, int a3, int a4, int a5); } + SYS_MSGSYS = 170 // { int msgsys(int which, int a2, int a3, int a4, int a5, int a6); } + SYS_SHMSYS = 171 // { int shmsys(int which, int a2, int a3, int a4); } + SYS_SETFIB = 175 // { int setfib(int fibnum); } + SYS_NTP_ADJTIME = 176 // { int ntp_adjtime(struct timex *tp); } + SYS_SETGID = 181 // { int setgid(gid_t gid); } + SYS_SETEGID = 182 // { int setegid(gid_t egid); } + SYS_SETEUID = 183 // { int seteuid(uid_t euid); } + SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } + SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } + SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } getrlimit __getrlimit_args int + SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } setrlimit __setrlimit_args int + SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } __sysctl sysctl_args int + SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); } + SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); } + SYS_UNDELETE = 205 // { int undelete(char *path); } + SYS_FUTIMES = 206 // { int futimes(int fd, struct timeval *tptr); } + SYS_GETPGID = 207 // { int getpgid(pid_t pid); } + SYS_POLL = 209 // { int poll(struct pollfd *fds, u_int nfds, int timeout); } + SYS_SEMGET = 221 // { int semget(key_t key, int nsems, int semflg); } + SYS_SEMOP = 222 // { int semop(int semid, struct sembuf *sops, size_t nsops); } + SYS_MSGGET = 225 // { int msgget(key_t key, int msgflg); } + SYS_MSGSND = 226 // { int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); } + SYS_MSGRCV = 227 // { ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + SYS_SHMAT = 228 // { int shmat(int shmid, const void *shmaddr, int shmflg); } + SYS_SHMDT = 230 // { int shmdt(const void *shmaddr); } + SYS_SHMGET = 231 // { int shmget(key_t key, size_t size, int shmflg); } + SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, struct timespec *tp); } + SYS_CLOCK_SETTIME = 233 // { int clock_settime(clockid_t clock_id, const struct timespec *tp); } + SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, struct timespec *tp); } + SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, struct sigevent *evp, int *timerid); } + SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); } + SYS_KTIMER_SETTIME = 237 // { int ktimer_settime(int timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); } + SYS_KTIMER_GETTIME = 238 // { int ktimer_gettime(int timerid, struct itimerspec *value); } + SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); } + SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); } + SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); } + SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate(struct ffclock_estimate *cest); } + SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate(struct ffclock_estimate *cest); } + SYS_CLOCK_NANOSLEEP = 244 // { int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); } + SYS_CLOCK_GETCPUCLOCKID2 = 247 // { int clock_getcpuclockid2(id_t id, int which, clockid_t *clock_id); } + SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); } + SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } + SYS_RFORK = 251 // { int rfork(int flags); } + SYS_ISSETUGID = 253 // { int issetugid(void); } + SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); } + SYS_AIO_READ = 255 // { int aio_read(struct aiocb *aiocbp); } + SYS_AIO_WRITE = 256 // { int aio_write(struct aiocb *aiocbp); } + SYS_LIO_LISTIO = 257 // { int lio_listio(int mode, struct aiocb* const *acb_list, int nent, struct sigevent *sig); } + SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); } + SYS_LUTIMES = 276 // { int lutimes(char *path, struct timeval *tptr); } + SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } + SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, u_int iovcnt, off_t offset); } + SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, int flags); } + SYS_MODNEXT = 300 // { int modnext(int modid); } + SYS_MODSTAT = 301 // { int modstat(int modid, struct module_stat* stat); } + SYS_MODFNEXT = 302 // { int modfnext(int modid); } + SYS_MODFIND = 303 // { int modfind(const char *name); } + SYS_KLDLOAD = 304 // { int kldload(const char *file); } + SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); } + SYS_KLDFIND = 306 // { int kldfind(const char *file); } + SYS_KLDNEXT = 307 // { int kldnext(int fileid); } + SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct kld_file_stat *stat); } + SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); } + SYS_GETSID = 310 // { int getsid(pid_t pid); } + SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } + SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } + SYS_AIO_RETURN = 314 // { ssize_t aio_return(struct aiocb *aiocbp); } + SYS_AIO_SUSPEND = 315 // { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } + SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, struct aiocb *aiocbp); } + SYS_AIO_ERROR = 317 // { int aio_error(struct aiocb *aiocbp); } + SYS_YIELD = 321 // { int yield(void); } + SYS_MLOCKALL = 324 // { int mlockall(int how); } + SYS_MUNLOCKALL = 325 // { int munlockall(void); } + SYS___GETCWD = 326 // { int __getcwd(char *buf, size_t buflen); } + SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, const struct sched_param *param); } + SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct sched_param *param); } + SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } + SYS_SCHED_GETSCHEDULER = 330 // { int sched_getscheduler (pid_t pid); } + SYS_SCHED_YIELD = 331 // { int sched_yield (void); } + SYS_SCHED_GET_PRIORITY_MAX = 332 // { int sched_get_priority_max (int policy); } + SYS_SCHED_GET_PRIORITY_MIN = 333 // { int sched_get_priority_min (int policy); } + SYS_SCHED_RR_GET_INTERVAL = 334 // { int sched_rr_get_interval (pid_t pid, struct timespec *interval); } + SYS_UTRACE = 335 // { int utrace(const void *addr, size_t len); } + SYS_KLDSYM = 337 // { int kldsym(int fileid, int cmd, void *data); } + SYS_JAIL = 338 // { int jail(struct jail *jail); } + SYS_SIGPROCMASK = 340 // { int sigprocmask(int how, const sigset_t *set, sigset_t *oset); } + SYS_SIGSUSPEND = 341 // { int sigsuspend(const sigset_t *sigmask); } + SYS_SIGPENDING = 343 // { int sigpending(sigset_t *set); } + SYS_SIGTIMEDWAIT = 345 // { int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout); } + SYS_SIGWAITINFO = 346 // { int sigwaitinfo(const sigset_t *set, siginfo_t *info); } + SYS___ACL_GET_FILE = 347 // { int __acl_get_file(const char *path, acl_type_t type, struct acl *aclp); } + SYS___ACL_SET_FILE = 348 // { int __acl_set_file(const char *path, acl_type_t type, struct acl *aclp); } + SYS___ACL_GET_FD = 349 // { int __acl_get_fd(int filedes, acl_type_t type, struct acl *aclp); } + SYS___ACL_SET_FD = 350 // { int __acl_set_fd(int filedes, acl_type_t type, struct acl *aclp); } + SYS___ACL_DELETE_FILE = 351 // { int __acl_delete_file(const char *path, acl_type_t type); } + SYS___ACL_DELETE_FD = 352 // { int __acl_delete_fd(int filedes, acl_type_t type); } + SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, acl_type_t type, struct acl *aclp); } + SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, acl_type_t type, struct acl *aclp); } + SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); } + SYS_EXTATTR_SET_FILE = 356 // { ssize_t extattr_set_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, int attrnamespace, const char *attrname); } + SYS_AIO_WAITCOMPLETE = 359 // { ssize_t aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } + SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } + SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } + SYS_KQUEUE = 362 // { int kqueue(void); } + SYS_EXTATTR_SET_FD = 371 // { ssize_t extattr_set_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, int attrnamespace, const char *attrname); } + SYS___SETUGID = 374 // { int __setugid(int flag); } + SYS_EACCESS = 376 // { int eaccess(char *path, int amode); } + SYS_NMOUNT = 378 // { int nmount(struct iovec *iovp, unsigned int iovcnt, int flags); } + SYS___MAC_GET_PROC = 384 // { int __mac_get_proc(struct mac *mac_p); } + SYS___MAC_SET_PROC = 385 // { int __mac_set_proc(struct mac *mac_p); } + SYS___MAC_GET_FD = 386 // { int __mac_get_fd(int fd, struct mac *mac_p); } + SYS___MAC_GET_FILE = 387 // { int __mac_get_file(const char *path_p, struct mac *mac_p); } + SYS___MAC_SET_FD = 388 // { int __mac_set_fd(int fd, struct mac *mac_p); } + SYS___MAC_SET_FILE = 389 // { int __mac_set_file(const char *path_p, struct mac *mac_p); } + SYS_KENV = 390 // { int kenv(int what, const char *name, char *value, int len); } + SYS_LCHFLAGS = 391 // { int lchflags(const char *path, u_long flags); } + SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, int count); } + SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags); } + SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, int call, void *arg); } + SYS_KSEM_CLOSE = 400 // { int ksem_close(semid_t id); } + SYS_KSEM_POST = 401 // { int ksem_post(semid_t id); } + SYS_KSEM_WAIT = 402 // { int ksem_wait(semid_t id); } + SYS_KSEM_TRYWAIT = 403 // { int ksem_trywait(semid_t id); } + SYS_KSEM_INIT = 404 // { int ksem_init(semid_t *idp, unsigned int value); } + SYS_KSEM_OPEN = 405 // { int ksem_open(semid_t *idp, const char *name, int oflag, mode_t mode, unsigned int value); } + SYS_KSEM_UNLINK = 406 // { int ksem_unlink(const char *name); } + SYS_KSEM_GETVALUE = 407 // { int ksem_getvalue(semid_t id, int *val); } + SYS_KSEM_DESTROY = 408 // { int ksem_destroy(semid_t id); } + SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } + SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, struct mac *mac_p); } + SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, struct mac *mac_p); } + SYS_EXTATTR_SET_LINK = 412 // { ssize_t extattr_set_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); } + SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link(const char *path, int attrnamespace, const char *attrname); } + SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, char **envv, struct mac *mac_p); } + SYS_SIGACTION = 416 // { int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); } + SYS_SIGRETURN = 417 // { int sigreturn(const struct __ucontext *sigcntxp); } + SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); } + SYS_SETCONTEXT = 422 // { int setcontext(const struct __ucontext *ucp); } + SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, const struct __ucontext *ucp); } + SYS_SWAPOFF = 424 // { int swapoff(const char *name); } + SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, acl_type_t type, struct acl *aclp); } + SYS___ACL_SET_LINK = 426 // { int __acl_set_link(const char *path, acl_type_t type, struct acl *aclp); } + SYS___ACL_DELETE_LINK = 427 // { int __acl_delete_link(const char *path, acl_type_t type); } + SYS___ACL_ACLCHECK_LINK = 428 // { int __acl_aclcheck_link(const char *path, acl_type_t type, struct acl *aclp); } + SYS_SIGWAIT = 429 // { int sigwait(const sigset_t *set, int *sig); } + SYS_THR_CREATE = 430 // { int thr_create(ucontext_t *ctx, long *id, int flags); } + SYS_THR_EXIT = 431 // { void thr_exit(long *state); } + SYS_THR_SELF = 432 // { int thr_self(long *id); } + SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); } + SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); } + SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); } + SYS_KSEM_TIMEDWAIT = 441 // { int ksem_timedwait(semid_t id, const struct timespec *abstime); } + SYS_THR_SUSPEND = 442 // { int thr_suspend(const struct timespec *timeout); } + SYS_THR_WAKE = 443 // { int thr_wake(long id); } + SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); } + SYS_AUDIT = 445 // { int audit(const void *record, u_int length); } + SYS_AUDITON = 446 // { int auditon(int cmd, void *data, u_int length); } + SYS_GETAUID = 447 // { int getauid(uid_t *auid); } + SYS_SETAUID = 448 // { int setauid(uid_t *auid); } + SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); } + SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); } + SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, u_int length); } + SYS_AUDITCTL = 453 // { int auditctl(char *path); } + SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, u_long val, void *uaddr1, void *uaddr2); } + SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, int param_size); } + SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); } + SYS_KMQ_OPEN = 457 // { int kmq_open(const char *path, int flags, mode_t mode, const struct mq_attr *attr); } + SYS_KMQ_SETATTR = 458 // { int kmq_setattr(int mqd, const struct mq_attr *attr, struct mq_attr *oattr); } + SYS_KMQ_TIMEDRECEIVE = 459 // { int kmq_timedreceive(int mqd, char *msg_ptr, size_t msg_len, unsigned *msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_TIMEDSEND = 460 // { int kmq_timedsend(int mqd, const char *msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec *abs_timeout); } + SYS_KMQ_NOTIFY = 461 // { int kmq_notify(int mqd, const struct sigevent *sigev); } + SYS_KMQ_UNLINK = 462 // { int kmq_unlink(const char *path); } + SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); } + SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); } + SYS_AIO_FSYNC = 465 // { int aio_fsync(int op, struct aiocb *aiocbp); } + SYS_RTPRIO_THREAD = 466 // { int rtprio_thread(int function, lwpid_t lwpid, struct rtprio *rtp); } + SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); } + SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } + SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, caddr_t to, __socklen_t tolen, struct sctp_sndrcvinfo *sinfo, int flags); } + SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, struct sockaddr *from, __socklen_t *fromlenaddr, struct sctp_sndrcvinfo *sinfo, int *msg_flags); } + SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); } + SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); } + SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } + SYS_LSEEK = 478 // { off_t lseek(int fd, off_t offset, int whence); } + SYS_TRUNCATE = 479 // { int truncate(char *path, off_t length); } + SYS_FTRUNCATE = 480 // { int ftruncate(int fd, off_t length); } + SYS_THR_KILL2 = 481 // { int thr_kill2(pid_t pid, long id, int sig); } + SYS_SHM_OPEN = 482 // { int shm_open(const char *path, int flags, mode_t mode); } + SYS_SHM_UNLINK = 483 // { int shm_unlink(const char *path); } + SYS_CPUSET = 484 // { int cpuset(cpusetid_t *setid); } + SYS_CPUSET_SETID = 485 // { int cpuset_setid(cpuwhich_t which, id_t id, cpusetid_t setid); } + SYS_CPUSET_GETID = 486 // { int cpuset_getid(cpulevel_t level, cpuwhich_t which, id_t id, cpusetid_t *setid); } + SYS_CPUSET_GETAFFINITY = 487 // { int cpuset_getaffinity(cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, cpuset_t *mask); } + SYS_CPUSET_SETAFFINITY = 488 // { int cpuset_setaffinity(cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, const cpuset_t *mask); } + SYS_FACCESSAT = 489 // { int faccessat(int fd, char *path, int amode, int flag); } + SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, int flag); } + SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag); } + SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, char **envv); } + SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, struct timeval *times); } + SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, char *path2, int flag); } + SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); } + SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); } + SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, mode_t mode); } + SYS_READLINKAT = 500 // { ssize_t readlinkat(int fd, char *path, char *buf, size_t bufsize); } + SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, char *new); } + SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, char *path2); } + SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); } + SYS_POSIX_OPENPT = 504 // { int posix_openpt(int flags); } + SYS_GSSD_SYSCALL = 505 // { int gssd_syscall(char *path); } + SYS_JAIL_GET = 506 // { int jail_get(struct iovec *iovp, unsigned int iovcnt, int flags); } + SYS_JAIL_SET = 507 // { int jail_set(struct iovec *iovp, unsigned int iovcnt, int flags); } + SYS_JAIL_REMOVE = 508 // { int jail_remove(int jid); } + SYS_CLOSEFROM = 509 // { int closefrom(int lowfd); } + SYS___SEMCTL = 510 // { int __semctl(int semid, int semnum, int cmd, union semun *arg); } + SYS_MSGCTL = 511 // { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } + SYS_SHMCTL = 512 // { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } + SYS_LPATHCONF = 513 // { int lpathconf(char *path, int name); } + SYS___CAP_RIGHTS_GET = 515 // { int __cap_rights_get(int version, int fd, cap_rights_t *rightsp); } + SYS_CAP_ENTER = 516 // { int cap_enter(void); } + SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); } + SYS_PDFORK = 518 // { int pdfork(int *fdp, int flags); } + SYS_PDKILL = 519 // { int pdkill(int fd, int signum); } + SYS_PDGETPID = 520 // { int pdgetpid(int fd, pid_t *pidp); } + SYS_PSELECT = 522 // { int pselect(int nd, fd_set *in, fd_set *ou, fd_set *ex, const struct timespec *ts, const sigset_t *sm); } + SYS_GETLOGINCLASS = 523 // { int getloginclass(char *namebuf, size_t namelen); } + SYS_SETLOGINCLASS = 524 // { int setloginclass(const char *namebuf); } + SYS_RCTL_GET_RACCT = 525 // { int rctl_get_racct(const void *inbufp, size_t inbuflen, void *outbufp, size_t outbuflen); } + SYS_RCTL_GET_RULES = 526 // { int rctl_get_rules(const void *inbufp, size_t inbuflen, void *outbufp, size_t outbuflen); } + SYS_RCTL_GET_LIMITS = 527 // { int rctl_get_limits(const void *inbufp, size_t inbuflen, void *outbufp, size_t outbuflen); } + SYS_RCTL_ADD_RULE = 528 // { int rctl_add_rule(const void *inbufp, size_t inbuflen, void *outbufp, size_t outbuflen); } + SYS_RCTL_REMOVE_RULE = 529 // { int rctl_remove_rule(const void *inbufp, size_t inbuflen, void *outbufp, size_t outbuflen); } + SYS_POSIX_FALLOCATE = 530 // { int posix_fallocate(int fd, off_t offset, off_t len); } + SYS_POSIX_FADVISE = 531 // { int posix_fadvise(int fd, off_t offset, off_t len, int advice); } + SYS_WAIT6 = 532 // { int wait6(idtype_t idtype, id_t id, int *status, int options, struct __wrusage *wrusage, siginfo_t *info); } + SYS_CAP_RIGHTS_LIMIT = 533 // { int cap_rights_limit(int fd, cap_rights_t *rightsp); } + SYS_CAP_IOCTLS_LIMIT = 534 // { int cap_ioctls_limit(int fd, const u_long *cmds, size_t ncmds); } + SYS_CAP_IOCTLS_GET = 535 // { ssize_t cap_ioctls_get(int fd, u_long *cmds, size_t maxcmds); } + SYS_CAP_FCNTLS_LIMIT = 536 // { int cap_fcntls_limit(int fd, uint32_t fcntlrights); } + SYS_CAP_FCNTLS_GET = 537 // { int cap_fcntls_get(int fd, uint32_t *fcntlrightsp); } + SYS_BINDAT = 538 // { int bindat(int fd, int s, caddr_t name, int namelen); } + SYS_CONNECTAT = 539 // { int connectat(int fd, int s, caddr_t name, int namelen); } + SYS_CHFLAGSAT = 540 // { int chflagsat(int fd, const char *path, u_long flags, int atflag); } + SYS_ACCEPT4 = 541 // { int accept4(int s, struct sockaddr * __restrict name, __socklen_t * __restrict anamelen, int flags); } + SYS_PIPE2 = 542 // { int pipe2(int *fildes, int flags); } + SYS_AIO_MLOCK = 543 // { int aio_mlock(struct aiocb *aiocbp); } + SYS_PROCCTL = 544 // { int procctl(idtype_t idtype, id_t id, int com, void *data); } + SYS_PPOLL = 545 // { int ppoll(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *set); } + SYS_FUTIMENS = 546 // { int futimens(int fd, struct timespec *times); } + SYS_UTIMENSAT = 547 // { int utimensat(int fd, char *path, struct timespec *times, int flag); } + SYS_FDATASYNC = 550 // { int fdatasync(int fd); } + SYS_FSTAT = 551 // { int fstat(int fd, struct stat *sb); } + SYS_FSTATAT = 552 // { int fstatat(int fd, char *path, struct stat *buf, int flag); } + SYS_FHSTAT = 553 // { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } + SYS_GETDIRENTRIES = 554 // { ssize_t getdirentries(int fd, char *buf, size_t count, off_t *basep); } + SYS_STATFS = 555 // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 556 // { int fstatfs(int fd, struct statfs *buf); } + SYS_GETFSSTAT = 557 // { int getfsstat(struct statfs *buf, long bufsize, int mode); } + SYS_FHSTATFS = 558 // { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } + SYS_MKNODAT = 559 // { int mknodat(int fd, char *path, mode_t mode, dev_t dev); } + SYS_KEVENT = 560 // { int kevent(int fd, struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_CPUSET_GETDOMAIN = 561 // { int cpuset_getdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int *policy); } + SYS_CPUSET_SETDOMAIN = 562 // { int cpuset_setdomain(cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, domainset_t *mask, int policy); } + SYS_GETRANDOM = 563 // { int getrandom(void *buf, size_t buflen, unsigned int flags); } + SYS_GETFHAT = 564 // { int getfhat(int fd, char *path, struct fhandle *fhp, int flags); } + SYS_FHLINK = 565 // { int fhlink(struct fhandle *fhp, const char *to); } + SYS_FHLINKAT = 566 // { int fhlinkat(struct fhandle *fhp, int tofd, const char *to,); } + SYS_FHREADLINK = 567 // { int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); } + SYS___SYSCTLBYNAME = 570 // { int __sysctlbyname(const char *name, size_t namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_CLOSE_RANGE = 575 // { int close_range(u_int lowfd, u_int highfd, int flags); } +) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index e443f9a3..44a764c9 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -85,8 +85,6 @@ const ( SYS_SPLICE = 76 SYS_TEE = 77 SYS_READLINKAT = 78 - SYS_FSTATAT = 79 - SYS_FSTAT = 80 SYS_SYNC = 81 SYS_FSYNC = 82 SYS_FDATASYNC = 83 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index c3a5af86..3a9c96b2 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -309,6 +309,7 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_MEMFD_SECRET = 447 SYS_PROCESS_MRELEASE = 448 SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 885842c0..e2a64f09 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -366,30 +366,57 @@ type ICMPv6Filter struct { Filt [8]uint32 } +type TCPConnectionInfo struct { + State uint8 + Snd_wscale uint8 + Rcv_wscale uint8 + _ uint8 + Options uint32 + Flags uint32 + Rto uint32 + Maxseg uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Snd_wnd uint32 + Snd_sbbytes uint32 + Rcv_wnd uint32 + Rttcur uint32 + Srtt uint32 + Rttvar uint32 + Txpackets uint64 + Txbytes uint64 + Txretransmitbytes uint64 + Rxpackets uint64 + Rxbytes uint64 + Rxoutoforderbytes uint64 + Txretransmitpackets uint64 +} + const ( - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofSockaddrDatalink = 0x14 - SizeofSockaddrCtl = 0x20 - SizeofSockaddrVM = 0xc - SizeofXvsockpcb = 0xa8 - SizeofXSocket = 0x64 - SizeofXSockbuf = 0x18 - SizeofXVSockPgen = 0x20 - SizeofXucred = 0x4c - SizeofLinger = 0x8 - SizeofIovec = 0x10 - SizeofIPMreq = 0x8 - SizeofIPMreqn = 0xc - SizeofIPv6Mreq = 0x14 - SizeofMsghdr = 0x30 - SizeofCmsghdr = 0xc - SizeofInet4Pktinfo = 0xc - SizeofInet6Pktinfo = 0x14 - SizeofIPv6MTUInfo = 0x20 - SizeofICMPv6Filter = 0x20 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofSockaddrCtl = 0x20 + SizeofSockaddrVM = 0xc + SizeofXvsockpcb = 0xa8 + SizeofXSocket = 0x64 + SizeofXSockbuf = 0x18 + SizeofXVSockPgen = 0x20 + SizeofXucred = 0x4c + SizeofLinger = 0x8 + SizeofIovec = 0x10 + SizeofIPMreq = 0x8 + SizeofIPMreqn = 0xc + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x30 + SizeofCmsghdr = 0xc + SizeofInet4Pktinfo = 0xc + SizeofInet6Pktinfo = 0x14 + SizeofIPv6MTUInfo = 0x20 + SizeofICMPv6Filter = 0x20 + SizeofTCPConnectionInfo = 0x70 ) const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index b23c0233..34aa7752 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -366,30 +366,57 @@ type ICMPv6Filter struct { Filt [8]uint32 } +type TCPConnectionInfo struct { + State uint8 + Snd_wscale uint8 + Rcv_wscale uint8 + _ uint8 + Options uint32 + Flags uint32 + Rto uint32 + Maxseg uint32 + Snd_ssthresh uint32 + Snd_cwnd uint32 + Snd_wnd uint32 + Snd_sbbytes uint32 + Rcv_wnd uint32 + Rttcur uint32 + Srtt uint32 + Rttvar uint32 + Txpackets uint64 + Txbytes uint64 + Txretransmitbytes uint64 + Rxpackets uint64 + Rxbytes uint64 + Rxoutoforderbytes uint64 + Txretransmitpackets uint64 +} + const ( - SizeofSockaddrInet4 = 0x10 - SizeofSockaddrInet6 = 0x1c - SizeofSockaddrAny = 0x6c - SizeofSockaddrUnix = 0x6a - SizeofSockaddrDatalink = 0x14 - SizeofSockaddrCtl = 0x20 - SizeofSockaddrVM = 0xc - SizeofXvsockpcb = 0xa8 - SizeofXSocket = 0x64 - SizeofXSockbuf = 0x18 - SizeofXVSockPgen = 0x20 - SizeofXucred = 0x4c - SizeofLinger = 0x8 - SizeofIovec = 0x10 - SizeofIPMreq = 0x8 - SizeofIPMreqn = 0xc - SizeofIPv6Mreq = 0x14 - SizeofMsghdr = 0x30 - SizeofCmsghdr = 0xc - SizeofInet4Pktinfo = 0xc - SizeofInet6Pktinfo = 0x14 - SizeofIPv6MTUInfo = 0x20 - SizeofICMPv6Filter = 0x20 + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofSockaddrCtl = 0x20 + SizeofSockaddrVM = 0xc + SizeofXvsockpcb = 0xa8 + SizeofXSocket = 0x64 + SizeofXSockbuf = 0x18 + SizeofXVSockPgen = 0x20 + SizeofXucred = 0x4c + SizeofLinger = 0x8 + SizeofIovec = 0x10 + SizeofIPMreq = 0x8 + SizeofIPMreqn = 0xc + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x30 + SizeofCmsghdr = 0xc + SizeofInet4Pktinfo = 0xc + SizeofInet6Pktinfo = 0x14 + SizeofIPv6MTUInfo = 0x20 + SizeofICMPv6Filter = 0x20 + SizeofTCPConnectionInfo = 0x70 ) const ( diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 4eec078e..dea0c9a6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -90,27 +90,6 @@ type Stat_t struct { Spare [10]uint64 } -type stat_freebsd11_t struct { - Dev uint32 - Ino uint32 - Mode uint16 - Nlink uint16 - Uid uint32 - Gid uint32 - Rdev uint32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize int32 - Flags uint32 - Gen uint32 - Lspare int32 - Btim Timespec - _ [8]byte -} - type Statfs_t struct { Version uint32 Type uint32 @@ -136,31 +115,6 @@ type Statfs_t struct { Mntonname [1024]byte } -type statfs_freebsd11_t struct { - Version uint32 - Type uint32 - Flags uint64 - Bsize uint64 - Iosize uint64 - Blocks uint64 - Bfree uint64 - Bavail int64 - Files uint64 - Ffree int64 - Syncwrites uint64 - Asyncwrites uint64 - Syncreads uint64 - Asyncreads uint64 - Spare [10]uint64 - Namemax uint32 - Owner uint32 - Fsid Fsid - Charspare [80]int8 - Fstypename [16]byte - Mntfromname [88]byte - Mntonname [88]byte -} - type Flock_t struct { Start int64 Len int64 @@ -181,14 +135,6 @@ type Dirent struct { Name [256]int8 } -type dirent_freebsd11 struct { - Fileno uint32 - Reclen uint16 - Type uint8 - Namlen uint8 - Name [256]int8 -} - type Fsid struct { Val [2]int32 } @@ -337,41 +283,9 @@ const ( ) const ( - PTRACE_ATTACH = 0xa - PTRACE_CONT = 0x7 - PTRACE_DETACH = 0xb - PTRACE_GETFPREGS = 0x23 - PTRACE_GETFSBASE = 0x47 - PTRACE_GETLWPLIST = 0xf - PTRACE_GETNUMLWPS = 0xe - PTRACE_GETREGS = 0x21 - PTRACE_GETXSTATE = 0x45 - PTRACE_IO = 0xc - PTRACE_KILL = 0x8 - PTRACE_LWPEVENTS = 0x18 - PTRACE_LWPINFO = 0xd - PTRACE_SETFPREGS = 0x24 - PTRACE_SETREGS = 0x22 - PTRACE_SINGLESTEP = 0x9 - PTRACE_TRACEME = 0x0 -) - -const ( - PIOD_READ_D = 0x1 - PIOD_WRITE_D = 0x2 - PIOD_READ_I = 0x3 - PIOD_WRITE_I = 0x4 -) - -const ( - PL_FLAG_BORN = 0x100 - PL_FLAG_EXITED = 0x200 - PL_FLAG_SI = 0x20 -) - -const ( - TRAP_BRKPT = 0x1 - TRAP_TRACE = 0x2 + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 ) type PtraceLwpInfoStruct struct { @@ -432,6 +346,8 @@ type FpReg struct { Pad [64]uint8 } +type FpExtendedPrecision struct{} + type PtraceIoDesc struct { Op int32 Offs *byte @@ -444,8 +360,9 @@ type Kevent_t struct { Filter int16 Flags uint16 Fflags uint32 - Data int32 + Data int64 Udata *byte + Ext [4]uint64 } type FdSet struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 7622904a..da0ea0d6 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -86,26 +86,6 @@ type Stat_t struct { Spare [10]uint64 } -type stat_freebsd11_t struct { - Dev uint32 - Ino uint32 - Mode uint16 - Nlink uint16 - Uid uint32 - Gid uint32 - Rdev uint32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize int32 - Flags uint32 - Gen uint32 - Lspare int32 - Btim Timespec -} - type Statfs_t struct { Version uint32 Type uint32 @@ -131,31 +111,6 @@ type Statfs_t struct { Mntonname [1024]byte } -type statfs_freebsd11_t struct { - Version uint32 - Type uint32 - Flags uint64 - Bsize uint64 - Iosize uint64 - Blocks uint64 - Bfree uint64 - Bavail int64 - Files uint64 - Ffree int64 - Syncwrites uint64 - Asyncwrites uint64 - Syncreads uint64 - Asyncreads uint64 - Spare [10]uint64 - Namemax uint32 - Owner uint32 - Fsid Fsid - Charspare [80]int8 - Fstypename [16]byte - Mntfromname [88]byte - Mntonname [88]byte -} - type Flock_t struct { Start int64 Len int64 @@ -177,14 +132,6 @@ type Dirent struct { Name [256]int8 } -type dirent_freebsd11 struct { - Fileno uint32 - Reclen uint16 - Type uint8 - Namlen uint8 - Name [256]int8 -} - type Fsid struct { Val [2]int32 } @@ -333,41 +280,9 @@ const ( ) const ( - PTRACE_ATTACH = 0xa - PTRACE_CONT = 0x7 - PTRACE_DETACH = 0xb - PTRACE_GETFPREGS = 0x23 - PTRACE_GETFSBASE = 0x47 - PTRACE_GETLWPLIST = 0xf - PTRACE_GETNUMLWPS = 0xe - PTRACE_GETREGS = 0x21 - PTRACE_GETXSTATE = 0x45 - PTRACE_IO = 0xc - PTRACE_KILL = 0x8 - PTRACE_LWPEVENTS = 0x18 - PTRACE_LWPINFO = 0xd - PTRACE_SETFPREGS = 0x24 - PTRACE_SETREGS = 0x22 - PTRACE_SINGLESTEP = 0x9 - PTRACE_TRACEME = 0x0 -) - -const ( - PIOD_READ_D = 0x1 - PIOD_WRITE_D = 0x2 - PIOD_READ_I = 0x3 - PIOD_WRITE_I = 0x4 -) - -const ( - PL_FLAG_BORN = 0x100 - PL_FLAG_EXITED = 0x200 - PL_FLAG_SI = 0x20 -) - -const ( - TRAP_BRKPT = 0x1 - TRAP_TRACE = 0x2 + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 ) type PtraceLwpInfoStruct struct { @@ -435,6 +350,8 @@ type FpReg struct { Spare [12]uint64 } +type FpExtendedPrecision struct{} + type PtraceIoDesc struct { Op int32 Offs *byte @@ -449,6 +366,7 @@ type Kevent_t struct { Fflags uint32 Data int64 Udata *byte + Ext [4]uint64 } type FdSet struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 19223ce8..da8f7404 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -33,7 +33,7 @@ type Timeval struct { _ [4]byte } -type Time_t int32 +type Time_t int64 type Rusage struct { Utime Timeval @@ -88,26 +88,6 @@ type Stat_t struct { Spare [10]uint64 } -type stat_freebsd11_t struct { - Dev uint32 - Ino uint32 - Mode uint16 - Nlink uint16 - Uid uint32 - Gid uint32 - Rdev uint32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize int32 - Flags uint32 - Gen uint32 - Lspare int32 - Btim Timespec -} - type Statfs_t struct { Version uint32 Type uint32 @@ -133,31 +113,6 @@ type Statfs_t struct { Mntonname [1024]byte } -type statfs_freebsd11_t struct { - Version uint32 - Type uint32 - Flags uint64 - Bsize uint64 - Iosize uint64 - Blocks uint64 - Bfree uint64 - Bavail int64 - Files uint64 - Ffree int64 - Syncwrites uint64 - Asyncwrites uint64 - Syncreads uint64 - Asyncreads uint64 - Spare [10]uint64 - Namemax uint32 - Owner uint32 - Fsid Fsid - Charspare [80]int8 - Fstypename [16]byte - Mntfromname [88]byte - Mntonname [88]byte -} - type Flock_t struct { Start int64 Len int64 @@ -179,14 +134,6 @@ type Dirent struct { Name [256]int8 } -type dirent_freebsd11 struct { - Fileno uint32 - Reclen uint16 - Type uint8 - Namlen uint8 - Name [256]int8 -} - type Fsid struct { Val [2]int32 } @@ -335,41 +282,9 @@ const ( ) const ( - PTRACE_ATTACH = 0xa - PTRACE_CONT = 0x7 - PTRACE_DETACH = 0xb - PTRACE_GETFPREGS = 0x23 - PTRACE_GETFSBASE = 0x47 - PTRACE_GETLWPLIST = 0xf - PTRACE_GETNUMLWPS = 0xe - PTRACE_GETREGS = 0x21 - PTRACE_GETXSTATE = 0x45 - PTRACE_IO = 0xc - PTRACE_KILL = 0x8 - PTRACE_LWPEVENTS = 0x18 - PTRACE_LWPINFO = 0xd - PTRACE_SETFPREGS = 0x24 - PTRACE_SETREGS = 0x22 - PTRACE_SINGLESTEP = 0x9 - PTRACE_TRACEME = 0x0 -) - -const ( - PIOD_READ_D = 0x1 - PIOD_WRITE_D = 0x2 - PIOD_READ_I = 0x3 - PIOD_WRITE_I = 0x4 -) - -const ( - PL_FLAG_BORN = 0x100 - PL_FLAG_EXITED = 0x200 - PL_FLAG_SI = 0x20 -) - -const ( - TRAP_BRKPT = 0x1 - TRAP_TRACE = 0x2 + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 ) type PtraceLwpInfoStruct struct { @@ -386,15 +301,15 @@ type PtraceLwpInfoStruct struct { } type __Siginfo struct { - Signo int32 - Errno int32 - Code int32 - Pid int32 - Uid uint32 - Status int32 - Addr *byte - Value [4]byte - X_reason [32]byte + Signo int32 + Errno int32 + Code int32 + Pid int32 + Uid uint32 + Status int32 + Addr *byte + Value [4]byte + _ [32]byte } type Sigset_t struct { @@ -402,16 +317,22 @@ type Sigset_t struct { } type Reg struct { - R [13]uint32 - R_sp uint32 - R_lr uint32 - R_pc uint32 - R_cpsr uint32 + R [13]uint32 + Sp uint32 + Lr uint32 + Pc uint32 + Cpsr uint32 } type FpReg struct { - Fpr_fpsr uint32 - Fpr [8][3]uint32 + Fpsr uint32 + Fpr [8]FpExtendedPrecision +} + +type FpExtendedPrecision struct { + Exponent uint32 + Mantissa_hi uint32 + Mantissa_lo uint32 } type PtraceIoDesc struct { @@ -426,8 +347,11 @@ type Kevent_t struct { Filter int16 Flags uint16 Fflags uint32 - Data int32 + _ [4]byte + Data int64 Udata *byte + _ [4]byte + Ext [4]uint64 } type FdSet struct { @@ -453,7 +377,7 @@ type ifMsghdr struct { Addrs int32 Flags int32 Index uint16 - _ [2]byte + _ uint16 Data ifData } @@ -464,7 +388,6 @@ type IfMsghdr struct { Addrs int32 Flags int32 Index uint16 - _ [2]byte Data IfData } @@ -532,7 +455,7 @@ type IfaMsghdr struct { Addrs int32 Flags int32 Index uint16 - _ [2]byte + _ uint16 Metric int32 } @@ -543,7 +466,7 @@ type IfmaMsghdr struct { Addrs int32 Flags int32 Index uint16 - _ [2]byte + _ uint16 } type IfAnnounceMsghdr struct { @@ -560,7 +483,7 @@ type RtMsghdr struct { Version uint8 Type uint8 Index uint16 - _ [2]byte + _ uint16 Flags int32 Addrs int32 Pid int32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 8e3e33f6..d69988e5 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -86,26 +86,6 @@ type Stat_t struct { Spare [10]uint64 } -type stat_freebsd11_t struct { - Dev uint32 - Ino uint32 - Mode uint16 - Nlink uint16 - Uid uint32 - Gid uint32 - Rdev uint32 - Atim Timespec - Mtim Timespec - Ctim Timespec - Size int64 - Blocks int64 - Blksize int32 - Flags uint32 - Gen uint32 - Lspare int32 - Btim Timespec -} - type Statfs_t struct { Version uint32 Type uint32 @@ -131,31 +111,6 @@ type Statfs_t struct { Mntonname [1024]byte } -type statfs_freebsd11_t struct { - Version uint32 - Type uint32 - Flags uint64 - Bsize uint64 - Iosize uint64 - Blocks uint64 - Bfree uint64 - Bavail int64 - Files uint64 - Ffree int64 - Syncwrites uint64 - Asyncwrites uint64 - Syncreads uint64 - Asyncreads uint64 - Spare [10]uint64 - Namemax uint32 - Owner uint32 - Fsid Fsid - Charspare [80]int8 - Fstypename [16]byte - Mntfromname [88]byte - Mntonname [88]byte -} - type Flock_t struct { Start int64 Len int64 @@ -177,14 +132,6 @@ type Dirent struct { Name [256]int8 } -type dirent_freebsd11 struct { - Fileno uint32 - Reclen uint16 - Type uint8 - Namlen uint8 - Name [256]int8 -} - type Fsid struct { Val [2]int32 } @@ -333,39 +280,9 @@ const ( ) const ( - PTRACE_ATTACH = 0xa - PTRACE_CONT = 0x7 - PTRACE_DETACH = 0xb - PTRACE_GETFPREGS = 0x23 - PTRACE_GETLWPLIST = 0xf - PTRACE_GETNUMLWPS = 0xe - PTRACE_GETREGS = 0x21 - PTRACE_IO = 0xc - PTRACE_KILL = 0x8 - PTRACE_LWPEVENTS = 0x18 - PTRACE_LWPINFO = 0xd - PTRACE_SETFPREGS = 0x24 - PTRACE_SETREGS = 0x22 - PTRACE_SINGLESTEP = 0x9 - PTRACE_TRACEME = 0x0 -) - -const ( - PIOD_READ_D = 0x1 - PIOD_WRITE_D = 0x2 - PIOD_READ_I = 0x3 - PIOD_WRITE_I = 0x4 -) - -const ( - PL_FLAG_BORN = 0x100 - PL_FLAG_EXITED = 0x200 - PL_FLAG_SI = 0x20 -) - -const ( - TRAP_BRKPT = 0x1 - TRAP_TRACE = 0x2 + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 ) type PtraceLwpInfoStruct struct { @@ -413,6 +330,8 @@ type FpReg struct { _ [8]byte } +type FpExtendedPrecision struct{} + type PtraceIoDesc struct { Op int32 Offs *byte @@ -427,6 +346,7 @@ type Kevent_t struct { Fflags uint32 Data int64 Udata *byte + Ext [4]uint64 } type FdSet struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go new file mode 100644 index 00000000..d6fd9e88 --- /dev/null +++ b/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -0,0 +1,626 @@ +// cgo -godefs -- -fsigned-char types_freebsd.go | go run mkpost.go +// Code generated by the command above; see README.md. DO NOT EDIT. + +//go:build riscv64 && freebsd +// +build riscv64,freebsd + +package unix + +const ( + SizeofPtr = 0x8 + SizeofShort = 0x2 + SizeofInt = 0x4 + SizeofLong = 0x8 + SizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int64 + _C_long_long int64 +) + +type Timespec struct { + Sec int64 + Nsec int64 +} + +type Timeval struct { + Sec int64 + Usec int64 +} + +type Time_t int64 + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int64 + Ixrss int64 + Idrss int64 + Isrss int64 + Minflt int64 + Majflt int64 + Nswap int64 + Inblock int64 + Oublock int64 + Msgsnd int64 + Msgrcv int64 + Nsignals int64 + Nvcsw int64 + Nivcsw int64 +} + +type Rlimit struct { + Cur int64 + Max int64 +} + +type _Gid_t uint32 + +const ( + _statfsVersion = 0x20140518 + _dirblksiz = 0x400 +) + +type Stat_t struct { + Dev uint64 + Ino uint64 + Nlink uint64 + Mode uint16 + _0 int16 + Uid uint32 + Gid uint32 + _1 int32 + Rdev uint64 + Atim Timespec + Mtim Timespec + Ctim Timespec + Btim Timespec + Size int64 + Blocks int64 + Blksize int32 + Flags uint32 + Gen uint64 + Spare [10]uint64 +} + +type Statfs_t struct { + Version uint32 + Type uint32 + Flags uint64 + Bsize uint64 + Iosize uint64 + Blocks uint64 + Bfree uint64 + Bavail int64 + Files uint64 + Ffree int64 + Syncwrites uint64 + Asyncwrites uint64 + Syncreads uint64 + Asyncreads uint64 + Spare [10]uint64 + Namemax uint32 + Owner uint32 + Fsid Fsid + Charspare [80]int8 + Fstypename [16]byte + Mntfromname [1024]byte + Mntonname [1024]byte +} + +type Flock_t struct { + Start int64 + Len int64 + Pid int32 + Type int16 + Whence int16 + Sysid int32 + _ [4]byte +} + +type Dirent struct { + Fileno uint64 + Off int64 + Reclen uint16 + Type uint8 + Pad0 uint8 + Namlen uint16 + Pad1 uint16 + Name [256]int8 +} + +type Fsid struct { + Val [2]int32 +} + +const ( + PathMax = 0x400 +) + +const ( + FADV_NORMAL = 0x0 + FADV_RANDOM = 0x1 + FADV_SEQUENTIAL = 0x2 + FADV_WILLNEED = 0x3 + FADV_DONTNEED = 0x4 + FADV_NOREUSE = 0x5 +) + +type RawSockaddrInet4 struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type RawSockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type RawSockaddrUnix struct { + Len uint8 + Family uint8 + Path [104]int8 +} + +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [46]int8 +} + +type RawSockaddr struct { + Len uint8 + Family uint8 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [92]int8 +} + +type _Socklen uint32 + +type Xucred struct { + Version uint32 + Uid uint32 + Ngroups int16 + Groups [16]uint32 + _ *byte +} + +type Linger struct { + Onoff int32 + Linger int32 +} + +type Iovec struct { + Base *byte + Len uint64 +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type Cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type Inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type IPv6MTUInfo struct { + Addr RawSockaddrInet6 + Mtu uint32 +} + +type ICMPv6Filter struct { + Filt [8]uint32 +} + +const ( + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x36 + SizeofXucred = 0x58 + SizeofLinger = 0x8 + SizeofIovec = 0x10 + SizeofIPMreq = 0x8 + SizeofIPMreqn = 0xc + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x30 + SizeofCmsghdr = 0xc + SizeofInet6Pktinfo = 0x14 + SizeofIPv6MTUInfo = 0x20 + SizeofICMPv6Filter = 0x20 +) + +const ( + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 +) + +type PtraceLwpInfoStruct struct { + Lwpid int32 + Event int32 + Flags int32 + Sigmask Sigset_t + Siglist Sigset_t + Siginfo __Siginfo + Tdname [20]int8 + Child_pid int32 + Syscall_code uint32 + Syscall_narg uint32 +} + +type __Siginfo struct { + Signo int32 + Errno int32 + Code int32 + Pid int32 + Uid uint32 + Status int32 + Addr *byte + Value [8]byte + _ [40]byte +} + +type Sigset_t struct { + Val [4]uint32 +} + +type Reg struct { + Ra uint64 + Sp uint64 + Gp uint64 + Tp uint64 + T [7]uint64 + S [12]uint64 + A [8]uint64 + Sepc uint64 + Sstatus uint64 +} + +type FpReg struct { + X [32][2]uint64 + Fcsr uint64 +} + +type FpExtendedPrecision struct{} + +type PtraceIoDesc struct { + Op int32 + Offs *byte + Addr *byte + Len uint64 +} + +type Kevent_t struct { + Ident uint64 + Filter int16 + Flags uint16 + Fflags uint32 + Data int64 + Udata *byte + Ext [4]uint64 +} + +type FdSet struct { + Bits [16]uint64 +} + +const ( + sizeofIfMsghdr = 0xa8 + SizeofIfMsghdr = 0xa8 + sizeofIfData = 0x98 + SizeofIfData = 0x98 + SizeofIfaMsghdr = 0x14 + SizeofIfmaMsghdr = 0x10 + SizeofIfAnnounceMsghdr = 0x18 + SizeofRtMsghdr = 0x98 + SizeofRtMetrics = 0x70 +) + +type ifMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + _ uint16 + Data ifData +} + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Data IfData +} + +type ifData struct { + Type uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Vhid uint8 + Datalen uint16 + Mtu uint32 + Metric uint32 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Oqdrops uint64 + Noproto uint64 + Hwassist uint64 + _ [8]byte + _ [16]byte +} + +type IfData struct { + Type uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Link_state uint8 + Spare_char1 uint8 + Spare_char2 uint8 + Datalen uint8 + Mtu uint64 + Metric uint64 + Baudrate uint64 + Ipackets uint64 + Ierrors uint64 + Opackets uint64 + Oerrors uint64 + Collisions uint64 + Ibytes uint64 + Obytes uint64 + Imcasts uint64 + Omcasts uint64 + Iqdrops uint64 + Noproto uint64 + Hwassist uint64 + Epoch int64 + Lastchange Timeval +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + _ uint16 + Metric int32 +} + +type IfmaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + _ uint16 +} + +type IfAnnounceMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Name [16]int8 + What uint16 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + _ uint16 + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Fmask int32 + Inits uint64 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint64 + Mtu uint64 + Hopcount uint64 + Expire uint64 + Recvpipe uint64 + Sendpipe uint64 + Ssthresh uint64 + Rtt uint64 + Rttvar uint64 + Pksent uint64 + Weight uint64 + Nhidx uint64 + Filler [2]uint64 +} + +const ( + SizeofBpfVersion = 0x4 + SizeofBpfStat = 0x8 + SizeofBpfZbuf = 0x18 + SizeofBpfProgram = 0x10 + SizeofBpfInsn = 0x8 + SizeofBpfHdr = 0x20 + SizeofBpfZbufHeader = 0x20 +) + +type BpfVersion struct { + Major uint16 + Minor uint16 +} + +type BpfStat struct { + Recv uint32 + Drop uint32 +} + +type BpfZbuf struct { + Bufa *byte + Bufb *byte + Buflen uint64 +} + +type BpfProgram struct { + Len uint32 + Insns *BpfInsn +} + +type BpfInsn struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type BpfHdr struct { + Tstamp Timeval + Caplen uint32 + Datalen uint32 + Hdrlen uint16 + _ [6]byte +} + +type BpfZbufHeader struct { + Kernel_gen uint32 + Kernel_len uint32 + User_gen uint32 + _ [5]uint32 +} + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} + +type Winsize struct { + Row uint16 + Col uint16 + Xpixel uint16 + Ypixel uint16 +} + +const ( + AT_FDCWD = -0x64 + AT_EACCESS = 0x100 + AT_SYMLINK_NOFOLLOW = 0x200 + AT_SYMLINK_FOLLOW = 0x400 + AT_REMOVEDIR = 0x800 +) + +type PollFd struct { + Fd int32 + Events int16 + Revents int16 +} + +const ( + POLLERR = 0x8 + POLLHUP = 0x10 + POLLIN = 0x1 + POLLINIGNEOF = 0x2000 + POLLNVAL = 0x20 + POLLOUT = 0x4 + POLLPRI = 0x2 + POLLRDBAND = 0x80 + POLLRDNORM = 0x40 + POLLWRBAND = 0x100 + POLLWRNORM = 0x4 +) + +type CapRights struct { + Rights [2]uint64 +} + +type Utsname struct { + Sysname [256]byte + Nodename [256]byte + Release [256]byte + Version [256]byte + Machine [256]byte +} + +const SizeofClockinfo = 0x14 + +type Clockinfo struct { + Hz int32 + Tick int32 + Spare int32 + Stathz int32 + Profhz int32 +} diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 9962d26b..86984798 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -1127,7 +1127,9 @@ const ( PERF_BR_SYSRET = 0x8 PERF_BR_COND_CALL = 0x9 PERF_BR_COND_RET = 0xa - PERF_BR_MAX = 0xb + PERF_BR_ERET = 0xb + PERF_BR_IRQ = 0xc + PERF_BR_MAX = 0xd PERF_SAMPLE_REGS_ABI_NONE = 0x0 PERF_SAMPLE_REGS_ABI_32 = 0x1 PERF_SAMPLE_REGS_ABI_64 = 0x2 @@ -2969,7 +2971,7 @@ const ( DEVLINK_CMD_TRAP_POLICER_NEW = 0x47 DEVLINK_CMD_TRAP_POLICER_DEL = 0x48 DEVLINK_CMD_HEALTH_REPORTER_TEST = 0x49 - DEVLINK_CMD_MAX = 0x4d + DEVLINK_CMD_MAX = 0x51 DEVLINK_PORT_TYPE_NOTSET = 0x0 DEVLINK_PORT_TYPE_AUTO = 0x1 DEVLINK_PORT_TYPE_ETH = 0x2 @@ -3198,7 +3200,7 @@ const ( DEVLINK_ATTR_RATE_NODE_NAME = 0xa8 DEVLINK_ATTR_RATE_PARENT_NODE_NAME = 0xa9 DEVLINK_ATTR_REGION_MAX_SNAPSHOTS = 0xaa - DEVLINK_ATTR_MAX = 0xaa + DEVLINK_ATTR_MAX = 0xae DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0x0 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 0x1 DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0x0 @@ -3638,7 +3640,11 @@ const ( ETHTOOL_A_RINGS_RX_MINI = 0x7 ETHTOOL_A_RINGS_RX_JUMBO = 0x8 ETHTOOL_A_RINGS_TX = 0x9 - ETHTOOL_A_RINGS_MAX = 0xa + ETHTOOL_A_RINGS_RX_BUF_LEN = 0xa + ETHTOOL_A_RINGS_TCP_DATA_SPLIT = 0xb + ETHTOOL_A_RINGS_CQE_SIZE = 0xc + ETHTOOL_A_RINGS_TX_PUSH = 0xd + ETHTOOL_A_RINGS_MAX = 0xd ETHTOOL_A_CHANNELS_UNSPEC = 0x0 ETHTOOL_A_CHANNELS_HEADER = 0x1 ETHTOOL_A_CHANNELS_RX_MAX = 0x2 @@ -4323,7 +4329,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x135 + NL80211_ATTR_MAX = 0x137 NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -4549,7 +4555,7 @@ const ( NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY = 0x3 NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE = 0x5 NL80211_BAND_IFTYPE_ATTR_IFTYPES = 0x1 - NL80211_BAND_IFTYPE_ATTR_MAX = 0x7 + NL80211_BAND_IFTYPE_ATTR_MAX = 0xb NL80211_BAND_S1GHZ = 0x4 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE = 0x2 NL80211_BITRATE_ATTR_MAX = 0x2 @@ -4887,7 +4893,7 @@ const ( NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf - NL80211_FREQUENCY_ATTR_MAX = 0x19 + NL80211_FREQUENCY_ATTR_MAX = 0x1b NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6 NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11 NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc @@ -5254,7 +5260,7 @@ const ( NL80211_RATE_INFO_HE_RU_ALLOC_52 = 0x1 NL80211_RATE_INFO_HE_RU_ALLOC_996 = 0x5 NL80211_RATE_INFO_HE_RU_ALLOC = 0x11 - NL80211_RATE_INFO_MAX = 0x11 + NL80211_RATE_INFO_MAX = 0x16 NL80211_RATE_INFO_MCS = 0x2 NL80211_RATE_INFO_SHORT_GI = 0x4 NL80211_RATE_INFO_VHT_MCS = 0x6 @@ -5588,3 +5594,8 @@ const ( FR_ACT_UNREACHABLE = 0x7 FR_ACT_PROHIBIT = 0x8 ) + +const ( + AUDIT_NLGRP_NONE = 0x0 + AUDIT_NLGRP_READLOG = 0x1 +) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 4948362f..7551af48 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -324,6 +324,13 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + _ [4]byte + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index f64345e0..3e738ac0 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -338,6 +338,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 72469c79..6183eef4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -315,6 +315,13 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + _ [4]byte + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 68f07228..968cecb1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -317,6 +317,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go index 090ae46c..8fe4c522 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -318,6 +318,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 03604cca..11426a30 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -320,6 +320,13 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + _ [4]byte + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index fe57a7b2..ad1c3b3d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -320,6 +320,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 3f0db4da..15fd84e4 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -320,6 +320,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 70ecd3b2..49c49825 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -320,6 +320,13 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + _ [4]byte + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index 4e700120..cd36d0da 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -327,6 +327,13 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + _ [4]byte + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint32 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 34a57c69..8c6fce03 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -327,6 +327,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index 6b84a472..20910f2a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -327,6 +327,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index c4a305fe..71b7b333 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -345,6 +345,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index a1f1e4c9..71184cc2 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -340,6 +340,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index df95ebf3..06156285 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -322,6 +322,12 @@ type Taskstats struct { Ac_btime64 uint64 Compact_count uint64 Compact_delay_total uint64 + Ac_tgid uint32 + Ac_tgetime uint64 + Ac_exe_dev uint64 + Ac_exe_inode uint64 + Wpcopy_count uint64 + Wpcopy_delay_total uint64 } type cpuMask uint64 diff --git a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go index ad4aad27..c1a9b83a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go @@ -178,7 +178,7 @@ type Linger struct { } type Iovec struct { - Base *int8 + Base *byte Len uint64 } diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 636e5de6..be3ec2bd 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -861,6 +861,7 @@ const socket_error = uintptr(^uint32(0)) //sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses //sys GetACP() (acp uint32) = kernel32.GetACP //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar +//sys getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) = iphlpapi.GetBestInterfaceEx // For testing: clients can set this flag to force // creation of IPv6 sockets to return EAFNOSUPPORT. @@ -1045,6 +1046,14 @@ func Connect(fd Handle, sa Sockaddr) (err error) { return connect(fd, ptr, n) } +func GetBestInterfaceEx(sa Sockaddr, pdwBestIfIndex *uint32) (err error) { + ptr, _, err := sa.sockaddr() + if err != nil { + return err + } + return getBestInterfaceEx(ptr, pdwBestIfIndex) +} + func Getsockname(fd Handle) (sa Sockaddr, err error) { var rsa RawSockaddrAny l := int32(unsafe.Sizeof(rsa)) diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index e19471c6..f9eaca52 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -160,6 +160,10 @@ const ( MAX_COMPUTERNAME_LENGTH = 15 + MAX_DHCPV6_DUID_LENGTH = 130 + + MAX_DNS_SUFFIX_STRING_LENGTH = 256 + TIME_ZONE_ID_UNKNOWN = 0 TIME_ZONE_ID_STANDARD = 1 @@ -2000,27 +2004,62 @@ type IpAdapterPrefix struct { } type IpAdapterAddresses struct { - Length uint32 - IfIndex uint32 - Next *IpAdapterAddresses - AdapterName *byte - FirstUnicastAddress *IpAdapterUnicastAddress - FirstAnycastAddress *IpAdapterAnycastAddress - FirstMulticastAddress *IpAdapterMulticastAddress - FirstDnsServerAddress *IpAdapterDnsServerAdapter - DnsSuffix *uint16 - Description *uint16 - FriendlyName *uint16 - PhysicalAddress [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte - PhysicalAddressLength uint32 - Flags uint32 - Mtu uint32 - IfType uint32 - OperStatus uint32 - Ipv6IfIndex uint32 - ZoneIndices [16]uint32 - FirstPrefix *IpAdapterPrefix - /* more fields might be present here. */ + Length uint32 + IfIndex uint32 + Next *IpAdapterAddresses + AdapterName *byte + FirstUnicastAddress *IpAdapterUnicastAddress + FirstAnycastAddress *IpAdapterAnycastAddress + FirstMulticastAddress *IpAdapterMulticastAddress + FirstDnsServerAddress *IpAdapterDnsServerAdapter + DnsSuffix *uint16 + Description *uint16 + FriendlyName *uint16 + PhysicalAddress [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte + PhysicalAddressLength uint32 + Flags uint32 + Mtu uint32 + IfType uint32 + OperStatus uint32 + Ipv6IfIndex uint32 + ZoneIndices [16]uint32 + FirstPrefix *IpAdapterPrefix + TransmitLinkSpeed uint64 + ReceiveLinkSpeed uint64 + FirstWinsServerAddress *IpAdapterWinsServerAddress + FirstGatewayAddress *IpAdapterGatewayAddress + Ipv4Metric uint32 + Ipv6Metric uint32 + Luid uint64 + Dhcpv4Server SocketAddress + CompartmentId uint32 + NetworkGuid GUID + ConnectionType uint32 + TunnelType uint32 + Dhcpv6Server SocketAddress + Dhcpv6ClientDuid [MAX_DHCPV6_DUID_LENGTH]byte + Dhcpv6ClientDuidLength uint32 + Dhcpv6Iaid uint32 + FirstDnsSuffix *IpAdapterDNSSuffix +} + +type IpAdapterWinsServerAddress struct { + Length uint32 + Reserved uint32 + Next *IpAdapterWinsServerAddress + Address SocketAddress +} + +type IpAdapterGatewayAddress struct { + Length uint32 + Reserved uint32 + Next *IpAdapterGatewayAddress + Address SocketAddress +} + +type IpAdapterDNSSuffix struct { + Next *IpAdapterDNSSuffix + String [MAX_DNS_SUFFIX_STRING_LENGTH]uint16 } const ( diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 68f52c1e..678262cd 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -177,6 +177,7 @@ var ( procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") + procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") @@ -1539,6 +1540,14 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) { return } +func getBestInterfaceEx(sockaddr unsafe.Pointer, pdwBestIfIndex *uint32) (errcode error) { + r0, _, _ := syscall.Syscall(procGetBestInterfaceEx.Addr(), 2, uintptr(sockaddr), uintptr(unsafe.Pointer(pdwBestIfIndex)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} + func GetIfEntry(pIfRow *MibIfRow) (errcode error) { r0, _, _ := syscall.Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) if r0 != 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index 674ea445..5c3ca424 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -124,6 +124,10 @@ github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp +# github.com/google/gopacket v1.1.19 +## explicit; go 1.12 +github.com/google/gopacket +github.com/google/gopacket/layers # github.com/google/uuid v1.3.0 ## explicit github.com/google/uuid @@ -341,7 +345,7 @@ golang.org/x/crypto/ssh/terminal ## explicit; go 1.12 golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e +# golang.org/x/net v0.0.0-20220812174116-3211cb980234 ## explicit; go 1.17 golang.org/x/net/bpf golang.org/x/net/context @@ -349,6 +353,7 @@ golang.org/x/net/context/ctxhttp golang.org/x/net/http/httpguts golang.org/x/net/http2 golang.org/x/net/http2/hpack +golang.org/x/net/icmp golang.org/x/net/idna golang.org/x/net/internal/iana golang.org/x/net/internal/socket @@ -366,7 +371,7 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ## explicit golang.org/x/sync/errgroup -# golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +# golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/execabs From d2bc15e2240cb5ec20579cbdfade283d71cda98e Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 17 Aug 2022 18:23:04 +0100 Subject: [PATCH 169/238] TUN-6667: DatagramMuxerV2 provides a method to receive RawPacket --- connection/quic.go | 8 ++--- quic/datagram_test.go | 71 +++++++++++++++++++++++++++++++++------- quic/datagramv2.go | 75 +++++++++++++++++++++++++++---------------- 3 files changed, 111 insertions(+), 43 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 33a2fc7a..edc17af2 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -47,7 +47,7 @@ type QUICConnection struct { // sessionManager tracks active sessions. It receives datagrams from quic connection via datagramMuxer sessionManager datagramsession.Manager // datagramMuxer mux/demux datagrams from quic connection - datagramMuxer *quicpogs.DatagramMuxer + datagramMuxer quicpogs.BaseDatagramMuxer controlStreamHandler ControlStreamHandler connOptions *tunnelpogs.ConnectionOptions } @@ -67,9 +67,9 @@ func NewQUICConnection( return nil, &EdgeQuicDialError{Cause: err} } - demuxChan := make(chan *packet.Session, demuxChanCapacity) - datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan) - sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, demuxChan) + sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) + datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan) + sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) return &QUICConnection{ session: session, diff --git a/quic/datagram_test.go b/quic/datagram_test.go index 32ce4399..c26362a9 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -9,13 +9,17 @@ import ( "encoding/pem" "fmt" "math/big" + "net/netip" "testing" "time" + "github.com/google/gopacket/layers" "github.com/google/uuid" "github.com/lucas-clemente/quic-go" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" "golang.org/x/sync/errgroup" "github.com/cloudflare/cloudflared/packet" @@ -68,15 +72,45 @@ func TestDatagram(t *testing.T) { Payload: maxPayload, }, } - flowPayloads := [][]byte{ - maxPayload, + + packets := []packet.ICMP{ + { + IP: &packet.IP{ + Src: netip.MustParseAddr("172.16.0.1"), + Dst: netip.MustParseAddr("192.168.0.1"), + Protocol: layers.IPProtocolICMPv4, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeTimeExceeded, + Code: 0, + Body: &icmp.TimeExceeded{ + Data: []byte("original packet"), + }, + }, + }, + { + IP: &packet.IP{ + Src: netip.MustParseAddr("172.16.0.2"), + Dst: netip.MustParseAddr("192.168.0.2"), + Protocol: layers.IPProtocolICMPv4, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: 6182, + Seq: 9151, + Data: []byte("Test ICMP echo"), + }, + }, + }, } testDatagram(t, 1, sessionToPayload, nil) - testDatagram(t, 2, sessionToPayload, flowPayloads) + testDatagram(t, 2, sessionToPayload, packets) } -func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Session, packetPayloads [][]byte) { +func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Session, packets []packet.ICMP) { quicConfig := &quic.Config{ KeepAlivePeriod: 5 * time.Millisecond, EnableDatagrams: true, @@ -103,12 +137,20 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Sessi muxer := NewDatagramMuxer(quicSession, &logger, sessionDemuxChan) muxer.ServeReceive(ctx) case 2: - packetDemuxChan := make(chan []byte, len(packetPayloads)) - muxer := NewDatagramMuxerV2(quicSession, &logger, sessionDemuxChan, packetDemuxChan) + muxer := NewDatagramMuxerV2(quicSession, &logger, sessionDemuxChan) muxer.ServeReceive(ctx) - for _, expectedPayload := range packetPayloads { - require.Equal(t, expectedPayload, <-packetDemuxChan) + icmpDecoder := packet.NewICMPDecoder() + for _, pk := range packets { + received, err := muxer.ReceivePacket(ctx) + require.NoError(t, err) + + receivedICMP, err := icmpDecoder.Decode(received.Data) + require.NoError(t, err) + require.Equal(t, pk.IP, receivedICMP.IP) + require.Equal(t, pk.Type, receivedICMP.Type) + require.Equal(t, pk.Code, receivedICMP.Code) + require.Equal(t, pk.Body, receivedICMP.Body) } default: return fmt.Errorf("unknown datagram version %d", version) @@ -141,12 +183,17 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Sessi case 1: muxer = NewDatagramMuxer(quicSession, &logger, nil) case 2: - muxerV2 := NewDatagramMuxerV2(quicSession, &logger, nil, nil) - for _, payload := range packetPayloads { - require.NoError(t, muxerV2.MuxPacket(payload)) + muxerV2 := NewDatagramMuxerV2(quicSession, &logger, nil) + encoder := packet.NewEncoder() + for _, pk := range packets { + encodedPacket, err := encoder.Encode(&pk) + require.NoError(t, err) + require.NoError(t, muxerV2.SendPacket(encodedPacket)) } // Payload larger than transport MTU, should not be sent - require.Error(t, muxerV2.MuxPacket(largePayload)) + require.Error(t, muxerV2.SendPacket(packet.RawPacket{ + Data: largePayload, + })) muxer = muxerV2 default: return fmt.Errorf("unknown datagram version %d", version) diff --git a/quic/datagramv2.go b/quic/datagramv2.go index d11bcfaf..03e60b1e 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -16,6 +16,8 @@ type datagramV2Type byte const ( udp datagramV2Type = iota ip + // Same as sessionDemuxChan capacity + packetChanCapacity = 16 ) func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) { @@ -35,24 +37,24 @@ type DatagramMuxerV2 struct { session quic.Connection logger *zerolog.Logger sessionDemuxChan chan<- *packet.Session - packetDemuxChan chan<- []byte + packetDemuxChan chan packet.RawPacket } func NewDatagramMuxerV2( quicSession quic.Connection, log *zerolog.Logger, sessionDemuxChan chan<- *packet.Session, - packetDemuxChan chan<- []byte) *DatagramMuxerV2 { +) *DatagramMuxerV2 { logger := log.With().Uint8("datagramVersion", 2).Logger() return &DatagramMuxerV2{ session: quicSession, logger: &logger, sessionDemuxChan: sessionDemuxChan, - packetDemuxChan: packetDemuxChan, + packetDemuxChan: make(chan packet.RawPacket, packetChanCapacity), } } -// MuxSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can +// SendToSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can // demultiplex the payload from multiple datagram sessions func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { if len(session.Payload) > dm.mtu() { @@ -73,10 +75,10 @@ func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { return nil } -// MuxPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing +// SendPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing // the payload as IP and look at the source and destination. -func (dm *DatagramMuxerV2) MuxPacket(packet []byte) error { - payloadWithVersion, err := suffixType(packet, ip) +func (dm *DatagramMuxerV2) SendPacket(pk packet.RawPacket) error { + payloadWithVersion, err := suffixType(pk.Data, ip) if err != nil { return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped") } @@ -102,6 +104,15 @@ func (dm *DatagramMuxerV2) ServeReceive(ctx context.Context) error { } } +func (dm *DatagramMuxerV2) ReceivePacket(ctx context.Context) (packet.RawPacket, error) { + select { + case <-ctx.Done(): + return packet.RawPacket{}, ctx.Err() + case pk := <-dm.packetDemuxChan: + return pk, nil + } +} + func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error { if len(msgWithType) < 1 { return fmt.Errorf("QUIC datagram should have at least 1 byte") @@ -110,28 +121,38 @@ func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error msg := msgWithType[0 : len(msgWithType)-1] switch msgType { case udp: - sessionID, payload, err := extractSessionID(msg) - if err != nil { - return err - } - sessionDatagram := packet.Session{ - ID: sessionID, - Payload: payload, - } - select { - case dm.sessionDemuxChan <- &sessionDatagram: - return nil - case <-ctx.Done(): - return ctx.Err() - } + return dm.handleSession(ctx, msg) case ip: - select { - case dm.packetDemuxChan <- msg: - return nil - case <-ctx.Done(): - return ctx.Err() - } + return dm.handlePacket(ctx, msg) default: return fmt.Errorf("Unexpected datagram type %d", msgType) } } + +func (dm *DatagramMuxerV2) handleSession(ctx context.Context, session []byte) error { + sessionID, payload, err := extractSessionID(session) + if err != nil { + return err + } + sessionDatagram := packet.Session{ + ID: sessionID, + Payload: payload, + } + select { + case dm.sessionDemuxChan <- &sessionDatagram: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +func (dm *DatagramMuxerV2) handlePacket(ctx context.Context, pk []byte) error { + select { + case <-ctx.Done(): + return ctx.Err() + case dm.packetDemuxChan <- packet.RawPacket{ + Data: pk, + }: + return nil + } +} From f6bd4aa03910c3a3995956df244e6286bb31592c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Tue, 16 Aug 2022 12:21:58 +0100 Subject: [PATCH 170/238] TUN-6676: Add suport for trailers in http2 connections --- connection/connection.go | 24 +++++++++++++--- connection/connection_test.go | 39 -------------------------- connection/h2mux.go | 4 +++ connection/http2.go | 29 +++++++++++++------ connection/quic.go | 4 +++ proxy/proxy.go | 52 ++++++++++++++--------------------- proxy/proxy_test.go | 26 ++++++++++++++---- 7 files changed, 89 insertions(+), 89 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index be72b5d2..5d2db19c 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -24,9 +24,16 @@ const ( LogFieldConnIndex = "connIndex" MaxGracePeriod = time.Minute * 3 MaxConcurrentStreams = math.MaxUint32 + + contentTypeHeader = "content-type" + sseContentType = "text/event-stream" + grpcContentType = "application/grpc" ) -var switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols)) +var ( + switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols)) + flushableContentTypes = []string{sseContentType, grpcContentType} +) type Orchestrator interface { UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse @@ -190,6 +197,7 @@ func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) erro type ResponseWriter interface { WriteRespHeaders(status int, header http.Header) error + AddTrailer(trailerName, trailerValue string) io.Writer } @@ -198,10 +206,18 @@ type ConnectedFuse interface { IsConnected() bool } -func IsServerSentEvent(headers http.Header) bool { - if contentType := headers.Get("content-type"); contentType != "" { - return strings.HasPrefix(strings.ToLower(contentType), "text/event-stream") +// Helper method to let the caller know what content-types should require a flush on every +// write to a ResponseWriter. +func shouldFlush(headers http.Header) bool { + if contentType := headers.Get(contentTypeHeader); contentType != "" { + contentType = strings.ToLower(contentType) + for _, c := range flushableContentTypes { + if strings.HasPrefix(contentType, c) { + return true + } + } } + return false } diff --git a/connection/connection_test.go b/connection/connection_test.go index ae37db75..3708e16a 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -6,11 +6,9 @@ import ( "io" "math/rand" "net/http" - "testing" "time" "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" "github.com/cloudflare/cloudflared/tracing" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" @@ -197,40 +195,3 @@ func (mcf mockConnectedFuse) Connected() {} func (mcf mockConnectedFuse) IsConnected() bool { return true } - -func TestIsEventStream(t *testing.T) { - tests := []struct { - headers http.Header - isEventStream bool - }{ - { - headers: newHeader("Content-Type", "text/event-stream"), - isEventStream: true, - }, - { - headers: newHeader("content-type", "text/event-stream"), - isEventStream: true, - }, - { - headers: newHeader("Content-Type", "text/event-stream; charset=utf-8"), - isEventStream: true, - }, - { - headers: newHeader("Content-Type", "application/json"), - isEventStream: false, - }, - { - headers: http.Header{}, - isEventStream: false, - }, - } - for _, test := range tests { - assert.Equal(t, test.isEventStream, IsServerSentEvent(test.headers)) - } -} - -func newHeader(key, value string) http.Header { - header := http.Header{} - header.Add(key, value) - return header -} diff --git a/connection/h2mux.go b/connection/h2mux.go index b78b433f..d8291b62 100644 --- a/connection/h2mux.go +++ b/connection/h2mux.go @@ -259,6 +259,10 @@ type h2muxRespWriter struct { *h2mux.MuxedStream } +func (rp *h2muxRespWriter) AddTrailer(trailerName, trailerValue string) { + // do nothing. we don't support trailers over h2mux +} + func (rp *h2muxRespWriter) WriteRespHeaders(status int, header http.Header) error { headers := H1ResponseToH2ResponseHeaders(status, header) headers = append(headers, h2mux.Header{Name: ResponseMetaHeader, Value: responseMetaHeaderOrigin}) diff --git a/connection/http2.go b/connection/http2.go index 8b488010..a6756b1d 100644 --- a/connection/http2.go +++ b/connection/http2.go @@ -191,11 +191,12 @@ func (c *HTTP2Connection) close() { } type http2RespWriter struct { - r io.Reader - w http.ResponseWriter - flusher http.Flusher - shouldFlush bool - log *zerolog.Logger + r io.Reader + w http.ResponseWriter + flusher http.Flusher + shouldFlush bool + statusWritten bool + log *zerolog.Logger } func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, log *zerolog.Logger) (*http2RespWriter, error) { @@ -219,11 +220,20 @@ func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, l }, nil } +func (rp *http2RespWriter) AddTrailer(trailerName, trailerValue string) { + if !rp.statusWritten { + rp.log.Warn().Msg("Tried to add Trailer to response before status written. Ignoring...") + return + } + + rp.w.Header().Add(http2.TrailerPrefix+trailerName, trailerValue) +} + func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) error { dest := rp.w.Header() userHeaders := make(http.Header, len(header)) for name, values := range header { - // Since these are http2 headers, they're required to be lowercase + // lowercase headers for simplicity check h2name := strings.ToLower(name) if h2name == "content-length" { @@ -234,7 +244,7 @@ func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) erro if h2name == tracing.IntCloudflaredTracingHeader { // Add cf-int-cloudflared-tracing header outside of serialized userHeaders - rp.w.Header()[tracing.CanonicalCloudflaredTracingHeader] = values + dest[tracing.CanonicalCloudflaredTracingHeader] = values continue } @@ -247,18 +257,21 @@ func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) erro // Perform user header serialization and set them in the single header dest.Set(CanonicalResponseUserHeaders, SerializeHeaders(userHeaders)) + rp.setResponseMetaHeader(responseMetaHeaderOrigin) // HTTP2 removes support for 101 Switching Protocols https://tools.ietf.org/html/rfc7540#section-8.1.1 if status == http.StatusSwitchingProtocols { status = http.StatusOK } rp.w.WriteHeader(status) - if IsServerSentEvent(header) { + if shouldFlush(header) { rp.shouldFlush = true } if rp.shouldFlush { rp.flusher.Flush() } + + rp.statusWritten = true return nil } diff --git a/connection/quic.go b/connection/quic.go index edc17af2..7a61bdbb 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -329,6 +329,10 @@ func newHTTPResponseAdapter(s *quicpogs.RequestServerStream) httpResponseAdapter return httpResponseAdapter{s} } +func (hrw httpResponseAdapter) AddTrailer(trailerName, trailerValue string) { + // we do not support trailers over QUIC +} + func (hrw httpResponseAdapter) WriteRespHeaders(status int, header http.Header) error { metadata := make([]quicpogs.Metadata, 0) metadata = append(metadata, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(status)}) diff --git a/proxy/proxy.go b/proxy/proxy.go index 9bc37615..6fe4efa4 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1,7 +1,6 @@ package proxy import ( - "bufio" "context" "fmt" "io" @@ -29,6 +28,8 @@ const ( LogFieldRule = "ingressRule" LogFieldOriginService = "originService" LogFieldFlowID = "flowID" + + trailerHeaderName = "Trailer" ) // Proxy represents a means to Proxy between cloudflared and the origin services. @@ -207,15 +208,16 @@ func (p *Proxy) proxyHTTPRequest( tracing.EndWithStatusCode(ttfbSpan, resp.StatusCode) defer resp.Body.Close() - // resp headers can be nil - if resp.Header == nil { - resp.Header = make(http.Header) + headers := make(http.Header, len(resp.Header)) + // copy headers + for k, v := range resp.Header { + headers[k] = v } // Add spans to response header (if available) - tr.AddSpans(resp.Header) + tr.AddSpans(headers) - err = w.WriteRespHeaders(resp.StatusCode, resp.Header) + err = w.WriteRespHeaders(resp.StatusCode, headers) if err != nil { return errors.Wrap(err, "Error writing response header") } @@ -236,12 +238,10 @@ func (p *Proxy) proxyHTTPRequest( return nil } - if connection.IsServerSentEvent(resp.Header) { - p.log.Debug().Msg("Detected Server-Side Events from Origin") - p.writeEventStream(w, resp.Body) - } else { - _, _ = cfio.Copy(w, resp.Body) - } + _, _ = cfio.Copy(w, resp.Body) + + // copy trailers + copyTrailers(w, resp) p.logOriginResponse(resp, fields) return nil @@ -296,26 +296,6 @@ func (wr *bidirectionalStream) Write(p []byte) (n int, err error) { return wr.writer.Write(p) } -func (p *Proxy) writeEventStream(w connection.ResponseWriter, respBody io.ReadCloser) { - reader := bufio.NewReader(respBody) - for { - line, readErr := reader.ReadBytes('\n') - - // We first try to write whatever we read even if an error occurred - // The reason for doing it is to guarantee we really push everything to the eyeball side - // before returning - if len(line) > 0 { - if _, writeErr := w.Write(line); writeErr != nil { - return - } - } - - if readErr != nil { - return - } - } -} - func (p *Proxy) appendTagHeaders(r *http.Request) { for _, tag := range p.tags { r.Header.Add(TagHeaderNamePrefix+tag.Name, tag.Value) @@ -329,6 +309,14 @@ type logFields struct { flowID string } +func copyTrailers(w connection.ResponseWriter, response *http.Response) { + for trailerHeader, trailerValues := range response.Trailer { + for _, trailerValue := range trailerValues { + w.AddTrailer(trailerHeader, trailerValue) + } + } +} + func (p *Proxy) logRequest(r *http.Request, fields logFields) { if fields.cfRay != "" { p.log.Debug().Msgf("CF-RAY: %s %s %s %s", fields.cfRay, r.Method, r.URL, r.Proto) diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index da90288e..5407acaa 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -22,6 +22,8 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" + "github.com/cloudflare/cloudflared/cfio" + "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/hello" @@ -62,6 +64,10 @@ func (w *mockHTTPRespWriter) WriteRespHeaders(status int, header http.Header) er return nil } +func (w *mockHTTPRespWriter) AddTrailer(trailerName, trailerValue string) { + // do nothing +} + func (w *mockHTTPRespWriter) Read(data []byte) (int, error) { return 0, fmt.Errorf("mockHTTPRespWriter doesn't implement io.Reader") } @@ -117,7 +123,10 @@ func newMockSSERespWriter() *mockSSERespWriter { } func (w *mockSSERespWriter) Write(data []byte) (int, error) { - w.writeNotification <- data + newData := make([]byte, len(data)) + copy(newData, data) + + w.writeNotification <- newData return len(data), nil } @@ -256,11 +265,8 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { for i := 0; i < pushCount; i++ { line := responseWriter.ReadBytes() - expect := fmt.Sprintf("%d\n", i) + expect := fmt.Sprintf("%d\n\n", i) require.Equal(t, []byte(expect), line, fmt.Sprintf("Expect to read %v, got %v", expect, line)) - - line = responseWriter.ReadBytes() - require.Equal(t, []byte("\n"), line, fmt.Sprintf("Expect to read '\n', got %v", line)) } cancel() @@ -276,7 +282,7 @@ func testProxySSEAllData(proxy *Proxy) func(t *testing.T) { responseWriter := newMockSSERespWriter() // responseWriter uses an unbuffered channel, so we call in a different go-routine - go proxy.writeEventStream(responseWriter, eyeballReader) + go cfio.Copy(responseWriter, eyeballReader) result := string(<-responseWriter.writeNotification) require.Equal(t, "data\r\r", result) @@ -825,6 +831,10 @@ func (w *wsRespWriter) WriteRespHeaders(status int, header http.Header) error { return nil } +func (w *wsRespWriter) AddTrailer(trailerName, trailerValue string) { + // do nothing +} + // respHeaders is a test function to read respHeaders func (w *wsRespWriter) headers() http.Header { // Removing indeterminstic header because it cannot be asserted. @@ -852,6 +862,10 @@ func (m *mockTCPRespWriter) Write(p []byte) (n int, err error) { return m.w.Write(p) } +func (w *mockTCPRespWriter) AddTrailer(trailerName, trailerValue string) { + // do nothing +} + func (m *mockTCPRespWriter) WriteRespHeaders(status int, header http.Header) error { m.responseHeaders = header m.code = status From 59f5b0df8302cd56ce57f0d558ffc48d8643c96e Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 18 Aug 2022 16:03:47 +0100 Subject: [PATCH 171/238] TUN-6530: Implement ICMPv4 proxy This proxy uses unprivileged datagram-oriented endpoint and is shared by all quic connections --- connection/quic.go | 50 +++++++++- connection/quic_test.go | 1 + ingress/origin_icmp_proxy.go | 139 +++++++++++++++++++++++++++ ingress/origin_icmp_proxy_test.go | 150 ++++++++++++++++++++++++++++++ packet/decoder.go | 8 +- packet/decoder_test.go | 12 +-- packet/flow.go | 30 +++--- quic/datagram_test.go | 2 +- supervisor/supervisor.go | 35 ++++++- supervisor/tunnel.go | 139 +++++++++------------------ 10 files changed, 440 insertions(+), 126 deletions(-) create mode 100644 ingress/origin_icmp_proxy.go create mode 100644 ingress/origin_icmp_proxy_test.go diff --git a/connection/quic.go b/connection/quic.go index 7a61bdbb..10361088 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -48,6 +48,7 @@ type QUICConnection struct { sessionManager datagramsession.Manager // datagramMuxer mux/demux datagrams from quic connection datagramMuxer quicpogs.BaseDatagramMuxer + packetRouter *packetRouter controlStreamHandler ControlStreamHandler connOptions *tunnelpogs.ConnectionOptions } @@ -61,6 +62,7 @@ func NewQUICConnection( connOptions *tunnelpogs.ConnectionOptions, controlStreamHandler ControlStreamHandler, logger *zerolog.Logger, + icmpProxy ingress.ICMPProxy, ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { @@ -68,7 +70,20 @@ func NewQUICConnection( } sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) - datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan) + var ( + datagramMuxer quicpogs.BaseDatagramMuxer + pr *packetRouter + ) + if icmpProxy != nil { + pr = &packetRouter{ + muxer: quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan), + icmpProxy: icmpProxy, + logger: logger, + } + datagramMuxer = pr.muxer + } else { + datagramMuxer = quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan) + } sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) return &QUICConnection{ @@ -77,6 +92,7 @@ func NewQUICConnection( logger: logger, sessionManager: sessionManager, datagramMuxer: datagramMuxer, + packetRouter: pr, controlStreamHandler: controlStreamHandler, connOptions: connOptions, }, nil @@ -117,6 +133,12 @@ func (q *QUICConnection) Serve(ctx context.Context) error { defer cancel() return q.datagramMuxer.ServeReceive(ctx) }) + if q.packetRouter != nil { + errGroup.Go(func() error { + defer cancel() + return q.packetRouter.serve(ctx) + }) + } return errGroup.Wait() } @@ -305,6 +327,32 @@ func (q *QUICConnection) UpdateConfiguration(ctx context.Context, version int32, return q.orchestrator.UpdateConfig(version, config) } +type packetRouter struct { + muxer *quicpogs.DatagramMuxerV2 + icmpProxy ingress.ICMPProxy + logger *zerolog.Logger +} + +func (pr *packetRouter) serve(ctx context.Context) error { + icmpDecoder := packet.NewICMPDecoder() + for { + pk, err := pr.muxer.ReceivePacket(ctx) + if err != nil { + return err + } + icmpPacket, err := icmpDecoder.Decode(pk) + if err != nil { + pr.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram") + continue + } + + if err := pr.icmpProxy.Request(icmpPacket, pr.muxer); err != nil { + pr.logger.Err(err).Str("src", icmpPacket.Src.String()).Str("dst", icmpPacket.Dst.String()).Msg("Failed to send ICMP packet") + continue + } + } +} + // streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to // the client. type streamReadWriteAcker struct { diff --git a/connection/quic_test.go b/connection/quic_test.go index 0afb3953..c860400e 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -682,6 +682,7 @@ func testQUICConnection(udpListenerAddr net.Addr, t *testing.T) *QUICConnection &tunnelpogs.ConnectionOptions{}, fakeControlStream{}, &log, + nil, ) require.NoError(t, err) return qc diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go new file mode 100644 index 00000000..672e833e --- /dev/null +++ b/ingress/origin_icmp_proxy.go @@ -0,0 +1,139 @@ +package ingress + +import ( + "context" + "fmt" + "net" + "strconv" + + "github.com/google/gopacket/layers" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/net/icmp" + + "github.com/cloudflare/cloudflared/packet" +) + +// ICMPProxy sends ICMP messages and listens for their responses +type ICMPProxy interface { + // Request sends an ICMP message + Request(pk *packet.ICMP, responder packet.FlowResponder) error + // ListenResponse listens for responses to the requests until context is done + ListenResponse(ctx context.Context) error +} + +// TODO: TUN-6654 Extend support to IPv6 +type icmpProxy struct { + srcFlowTracker *packet.FlowTracker + conn *icmp.PacketConn + logger *zerolog.Logger + encoder *packet.Encoder +} + +// TODO: TUN-6586: Use echo ID as FlowID +type seqNumFlowID int + +func (snf seqNumFlowID) ID() string { + return strconv.FormatInt(int64(snf), 10) +} + +func NewICMPProxy(network string, listenIP net.IP, logger *zerolog.Logger) (*icmpProxy, error) { + conn, err := icmp.ListenPacket(network, listenIP.String()) + if err != nil { + return nil, err + } + return &icmpProxy{ + srcFlowTracker: packet.NewFlowTracker(), + conn: conn, + logger: logger, + encoder: packet.NewEncoder(), + }, nil +} + +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { + switch body := pk.Message.Body.(type) { + case *icmp.Echo: + return ip.sendICMPEchoRequest(pk, body, responder) + default: + return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) + } +} + +func (ip *icmpProxy) ListenResponse(ctx context.Context) error { + go func() { + <-ctx.Done() + ip.conn.Close() + }() + buf := make([]byte, 1500) + for { + n, src, err := ip.conn.ReadFrom(buf) + if err != nil { + return err + } + // TODO: TUN-6654 Check for IPv6 + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), buf[:n]) + if err != nil { + ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to parse ICMP message") + continue + } + switch body := msg.Body.(type) { + case *icmp.Echo: + if err := ip.handleEchoResponse(msg, body); err != nil { + ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to handle ICMP response") + continue + } + default: + ip.logger.Warn(). + Str("icmpType", fmt.Sprintf("%s", msg.Type)). + Msgf("Responding to this type of ICMP is not implemented") + continue + } + } +} + +func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { + flow := packet.Flow{ + Src: pk.Src, + Dst: pk.Dst, + Responder: responder, + } + // TODO: TUN-6586 rewrite ICMP echo request identifier and use it to track flows + flowID := seqNumFlowID(echo.Seq) + // TODO: TUN-6588 clean up flows + if replaced := ip.srcFlowTracker.Register(flowID, &flow, true); replaced { + ip.logger.Info().Str("src", flow.Src.String()).Str("dst", flow.Dst.String()).Msg("Replaced flow") + } + var pseudoHeader []byte = nil + serializedMsg, err := pk.Marshal(pseudoHeader) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + // The address needs to be of type UDPAddr when conn is created without priviledge + _, err = ip.conn.WriteTo(serializedMsg, &net.UDPAddr{ + IP: pk.Dst.AsSlice(), + }) + return err +} + +func (ip *icmpProxy) handleEchoResponse(msg *icmp.Message, echo *icmp.Echo) error { + flow, ok := ip.srcFlowTracker.Get(seqNumFlowID(echo.Seq)) + if !ok { + return fmt.Errorf("flow not found") + } + icmpPacket := packet.ICMP{ + IP: &packet.IP{ + Src: flow.Dst, + Dst: flow.Src, + Protocol: layers.IPProtocol(msg.Type.Protocol()), + }, + Message: msg, + } + serializedPacket, err := ip.encoder.Encode(&icmpPacket) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + if err := flow.Responder.SendPacket(serializedPacket); err != nil { + return errors.Wrap(err, "Failed to send packet to the edge") + } + return nil +} diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go new file mode 100644 index 00000000..9ca8ebee --- /dev/null +++ b/ingress/origin_icmp_proxy_test.go @@ -0,0 +1,150 @@ +package ingress + +import ( + "context" + "fmt" + "net/netip" + "runtime" + "testing" + + "github.com/google/gopacket/layers" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + + "github.com/cloudflare/cloudflared/packet" +) + +var ( + noopLogger = zerolog.Nop() + localhostIP = netip.MustParseAddr("127.0.0.1") +) + +// TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the +// ListenResponse method +func TestICMPProxyEcho(t *testing.T) { + skipWindows(t) + const ( + echoID = 36571 + endSeq = 100 + ) + proxy, err := NewICMPProxy("udp4", localhostIP.AsSlice(), &noopLogger) + require.NoError(t, err) + + proxyDone := make(chan struct{}) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + proxy.ListenResponse(ctx) + close(proxyDone) + }() + + responder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte), + } + + ip := packet.IP{ + Src: localhostIP, + Dst: localhostIP, + Protocol: layers.IPProtocolICMPv4, + } + for i := 0; i < endSeq; i++ { + pk := packet.ICMP{ + IP: &ip, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: echoID, + Seq: i, + Data: []byte(fmt.Sprintf("icmp echo seq %d", i)), + }, + }, + } + require.NoError(t, proxy.Request(&pk, &responder)) + responder.validate(t, &pk) + } + cancel() + <-proxyDone +} + +// TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo +func TestICMPProxyRejectNotEcho(t *testing.T) { + skipWindows(t) + msgs := []icmp.Message{ + { + Type: ipv4.ICMPTypeDestinationUnreachable, + Code: 1, + Body: &icmp.DstUnreach{ + Data: []byte("original packet"), + }, + }, + { + Type: ipv4.ICMPTypeTimeExceeded, + Code: 1, + Body: &icmp.TimeExceeded{ + Data: []byte("original packet"), + }, + }, + { + Type: ipv4.ICMPType(2), + Code: 0, + Body: &icmp.PacketTooBig{ + MTU: 1280, + Data: []byte("original packet"), + }, + }, + } + proxy, err := NewICMPProxy("udp4", localhostIP.AsSlice(), &noopLogger) + require.NoError(t, err) + + responder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte), + } + for _, m := range msgs { + pk := packet.ICMP{ + IP: &packet.IP{ + Src: localhostIP, + Dst: localhostIP, + Protocol: layers.IPProtocolICMPv4, + }, + Message: &m, + } + require.Error(t, proxy.Request(&pk, &responder)) + } +} + +func skipWindows(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Cannot create non-privileged datagram-oriented ICMP endpoint on Windows") + } +} + +type echoFlowResponder struct { + decoder *packet.ICMPDecoder + respChan chan []byte +} + +func (efr *echoFlowResponder) SendPacket(pk packet.RawPacket) error { + copiedPacket := make([]byte, len(pk.Data)) + copy(copiedPacket, pk.Data) + efr.respChan <- copiedPacket + return nil +} + +func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { + pk := <-efr.respChan + decoded, err := efr.decoder.Decode(packet.RawPacket{Data: pk}) + require.NoError(t, err) + require.Equal(t, decoded.Src, echoReq.Dst) + require.Equal(t, decoded.Dst, echoReq.Src) + require.Equal(t, echoReq.Protocol, decoded.Protocol) + + require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type) + require.Equal(t, 0, decoded.Code) + require.NotZero(t, decoded.Checksum) + // TODO: TUN-6586: Enable this validation when ICMP echo ID matches on Linux + //require.Equal(t, echoReq.Body, decoded.Body) +} diff --git a/packet/decoder.go b/packet/decoder.go index 3af4d025..147cbd16 100644 --- a/packet/decoder.go +++ b/packet/decoder.go @@ -75,9 +75,9 @@ func NewIPDecoder() *IPDecoder { } } -func (pd *IPDecoder) Decode(packet []byte) (*IP, error) { +func (pd *IPDecoder) Decode(packet RawPacket) (*IP, error) { // Should decode to IP layer - decoded, err := pd.decodeByVersion(packet) + decoded, err := pd.decodeByVersion(packet.Data) if err != nil { return nil, err } @@ -139,9 +139,9 @@ func NewICMPDecoder() *ICMPDecoder { } } -func (pd *ICMPDecoder) Decode(packet []byte) (*ICMP, error) { +func (pd *ICMPDecoder) Decode(packet RawPacket) (*ICMP, error) { // Should decode to IP and optionally ICMP layer - decoded, err := pd.decodeByVersion(packet) + decoded, err := pd.decodeByVersion(packet.Data) if err != nil { return nil, err } diff --git a/packet/decoder_test.go b/packet/decoder_test.go index e315bf1f..6db377dd 100644 --- a/packet/decoder_test.go +++ b/packet/decoder_test.go @@ -43,11 +43,11 @@ func TestDecodeIP(t *testing.T) { p, err := encoder.Encode(&udp) require.NoError(t, err) - ipPacket, err := ipDecoder.Decode(p.Data) + ipPacket, err := ipDecoder.Decode(p) require.NoError(t, err) assertIPLayer(t, &udp.IP, ipPacket) - icmpPacket, err := icmpDecoder.Decode(p.Data) + icmpPacket, err := icmpDecoder.Decode(p) require.Error(t, err) require.Nil(t, icmpPacket) } @@ -137,14 +137,14 @@ func TestDecodeICMP(t *testing.T) { p, err := encoder.Encode(test.packet) require.NoError(t, err) - ipPacket, err := ipDecoder.Decode(p.Data) + ipPacket, err := ipDecoder.Decode(p) require.NoError(t, err) if ipPacket.Src.Is4() { assertIPLayer(t, &ipv4Packet, ipPacket) } else { assertIPLayer(t, &ipv6Packet, ipPacket) } - icmpPacket, err := icmpDecoder.Decode(p.Data) + icmpPacket, err := icmpDecoder.Decode(p) require.NoError(t, err) require.Equal(t, ipPacket, icmpPacket.IP) @@ -202,11 +202,11 @@ func TestDecodeBadPackets(t *testing.T) { ipDecoder := NewIPDecoder() icmpDecoder := NewICMPDecoder() for _, test := range tests { - ipPacket, err := ipDecoder.Decode(test.packet) + ipPacket, err := ipDecoder.Decode(RawPacket{Data: test.packet}) require.Error(t, err) require.Nil(t, ipPacket) - icmpPacket, err := icmpDecoder.Decode(test.packet) + icmpPacket, err := icmpDecoder.Decode(RawPacket{Data: test.packet}) require.Error(t, err) require.Nil(t, icmpPacket) } diff --git a/packet/flow.go b/packet/flow.go index f4196fa1..3eea5652 100644 --- a/packet/flow.go +++ b/packet/flow.go @@ -2,19 +2,17 @@ package packet import ( "errors" - "net" "net/netip" "sync" ) -type flowID string - var ( ErrFlowNotFound = errors.New("flow not found") ) -func newFlowID(ip net.IP) flowID { - return flowID(ip.String()) +// FlowID represents a key type that can be used by FlowTracker +type FlowID interface { + ID() string } type Flow struct { @@ -37,32 +35,29 @@ type FlowResponder interface { SendPacket(pk RawPacket) error } -// SrcFlowTracker tracks flow from the perspective of eyeball to origin -// flowID is the source IP -type SrcFlowTracker struct { +// FlowTracker tracks flow from the perspective of eyeball to origin +type FlowTracker struct { lock sync.RWMutex - flows map[flowID]*Flow + flows map[FlowID]*Flow } -func NewSrcFlowTracker() *SrcFlowTracker { - return &SrcFlowTracker{ - flows: make(map[flowID]*Flow), +func NewFlowTracker() *FlowTracker { + return &FlowTracker{ + flows: make(map[FlowID]*Flow), } } -func (sft *SrcFlowTracker) Get(srcIP net.IP) (*Flow, bool) { +func (sft *FlowTracker) Get(id FlowID) (*Flow, bool) { sft.lock.RLock() defer sft.lock.RUnlock() - id := newFlowID(srcIP) flow, ok := sft.flows[id] return flow, ok } // Registers a flow. If shouldReplace = true, replace the current flow -func (sft *SrcFlowTracker) Register(flow *Flow, shouldReplace bool) (replaced bool) { +func (sft *FlowTracker) Register(id FlowID, flow *Flow, shouldReplace bool) (replaced bool) { sft.lock.Lock() defer sft.lock.Unlock() - id := flowID(flow.Src.String()) currentFlow, ok := sft.flows[id] if !ok { sft.flows[id] = flow @@ -77,10 +72,9 @@ func (sft *SrcFlowTracker) Register(flow *Flow, shouldReplace bool) (replaced bo } // Unregisters a flow. If force = true, delete it even if it maps to a different flow -func (sft *SrcFlowTracker) Unregister(flow *Flow, force bool) (forceDeleted bool) { +func (sft *FlowTracker) Unregister(id FlowID, flow *Flow, force bool) (forceDeleted bool) { sft.lock.Lock() defer sft.lock.Unlock() - id := flowID(flow.Src.String()) currentFlow, ok := sft.flows[id] if !ok { return false diff --git a/quic/datagram_test.go b/quic/datagram_test.go index c26362a9..bd55f425 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -145,7 +145,7 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Sessi received, err := muxer.ReceivePacket(ctx) require.NoError(t, err) - receivedICMP, err := icmpDecoder.Decode(received.Data) + receivedICMP, err := icmpDecoder.Decode(received) require.NoError(t, err) require.Equal(t, pk.IP, receivedICMP.IP) require.Equal(t, pk.Type, receivedICMP.Type) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 7a100a59..af556b16 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net" "strings" "time" @@ -15,6 +16,7 @@ import ( "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/orchestration" "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" @@ -44,7 +46,7 @@ type Supervisor struct { config *TunnelConfig orchestrator *orchestration.Orchestrator edgeIPs *edgediscovery.Edge - edgeTunnelServer EdgeTunnelServer + edgeTunnelServer *EdgeTunnelServer tunnelErrors chan tunnelError tunnelsConnecting map[int]chan struct{} tunnelsProtocolFallback map[int]*protocolFallback @@ -114,6 +116,15 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato gracefulShutdownC: gracefulShutdownC, connAwareLogger: log, } + if useDatagramV2(config) { + // For non-privileged datagram-oriented ICMP endpoints, network must be "udp4" or "udp6" + // TODO: TUN-6654 listen for IPv6 and decide if it should listen on specific IP + icmpProxy, err := ingress.NewICMPProxy("udp4", net.IPv4zero, config.Log) + if err != nil { + return nil, err + } + edgeTunnelServer.icmpProxy = icmpProxy + } useReconnectToken := false if config.ClassicTunnel != nil { @@ -125,7 +136,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato config: config, orchestrator: orchestrator, edgeIPs: edgeIPs, - edgeTunnelServer: edgeTunnelServer, + edgeTunnelServer: &edgeTunnelServer, tunnelErrors: make(chan tunnelError), tunnelsConnecting: map[int]chan struct{}{}, tunnelsProtocolFallback: map[int]*protocolFallback{}, @@ -142,6 +153,14 @@ func (s *Supervisor) Run( ctx context.Context, connectedSignal *signal.Signal, ) error { + if s.edgeTunnelServer.icmpProxy != nil { + go func() { + if err := s.edgeTunnelServer.icmpProxy.ListenResponse(ctx); err != nil { + s.log.Logger().Err(err).Msg("icmp proxy terminated") + } + }() + } + if err := s.initialize(ctx, connectedSignal); err != nil { if err == errEarlyShutdown { return nil @@ -413,3 +432,15 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int) registrationOptions.NumPreviousAttempts = uint8(numPreviousAttempts) return rpcClient.Authenticate(ctx, s.config.ClassicTunnel, registrationOptions) } + +func useDatagramV2(config *TunnelConfig) bool { + if config.NamedTunnel == nil { + return false + } + for _, feature := range config.NamedTunnel.Client.Features { + if feature == FeatureDatagramV2 { + return true + } + } + return false +} diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 0b9998d8..2652c22d 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -20,6 +20,7 @@ import ( "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/h2mux" + "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/orchestration" quicpogs "github.com/cloudflare/cloudflared/quic" "github.com/cloudflare/cloudflared/retry" @@ -193,11 +194,12 @@ type EdgeTunnelServer struct { reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} tracker *tunnelstate.ConnTracker + icmpProxy ingress.ICMPProxy connAwareLogger *ConnAwareLogger } -func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFallback *protocolFallback, connectedSignal *signal.Signal) error { +func (e *EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFallback *protocolFallback, connectedSignal *signal.Signal) error { haConnections.Inc() defer haConnections.Dec() @@ -229,20 +231,14 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFa // to another protocol when a particular metal doesn't support new protocol // Each connection can also have it's own IP version because individual connections might fallback // to another IP version. - err, recoverable := ServeTunnel( + err, recoverable := e.serveTunnel( ctx, connLog, - e.credentialManager, - e.config, - e.orchestrator, addr, connIndex, connectedFuse, protocolFallback, - e.cloudflaredUUID, - e.reconnectCh, protocolFallback.protocol, - e.gracefulShutdownC, ) // If the connection is recoverable, we want to maintain the same IP @@ -361,20 +357,14 @@ func selectNextProtocol( // ServeTunnel runs a single tunnel connection, returns nil on graceful shutdown, // on error returns a flag indicating if error can be retried -func ServeTunnel( +func (e *EdgeTunnelServer) serveTunnel( ctx context.Context, connLog *ConnAwareLogger, - credentialManager *reconnectCredentialManager, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, addr *allregions.EdgeAddr, connIndex uint8, fuse *h2mux.BooleanFuse, backoff *protocolFallback, - cloudflaredUUID uuid.UUID, - reconnectCh chan ReconnectSignal, protocol connection.Protocol, - gracefulShutdownC <-chan struct{}, ) (err error, recoverable bool) { // Treat panics as recoverable errors defer func() { @@ -389,21 +379,15 @@ func ServeTunnel( } }() - defer config.Observer.SendDisconnect(connIndex) - err, recoverable = serveTunnel( + defer e.config.Observer.SendDisconnect(connIndex) + err, recoverable = e.serveConnection( ctx, connLog, - credentialManager, - config, - orchestrator, addr, connIndex, fuse, backoff, - cloudflaredUUID, - reconnectCh, protocol, - gracefulShutdownC, ) if err != nil { @@ -416,7 +400,7 @@ func ServeTunnel( connLog.ConnAwareLogger().Err(err).Msg("Register tunnel error from server side") // Don't send registration error return from server to Sentry. They are // logged on server side - if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 { + if incidents := e.config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 { connLog.ConnAwareLogger().Msg(activeIncidentsMsg(incidents)) } return err.Cause, !err.Permanent @@ -442,93 +426,73 @@ func ServeTunnel( return nil, false } -func serveTunnel( +func (e *EdgeTunnelServer) serveConnection( ctx context.Context, connLog *ConnAwareLogger, - credentialManager *reconnectCredentialManager, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, addr *allregions.EdgeAddr, connIndex uint8, fuse *h2mux.BooleanFuse, backoff *protocolFallback, - cloudflaredUUID uuid.UUID, - reconnectCh chan ReconnectSignal, protocol connection.Protocol, - gracefulShutdownC <-chan struct{}, ) (err error, recoverable bool) { connectedFuse := &connectedFuse{ fuse: fuse, backoff: backoff, } controlStream := connection.NewControlStream( - config.Observer, + e.config.Observer, connectedFuse, - config.NamedTunnel, + e.config.NamedTunnel, connIndex, addr.UDP.IP, nil, - gracefulShutdownC, - config.GracePeriod, + e.gracefulShutdownC, + e.config.GracePeriod, protocol, ) switch protocol { case connection.QUIC, connection.QUICWarp: - connOptions := config.connectionOptions(addr.UDP.String(), uint8(backoff.Retries())) - return ServeQUIC(ctx, + connOptions := e.config.connectionOptions(addr.UDP.String(), uint8(backoff.Retries())) + return e.serveQUIC(ctx, addr.UDP, - config, - orchestrator, connLog, connOptions, controlStream, - connIndex, - reconnectCh, - gracefulShutdownC) + connIndex) case connection.HTTP2, connection.HTTP2Warp: - edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP) + edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, e.config.EdgeTLSConfigs[protocol], addr.TCP) if err != nil { connLog.ConnAwareLogger().Err(err).Msg("Unable to establish connection with Cloudflare edge") return err, true } - connOptions := config.connectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.Retries())) - if err := ServeHTTP2( + connOptions := e.config.connectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.Retries())) + if err := e.serveHTTP2( ctx, connLog, - config, - orchestrator, edgeConn, connOptions, controlStream, connIndex, - gracefulShutdownC, - reconnectCh, ); err != nil { return err, false } default: - edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP) + edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, e.config.EdgeTLSConfigs[protocol], addr.TCP) if err != nil { connLog.ConnAwareLogger().Err(err).Msg("Unable to establish connection with Cloudflare edge") return err, true } - if err := ServeH2mux( + if err := e.serveH2mux( ctx, connLog, - credentialManager, - config, - orchestrator, edgeConn, connIndex, connectedFuse, - cloudflaredUUID, - reconnectCh, - gracefulShutdownC, ); err != nil { return err, false } @@ -544,30 +508,24 @@ func (r unrecoverableError) Error() string { return r.err.Error() } -func ServeH2mux( +func (e *EdgeTunnelServer) serveH2mux( ctx context.Context, connLog *ConnAwareLogger, - credentialManager *reconnectCredentialManager, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, edgeConn net.Conn, connIndex uint8, connectedFuse *connectedFuse, - cloudflaredUUID uuid.UUID, - reconnectCh chan ReconnectSignal, - gracefulShutdownC <-chan struct{}, ) error { connLog.Logger().Debug().Msgf("Connecting via h2mux") // Returns error from parsing the origin URL or handshake errors handler, err, recoverable := connection.NewH2muxConnection( - orchestrator, - config.GracePeriod, - config.MuxerConfig, + e.orchestrator, + e.config.GracePeriod, + e.config.MuxerConfig, edgeConn, connIndex, - config.Observer, - gracefulShutdownC, - config.Log, + e.config.Observer, + e.gracefulShutdownC, + e.config.Log, ) if err != nil { if !recoverable { @@ -579,42 +537,38 @@ func ServeH2mux( errGroup, serveCtx := errgroup.WithContext(ctx) errGroup.Go(func() error { - if config.NamedTunnel != nil { - connOptions := config.connectionOptions(edgeConn.LocalAddr().String(), uint8(connectedFuse.backoff.Retries())) - return handler.ServeNamedTunnel(serveCtx, config.NamedTunnel, connOptions, connectedFuse) + if e.config.NamedTunnel != nil { + connOptions := e.config.connectionOptions(edgeConn.LocalAddr().String(), uint8(connectedFuse.backoff.Retries())) + return handler.ServeNamedTunnel(serveCtx, e.config.NamedTunnel, connOptions, connectedFuse) } - registrationOptions := config.registrationOptions(connIndex, edgeConn.LocalAddr().String(), cloudflaredUUID) - return handler.ServeClassicTunnel(serveCtx, config.ClassicTunnel, credentialManager, registrationOptions, connectedFuse) + registrationOptions := e.config.registrationOptions(connIndex, edgeConn.LocalAddr().String(), e.cloudflaredUUID) + return handler.ServeClassicTunnel(serveCtx, e.config.ClassicTunnel, e.credentialManager, registrationOptions, connectedFuse) }) errGroup.Go(func() error { - return listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) + return listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) }) return errGroup.Wait() } -func ServeHTTP2( +func (e *EdgeTunnelServer) serveHTTP2( ctx context.Context, connLog *ConnAwareLogger, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, tlsServerConn net.Conn, connOptions *tunnelpogs.ConnectionOptions, controlStreamHandler connection.ControlStreamHandler, connIndex uint8, - gracefulShutdownC <-chan struct{}, - reconnectCh chan ReconnectSignal, ) error { connLog.Logger().Debug().Msgf("Connecting via http2") h2conn := connection.NewHTTP2Connection( tlsServerConn, - orchestrator, + e.orchestrator, connOptions, - config.Observer, + e.config.Observer, connIndex, controlStreamHandler, - config.Log, + e.config.Log, ) errGroup, serveCtx := errgroup.WithContext(ctx) @@ -623,7 +577,7 @@ func ServeHTTP2( }) errGroup.Go(func() error { - err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) + err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) connLog.Logger().Debug().Msg("Forcefully breaking http2 connection") @@ -635,19 +589,15 @@ func ServeHTTP2( return errGroup.Wait() } -func ServeQUIC( +func (e *EdgeTunnelServer) serveQUIC( ctx context.Context, edgeAddr *net.UDPAddr, - config *TunnelConfig, - orchestrator *orchestration.Orchestrator, connLogger *ConnAwareLogger, connOptions *tunnelpogs.ConnectionOptions, controlStreamHandler connection.ControlStreamHandler, connIndex uint8, - reconnectCh chan ReconnectSignal, - gracefulShutdownC <-chan struct{}, ) (err error, recoverable bool) { - tlsConfig := config.EdgeTLSConfigs[connection.QUIC] + tlsConfig := e.config.EdgeTLSConfigs[connection.QUIC] quicConfig := &quic.Config{ HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout, MaxIdleTimeout: quicpogs.MaxIdleTimeout, @@ -663,10 +613,11 @@ func ServeQUIC( quicConfig, edgeAddr, tlsConfig, - orchestrator, + e.orchestrator, connOptions, controlStreamHandler, - connLogger.Logger()) + connLogger.Logger(), + e.icmpProxy) if err != nil { connLogger.ConnAwareLogger().Err(err).Msgf("Failed to create new quic connection") return err, true @@ -682,7 +633,7 @@ func ServeQUIC( }) errGroup.Go(func() error { - err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC) + err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) connLogger.Logger().Debug().Msg("Forcefully breaking quic connection") From fc5749328d88db171b4915d122f96aaf2d1bda48 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 24 Aug 2022 15:25:19 -0700 Subject: [PATCH 172/238] TUN-6691: Properly error check for net.ErrClosed UDP session would check if the socket was closed before returning but the net.ErrClosed could be wrapped in another error. --- datagramsession/session.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/datagramsession/session.go b/datagramsession/session.go index c28c27fb..94562673 100644 --- a/datagramsession/session.go +++ b/datagramsession/session.go @@ -2,6 +2,7 @@ package datagramsession import ( "context" + "errors" "fmt" "io" "net" @@ -49,10 +50,10 @@ func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (clos readBuffer := make([]byte, maxPacketSize) for { if closeSession, err := s.dstToTransport(readBuffer); err != nil { - if err != net.ErrClosed { - s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport") + if errors.Is(err, net.ErrClosed) { + s.log.Debug().Msg("Destination connection closed") } else { - s.log.Debug().Msg("Session cannot read from destination because the connection is closed") + s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport") } if closeSession { s.closeChan <- err From e3390fcb15c52a8e2a33fd9002680852b12e2f67 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 25 Aug 2022 18:52:18 +0100 Subject: [PATCH 173/238] TUN-6705: Tunnel should retry connections forever Protocolbackoff arrays now have Retryforever flag set to true to enable cloudflared to keep trying to reconnect perpetually. --- supervisor/supervisor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index af556b16..135c59a7 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -268,7 +268,7 @@ func (s *Supervisor) initialize( s.config.HAConnections = availableAddrs } s.tunnelsProtocolFallback[0] = &protocolFallback{ - retry.BackoffHandler{MaxRetries: s.config.Retries}, + retry.BackoffHandler{MaxRetries: s.config.Retries, RetryForever: true}, s.config.ProtocolSelector.Current(), false, } @@ -290,7 +290,7 @@ func (s *Supervisor) initialize( // At least one successful connection, so start the rest for i := 1; i < s.config.HAConnections; i++ { s.tunnelsProtocolFallback[i] = &protocolFallback{ - retry.BackoffHandler{MaxRetries: s.config.Retries}, + retry.BackoffHandler{MaxRetries: s.config.Retries, RetryForever: true}, s.config.ProtocolSelector.Current(), false, } From af6bf5c4e5bd06c9bdcaf8861253bfa81347cebb Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 25 Aug 2022 12:01:16 -0700 Subject: [PATCH 174/238] TUN-6704: Honor protocol flag when edge discovery is unreachable --- connection/protocol.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connection/protocol.go b/connection/protocol.go index ce97bffc..b8b7d5ce 100644 --- a/connection/protocol.go +++ b/connection/protocol.go @@ -209,8 +209,8 @@ func NewProtocolSelector( threshold := switchThreshold(namedTunnel.Credentials.AccountTag) fetchedProtocol, err := getProtocol([]Protocol{QUIC, HTTP2}, fetchFunc, threshold) - if err != nil { - log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can set `--protocol h2mux` in your cloudflared command.") + if err != nil && protocolFlag == "auto" { + log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can attempt `--protocol quic` instead.") return &staticProtocolSelector{ current: HTTP2, }, nil From e131125558d513f587da3f2f7bf6cea02ec356bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Thu, 25 Aug 2022 11:05:01 +0100 Subject: [PATCH 175/238] TUN-6699: Add metric for packet too big dropped --- quic/datagram.go | 3 +-- quic/datagramv2.go | 2 +- quic/metrics.go | 8 ++++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/quic/datagram.go b/quic/datagram.go index 8527d948..334754d4 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -46,8 +46,7 @@ func (dm *DatagramMuxer) mtu() int { func (dm *DatagramMuxer) SendToSession(session *packet.Session) error { if len(session.Payload) > dm.mtu() { - // TODO: TUN-5302 return ICMP packet too big message - // drop packet for now, eventually reply with ICMP for PMTUD + packetTooBigDropped.Inc() return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } payloadWithMetadata, err := suffixSessionID(session.ID, session.Payload) diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 03e60b1e..7e2fce89 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -58,7 +58,7 @@ func NewDatagramMuxerV2( // demultiplex the payload from multiple datagram sessions func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { if len(session.Payload) > dm.mtu() { - // TODO: TUN-5302 return ICMP packet too big message + packetTooBigDropped.Inc() return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } msgWithID, err := suffixSessionID(session.ID, session.Payload) diff --git a/quic/metrics.go b/quic/metrics.go index 480ad371..631cbbb6 100644 --- a/quic/metrics.go +++ b/quic/metrics.go @@ -144,6 +144,13 @@ var ( } registerClient = sync.Once{} registerServer = sync.Once{} + + packetTooBigDropped = prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: perspectiveString(logging.PerspectiveClient), + Name: "packet_too_big_dropped", + Help: "Count of packets received from origin that are too big to send to the edge and are dropped as a result", + }) ) // MetricsCollector abstracts the difference between client and server metrics from connTracer @@ -264,6 +271,7 @@ func newClientCollector(index uint8) MetricsCollector { clientMetrics.minRTT, clientMetrics.latestRTT, clientMetrics.smoothedRTT, + packetTooBigDropped, ) }) return &clientCollector{ From efb99d90d7cd02bc11d34767be114fec37382973 Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 26 Aug 2022 17:52:06 +0100 Subject: [PATCH 176/238] TUN-6708: Fix replace flow logic --- packet/flow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packet/flow.go b/packet/flow.go index 3eea5652..2b82e562 100644 --- a/packet/flow.go +++ b/packet/flow.go @@ -64,7 +64,7 @@ func (sft *FlowTracker) Register(id FlowID, flow *Flow, shouldReplace bool) (rep return false } - if shouldReplace && isSameFlow(currentFlow, flow) { + if shouldReplace && !isSameFlow(currentFlow, flow) { sft.flows[id] = flow return true } From 7e760f9fccae474d2f940f8111813b8f447e6a49 Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 22 Aug 2022 17:41:51 +0100 Subject: [PATCH 177/238] TUN-6586: Change ICMP proxy to only build for Darwin and use echo ID to track flows --- ingress/icmp_darwin.go | 231 ++++++++++++++++++++++++++++++ ingress/icmp_darwin_test.go | 91 ++++++++++++ ingress/icmp_generic.go | 15 ++ ingress/origin_icmp_proxy.go | 121 +--------------- ingress/origin_icmp_proxy_test.go | 15 +- packet/flow.go | 5 +- supervisor/supervisor.go | 7 +- 7 files changed, 355 insertions(+), 130 deletions(-) create mode 100644 ingress/icmp_darwin.go create mode 100644 ingress/icmp_darwin_test.go create mode 100644 ingress/icmp_generic.go diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go new file mode 100644 index 00000000..20509b2f --- /dev/null +++ b/ingress/icmp_darwin.go @@ -0,0 +1,231 @@ +//go:build darwin + +package ingress + +import ( + "context" + "fmt" + "math" + "net" + "net/netip" + "strconv" + "sync" + + "github.com/google/gopacket/layers" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/net/icmp" + + "github.com/cloudflare/cloudflared/packet" +) + +// TODO: TUN-6654 Extend support to IPv6 +// On Darwin, a non-privileged ICMP socket can read messages from all echo IDs, so we use it for all sources. +type icmpProxy struct { + // TODO: TUN-6588 clean up flows + srcFlowTracker *packet.FlowTracker + echoIDTracker *echoIDTracker + conn *icmp.PacketConn + logger *zerolog.Logger + encoder *packet.Encoder +} + +// echoIDTracker tracks which ID has been assigned. It first loops through assignment from lastAssignment to then end, +// then from the beginning to lastAssignment. +// ICMP echo are short lived. By the time an ID is revisited, it should have been released. +type echoIDTracker struct { + lock sync.RWMutex + // maps the source IP to an echo ID obtained from assignment + srcIPMapping map[netip.Addr]uint16 + // assignment tracks if an ID is assigned using index as the ID + // The size of the array is math.MaxUint16 because echo ID is 2 bytes + assignment [math.MaxUint16]bool + // nextAssignment is the next number to check for assigment + nextAssignment uint16 +} + +func newEchoIDTracker() *echoIDTracker { + return &echoIDTracker{ + srcIPMapping: make(map[netip.Addr]uint16), + } +} + +func (eit *echoIDTracker) get(srcIP netip.Addr) (uint16, bool) { + eit.lock.RLock() + defer eit.lock.RUnlock() + id, ok := eit.srcIPMapping[srcIP] + return id, ok +} + +func (eit *echoIDTracker) assign(srcIP netip.Addr) (uint16, bool) { + eit.lock.Lock() + defer eit.lock.Unlock() + + if eit.nextAssignment == math.MaxUint16 { + eit.nextAssignment = 0 + } + + for i, assigned := range eit.assignment[eit.nextAssignment:] { + if !assigned { + echoID := uint16(i) + eit.nextAssignment + eit.set(srcIP, echoID) + return echoID, true + } + } + for i, assigned := range eit.assignment[0:eit.nextAssignment] { + if !assigned { + echoID := uint16(i) + eit.set(srcIP, echoID) + return echoID, true + } + } + return 0, false +} + +// Caller should hold the lock +func (eit *echoIDTracker) set(srcIP netip.Addr, echoID uint16) { + eit.assignment[echoID] = true + eit.srcIPMapping[srcIP] = echoID + eit.nextAssignment = echoID + 1 +} + +func (eit *echoIDTracker) release(srcIP netip.Addr, id uint16) bool { + eit.lock.Lock() + defer eit.lock.Unlock() + + currentID, ok := eit.srcIPMapping[srcIP] + if ok && id == currentID { + delete(eit.srcIPMapping, srcIP) + eit.assignment[id] = false + return true + } + return false +} + +type echoFlowID uint16 + +func (snf echoFlowID) Type() string { + return "echoID" +} + +func (snf echoFlowID) String() string { + return strconv.FormatUint(uint64(snf), 10) +} + +func newICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { + network := "udp6" + if listenIP.To4() != nil { + network = "udp4" + } + // Opens a non-privileged ICMP socket + conn, err := icmp.ListenPacket(network, listenIP.String()) + if err != nil { + return nil, err + } + return &icmpProxy{ + srcFlowTracker: packet.NewFlowTracker(), + echoIDTracker: newEchoIDTracker(), + conn: conn, + logger: logger, + encoder: packet.NewEncoder(), + }, nil +} + +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { + switch body := pk.Message.Body.(type) { + case *icmp.Echo: + return ip.sendICMPEchoRequest(pk, body, responder) + default: + return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) + } +} + +func (ip *icmpProxy) ListenResponse(ctx context.Context) error { + go func() { + <-ctx.Done() + ip.conn.Close() + }() + buf := make([]byte, 1500) + for { + n, src, err := ip.conn.ReadFrom(buf) + if err != nil { + return err + } + // TODO: TUN-6654 Check for IPv6 + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), buf[:n]) + if err != nil { + ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to parse ICMP message") + continue + } + switch body := msg.Body.(type) { + case *icmp.Echo: + if err := ip.handleEchoResponse(msg, body); err != nil { + ip.logger.Error().Err(err). + Str("src", src.String()). + Str("flowID", echoFlowID(body.ID).String()). + Msg("Failed to handle ICMP response") + continue + } + default: + ip.logger.Warn(). + Str("icmpType", fmt.Sprintf("%s", msg.Type)). + Msgf("Responding to this type of ICMP is not implemented") + continue + } + } +} + +func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { + echoID, ok := ip.echoIDTracker.get(pk.Src) + if !ok { + echoID, ok = ip.echoIDTracker.assign(pk.Src) + if !ok { + return fmt.Errorf("failed to assign unique echo ID") + } + flowID := echoFlowID(echoID) + flow := packet.Flow{ + Src: pk.Src, + Dst: pk.Dst, + Responder: responder, + } + if replaced := ip.srcFlowTracker.Register(flowID, &flow, true); replaced { + ip.logger.Info().Str("src", flow.Src.String()).Str("dst", flow.Dst.String()).Msg("Replaced flow") + } + } + + echo.ID = int(echoID) + var pseudoHeader []byte = nil + serializedMsg, err := pk.Marshal(pseudoHeader) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + // The address needs to be of type UDPAddr when conn is created without priviledge + _, err = ip.conn.WriteTo(serializedMsg, &net.UDPAddr{ + IP: pk.Dst.AsSlice(), + }) + return err +} + +func (ip *icmpProxy) handleEchoResponse(msg *icmp.Message, echo *icmp.Echo) error { + flowID := echoFlowID(echo.ID) + flow, ok := ip.srcFlowTracker.Get(flowID) + if !ok { + return fmt.Errorf("flow not found") + } + icmpPacket := packet.ICMP{ + IP: &packet.IP{ + Src: flow.Dst, + Dst: flow.Src, + Protocol: layers.IPProtocol(msg.Type.Protocol()), + }, + Message: msg, + } + serializedPacket, err := ip.encoder.Encode(&icmpPacket) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + if err := flow.Responder.SendPacket(serializedPacket); err != nil { + return errors.Wrap(err, "Failed to send packet to the edge") + } + return nil +} diff --git a/ingress/icmp_darwin_test.go b/ingress/icmp_darwin_test.go new file mode 100644 index 00000000..8baccb58 --- /dev/null +++ b/ingress/icmp_darwin_test.go @@ -0,0 +1,91 @@ +//go:build darwin + +package ingress + +import ( + "math" + "net/netip" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSingleEchoIDTracker(t *testing.T) { + tracker := newEchoIDTracker() + srcIP := netip.MustParseAddr("127.0.0.1") + echoID, ok := tracker.get(srcIP) + require.False(t, ok) + require.Equal(t, uint16(0), echoID) + + // not assigned yet, so nothing to release + require.False(t, tracker.release(srcIP, echoID)) + + echoID, ok = tracker.assign(srcIP) + require.True(t, ok) + require.Equal(t, uint16(0), echoID) + + echoID, ok = tracker.get(srcIP) + require.True(t, ok) + require.Equal(t, uint16(0), echoID) + + // releasing a different ID returns false + require.False(t, tracker.release(srcIP, 1999)) + require.True(t, tracker.release(srcIP, echoID)) + // releasing the second time returns false + require.False(t, tracker.release(srcIP, echoID)) + + echoID, ok = tracker.get(srcIP) + require.False(t, ok) + require.Equal(t, uint16(0), echoID) + + // Move to the next IP + echoID, ok = tracker.assign(srcIP) + require.True(t, ok) + require.Equal(t, uint16(1), echoID) +} + +func TestFullEchoIDTracker(t *testing.T) { + tracker := newEchoIDTracker() + firstIP := netip.MustParseAddr("172.16.0.1") + srcIP := firstIP + + for i := uint16(0); i < math.MaxUint16; i++ { + echoID, ok := tracker.assign(srcIP) + require.True(t, ok) + require.Equal(t, i, echoID) + + echoID, ok = tracker.get(srcIP) + require.True(t, ok) + require.Equal(t, i, echoID) + srcIP = srcIP.Next() + } + + // All echo IDs are assigned + echoID, ok := tracker.assign(srcIP.Next()) + require.False(t, ok) + require.Equal(t, uint16(0), echoID) + + srcIP = firstIP + for i := uint16(0); i < math.MaxUint16; i++ { + ok := tracker.release(srcIP, i) + require.True(t, ok) + + echoID, ok = tracker.get(srcIP) + require.False(t, ok) + require.Equal(t, uint16(0), echoID) + srcIP = srcIP.Next() + } + + // The IDs are assignable again + srcIP = firstIP + for i := uint16(0); i < math.MaxUint16; i++ { + echoID, ok := tracker.assign(srcIP) + require.True(t, ok) + require.Equal(t, i, echoID) + + echoID, ok = tracker.get(srcIP) + require.True(t, ok) + require.Equal(t, i, echoID) + srcIP = srcIP.Next() + } +} diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go new file mode 100644 index 00000000..dafa96e7 --- /dev/null +++ b/ingress/icmp_generic.go @@ -0,0 +1,15 @@ +//go:build !darwin + +package ingress + +import ( + "fmt" + "net" + "runtime" + + "github.com/rs/zerolog" +) + +func newICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { + return nil, fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) +} diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index 672e833e..aabaa51f 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -2,14 +2,9 @@ package ingress import ( "context" - "fmt" "net" - "strconv" - "github.com/google/gopacket/layers" - "github.com/pkg/errors" "github.com/rs/zerolog" - "golang.org/x/net/icmp" "github.com/cloudflare/cloudflared/packet" ) @@ -22,118 +17,6 @@ type ICMPProxy interface { ListenResponse(ctx context.Context) error } -// TODO: TUN-6654 Extend support to IPv6 -type icmpProxy struct { - srcFlowTracker *packet.FlowTracker - conn *icmp.PacketConn - logger *zerolog.Logger - encoder *packet.Encoder -} - -// TODO: TUN-6586: Use echo ID as FlowID -type seqNumFlowID int - -func (snf seqNumFlowID) ID() string { - return strconv.FormatInt(int64(snf), 10) -} - -func NewICMPProxy(network string, listenIP net.IP, logger *zerolog.Logger) (*icmpProxy, error) { - conn, err := icmp.ListenPacket(network, listenIP.String()) - if err != nil { - return nil, err - } - return &icmpProxy{ - srcFlowTracker: packet.NewFlowTracker(), - conn: conn, - logger: logger, - encoder: packet.NewEncoder(), - }, nil -} - -func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { - switch body := pk.Message.Body.(type) { - case *icmp.Echo: - return ip.sendICMPEchoRequest(pk, body, responder) - default: - return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) - } -} - -func (ip *icmpProxy) ListenResponse(ctx context.Context) error { - go func() { - <-ctx.Done() - ip.conn.Close() - }() - buf := make([]byte, 1500) - for { - n, src, err := ip.conn.ReadFrom(buf) - if err != nil { - return err - } - // TODO: TUN-6654 Check for IPv6 - msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), buf[:n]) - if err != nil { - ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to parse ICMP message") - continue - } - switch body := msg.Body.(type) { - case *icmp.Echo: - if err := ip.handleEchoResponse(msg, body); err != nil { - ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to handle ICMP response") - continue - } - default: - ip.logger.Warn(). - Str("icmpType", fmt.Sprintf("%s", msg.Type)). - Msgf("Responding to this type of ICMP is not implemented") - continue - } - } -} - -func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { - flow := packet.Flow{ - Src: pk.Src, - Dst: pk.Dst, - Responder: responder, - } - // TODO: TUN-6586 rewrite ICMP echo request identifier and use it to track flows - flowID := seqNumFlowID(echo.Seq) - // TODO: TUN-6588 clean up flows - if replaced := ip.srcFlowTracker.Register(flowID, &flow, true); replaced { - ip.logger.Info().Str("src", flow.Src.String()).Str("dst", flow.Dst.String()).Msg("Replaced flow") - } - var pseudoHeader []byte = nil - serializedMsg, err := pk.Marshal(pseudoHeader) - if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") - } - // The address needs to be of type UDPAddr when conn is created without priviledge - _, err = ip.conn.WriteTo(serializedMsg, &net.UDPAddr{ - IP: pk.Dst.AsSlice(), - }) - return err -} - -func (ip *icmpProxy) handleEchoResponse(msg *icmp.Message, echo *icmp.Echo) error { - flow, ok := ip.srcFlowTracker.Get(seqNumFlowID(echo.Seq)) - if !ok { - return fmt.Errorf("flow not found") - } - icmpPacket := packet.ICMP{ - IP: &packet.IP{ - Src: flow.Dst, - Dst: flow.Src, - Protocol: layers.IPProtocol(msg.Type.Protocol()), - }, - Message: msg, - } - serializedPacket, err := ip.encoder.Encode(&icmpPacket) - if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") - } - if err := flow.Responder.SendPacket(serializedPacket); err != nil { - return errors.Wrap(err, "Failed to send packet to the edge") - } - return nil +func NewICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { + return newICMPProxy(listenIP, logger) } diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index 9ca8ebee..eb03a008 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -24,12 +24,13 @@ var ( // TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the // ListenResponse method func TestICMPProxyEcho(t *testing.T) { - skipWindows(t) + skipNonDarwin(t) const ( echoID = 36571 endSeq = 100 ) - proxy, err := NewICMPProxy("udp4", localhostIP.AsSlice(), &noopLogger) + + proxy, err := NewICMPProxy(localhostIP.AsSlice(), &noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) @@ -71,7 +72,7 @@ func TestICMPProxyEcho(t *testing.T) { // TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo func TestICMPProxyRejectNotEcho(t *testing.T) { - skipWindows(t) + skipNonDarwin(t) msgs := []icmp.Message{ { Type: ipv4.ICMPTypeDestinationUnreachable, @@ -96,7 +97,7 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { }, }, } - proxy, err := NewICMPProxy("udp4", localhostIP.AsSlice(), &noopLogger) + proxy, err := NewICMPProxy(localhostIP.AsSlice(), &noopLogger) require.NoError(t, err) responder := echoFlowResponder{ @@ -116,8 +117,8 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { } } -func skipWindows(t *testing.T) { - if runtime.GOOS == "windows" { +func skipNonDarwin(t *testing.T) { + if runtime.GOOS != "darwin" { t.Skip("Cannot create non-privileged datagram-oriented ICMP endpoint on Windows") } } @@ -146,5 +147,5 @@ func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { require.Equal(t, 0, decoded.Code) require.NotZero(t, decoded.Checksum) // TODO: TUN-6586: Enable this validation when ICMP echo ID matches on Linux - //require.Equal(t, echoReq.Body, decoded.Body) + require.Equal(t, echoReq.Body, decoded.Body) } diff --git a/packet/flow.go b/packet/flow.go index 2b82e562..188f3063 100644 --- a/packet/flow.go +++ b/packet/flow.go @@ -2,6 +2,7 @@ package packet import ( "errors" + "fmt" "net/netip" "sync" ) @@ -12,7 +13,9 @@ var ( // FlowID represents a key type that can be used by FlowTracker type FlowID interface { - ID() string + // Type returns the name of the type that implements the FlowID + Type() string + fmt.Stringer } type Flow struct { diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 135c59a7..c6bca29e 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -119,11 +119,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato if useDatagramV2(config) { // For non-privileged datagram-oriented ICMP endpoints, network must be "udp4" or "udp6" // TODO: TUN-6654 listen for IPv6 and decide if it should listen on specific IP - icmpProxy, err := ingress.NewICMPProxy("udp4", net.IPv4zero, config.Log) + icmpProxy, err := ingress.NewICMPProxy(net.IPv4zero, config.Log) if err != nil { - return nil, err + log.Logger().Warn().Err(err).Msg("Failed to create icmp proxy, will continue to use datagram v1") + } else { + edgeTunnelServer.icmpProxy = icmpProxy } - edgeTunnelServer.icmpProxy = icmpProxy } useReconnectToken := false From 2b3707e2b9cf341c393372af6cf7ddfe8f253d20 Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 31 Aug 2022 12:22:57 +0100 Subject: [PATCH 178/238] TUN-6717: Update Github action to run with Go 1.19 --- .github/workflows/check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index aa61d4d6..33259856 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -4,7 +4,7 @@ jobs: check: strategy: matrix: - go-version: [1.17.x] + go-version: [1.19.x] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: From 8ec0f7746b715e29aebdc3f6975893b47cb11c84 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Mon, 29 Aug 2022 09:08:09 -0700 Subject: [PATCH 179/238] Release 2022.8.3 --- RELEASE_NOTES | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 417d4cc5..18946e70 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,18 @@ +2022.8.3 +- 2022-08-26 TUN-6708: Fix replace flow logic +- 2022-08-25 TUN-6705: Tunnel should retry connections forever +- 2022-08-25 TUN-6704: Honor protocol flag when edge discovery is unreachable +- 2022-08-25 TUN-6699: Add metric for packet too big dropped +- 2022-08-24 TUN-6691: Properly error check for net.ErrClosed +- 2022-08-22 TUN-6679: Allow client side of quic request to close body +- 2022-08-22 TUN-6586: Change ICMP proxy to only build for Darwin and use echo ID to track flows +- 2022-08-18 TUN-6530: Implement ICMPv4 proxy +- 2022-08-17 TUN-6666: Define packet package +- 2022-08-17 TUN-6667: DatagramMuxerV2 provides a method to receive RawPacket +- 2022-08-16 TUN-6657: Ask for Tunnel ID and Configuration on Bug Report +- 2022-08-16 TUN-6676: Add suport for trailers in http2 connections +- 2022-08-11 TUN-6575: Consume cf-trace-id from incoming http2 TCP requests + 2022.8.2 - 2022-08-16 TUN-6656: Docker for arm64 should not be deployed in an amd64 container From cfef0e737f3f3ba7b7d719be8ca51d38c30c2737 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 31 Aug 2022 12:52:44 -0700 Subject: [PATCH 180/238] TUN-6720: Remove forcibly closing connection during reconnect signal Previously allowing the reconnect signal forcibly close the connection caused a race condition on which error was returned by the errgroup in the tunnel connection. Allowing the signal to return and provide a context cancel to the connection provides a safer shutdown of the tunnel for this test-only scenario. --- cmd/cloudflared/tunnel/cmd.go | 2 +- component-tests/test_reconnect.py | 17 ++++++----------- component-tests/util.py | 7 +++++-- supervisor/supervisor.go | 3 +-- supervisor/tunnel.go | 12 +++++++++--- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 793f8c2b..a45e6e0c 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -375,7 +375,7 @@ func StartServer( errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, orchestrator, log) }() - reconnectCh := make(chan supervisor.ReconnectSignal, 1) + reconnectCh := make(chan supervisor.ReconnectSignal, c.Int("ha-connections")) if c.IsSet("stdin-control") { log.Info().Msg("Enabling control through stdin") go stdinControl(reconnectCh, log) diff --git a/component-tests/test_reconnect.py b/component-tests/test_reconnect.py index 0b601171..e125845a 100644 --- a/component-tests/test_reconnect.py +++ b/component-tests/test_reconnect.py @@ -47,17 +47,12 @@ class TestReconnect: cloudflared.stdin.flush() def assert_reconnect(self, config, cloudflared, repeat): - wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns) + wait_tunnel_ready(tunnel_url=config.get_url(), + require_min_connections=self.default_ha_conns) for _ in range(repeat): - for i in range(self.default_ha_conns): + for _ in range(self.default_ha_conns): self.send_reconnect(cloudflared, self.default_reconnect_secs) - expect_connections = self.default_ha_conns-i-1 - if expect_connections > 0: - # Don't check if tunnel returns 200 here because there is a race condition between wait_tunnel_ready - # retrying to get 200 response and reconnecting - wait_tunnel_ready(require_min_connections=expect_connections) - else: - check_tunnel_not_connected() - + check_tunnel_not_connected() sleep(self.default_reconnect_secs * 2) - wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns) + wait_tunnel_ready(tunnel_url=config.get_url(), + require_min_connections=self.default_ha_conns) diff --git a/component-tests/util.py b/component-tests/util.py index 6e60d08e..34f5faf7 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -15,6 +15,7 @@ from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS LOGGER = logging.getLogger(__name__) + def select_platform(plat): return pytest.mark.skipif( platform.system() != plat, reason=f"Only runs on {plat}") @@ -108,13 +109,15 @@ def _log_cloudflared_logs(cfd_logs): LOGGER.warning(line) -@retry(stop_max_attempt_number=MAX_RETRIES * BACKOFF_SECS, wait_fixed=1000) +@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000) def check_tunnel_not_connected(): url = f'http://localhost:{METRICS_PORT}/ready' try: - resp = requests.get(url, timeout=1) + resp = requests.get(url, timeout=BACKOFF_SECS) assert resp.status_code == 503, f"Expect {url} returns 503, got {resp.status_code}" + assert resp.json()[ + "readyConnections"] == 0, "Expected all connections to be terminated (pending reconnect)" # cloudflared might already terminate except requests.exceptions.ConnectionError as e: LOGGER.warning(f"Failed to connect to {url}, error: {e}") diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index c6bca29e..bc076f0c 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -295,8 +295,7 @@ func (s *Supervisor) initialize( s.config.ProtocolSelector.Current(), false, } - ch := signal.New(make(chan struct{})) - go s.startTunnel(ctx, i, ch) + go s.startTunnel(ctx, i, s.newConnectedTunnelSignal(i)) time.Sleep(registrationInterval) } return nil diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 2652c22d..f23eeff7 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -546,7 +546,13 @@ func (e *EdgeTunnelServer) serveH2mux( }) errGroup.Go(func() error { - return listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) + err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) + if err != nil { + // forcefully break the connection (this is only used for testing) + // errgroup will return context canceled for the handler.ServeClassicTunnel + connLog.Logger().Debug().Msg("Forcefully breaking h2mux connection") + } + return err }) return errGroup.Wait() @@ -580,8 +586,8 @@ func (e *EdgeTunnelServer) serveHTTP2( err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + // errgroup will return context canceled for the h2conn.Serve connLog.Logger().Debug().Msg("Forcefully breaking http2 connection") - _ = tlsServerConn.Close() } return err }) @@ -636,8 +642,8 @@ func (e *EdgeTunnelServer) serveQUIC( err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC) if err != nil { // forcefully break the connection (this is only used for testing) + // errgroup will return context canceled for the quicConn.Serve connLogger.Logger().Debug().Msg("Forcefully breaking quic connection") - quicConn.Close() } return err }) From 075ac1acf1b79c3dd5281d94188fa131b167483b Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 31 Aug 2022 15:19:40 -0700 Subject: [PATCH 181/238] Release 2022.8.4 --- RELEASE_NOTES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 18946e70..030e1360 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,8 @@ +2022.8.4 +- 2022-08-31 TUN-6717: Update Github action to run with Go 1.19 +- 2022-08-31 TUN-6720: Remove forcibly closing connection during reconnect signal +- 2022-08-29 Release 2022.8.3 + 2022.8.3 - 2022-08-26 TUN-6708: Fix replace flow logic - 2022-08-25 TUN-6705: Tunnel should retry connections forever From 4ac68711cd9c3d54b6b474ef03a4225d62e03d3c Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 1 Sep 2022 15:41:33 +0100 Subject: [PATCH 182/238] TUN-6725: Fix testProxySSEAllData This test was failing on Windows. We did not catch it before because our TeamCity Windows builds were ignoring failed unit tests: TUN-6727 - the fix is implementing WriteString for mockSSERespWriter - reason is because cfio.Copy was calling that, and not Write method, thus not triggering the usage of the channel for the test to continue - mockSSERespWriter was providing a valid implementation of WriteString via ResponseRecorder, which it implements via the embedded mockHTTPRespWriter - it is not clear why this only happened on Windows - changed it to be a top-level test since it did not share any code with other sub-tests in the same top-level test --- proxy/proxy_test.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 5407acaa..384298c3 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -130,6 +130,10 @@ func (w *mockSSERespWriter) Write(data []byte) (int, error) { return len(data), nil } +func (w *mockSSERespWriter) WriteString(str string) (int, error) { + return w.Write([]byte(str)) +} + func (w *mockSSERespWriter) ReadBytes() []byte { return <-w.writeNotification } @@ -156,7 +160,6 @@ func TestProxySingleOrigin(t *testing.T) { t.Run("testProxyHTTP", testProxyHTTP(proxy)) t.Run("testProxyWebsocket", testProxyWebsocket(proxy)) t.Run("testProxySSE", testProxySSE(proxy)) - t.Run("testProxySSEAllData", testProxySSEAllData(proxy)) cancel() } @@ -276,17 +279,15 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) { // Regression test to guarantee that we always write the contents downstream even if EOF is reached without // hitting the delimiter -func testProxySSEAllData(proxy *Proxy) func(t *testing.T) { - return func(t *testing.T) { - eyeballReader := io.NopCloser(strings.NewReader("data\r\r")) - responseWriter := newMockSSERespWriter() +func TestProxySSEAllData(t *testing.T) { + eyeballReader := io.NopCloser(strings.NewReader("data\r\r")) + responseWriter := newMockSSERespWriter() - // responseWriter uses an unbuffered channel, so we call in a different go-routine - go cfio.Copy(responseWriter, eyeballReader) + // responseWriter uses an unbuffered channel, so we call in a different go-routine + go cfio.Copy(responseWriter, eyeballReader) - result := string(<-responseWriter.writeNotification) - require.Equal(t, "data\r\r", result) - } + result := string(<-responseWriter.writeNotification) + require.Equal(t, "data\r\r", result) } func TestProxyMultipleOrigins(t *testing.T) { From 7ca5f7569a8ef2b50a433be367c853c2b0e8034c Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 1 Sep 2022 18:50:25 +0100 Subject: [PATCH 183/238] TUN-6726: Fix maxDatagramPayloadSize for Windows QUIC datagrams --- quic/datagramv2.go | 13 +++++++------ quic/param_windows.go | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 7e2fce89..5a3e998c 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -14,14 +14,15 @@ import ( type datagramV2Type byte const ( - udp datagramV2Type = iota + typeIDLen = 1 + udp datagramV2Type = iota ip // Same as sessionDemuxChan capacity packetChanCapacity = 16 ) func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) { - if len(b)+1 > MaxDatagramFrameSize { + if len(b)+typeIDLen > MaxDatagramFrameSize { return nil, fmt.Errorf("datagram size %d exceeds max frame size %d", len(b), MaxDatagramFrameSize) } b = append(b, byte(datagramType)) @@ -114,11 +115,11 @@ func (dm *DatagramMuxerV2) ReceivePacket(ctx context.Context) (packet.RawPacket, } func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error { - if len(msgWithType) < 1 { - return fmt.Errorf("QUIC datagram should have at least 1 byte") + if len(msgWithType) < typeIDLen { + return fmt.Errorf("QUIC datagram should have at least %d byte", typeIDLen) } - msgType := datagramV2Type(msgWithType[len(msgWithType)-1]) - msg := msgWithType[0 : len(msgWithType)-1] + msgType := datagramV2Type(msgWithType[len(msgWithType)-typeIDLen]) + msg := msgWithType[0 : len(msgWithType)-typeIDLen] switch msgType { case udp: return dm.handleSession(ctx, msg) diff --git a/quic/param_windows.go b/quic/param_windows.go index 246e83de..13b29ed0 100644 --- a/quic/param_windows.go +++ b/quic/param_windows.go @@ -7,5 +7,5 @@ const ( // 1220 is the default value https://github.com/lucas-clemente/quic-go/blob/84e03e59760ceee37359688871bb0688fcc4e98f/internal/protocol/params.go#L138 MaxDatagramFrameSize = 1220 // 3 more bytes are reserved at https://github.com/lucas-clemente/quic-go/blob/v0.24.0/internal/wire/datagram_frame.go#L61 - maxDatagramPayloadSize = MaxDatagramFrameSize - 3 - sessionIDLen + maxDatagramPayloadSize = MaxDatagramFrameSize - 3 - sessionIDLen - typeIDLen ) From 902e5beb4ffed2c90f2e9743fb9b967920327aee Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 1 Sep 2022 19:29:11 +0100 Subject: [PATCH 184/238] TUN-6729: Fix flaky TestClosePreviousProxies I can only reproduce the flakiness, which is the hello world still responding when it should be shut down already, in Windows (both in TeamCity as well as my local VM). Locally, it only happens when the machine is under high load. Anyway, it's valid that the proxies take some time to shut down since they handle that via channels asynchronously with regards to the event that updates the configuration. Hence, nothing is wrong, as long as they eventually shut down, which the test still verifies. --- orchestration/orchestrator_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index dc94d29c..5c18b3a7 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -500,7 +500,8 @@ func TestClosePreviousProxies(t *testing.T) { require.NoError(t, err) require.Equal(t, http.StatusTeapot, resp.StatusCode) - // The hello-world server in config v1 should have been stopped + // The hello-world server in config v1 should have been stopped. We wait a bit since it's closed asynchronously. + time.Sleep(time.Millisecond * 10) resp, err = proxyHTTP(originProxyV1, hostname) require.Error(t, err) require.Nil(t, resp) From f7a14d92002fcc86e74e3235df0e0ec5bb8f4656 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 1 Sep 2022 14:20:22 -0700 Subject: [PATCH 185/238] TUN-6728: Verify http status code ingress rule --- CHANGES.md | 4 ++++ ingress/ingress.go | 9 ++++++--- ingress/ingress_test.go | 8 ++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3dbd8749..146703e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 2022.9.0 +### New Features +- cloudflared now rejects ingress rules with invalid http status codes for http_status. + ## 2022.8.1 ### New Features - cloudflared now remembers if it connected to a certain protocol successfully. If it did, it does not fall back to a lower diff --git a/ingress/ingress.go b/ingress/ingress.go index 05a90a8b..b15eced3 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -182,11 +182,14 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq path := strings.TrimPrefix(r.Service, prefix) service = &unixSocketPath{path: path, scheme: "https"} } else if prefix := "http_status:"; strings.HasPrefix(r.Service, prefix) { - status, err := strconv.Atoi(strings.TrimPrefix(r.Service, prefix)) + statusCode, err := strconv.Atoi(strings.TrimPrefix(r.Service, prefix)) if err != nil { - return Ingress{}, errors.Wrap(err, "invalid HTTP status") + return Ingress{}, errors.Wrap(err, "invalid HTTP status code") } - srv := newStatusCode(status) + if statusCode < 100 || statusCode > 999 { + return Ingress{}, fmt.Errorf("invalid HTTP status code: %d", statusCode) + } + srv := newStatusCode(statusCode) service = &srv } else if r.Service == HelloWorldService || r.Service == "hello-world" || r.Service == "helloworld" { service = new(helloWorld) diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 1ff5e11c..2f1c2850 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -208,6 +208,14 @@ ingress: args: args{rawYAML: ` ingress: - service: http_status:asdf +`}, + wantErr: true, + }, + { + name: "Invalid HTTP status code", + args: args{rawYAML: ` +ingress: + - service: http_status:8080 `}, wantErr: true, }, From faa86ffeca205fa304d43e662bfdc85e8de1a3c8 Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 5 Sep 2022 15:09:53 +0100 Subject: [PATCH 186/238] TUN-6737: Fix datagramV2Type should be declared in its own block so it starts at 0 --- quic/datagramv2.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 5a3e998c..9bc38f40 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -14,9 +14,12 @@ import ( type datagramV2Type byte const ( - typeIDLen = 1 - udp datagramV2Type = iota + udp datagramV2Type = iota ip +) + +const ( + typeIDLen = 1 // Same as sessionDemuxChan capacity packetChanCapacity = 16 ) From fc20a22685f1faf68ff0824eb4bdf3e217b73083 Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 25 Aug 2022 12:34:19 +0100 Subject: [PATCH 187/238] TUN-6695: Implement ICMP proxy for linux --- ingress/icmp_darwin.go | 26 ++- ingress/icmp_generic.go | 6 +- ingress/icmp_linux.go | 267 ++++++++++++++++++++++++++++++ ingress/icmp_linux_test.go | 52 ++++++ ingress/origin_icmp_proxy.go | 30 +++- ingress/origin_icmp_proxy_test.go | 15 +- supervisor/supervisor.go | 11 +- 7 files changed, 374 insertions(+), 33 deletions(-) create mode 100644 ingress/icmp_linux.go create mode 100644 ingress/icmp_linux_test.go diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 20509b2f..2b1c9cdb 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -27,7 +27,6 @@ type icmpProxy struct { echoIDTracker *echoIDTracker conn *icmp.PacketConn logger *zerolog.Logger - encoder *packet.Encoder } // echoIDTracker tracks which ID has been assigned. It first loops through assignment from lastAssignment to then end, @@ -112,13 +111,8 @@ func (snf echoFlowID) String() string { return strconv.FormatUint(uint64(snf), 10) } -func newICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { - network := "udp6" - if listenIP.To4() != nil { - network = "udp4" - } - // Opens a non-privileged ICMP socket - conn, err := icmp.ListenPacket(network, listenIP.String()) +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { + conn, err := newICMPConn(listenIP) if err != nil { return nil, err } @@ -127,11 +121,13 @@ func newICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { echoIDTracker: newEchoIDTracker(), conn: conn, logger: logger, - encoder: packet.NewEncoder(), }, nil } func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { + if pk == nil { + return errPacketNil + } switch body := pk.Message.Body.(type) { case *icmp.Echo: return ip.sendICMPEchoRequest(pk, body, responder) @@ -140,12 +136,14 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) er } } -func (ip *icmpProxy) ListenResponse(ctx context.Context) error { +// Serve listens for responses to the requests until context is done +func (ip *icmpProxy) Serve(ctx context.Context) error { go func() { <-ctx.Done() ip.conn.Close() }() - buf := make([]byte, 1500) + buf := make([]byte, mtu) + encoder := packet.NewEncoder() for { n, src, err := ip.conn.ReadFrom(buf) if err != nil { @@ -159,7 +157,7 @@ func (ip *icmpProxy) ListenResponse(ctx context.Context) error { } switch body := msg.Body.(type) { case *icmp.Echo: - if err := ip.handleEchoResponse(msg, body); err != nil { + if err := ip.handleEchoResponse(encoder, msg, body); err != nil { ip.logger.Error().Err(err). Str("src", src.String()). Str("flowID", echoFlowID(body.ID).String()). @@ -206,7 +204,7 @@ func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, respo return err } -func (ip *icmpProxy) handleEchoResponse(msg *icmp.Message, echo *icmp.Echo) error { +func (ip *icmpProxy) handleEchoResponse(encoder *packet.Encoder, msg *icmp.Message, echo *icmp.Echo) error { flowID := echoFlowID(echo.ID) flow, ok := ip.srcFlowTracker.Get(flowID) if !ok { @@ -220,7 +218,7 @@ func (ip *icmpProxy) handleEchoResponse(msg *icmp.Message, echo *icmp.Echo) erro }, Message: msg, } - serializedPacket, err := ip.encoder.Encode(&icmpPacket) + serializedPacket, err := encoder.Encode(&icmpPacket) if err != nil { return errors.Wrap(err, "Failed to encode ICMP message") } diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index dafa96e7..e2c9aae7 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -1,15 +1,15 @@ -//go:build !darwin +//go:build !darwin && !linux package ingress import ( "fmt" - "net" + "net/netip" "runtime" "github.com/rs/zerolog" ) -func newICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { return nil, fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) } diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go new file mode 100644 index 00000000..9803bb37 --- /dev/null +++ b/ingress/icmp_linux.go @@ -0,0 +1,267 @@ +//go:build linux + +package ingress + +import ( + "context" + "fmt" + "net" + "net/netip" + "sync" + "sync/atomic" + "time" + + "github.com/google/gopacket/layers" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/net/icmp" + + "github.com/cloudflare/cloudflared/packet" +) + +// The request echo ID is rewritten to the port of the socket. The kernel uses the reply echo ID to demultiplex +// We can open a socket for each source so multiple sources requesting the same destination doesn't collide +type icmpProxy struct { + srcToFlowTracker *srcToFlowTracker + listenIP netip.Addr + logger *zerolog.Logger + shutdownC chan struct{} +} + +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { + if err := testPermission(listenIP); err != nil { + return nil, err + } + return &icmpProxy{ + srcToFlowTracker: newSrcToConnTracker(), + listenIP: listenIP, + logger: logger, + shutdownC: make(chan struct{}), + }, nil +} + +func testPermission(listenIP netip.Addr) error { + // Opens a non-privileged ICMP socket. On Linux the group ID of the process needs to be in ping_group_range + // For more information, see https://man7.org/linux/man-pages/man7/icmp.7.html and https://lwn.net/Articles/422330/ + conn, err := newICMPConn(listenIP) + if err != nil { + // TODO: TUN-6715 check if cloudflared is in ping_group_range if the check failed. If not log instruction to + // change the group ID + return err + } + // This conn is only to test if cloudflared has permission to open this type of socket + conn.Close() + return nil +} + +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { + if pk == nil { + return errPacketNil + } + switch body := pk.Message.Body.(type) { + case *icmp.Echo: + return ip.sendICMPEchoRequest(pk, body, responder) + default: + return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) + } +} + +func (ip *icmpProxy) Serve(ctx context.Context) error { + <-ctx.Done() + close(ip.shutdownC) + return ctx.Err() +} + +func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { + icmpFlow, ok := ip.srcToFlowTracker.get(pk.Src) + if ok { + return icmpFlow.send(pk) + } + + conn, err := newICMPConn(ip.listenIP) + if err != nil { + return err + } + flow := packet.Flow{ + Src: pk.Src, + Dst: pk.Dst, + Responder: responder, + } + icmpFlow = newICMPFlow(conn, &flow, uint16(echo.ID), ip.logger) + go func() { + defer ip.srcToFlowTracker.delete(pk.Src) + + if err := icmpFlow.serve(ip.shutdownC, defaultCloseAfterIdle); err != nil { + ip.logger.Debug().Err(err).Uint16("flowID", icmpFlow.echoID).Msg("flow terminated") + } + }() + ip.srcToFlowTracker.set(pk.Src, icmpFlow) + return icmpFlow.send(pk) +} + +type srcIPFlowID netip.Addr + +func (sifd srcIPFlowID) Type() string { + return "srcIP" +} + +func (sifd srcIPFlowID) String() string { + return netip.Addr(sifd).String() +} + +type srcToFlowTracker struct { + lock sync.RWMutex + // srcIPToConn tracks source IP to ICMP connection + srcToFlow map[netip.Addr]*icmpFlow +} + +func newSrcToConnTracker() *srcToFlowTracker { + return &srcToFlowTracker{ + srcToFlow: make(map[netip.Addr]*icmpFlow), + } +} + +func (sft *srcToFlowTracker) get(srcIP netip.Addr) (*icmpFlow, bool) { + sft.lock.RLock() + defer sft.lock.RUnlock() + + flow, ok := sft.srcToFlow[srcIP] + return flow, ok +} + +func (sft *srcToFlowTracker) set(srcIP netip.Addr, flow *icmpFlow) { + sft.lock.Lock() + defer sft.lock.Unlock() + + sft.srcToFlow[srcIP] = flow +} + +func (sft *srcToFlowTracker) delete(srcIP netip.Addr) { + sft.lock.Lock() + defer sft.lock.Unlock() + + delete(sft.srcToFlow, srcIP) +} + +type icmpFlow struct { + conn *icmp.PacketConn + flow *packet.Flow + echoID uint16 + // last active unix time. Unit is seconds + lastActive int64 + logger *zerolog.Logger +} + +func newICMPFlow(conn *icmp.PacketConn, flow *packet.Flow, echoID uint16, logger *zerolog.Logger) *icmpFlow { + return &icmpFlow{ + conn: conn, + flow: flow, + echoID: echoID, + lastActive: time.Now().Unix(), + logger: logger, + } +} + +func (f *icmpFlow) serve(shutdownC chan struct{}, closeAfterIdle time.Duration) error { + errC := make(chan error) + go func() { + errC <- f.listenResponse() + }() + + checkIdleTicker := time.NewTicker(closeAfterIdle) + defer f.conn.Close() + defer checkIdleTicker.Stop() + for { + select { + case err := <-errC: + return err + case <-shutdownC: + return nil + case <-checkIdleTicker.C: + now := time.Now().Unix() + lastActive := atomic.LoadInt64(&f.lastActive) + if now > lastActive+int64(closeAfterIdle.Seconds()) { + return errFlowInactive + } + } + } +} + +func (f *icmpFlow) send(pk *packet.ICMP) error { + f.updateLastActive() + + // For IPv4, the pseudoHeader is not used because the checksum is always calculated + var pseudoHeader []byte = nil + serializedMsg, err := pk.Marshal(pseudoHeader) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + // The address needs to be of type UDPAddr when conn is created without priviledge + _, err = f.conn.WriteTo(serializedMsg, &net.UDPAddr{ + IP: pk.Dst.AsSlice(), + }) + return err +} + +func (f *icmpFlow) listenResponse() error { + buf := make([]byte, mtu) + encoder := packet.NewEncoder() + for { + n, src, err := f.conn.ReadFrom(buf) + if err != nil { + return err + } + f.updateLastActive() + + if err := f.handleResponse(encoder, src, buf[:n]); err != nil { + f.logger.Err(err).Str("dst", src.String()).Msg("Failed to handle ICMP response") + continue + } + } +} + +func (f *icmpFlow) handleResponse(encoder *packet.Encoder, from net.Addr, rawPacket []byte) error { + // TODO: TUN-6654 Check for IPv6 + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), rawPacket) + if err != nil { + return err + } + + echo, ok := msg.Body.(*icmp.Echo) + if !ok { + return fmt.Errorf("received unexpected icmp type %s from non-privileged ICMP socket", msg.Type) + } + + addrPort, err := netip.ParseAddrPort(from.String()) + if err != nil { + return err + } + icmpPacket := packet.ICMP{ + IP: &packet.IP{ + Src: addrPort.Addr(), + Dst: f.flow.Src, + Protocol: layers.IPProtocol(msg.Type.Protocol()), + }, + Message: &icmp.Message{ + Type: msg.Type, + Code: msg.Code, + Body: &icmp.Echo{ + ID: int(f.echoID), + Seq: echo.Seq, + Data: echo.Data, + }, + }, + } + serializedPacket, err := encoder.Encode(&icmpPacket) + if err != nil { + return errors.Wrap(err, "Failed to encode ICMP message") + } + if err := f.flow.Responder.SendPacket(serializedPacket); err != nil { + return errors.Wrap(err, "Failed to send packet to the edge") + } + return nil +} + +func (f *icmpFlow) updateLastActive() { + atomic.StoreInt64(&f.lastActive, time.Now().Unix()) +} diff --git a/ingress/icmp_linux_test.go b/ingress/icmp_linux_test.go new file mode 100644 index 00000000..40aee7a3 --- /dev/null +++ b/ingress/icmp_linux_test.go @@ -0,0 +1,52 @@ +//go:build linux + +package ingress + +import ( + "errors" + "net" + "net/netip" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cloudflare/cloudflared/packet" +) + +func TestCloseIdleFlow(t *testing.T) { + const ( + echoID = 19234 + idleTimeout = time.Millisecond * 100 + ) + conn, err := newICMPConn(localhostIP) + require.NoError(t, err) + flow := packet.Flow{ + Src: netip.MustParseAddr("172.16.0.1"), + } + icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger) + shutdownC := make(chan struct{}) + flowErr := make(chan error) + go func() { + flowErr <- icmpFlow.serve(shutdownC, idleTimeout) + }() + + require.Equal(t, errFlowInactive, <-flowErr) +} + +func TestCloseConnStopFlow(t *testing.T) { + const ( + echoID = 19234 + ) + conn, err := newICMPConn(localhostIP) + require.NoError(t, err) + flow := packet.Flow{ + Src: netip.MustParseAddr("172.16.0.1"), + } + icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger) + shutdownC := make(chan struct{}) + conn.Close() + + err = icmpFlow.serve(shutdownC, defaultCloseAfterIdle) + require.True(t, errors.Is(err, net.ErrClosed)) +} diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index aabaa51f..0ac851be 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -2,21 +2,43 @@ package ingress import ( "context" - "net" + "fmt" + "net/netip" + "time" "github.com/rs/zerolog" + "golang.org/x/net/icmp" "github.com/cloudflare/cloudflared/packet" ) +const ( + defaultCloseAfterIdle = time.Second * 15 + mtu = 1500 +) + +var ( + errFlowInactive = fmt.Errorf("flow is inactive") + errPacketNil = fmt.Errorf("packet is nil") +) + // ICMPProxy sends ICMP messages and listens for their responses type ICMPProxy interface { + // Serve starts listening for responses to the requests until context is done + Serve(ctx context.Context) error // Request sends an ICMP message Request(pk *packet.ICMP, responder packet.FlowResponder) error - // ListenResponse listens for responses to the requests until context is done - ListenResponse(ctx context.Context) error } -func NewICMPProxy(listenIP net.IP, logger *zerolog.Logger) (ICMPProxy, error) { +func NewICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { return newICMPProxy(listenIP, logger) } + +// Opens a non-privileged ICMP socket on Linux and Darwin +func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) { + network := "udp6" + if listenIP.Is4() { + network = "udp4" + } + return icmp.ListenPacket(network, listenIP.String()) +} diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index eb03a008..b38f2cd9 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -24,19 +24,19 @@ var ( // TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the // ListenResponse method func TestICMPProxyEcho(t *testing.T) { - skipNonDarwin(t) + onlyDarwinOrLinux(t) const ( echoID = 36571 endSeq = 100 ) - proxy, err := NewICMPProxy(localhostIP.AsSlice(), &noopLogger) + proxy, err := NewICMPProxy(localhostIP, &noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) go func() { - proxy.ListenResponse(ctx) + proxy.Serve(ctx) close(proxyDone) }() @@ -72,7 +72,7 @@ func TestICMPProxyEcho(t *testing.T) { // TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo func TestICMPProxyRejectNotEcho(t *testing.T) { - skipNonDarwin(t) + onlyDarwinOrLinux(t) msgs := []icmp.Message{ { Type: ipv4.ICMPTypeDestinationUnreachable, @@ -97,7 +97,7 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { }, }, } - proxy, err := NewICMPProxy(localhostIP.AsSlice(), &noopLogger) + proxy, err := NewICMPProxy(localhostIP, &noopLogger) require.NoError(t, err) responder := echoFlowResponder{ @@ -117,8 +117,8 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { } } -func skipNonDarwin(t *testing.T) { - if runtime.GOOS != "darwin" { +func onlyDarwinOrLinux(t *testing.T) { + if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { t.Skip("Cannot create non-privileged datagram-oriented ICMP endpoint on Windows") } } @@ -146,6 +146,5 @@ func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type) require.Equal(t, 0, decoded.Code) require.NotZero(t, decoded.Checksum) - // TODO: TUN-6586: Enable this validation when ICMP echo ID matches on Linux require.Equal(t, echoReq.Body, decoded.Body) } diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index bc076f0c..52ce46a4 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "net" + "net/netip" "strings" "time" @@ -117,9 +117,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato connAwareLogger: log, } if useDatagramV2(config) { - // For non-privileged datagram-oriented ICMP endpoints, network must be "udp4" or "udp6" // TODO: TUN-6654 listen for IPv6 and decide if it should listen on specific IP - icmpProxy, err := ingress.NewICMPProxy(net.IPv4zero, config.Log) + listenIP, err := netip.ParseAddr("0.0.0.0") + if err != nil { + return nil, err + } + icmpProxy, err := ingress.NewICMPProxy(listenIP, config.Log) if err != nil { log.Logger().Warn().Err(err).Msg("Failed to create icmp proxy, will continue to use datagram v1") } else { @@ -156,7 +159,7 @@ func (s *Supervisor) Run( ) error { if s.edgeTunnelServer.icmpProxy != nil { go func() { - if err := s.edgeTunnelServer.icmpProxy.ListenResponse(ctx); err != nil { + if err := s.edgeTunnelServer.icmpProxy.Serve(ctx); err != nil { s.log.Logger().Err(err).Msg("icmp proxy terminated") } }() From 4b75943d5943b8e432e0d326bd0eb5986e82c7cb Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Wed, 7 Sep 2022 07:58:04 +0100 Subject: [PATCH 188/238] Release 2022.9.0 --- RELEASE_NOTES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 030e1360..7cbd9706 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,11 @@ +2022.9.0 +- 2022-09-05 TUN-6737: Fix datagramV2Type should be declared in its own block so it starts at 0 +- 2022-09-01 TUN-6725: Fix testProxySSEAllData +- 2022-09-01 TUN-6726: Fix maxDatagramPayloadSize for Windows QUIC datagrams +- 2022-09-01 TUN-6729: Fix flaky TestClosePreviousProxies +- 2022-09-01 TUN-6728: Verify http status code ingress rule +- 2022-08-25 TUN-6695: Implement ICMP proxy for linux + 2022.8.4 - 2022-08-31 TUN-6717: Update Github action to run with Go 1.19 - 2022-08-31 TUN-6720: Remove forcibly closing connection during reconnect signal From 7a1979868211d892c8bf16e7234ec7dad3f91c9d Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Tue, 6 Sep 2022 13:20:50 +0100 Subject: [PATCH 189/238] TUN-6740: Detect no UDP packets allowed and fallback from QUIC in that case --- connection/errors.go | 4 ++++ ingress/origin_icmp_proxy_test.go | 4 ++++ supervisor/tunnel.go | 25 +++++++++++++++++-------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/connection/errors.go b/connection/errors.go index 10e3b5d6..17cf58a3 100644 --- a/connection/errors.go +++ b/connection/errors.go @@ -27,6 +27,10 @@ func (e *EdgeQuicDialError) Error() string { return "failed to dial to edge with quic: " + e.Cause.Error() } +func (e *EdgeQuicDialError) Unwrap() error { + return e.Cause +} + // RegisterTunnel error from server type ServerRegisterTunnelError struct { Cause error diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index b38f2cd9..efb7d489 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -23,6 +23,10 @@ var ( // TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the // ListenResponse method +// +// Note: if this test fails on your device under Linux, then most likely you need to make sure that your user +// is allowed in ping_group_range. See the following gist for how to do that: +// https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md func TestICMPProxyEcho(t *testing.T) { onlyDarwinOrLinux(t) const ( diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index f23eeff7..9b6f0db4 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -317,16 +317,11 @@ func selectNextProtocol( selector connection.ProtocolSelector, cause error, ) bool { - var idleTimeoutError *quic.IdleTimeoutError - isNetworkActivityTimeout := errors.As(cause, &idleTimeoutError) - edgeQuicDialError, ok := cause.(*connection.EdgeQuicDialError) - if !isNetworkActivityTimeout && ok { - isNetworkActivityTimeout = errors.As(edgeQuicDialError.Cause, &idleTimeoutError) - } + isQuicBroken := isQuicBroken(cause) _, hasFallback := selector.Fallback() - if protocolBackoff.ReachedMaxRetries() || (hasFallback && isNetworkActivityTimeout) { - if isNetworkActivityTimeout { + if protocolBackoff.ReachedMaxRetries() || (hasFallback && isQuicBroken) { + if isQuicBroken { connLog.Warn().Msg("If this log occurs persistently, and cloudflared is unable to connect to " + "Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " + "UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " + @@ -355,6 +350,20 @@ func selectNextProtocol( return true } +func isQuicBroken(cause error) bool { + var idleTimeoutError *quic.IdleTimeoutError + if errors.As(cause, &idleTimeoutError) { + return true + } + + var transportError *quic.TransportError + if errors.As(cause, &transportError) && strings.Contains(cause.Error(), "operation not permitted") { + return true + } + + return false +} + // ServeTunnel runs a single tunnel connection, returns nil on graceful shutdown, // on error returns a flag indicating if error can be retried func (e *EdgeTunnelServer) serveTunnel( From 3e0ff3a7713378a170b34863ec630aa4d72ea41a Mon Sep 17 00:00:00 2001 From: Chung-Ting Huang Date: Mon, 29 Aug 2022 18:49:07 +0100 Subject: [PATCH 190/238] TUN-6531: Implement ICMP proxy for Windows using IcmpSendEcho --- ingress/icmp_generic.go | 2 +- ingress/icmp_linux.go | 9 +- ingress/icmp_windows.go | 317 ++++++++++++++++++++++++++++++ ingress/icmp_windows_test.go | 139 +++++++++++++ ingress/origin_icmp_proxy.go | 9 + ingress/origin_icmp_proxy_test.go | 11 +- quic/datagramv2.go | 2 +- 7 files changed, 472 insertions(+), 17 deletions(-) create mode 100644 ingress/icmp_windows.go create mode 100644 ingress/icmp_windows_test.go diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index e2c9aae7..976387f9 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -1,4 +1,4 @@ -//go:build !darwin && !linux +//go:build !darwin && !linux && !windows package ingress diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index 9803bb37..f7e2d936 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -58,12 +58,11 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) er if pk == nil { return errPacketNil } - switch body := pk.Message.Body.(type) { - case *icmp.Echo: - return ip.sendICMPEchoRequest(pk, body, responder) - default: - return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) + echo, err := getICMPEcho(pk) + if err != nil { + return err } + return ip.sendICMPEchoRequest(pk, echo, responder) } func (ip *icmpProxy) Serve(ctx context.Context) error { diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go new file mode 100644 index 00000000..36eb90d5 --- /dev/null +++ b/ingress/icmp_windows.go @@ -0,0 +1,317 @@ +//go:build windows + +package ingress + +/* +#include +#include +*/ +import "C" +import ( + "context" + "encoding/binary" + "fmt" + "net/netip" + "runtime/debug" + "sync" + "syscall" + "unsafe" + + "github.com/google/gopacket/layers" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" + + "github.com/cloudflare/cloudflared/packet" +) + +const ( + icmpEchoReplyCode = 0 +) + +var ( + Iphlpapi = syscall.NewLazyDLL("Iphlpapi.dll") + IcmpCreateFile_proc = Iphlpapi.NewProc("IcmpCreateFile") + IcmpSendEcho_proc = Iphlpapi.NewProc("IcmpSendEcho") + echoReplySize = unsafe.Sizeof(echoReply{}) + endian = binary.LittleEndian +) + +// IP_STATUS code, see https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-icmp_echo_reply32#members +// for possible values +type ipStatus uint32 + +const ( + success ipStatus = 0 + bufTooSmall = iota + 11000 + destNetUnreachable + destHostUnreachable + destProtocolUnreachable + destPortUnreachable + noResources + badOption + hwError + packetTooBig + reqTimedOut + badReq + badRoute + ttlExpiredTransit + ttlExpiredReassembly + paramProblem + sourceQuench + optionTooBig + badDestination + // Can be returned for malformed ICMP packets + generalFailure = 11050 +) + +func (is ipStatus) String() string { + switch is { + case success: + return "Success" + case bufTooSmall: + return "The reply buffer too small" + case destNetUnreachable: + return "The destination network was unreachable" + case destHostUnreachable: + return "The destination host was unreachable" + case destProtocolUnreachable: + return "The destination protocol was unreachable" + case destPortUnreachable: + return "The destination port was unreachable" + case noResources: + return "Insufficient IP resources were available" + case badOption: + return "A bad IP option was specified" + case hwError: + return "A hardware error occurred" + case packetTooBig: + return "The packet was too big" + case reqTimedOut: + return "The request timed out" + case badReq: + return "Bad request" + case badRoute: + return "Bad route" + case ttlExpiredTransit: + return "The TTL expired in transit" + case ttlExpiredReassembly: + return "The TTL expired during fragment reassembly" + case paramProblem: + return "A parameter problem" + case sourceQuench: + return "Datagrams are arriving too fast to be processed and datagrams may have been discarded" + case optionTooBig: + return "The IP option was too big" + case badDestination: + return "Bad destination" + case generalFailure: + return "The ICMP packet might be malformed" + default: + return fmt.Sprintf("Unknown ip status %d", is) + } +} + +// https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-ip_option_information +type ipOption struct { + TTL uint8 + Tos uint8 + Flags uint8 + OptionsSize uint8 + OptionsData uintptr +} + +// https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-icmp_echo_reply +type echoReply struct { + Address uint32 + Status ipStatus + RoundTripTime uint32 + DataSize uint16 + Reserved uint16 + // The pointer size defers between 32-bit and 64-bit platforms + DataPointer *byte + Options ipOption +} + +type icmpProxy struct { + // An open handle that can send ICMP requests https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpcreatefile + handle uintptr + logger *zerolog.Logger + // A pool of reusable *packet.Encoder + encoderPool sync.Pool +} + +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { + handle, _, err := IcmpCreateFile_proc.Call() + // Windows procedure calls always return non-nil error constructed from the result of GetLastError. + // Caller need to inspect the primary returned value + if syscall.Handle(handle) == syscall.InvalidHandle { + return nil, errors.Wrap(err, "invalid ICMP handle") + } + return &icmpProxy{ + handle: handle, + logger: logger, + encoderPool: sync.Pool{ + New: func() any { + return packet.NewEncoder() + }, + }, + }, nil +} + +func (ip *icmpProxy) Serve(ctx context.Context) error { + <-ctx.Done() + syscall.CloseHandle(syscall.Handle(ip.handle)) + return ctx.Err() +} + +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { + if pk == nil { + return errPacketNil + } + defer func() { + if r := recover(); r != nil { + ip.logger.Error().Interface("error", r).Msgf("Recover panic from sending icmp request/response, error %s", debug.Stack()) + } + }() + echo, err := getICMPEcho(pk) + if err != nil { + return err + } + + resp, err := ip.icmpSendEcho(pk.Dst, echo) + if err != nil { + return errors.Wrap(err, "failed to send/receive ICMP echo") + } + + err = ip.handleEchoResponse(pk, echo, resp, responder) + if err != nil { + return errors.Wrap(err, "failed to handle ICMP echo reply") + } + return nil +} + +func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo, resp *echoResp, responder packet.FlowResponder) error { + var replyType icmp.Type + if request.Dst.Is4() { + replyType = ipv4.ICMPTypeEchoReply + } else { + replyType = ipv6.ICMPTypeEchoReply + } + + pk := packet.ICMP{ + IP: &packet.IP{ + Src: request.Dst, + Dst: request.Src, + Protocol: layers.IPProtocol(request.Type.Protocol()), + }, + Message: &icmp.Message{ + Type: replyType, + Code: icmpEchoReplyCode, + Body: &icmp.Echo{ + ID: echoReq.ID, + Seq: echoReq.Seq, + Data: resp.data, + }, + }, + } + + serializedPacket, err := ip.encodeICMPReply(&pk) + if err != nil { + return err + } + return responder.SendPacket(serializedPacket) +} + +func (ip *icmpProxy) encodeICMPReply(pk *packet.ICMP) (packet.RawPacket, error) { + cachedEncoder := ip.encoderPool.Get() + defer ip.encoderPool.Put(cachedEncoder) + encoder, ok := cachedEncoder.(*packet.Encoder) + if !ok { + return packet.RawPacket{}, fmt.Errorf("encoderPool returned %T, expect *packet.Encoder", cachedEncoder) + } + return encoder.Encode(pk) +} + +/* + Wrapper to call https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpsendecho + Parameters: + - IcmpHandle: + - DestinationAddress: IPv4 in the form of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax + - RequestData: A pointer to echo data + - RequestSize: Number of bytes in buffer pointed by echo data + - RequestOptions: IP header options + - ReplyBuffer: A pointer to the buffer for echoReply, options and data + - ReplySize: Number of bytes allocated for ReplyBuffer + - Timeout: Timeout in milliseconds to wait for a reply + Returns: + - the number of replies in uint32 https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpsendecho#return-value + To retain the reference allocated objects, conversion from pointer to uintptr must happen as arguments to the + syscall function +*/ +func (ip *icmpProxy) icmpSendEcho(dst netip.Addr, echo *icmp.Echo) (*echoResp, error) { + dataSize := len(echo.Data) + replySize := echoReplySize + uintptr(dataSize) + replyBuf := make([]byte, replySize) + noIPHeaderOption := uintptr(0) + inAddr, err := inAddrV4(dst) + if err != nil { + return nil, err + } + replyCount, _, err := IcmpSendEcho_proc.Call(ip.handle, uintptr(inAddr), uintptr(unsafe.Pointer(&echo.Data[0])), + uintptr(dataSize), noIPHeaderOption, uintptr(unsafe.Pointer(&replyBuf[0])), + replySize, icmpTimeoutMs) + if replyCount == 0 { + // status is returned in 5th to 8th byte of reply buffer + if status, err := unmarshalIPStatus(replyBuf[4:8]); err == nil { + return nil, fmt.Errorf("received ip status: %s", status) + } + return nil, errors.Wrap(err, "did not receive ICMP echo reply") + } else if replyCount > 1 { + ip.logger.Warn().Msgf("Received %d ICMP echo replies, only sending 1 back", replyCount) + } + return newEchoResp(replyBuf) +} + +type echoResp struct { + reply *echoReply + data []byte +} + +func newEchoResp(replyBuf []byte) (*echoResp, error) { + if len(replyBuf) == 0 { + return nil, fmt.Errorf("reply buffer is empty") + } + // This is pattern 1 of https://pkg.go.dev/unsafe@master#Pointer, conversion of *replyBuf to *echoReply + // replyBuf size is larger than echoReply + reply := *(*echoReply)(unsafe.Pointer(&replyBuf[0])) + if reply.Status != success { + return nil, fmt.Errorf("status %d", reply.Status) + } + dataBufStart := len(replyBuf) - int(reply.DataSize) + if dataBufStart < int(echoReplySize) { + return nil, fmt.Errorf("reply buffer size %d is too small to hold data of size %d", len(replyBuf), int(reply.DataSize)) + } + return &echoResp{ + reply: &reply, + data: replyBuf[dataBufStart:], + }, nil +} + +// Third definition of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax is address in uint32 +func inAddrV4(ip netip.Addr) (uint32, error) { + if !ip.Is4() { + return 0, fmt.Errorf("%s is not IPv4", ip) + } + v4 := ip.As4() + return endian.Uint32(v4[:]), nil +} + +func unmarshalIPStatus(replyBuf []byte) (ipStatus, error) { + if len(replyBuf) != 4 { + return 0, fmt.Errorf("ipStatus needs to be 4 bytes, got %d", len(replyBuf)) + } + return ipStatus(endian.Uint32(replyBuf)), nil +} diff --git a/ingress/icmp_windows_test.go b/ingress/icmp_windows_test.go new file mode 100644 index 00000000..633ddf47 --- /dev/null +++ b/ingress/icmp_windows_test.go @@ -0,0 +1,139 @@ +//go:build windows + +package ingress + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net/netip" + "testing" + "unsafe" + + "golang.org/x/net/icmp" + + "github.com/stretchr/testify/require" +) + +// TestParseEchoReply tests parsing raw bytes from icmpSendEcho into echoResp +func TestParseEchoReply(t *testing.T) { + dst, err := inAddrV4(netip.MustParseAddr("192.168.10.20")) + require.NoError(t, err) + + validReplyData := []byte(t.Name()) + validReply := echoReply{ + Address: dst, + Status: success, + RoundTripTime: uint32(20), + DataSize: uint16(len(validReplyData)), + DataPointer: &validReplyData[0], + Options: ipOption{ + TTL: 59, + }, + } + + destHostUnreachableReply := validReply + destHostUnreachableReply.Status = destHostUnreachable + + tests := []struct { + testCase string + replyBuf []byte + expectedReply *echoReply + expectedData []byte + }{ + { + testCase: "empty buffer", + }, + { + testCase: "status not success", + replyBuf: destHostUnreachableReply.marshal(t, []byte{}), + }, + { + testCase: "valid reply", + replyBuf: validReply.marshal(t, validReplyData), + expectedReply: &validReply, + expectedData: validReplyData, + }, + } + + for _, test := range tests { + resp, err := newEchoResp(test.replyBuf) + if test.expectedReply == nil { + require.Error(t, err) + require.Nil(t, resp) + } else { + require.NoError(t, err) + require.Equal(t, resp.reply, test.expectedReply) + require.True(t, bytes.Equal(resp.data, test.expectedData)) + } + } +} + +// TestSendEchoErrors makes sure icmpSendEcho handles error cases +func TestSendEchoErrors(t *testing.T) { + proxy, err := newICMPProxy(localhostIP, &noopLogger) + require.NoError(t, err) + winProxy := proxy.(*icmpProxy) + + echo := icmp.Echo{ + ID: 6193, + Seq: 25712, + Data: []byte(t.Name()), + } + documentIP := netip.MustParseAddr("192.0.2.200") + resp, err := winProxy.icmpSendEcho(documentIP, &echo) + require.Error(t, err) + require.Nil(t, resp) +} + +func (er *echoReply) marshal(t *testing.T, data []byte) []byte { + buf := new(bytes.Buffer) + + for _, field := range []any{ + er.Address, + er.Status, + er.RoundTripTime, + er.DataSize, + er.Reserved, + } { + require.NoError(t, binary.Write(buf, endian, field)) + } + + require.NoError(t, marshalPointer(buf, uintptr(unsafe.Pointer(er.DataPointer)))) + + for _, field := range []any{ + er.Options.TTL, + er.Options.Tos, + er.Options.Flags, + er.Options.OptionsSize, + } { + require.NoError(t, binary.Write(buf, endian, field)) + } + + require.NoError(t, marshalPointer(buf, er.Options.OptionsData)) + + padSize := buf.Len() % int(unsafe.Alignof(er)) + padding := make([]byte, padSize) + n, err := buf.Write(padding) + require.NoError(t, err) + require.Equal(t, padSize, n) + + n, err = buf.Write(data) + require.NoError(t, err) + require.Equal(t, len(data), n) + + return buf.Bytes() +} + +func marshalPointer(buf io.Writer, ptr uintptr) error { + size := unsafe.Sizeof(ptr) + switch size { + case 4: + return binary.Write(buf, endian, uint32(ptr)) + case 8: + return binary.Write(buf, endian, uint64(ptr)) + default: + return fmt.Errorf("unexpected pointer size %d", size) + } +} diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index 0ac851be..f2a4180c 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -15,6 +15,7 @@ import ( const ( defaultCloseAfterIdle = time.Second * 15 mtu = 1500 + icmpTimeoutMs = 1000 ) var ( @@ -42,3 +43,11 @@ func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) { } return icmp.ListenPacket(network, listenIP.String()) } + +func getICMPEcho(pk *packet.ICMP) (*icmp.Echo, error) { + echo, ok := pk.Message.Body.(*icmp.Echo) + if !ok { + return nil, fmt.Errorf("expect ICMP echo, got %s", pk.Type) + } + return echo, nil +} diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index efb7d489..7a1ebc89 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net/netip" - "runtime" "testing" "github.com/google/gopacket/layers" @@ -28,7 +27,6 @@ var ( // is allowed in ping_group_range. See the following gist for how to do that: // https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md func TestICMPProxyEcho(t *testing.T) { - onlyDarwinOrLinux(t) const ( echoID = 36571 endSeq = 100 @@ -46,7 +44,7 @@ func TestICMPProxyEcho(t *testing.T) { responder := echoFlowResponder{ decoder: packet.NewICMPDecoder(), - respChan: make(chan []byte), + respChan: make(chan []byte, 1), } ip := packet.IP{ @@ -76,7 +74,6 @@ func TestICMPProxyEcho(t *testing.T) { // TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo func TestICMPProxyRejectNotEcho(t *testing.T) { - onlyDarwinOrLinux(t) msgs := []icmp.Message{ { Type: ipv4.ICMPTypeDestinationUnreachable, @@ -121,12 +118,6 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { } } -func onlyDarwinOrLinux(t *testing.T) { - if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { - t.Skip("Cannot create non-privileged datagram-oriented ICMP endpoint on Windows") - } -} - type echoFlowResponder struct { decoder *packet.ICMPDecoder respChan chan []byte diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 9bc38f40..3f1c8f0e 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -21,7 +21,7 @@ const ( const ( typeIDLen = 1 // Same as sessionDemuxChan capacity - packetChanCapacity = 16 + packetChanCapacity = 128 ) func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) { From 11cbff4ff763fb10bd165e1f5a02bddf9016519d Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 24 Aug 2022 14:33:10 +0200 Subject: [PATCH 191/238] RTG-1339 Support post-quantum hybrid key exchange Func spec: https://wiki.cfops.it/x/ZcBKHw --- cmd/cloudflared/tunnel/configuration.go | 30 +- cmd/cloudflared/tunnel/fips.go | 3 + cmd/cloudflared/tunnel/subcommands.go | 8 + connection/protocol.go | 25 +- connection/protocol_test.go | 8 +- fips/fips.go.linux-amd64 | 9 +- go.mod | 16 +- go.sum | 22 +- supervisor/pqtunnels.go | 100 + supervisor/tunnel.go | 33 + supervisor/tunnel_test.go | 2 + vendor/github.com/cloudflare/circl/LICENSE | 57 + .../cloudflare/circl/dh/x25519/curve.go | 96 + .../cloudflare/circl/dh/x25519/curve_amd64.go | 30 + .../cloudflare/circl/dh/x25519/curve_amd64.h | 111 + .../cloudflare/circl/dh/x25519/curve_amd64.s | 156 ++ .../circl/dh/x25519/curve_generic.go | 85 + .../cloudflare/circl/dh/x25519/curve_noasm.go | 11 + .../cloudflare/circl/dh/x25519/doc.go | 19 + .../cloudflare/circl/dh/x25519/key.go | 47 + .../cloudflare/circl/dh/x25519/table.go | 268 ++ .../cloudflare/circl/dh/x448/curve.go | 104 + .../cloudflare/circl/dh/x448/curve_amd64.go | 30 + .../cloudflare/circl/dh/x448/curve_amd64.h | 111 + .../cloudflare/circl/dh/x448/curve_amd64.s | 193 ++ .../cloudflare/circl/dh/x448/curve_generic.go | 100 + .../cloudflare/circl/dh/x448/curve_noasm.go | 11 + .../cloudflare/circl/dh/x448/doc.go | 19 + .../cloudflare/circl/dh/x448/key.go | 46 + .../cloudflare/circl/dh/x448/table.go | 460 ++++ .../cloudflare/circl/internal/conv/conv.go | 140 + .../cloudflare/circl/internal/sha3/doc.go | 62 + .../cloudflare/circl/internal/sha3/hashes.go | 69 + .../cloudflare/circl/internal/sha3/keccakf.go | 385 +++ .../circl/internal/sha3/keccakf_amd64.go | 14 + .../circl/internal/sha3/keccakf_amd64.s | 390 +++ .../cloudflare/circl/internal/sha3/rc.go | 29 + .../cloudflare/circl/internal/sha3/sha3.go | 195 ++ .../circl/internal/sha3/sha3_s390x.s | 33 + .../cloudflare/circl/internal/sha3/shake.go | 79 + .../cloudflare/circl/internal/sha3/xor.go | 15 + .../circl/internal/sha3/xor_generic.go | 33 + .../circl/internal/sha3/xor_unaligned.go | 61 + .../cloudflare/circl/kem/hybrid/hybrid.go | 335 +++ .../cloudflare/circl/kem/hybrid/xkem.go | 208 ++ vendor/github.com/cloudflare/circl/kem/kem.go | 118 + .../circl/kem/kyber/kyber1024/kyber.go | 402 +++ .../circl/kem/kyber/kyber512/kyber.go | 402 +++ .../circl/kem/kyber/kyber768/kyber.go | 402 +++ .../cloudflare/circl/math/fp25519/fp.go | 205 ++ .../cloudflare/circl/math/fp25519/fp_amd64.go | 45 + .../cloudflare/circl/math/fp25519/fp_amd64.h | 350 +++ .../cloudflare/circl/math/fp25519/fp_amd64.s | 111 + .../circl/math/fp25519/fp_generic.go | 317 +++ .../cloudflare/circl/math/fp25519/fp_noasm.go | 13 + .../cloudflare/circl/math/fp448/fp.go | 164 ++ .../cloudflare/circl/math/fp448/fp_amd64.go | 43 + .../cloudflare/circl/math/fp448/fp_amd64.h | 590 +++++ .../cloudflare/circl/math/fp448/fp_amd64.s | 74 + .../cloudflare/circl/math/fp448/fp_generic.go | 339 +++ .../cloudflare/circl/math/fp448/fp_noasm.go | 12 + .../cloudflare/circl/math/fp448/fuzzer.go | 75 + .../circl/pke/kyber/internal/common/amd64.go | 302 +++ .../circl/pke/kyber/internal/common/amd64.s | 2354 +++++++++++++++++ .../circl/pke/kyber/internal/common/field.go | 74 + .../pke/kyber/internal/common/generic.go | 77 + .../circl/pke/kyber/internal/common/ntt.go | 193 ++ .../circl/pke/kyber/internal/common/params.go | 22 + .../kyber/internal/common/params/params.go | 21 + .../circl/pke/kyber/internal/common/poly.go | 324 +++ .../circl/pke/kyber/internal/common/sample.go | 236 ++ .../pke/kyber/internal/common/stubs_amd64.go | 33 + .../pke/kyber/kyber1024/internal/cpapke.go | 176 ++ .../circl/pke/kyber/kyber1024/internal/mat.go | 85 + .../pke/kyber/kyber1024/internal/params.go | 21 + .../circl/pke/kyber/kyber1024/internal/vec.go | 125 + .../circl/pke/kyber/kyber1024/kyber.go | 145 + .../pke/kyber/kyber512/internal/cpapke.go | 174 ++ .../circl/pke/kyber/kyber512/internal/mat.go | 83 + .../pke/kyber/kyber512/internal/params.go | 21 + .../circl/pke/kyber/kyber512/internal/vec.go | 123 + .../circl/pke/kyber/kyber512/kyber.go | 145 + .../pke/kyber/kyber768/internal/cpapke.go | 176 ++ .../circl/pke/kyber/kyber768/internal/mat.go | 85 + .../pke/kyber/kyber768/internal/params.go | 21 + .../circl/pke/kyber/kyber768/internal/vec.go | 125 + .../circl/pke/kyber/kyber768/kyber.go | 145 + .../circl/simd/keccakf1600/f1600x.go | 149 ++ .../circl/simd/keccakf1600/f1600x2_arm64.go | 13 + .../circl/simd/keccakf1600/f1600x2_arm64.s | 130 + .../circl/simd/keccakf1600/f1600x4_amd64.go | 7 + .../circl/simd/keccakf1600/f1600x4_amd64.s | 894 +++++++ .../simd/keccakf1600/f1600x4stubs_amd64.go | 9 + .../circl/simd/keccakf1600/fallback.go | 8 + .../marten-seemann/qtls-go1-18/cfkem.go | 170 ++ .../marten-seemann/qtls-go1-18/common.go | 1 + .../qtls-go1-18/handshake_client.go | 52 +- .../qtls-go1-18/handshake_client_tls13.go | 71 +- .../qtls-go1-18/handshake_server_tls13.go | 35 +- .../qtls-go1-18/key_agreement.go | 2 +- .../marten-seemann/qtls-go1-19/cfkem.go | 170 ++ .../marten-seemann/qtls-go1-19/common.go | 1 + .../qtls-go1-19/handshake_client.go | 52 +- .../qtls-go1-19/handshake_client_tls13.go | 71 +- .../qtls-go1-19/handshake_server_tls13.go | 35 +- .../qtls-go1-19/key_agreement.go | 2 +- vendor/golang.org/x/crypto/AUTHORS | 3 - vendor/golang.org/x/crypto/CONTRIBUTORS | 3 - .../x/crypto/curve25519/curve25519.go | 9 +- vendor/golang.org/x/crypto/ssh/certs.go | 2 + vendor/golang.org/x/sys/cpu/cpu_arm64.go | 7 +- .../golang.org/x/sys/cpu/cpu_openbsd_arm64.go | 65 + .../golang.org/x/sys/cpu/cpu_openbsd_arm64.s | 11 + .../golang.org/x/sys/cpu/cpu_other_arm64.go | 4 +- vendor/golang.org/x/sys/unix/mkall.sh | 6 +- vendor/golang.org/x/sys/unix/syscall_aix.go | 2 +- vendor/golang.org/x/sys/unix/syscall_bsd.go | 2 +- vendor/golang.org/x/sys/unix/syscall_linux.go | 2 +- .../x/sys/unix/zerrors_linux_386.go | 4 +- .../x/sys/unix/zerrors_linux_amd64.go | 4 +- .../x/sys/unix/zerrors_linux_arm.go | 4 +- .../x/sys/unix/zerrors_linux_arm64.go | 4 +- .../x/sys/unix/zerrors_linux_loong64.go | 4 +- .../x/sys/unix/zerrors_linux_mips.go | 4 +- .../x/sys/unix/zerrors_linux_mips64.go | 4 +- .../x/sys/unix/zerrors_linux_mips64le.go | 4 +- .../x/sys/unix/zerrors_linux_mipsle.go | 4 +- .../x/sys/unix/zerrors_linux_ppc.go | 4 +- .../x/sys/unix/zerrors_linux_ppc64.go | 4 +- .../x/sys/unix/zerrors_linux_ppc64le.go | 4 +- .../x/sys/unix/zerrors_linux_riscv64.go | 4 +- .../x/sys/unix/zerrors_linux_s390x.go | 4 +- .../x/sys/unix/zerrors_linux_sparc64.go | 4 +- .../x/sys/unix/zsyscall_darwin_amd64.1_13.s | 2 +- .../x/sys/unix/zsyscall_darwin_amd64.s | 2 +- .../x/sys/unix/zsyscall_darwin_arm64.1_13.s | 2 +- .../x/sys/unix/zsyscall_darwin_arm64.s | 2 +- .../x/sys/unix/zsysnum_linux_386.go | 2 +- .../x/sys/unix/zsysnum_linux_amd64.go | 2 +- .../x/sys/unix/zsysnum_linux_arm.go | 2 +- .../x/sys/unix/zsysnum_linux_arm64.go | 2 +- .../x/sys/unix/zsysnum_linux_loong64.go | 2 +- .../x/sys/unix/zsysnum_linux_mips.go | 2 +- .../x/sys/unix/zsysnum_linux_mips64.go | 2 +- .../x/sys/unix/zsysnum_linux_mips64le.go | 2 +- .../x/sys/unix/zsysnum_linux_mipsle.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc64.go | 2 +- .../x/sys/unix/zsysnum_linux_ppc64le.go | 2 +- .../x/sys/unix/zsysnum_linux_riscv64.go | 2 +- .../x/sys/unix/zsysnum_linux_s390x.go | 2 +- .../x/sys/unix/zsysnum_linux_sparc64.go | 2 +- vendor/golang.org/x/sys/unix/ztypes_linux.go | 8 + .../golang.org/x/sys/unix/ztypes_linux_386.go | 2 +- .../x/sys/unix/ztypes_linux_amd64.go | 2 +- .../golang.org/x/sys/unix/ztypes_linux_arm.go | 2 +- .../x/sys/unix/ztypes_linux_arm64.go | 2 +- .../x/sys/unix/ztypes_linux_loong64.go | 2 +- .../x/sys/unix/ztypes_linux_mips.go | 2 +- .../x/sys/unix/ztypes_linux_mips64.go | 2 +- .../x/sys/unix/ztypes_linux_mips64le.go | 2 +- .../x/sys/unix/ztypes_linux_mipsle.go | 2 +- .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 2 +- .../x/sys/unix/ztypes_linux_ppc64.go | 2 +- .../x/sys/unix/ztypes_linux_ppc64le.go | 2 +- .../x/sys/unix/ztypes_linux_riscv64.go | 2 +- .../x/sys/unix/ztypes_linux_s390x.go | 2 +- .../x/sys/unix/ztypes_linux_sparc64.go | 2 +- .../x/sys/windows/syscall_windows.go | 95 + .../x/sys/windows/zsyscall_windows.go | 9 + vendor/modules.txt | 32 +- 171 files changed, 15270 insertions(+), 196 deletions(-) create mode 100644 cmd/cloudflared/tunnel/fips.go create mode 100644 supervisor/pqtunnels.go create mode 100644 vendor/github.com/cloudflare/circl/LICENSE create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.h create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve_generic.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/curve_noasm.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/doc.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/key.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x25519/table.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.h create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve_generic.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/curve_noasm.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/doc.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/key.go create mode 100644 vendor/github.com/cloudflare/circl/dh/x448/table.go create mode 100644 vendor/github.com/cloudflare/circl/internal/conv/conv.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/doc.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/hashes.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/rc.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/sha3.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/sha3_s390x.s create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/shake.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/xor.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/xor_generic.go create mode 100644 vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go create mode 100644 vendor/github.com/cloudflare/circl/kem/hybrid/hybrid.go create mode 100644 vendor/github.com/cloudflare/circl/kem/hybrid/xkem.go create mode 100644 vendor/github.com/cloudflare/circl/kem/kem.go create mode 100644 vendor/github.com/cloudflare/circl/kem/kyber/kyber1024/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/kem/kyber/kyber512/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/kem/kyber/kyber768/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.h create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp_generic.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp25519/fp_noasm.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.h create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp_generic.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fp_noasm.go create mode 100644 vendor/github.com/cloudflare/circl/math/fp448/fuzzer.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.s create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/field.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/generic.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/ntt.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params/params.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/poly.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/sample.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/internal/common/stubs_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/cpapke.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/mat.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/params.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/vec.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/cpapke.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/mat.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/params.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/vec.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber512/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/cpapke.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/mat.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/params.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/vec.go create mode 100644 vendor/github.com/cloudflare/circl/pke/kyber/kyber768/kyber.go create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x.go create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.go create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.s create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.s create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4stubs_amd64.go create mode 100644 vendor/github.com/cloudflare/circl/simd/keccakf1600/fallback.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-18/cfkem.go create mode 100644 vendor/github.com/marten-seemann/qtls-go1-19/cfkem.go delete mode 100644 vendor/golang.org/x/crypto/AUTHORS delete mode 100644 vendor/golang.org/x/crypto/CONTRIBUTORS create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 36257cc7..f8930357 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "fmt" "io/ioutil" + mathRand "math/rand" "os" "path/filepath" "strings" @@ -220,6 +221,19 @@ func prepareTunnelConfig( ) transportProtocol := c.String("protocol") + + needPQ := c.Bool("post-quantum") + if needPQ { + if FipsEnabled { + return nil, nil, fmt.Errorf("post-quantum not supported in FIPS mode") + } + // Error if the user tries to force a non-quic transport protocol + if transportProtocol != connection.AutoSelectFlag && transportProtocol != connection.QUIC.String() { + return nil, nil, fmt.Errorf("post-quantum is only supported with the quic transport") + } + transportProtocol = connection.QUIC.String() + } + protocolFetcher := edgediscovery.ProtocolPercentage cfg := config.GetConfiguration() @@ -230,6 +244,9 @@ func prepareTunnelConfig( } log.Info().Msgf("Generated Connector ID: %s", clientUUID) features := append(c.StringSlice("features"), defaultFeatures...) + if needPQ { + features = append(features, supervisor.FeaturePostQuantum) + } if c.IsSet(TunnelTokenFlag) { if transportProtocol == connection.AutoSelectFlag { protocolFetcher = func() (edgediscovery.ProtocolPercents, error) { @@ -291,7 +308,7 @@ func prepareTunnelConfig( } warpRoutingEnabled := isWarpRoutingEnabled(cfg.WarpRouting, isNamedTunnel) - protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, protocolFetcher, supervisor.ResolveTTL, log) + protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, protocolFetcher, supervisor.ResolveTTL, log, c.Bool("post-quantum")) if err != nil { return nil, nil, err } @@ -330,6 +347,15 @@ func prepareTunnelConfig( return nil, nil, err } + var pqKexIdx int + if needPQ { + pqKexIdx = mathRand.Intn(len(supervisor.PQKexes)) + log.Info().Msgf( + "Using experimental hybrid post-quantum key agreement %s", + supervisor.PQKexNames[supervisor.PQKexes[pqKexIdx]], + ) + } + tunnelConfig := &supervisor.TunnelConfig{ GracePeriod: gracePeriod, ReplaceExisting: c.Bool("force"), @@ -355,6 +381,8 @@ func prepareTunnelConfig( MuxerConfig: muxerConfig, ProtocolSelector: protocolSelector, EdgeTLSConfigs: edgeTLSConfigs, + NeedPQ: needPQ, + PQKexIdx: pqKexIdx, } orchestratorConfig := &orchestration.Config{ Ingress: &ingressRules, diff --git a/cmd/cloudflared/tunnel/fips.go b/cmd/cloudflared/tunnel/fips.go new file mode 100644 index 00000000..03ae6d26 --- /dev/null +++ b/cmd/cloudflared/tunnel/fips.go @@ -0,0 +1,3 @@ +package tunnel + +var FipsEnabled bool diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index d3549856..71435465 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -140,6 +140,13 @@ var ( EnvVars: []string{"TUNNEL_TRANSPORT_PROTOCOL"}, Hidden: true, }) + postQuantumFlag = altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "post-quantum", + Usage: "When given creates an experimental post-quantum secure tunnel", + Aliases: []string{"pq"}, + EnvVars: []string{"TUNNEL_POST_QUANTUM"}, + Hidden: FipsEnabled, + }) sortInfoByFlag = &cli.StringFlag{ Name: "sort-by", Value: "createdAt", @@ -602,6 +609,7 @@ func buildRunCommand() *cli.Command { forceFlag, credentialsFileFlag, credentialsContentsFlag, + postQuantumFlag, selectProtocolFlag, featuresFlag, tunnelTokenFlag, diff --git a/connection/protocol.go b/connection/protocol.go index b8b7d5ce..ddf9bb76 100644 --- a/connection/protocol.go +++ b/connection/protocol.go @@ -1,6 +1,7 @@ package connection import ( + "errors" "fmt" "hash/fnv" "sync" @@ -130,6 +131,7 @@ type autoProtocolSelector struct { refreshAfter time.Time ttl time.Duration log *zerolog.Logger + needPQ bool } func newAutoProtocolSelector( @@ -139,6 +141,7 @@ func newAutoProtocolSelector( fetchFunc PercentageFetcher, ttl time.Duration, log *zerolog.Logger, + needPQ bool, ) *autoProtocolSelector { return &autoProtocolSelector{ current: current, @@ -148,6 +151,7 @@ func newAutoProtocolSelector( refreshAfter: time.Now().Add(ttl), ttl: ttl, log: log, + needPQ: needPQ, } } @@ -187,6 +191,9 @@ func getProtocol(protocolPool []Protocol, fetchFunc PercentageFetcher, switchThr func (s *autoProtocolSelector) Fallback() (Protocol, bool) { s.lock.RLock() defer s.lock.RUnlock() + if s.needPQ { + return 0, false + } return s.current.fallback() } @@ -199,9 +206,14 @@ func NewProtocolSelector( fetchFunc PercentageFetcher, ttl time.Duration, log *zerolog.Logger, + needPQ bool, ) (ProtocolSelector, error) { // Classic tunnel is only supported with h2mux if namedTunnel == nil { + if needPQ { + return nil, errors.New("Classic tunnel does not support post-quantum") + } + return &staticProtocolSelector{ current: H2mux, }, nil @@ -211,6 +223,9 @@ func NewProtocolSelector( fetchedProtocol, err := getProtocol([]Protocol{QUIC, HTTP2}, fetchFunc, threshold) if err != nil && protocolFlag == "auto" { log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can attempt `--protocol quic` instead.") + if needPQ { + return nil, errors.New("http2 does not support post-quantum") + } return &staticProtocolSelector{ current: HTTP2, }, nil @@ -221,10 +236,10 @@ func NewProtocolSelector( protocolFlag = HTTP2.String() fetchedProtocol = HTTP2Warp } - return selectWarpRoutingProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol) + return selectWarpRoutingProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol, needPQ) } - return selectNamedTunnelProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol) + return selectNamedTunnelProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol, needPQ) } func selectNamedTunnelProtocols( @@ -234,6 +249,7 @@ func selectNamedTunnelProtocols( log *zerolog.Logger, threshold int32, protocol Protocol, + needPQ bool, ) (ProtocolSelector, error) { // If the user picks a protocol, then we stick to it no matter what. switch protocolFlag { @@ -248,7 +264,7 @@ func selectNamedTunnelProtocols( // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and // fallback on failures. if protocolFlag == AutoSelectFlag { - return newAutoProtocolSelector(protocol, []Protocol{QUIC, HTTP2, H2mux}, threshold, fetchFunc, ttl, log), nil + return newAutoProtocolSelector(protocol, []Protocol{QUIC, HTTP2, H2mux}, threshold, fetchFunc, ttl, log, needPQ), nil } return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage) @@ -261,6 +277,7 @@ func selectWarpRoutingProtocols( log *zerolog.Logger, threshold int32, protocol Protocol, + needPQ bool, ) (ProtocolSelector, error) { // If the user picks a protocol, then we stick to it no matter what. switch protocolFlag { @@ -273,7 +290,7 @@ func selectWarpRoutingProtocols( // If the user does not pick (hopefully the majority) then we use the one derived from the TXT DNS record and // fallback on failures. if protocolFlag == AutoSelectFlag { - return newAutoProtocolSelector(protocol, []Protocol{QUICWarp, HTTP2Warp}, threshold, fetchFunc, ttl, log), nil + return newAutoProtocolSelector(protocol, []Protocol{QUICWarp, HTTP2Warp}, threshold, fetchFunc, ttl, log, needPQ), nil } return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage) diff --git a/connection/protocol_test.go b/connection/protocol_test.go index 9eef7b6c..b3bda44a 100644 --- a/connection/protocol_test.go +++ b/connection/protocol_test.go @@ -219,7 +219,7 @@ func TestNewProtocolSelector(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - selector, err := NewProtocolSelector(test.protocol, test.warpRoutingEnabled, test.namedTunnelConfig, test.fetchFunc, testNoTTL, &log) + selector, err := NewProtocolSelector(test.protocol, test.warpRoutingEnabled, test.namedTunnelConfig, test.fetchFunc, testNoTTL, &log, false) if test.wantErr { assert.Error(t, err, fmt.Sprintf("test %s failed", test.name)) } else { @@ -237,7 +237,7 @@ func TestNewProtocolSelector(t *testing.T) { func TestAutoProtocolSelectorRefresh(t *testing.T) { fetcher := dynamicMockFetcher{} - selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log) + selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log, false) assert.NoError(t, err) assert.Equal(t, H2mux, selector.Current()) @@ -267,7 +267,7 @@ func TestAutoProtocolSelectorRefresh(t *testing.T) { func TestHTTP2ProtocolSelectorRefresh(t *testing.T) { fetcher := dynamicMockFetcher{} // Since the user chooses http2 on purpose, we always stick to it. - selector, err := NewProtocolSelector("http2", noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log) + selector, err := NewProtocolSelector("http2", noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), testNoTTL, &log, false) assert.NoError(t, err) assert.Equal(t, HTTP2, selector.Current()) @@ -297,7 +297,7 @@ func TestHTTP2ProtocolSelectorRefresh(t *testing.T) { func TestProtocolSelectorRefreshTTL(t *testing.T) { fetcher := dynamicMockFetcher{} fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}} - selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), time.Hour, &log) + selector, err := NewProtocolSelector(AutoSelectFlag, noWarpRoutingEnabled, testNamedTunnelProperties, fetcher.fetch(), time.Hour, &log, false) assert.NoError(t, err) assert.Equal(t, QUIC, selector.Current()) diff --git a/fips/fips.go.linux-amd64 b/fips/fips.go.linux-amd64 index e6b3585b..5075f298 100644 --- a/fips/fips.go.linux-amd64 +++ b/fips/fips.go.linux-amd64 @@ -2,4 +2,11 @@ package main -import _ "crypto/tls/fipsonly" +import ( + _ "crypto/tls/fipsonly" + "github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel" +) + +func init () { + tunnel.FipsEnabled = true +} diff --git a/go.mod b/go.mod index 3cec487f..02c0bf76 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/getsentry/raven-go v0.0.0-20180517221441-ed7bcb39ff10 github.com/gobwas/ws v1.0.4 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 + github.com/google/gopacket v1.1.19 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 @@ -33,10 +34,10 @@ require ( go.opentelemetry.io/otel/trace v1.6.3 go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 - golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/net v0.0.0-20220812174116-3211cb980234 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 + golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/protobuf v1.28.0 gopkg.in/coreos/go-oidc.v2 v2.2.1 @@ -53,6 +54,7 @@ require ( github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cheekybits/genny v1.0.0 // indirect + github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 // indirect github.com/coredns/caddy v1.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -67,7 +69,6 @@ require ( github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gopacket v1.1.19 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -107,3 +108,12 @@ replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.2 replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 + +// Post-quantum tunnel RTG-1339 +replace ( + // branch go1.18 + github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af + + // branch go1.19 + github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af +) diff --git a/go.sum b/go.sum index 2c4d11fb..7f2012a8 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bwesterb/go-ristretto v1.2.2/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA= @@ -117,8 +118,14 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 h1:QrGfkZDnMxcWHaYDdB7CmqS9i26OAnUj/xcus/abYkY= github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93/go.mod h1:QiTe66jFdP7cUKMCCf/WrvDyYdtdmdZfVcdoLbzaKVY= +github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 h1:YzpECHxZ9TzO7LpnKmPxItSd79lLgrR5heIlnqU4dTU= +github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47/go.mod h1:qhx8gBILsYlbam7h09SvHDSkjpe3TfLA7b/z4rxJvkE= github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc h1:Dvk3ySBsOm5EviLx6VCyILnafPcQinXGP5jbTdHUJgE= github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc/go.mod h1:HlgKKR8V5a1wroIDDIz3/A+T+9Janfq+7n1P5sEFdi0= +github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af h1:JMpOQAaXjRRBkUX73fTNe9mConJLFl6FsIp9fHdLm7Y= +github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af/go.mod h1:aIsWqC0WXyUiUxBl/RfxAjDyWE9CCLqvSMnCMTd/+bc= +github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af h1:bhCmedjwrOSyzLtHVeQ+KhimcNTSfs0P5T7kbRQS+gA= +github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af/go.mod h1:mW0BgKFFDAiSmOdUwoORtjo0V2vqw5QzVYRtKQqw/Jg= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -401,10 +408,6 @@ github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtU github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32BZqe/LEEnBrWcH/cOqQ= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -621,8 +624,8 @@ golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -708,9 +711,9 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -818,10 +821,9 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= +golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/supervisor/pqtunnels.go b/supervisor/pqtunnels.go new file mode 100644 index 00000000..f682828c --- /dev/null +++ b/supervisor/pqtunnels.go @@ -0,0 +1,100 @@ +package supervisor + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "net/http" + "sync" +) + +// When experimental post-quantum tunnels are enabled, and we're hitting an +// issue creating the tunnel, we'll report the first error +// to https://pqtunnels.cloudflareresearch.com. + +var ( + PQKexes = [...]tls.CurveID{ + tls.CurveID(0xfe30), // X25519Kyber512Draft00 + tls.CurveID(0xfe31), // X25519Kyber768Draft00 + } + PQKexNames map[tls.CurveID]string = map[tls.CurveID]string{ + tls.CurveID(0xfe30): "X25519Kyber512Draft00", + tls.CurveID(0xfe31): "X25519Kyber768Draft00", + } + + pqtMux sync.Mutex // protects pqtSubmitted and pqtWaitForMessage + pqtSubmitted bool // whether an error has already been submitted + + // Number of errors to ignore before printing elaborate instructions. + pqtWaitForMessage int +) + +func handlePQTunnelError(rep error, config *TunnelConfig) { + needToMessage := false + + pqtMux.Lock() + needToSubmit := !pqtSubmitted + if needToSubmit { + pqtSubmitted = true + } + pqtWaitForMessage-- + if pqtWaitForMessage < 0 { + pqtWaitForMessage = 5 + needToMessage = true + } + pqtMux.Unlock() + + if needToMessage { + config.Log.Info().Msgf( + "\n\n" + + "===================================================================================\n" + + "You are hitting an error while using the experimental post-quantum tunnels feature.\n" + + "\n" + + "Please check:\n" + + "\n" + + " https://pqtunnels.cloudflareresearch.com\n" + + "\n" + + "for known problems.\n" + + "===================================================================================\n\n", + ) + } + + if needToSubmit { + go submitPQTunnelError(rep, config) + } +} + +func submitPQTunnelError(rep error, config *TunnelConfig) { + body, err := json.Marshal(struct { + Group int `json:"g"` + Message string `json:"m"` + Version string `json:"v"` + }{ + Group: int(PQKexes[config.PQKexIdx]), + Message: rep.Error(), + Version: config.ReportedVersion, + }) + if err != nil { + config.Log.Err(err).Msg("Failed to create error report") + return + } + + resp, err := http.Post( + "https://pqtunnels.cloudflareresearch.com", + "application/json", + bytes.NewBuffer(body), + ) + if err != nil { + config.Log.Err(err).Msg( + "Failed to submit post-quantum tunnel error report", + ) + return + } + if resp.StatusCode != 200 { + config.Log.Error().Msgf( + "Failed to submit post-quantum tunnel error report: status %d", + resp.StatusCode, + ) + } + resp.Body.Close() +} diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 9b6f0db4..34f6cecc 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -36,6 +36,7 @@ const ( FeatureQuickReconnects = "quick_reconnects" FeatureAllowRemoteConfig = "allow_remote_config" FeatureDatagramV2 = "support_datagram_v2" + FeaturePostQuantum = "postquantum" ) type TunnelConfig struct { @@ -59,6 +60,11 @@ type TunnelConfig struct { Retries uint RunFromTerminal bool + NeedPQ bool + + // Index into PQKexes of post-quantum kex to use if NeedPQ is set. + PQKexIdx int + NamedTunnel *connection.NamedTunnelProperties ClassicTunnel *connection.ClassicTunnelProperties MuxerConfig *connection.MuxerConfig @@ -524,6 +530,9 @@ func (e *EdgeTunnelServer) serveH2mux( connIndex uint8, connectedFuse *connectedFuse, ) error { + if e.config.NeedPQ { + return unrecoverableError{errors.New("H2Mux transport does not support post-quantum")} + } connLog.Logger().Debug().Msgf("Connecting via h2mux") // Returns error from parsing the origin URL or handshake errors handler, err, recoverable := connection.NewH2muxConnection( @@ -575,6 +584,10 @@ func (e *EdgeTunnelServer) serveHTTP2( controlStreamHandler connection.ControlStreamHandler, connIndex uint8, ) error { + if e.config.NeedPQ { + return unrecoverableError{errors.New("HTTP/2 transport does not support post-quantum")} + } + connLog.Logger().Debug().Msgf("Connecting via http2") h2conn := connection.NewHTTP2Connection( tlsServerConn, @@ -613,6 +626,22 @@ func (e *EdgeTunnelServer) serveQUIC( connIndex uint8, ) (err error, recoverable bool) { tlsConfig := e.config.EdgeTLSConfigs[connection.QUIC] + + if e.config.NeedPQ { + // If the user passes the -post-quantum flag, we override + // CurvePreferences to only support hybrid post-quantum key agreements. + cs := make([]tls.CurveID, len(PQKexes)) + copy(cs, PQKexes[:]) + + // It is unclear whether Kyber512 or Kyber768 will become the standard. + // Kyber768 is a bit bigger (and doesn't fit in one initial + // datagram anymore). We're enabling both, but pick randomly which + // one to put first. (TLS will use the first one in the list + // and allows a fallback to the second.) + cs[0], cs[e.config.PQKexIdx] = cs[e.config.PQKexIdx], cs[0] + tlsConfig.CurvePreferences = cs + } + quicConfig := &quic.Config{ HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout, MaxIdleTimeout: quicpogs.MaxIdleTimeout, @@ -634,6 +663,10 @@ func (e *EdgeTunnelServer) serveQUIC( connLogger.Logger(), e.icmpProxy) if err != nil { + if e.config.NeedPQ { + handlePQTunnelError(err, e.config) + } + connLogger.ConnAwareLogger().Err(err).Msgf("Failed to create new quic connection") return err, true } diff --git a/supervisor/tunnel_test.go b/supervisor/tunnel_test.go index 2e646089..3f9ae62c 100644 --- a/supervisor/tunnel_test.go +++ b/supervisor/tunnel_test.go @@ -44,6 +44,7 @@ func TestWaitForBackoffFallback(t *testing.T) { mockFetcher.fetch(), resolveTTL, &log, + false, ) assert.NoError(t, err) @@ -104,6 +105,7 @@ func TestWaitForBackoffFallback(t *testing.T) { mockFetcher.fetch(), resolveTTL, &log, + false, ) assert.NoError(t, err) protoFallback = &protocolFallback{backoff, protocolSelector.Current(), false} diff --git a/vendor/github.com/cloudflare/circl/LICENSE b/vendor/github.com/cloudflare/circl/LICENSE new file mode 100644 index 00000000..67edaa90 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/LICENSE @@ -0,0 +1,57 @@ +Copyright (c) 2019 Cloudflare. 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 Cloudflare 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. + +======================================================================== + +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/cloudflare/circl/dh/x25519/curve.go b/vendor/github.com/cloudflare/circl/dh/x25519/curve.go new file mode 100644 index 00000000..f9057c2b --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve.go @@ -0,0 +1,96 @@ +package x25519 + +import ( + fp "github.com/cloudflare/circl/math/fp25519" +) + +// ladderJoye calculates a fixed-point multiplication with the generator point. +// The algorithm is the right-to-left Joye's ladder as described +// in "How to precompute a ladder" in SAC'2017. +func ladderJoye(k *Key) { + w := [5]fp.Elt{} // [mu,x1,z1,x2,z2] order must be preserved. + fp.SetOne(&w[1]) // x1 = 1 + fp.SetOne(&w[2]) // z1 = 1 + w[3] = fp.Elt{ // x2 = G-S + 0xbd, 0xaa, 0x2f, 0xc8, 0xfe, 0xe1, 0x94, 0x7e, + 0xf8, 0xed, 0xb2, 0x14, 0xae, 0x95, 0xf0, 0xbb, + 0xe2, 0x48, 0x5d, 0x23, 0xb9, 0xa0, 0xc7, 0xad, + 0x34, 0xab, 0x7c, 0xe2, 0xee, 0xcd, 0xae, 0x1e, + } + fp.SetOne(&w[4]) // z2 = 1 + + const n = 255 + const h = 3 + swap := uint(1) + for s := 0; s < n-h; s++ { + i := (s + h) / 8 + j := (s + h) % 8 + bit := uint((k[i] >> uint(j)) & 1) + copy(w[0][:], tableGenerator[s*Size:(s+1)*Size]) + diffAdd(&w, swap^bit) + swap = bit + } + for s := 0; s < h; s++ { + double(&w[1], &w[2]) + } + toAffine((*[fp.Size]byte)(k), &w[1], &w[2]) +} + +// ladderMontgomery calculates a generic scalar point multiplication +// The algorithm implemented is the left-to-right Montgomery's ladder. +func ladderMontgomery(k, xP *Key) { + w := [5]fp.Elt{} // [x1, x2, z2, x3, z3] order must be preserved. + w[0] = *(*fp.Elt)(xP) // x1 = xP + fp.SetOne(&w[1]) // x2 = 1 + w[3] = *(*fp.Elt)(xP) // x3 = xP + fp.SetOne(&w[4]) // z3 = 1 + + move := uint(0) + for s := 255 - 1; s >= 0; s-- { + i := s / 8 + j := s % 8 + bit := uint((k[i] >> uint(j)) & 1) + ladderStep(&w, move^bit) + move = bit + } + toAffine((*[fp.Size]byte)(k), &w[1], &w[2]) +} + +func toAffine(k *[fp.Size]byte, x, z *fp.Elt) { + fp.Inv(z, z) + fp.Mul(x, x, z) + _ = fp.ToBytes(k[:], x) +} + +var lowOrderPoints = [5]fp.Elt{ + { /* (0,_,1) point of order 2 on Curve25519 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + { /* (1,_,1) point of order 4 on Curve25519 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + { /* (x,_,1) first point of order 8 on Curve25519 */ + 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, + 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, + 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, + 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00, + }, + { /* (x,_,1) second point of order 8 on Curve25519 */ + 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, + 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, + 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, + 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, + }, + { /* (-1,_,1) a point of order 4 on the twist of Curve25519 */ + 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + }, +} diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.go b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.go new file mode 100644 index 00000000..8a3d54c5 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.go @@ -0,0 +1,30 @@ +//go:build amd64 && !purego +// +build amd64,!purego + +package x25519 + +import ( + fp "github.com/cloudflare/circl/math/fp25519" + "golang.org/x/sys/cpu" +) + +var hasBmi2Adx = cpu.X86.HasBMI2 && cpu.X86.HasADX + +var _ = hasBmi2Adx + +func double(x, z *fp.Elt) { doubleAmd64(x, z) } +func diffAdd(w *[5]fp.Elt, b uint) { diffAddAmd64(w, b) } +func ladderStep(w *[5]fp.Elt, b uint) { ladderStepAmd64(w, b) } +func mulA24(z, x *fp.Elt) { mulA24Amd64(z, x) } + +//go:noescape +func ladderStepAmd64(w *[5]fp.Elt, b uint) + +//go:noescape +func diffAddAmd64(w *[5]fp.Elt, b uint) + +//go:noescape +func doubleAmd64(x, z *fp.Elt) + +//go:noescape +func mulA24Amd64(z, x *fp.Elt) diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.h b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.h new file mode 100644 index 00000000..8c1ae4d0 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.h @@ -0,0 +1,111 @@ +#define ladderStepLeg \ + addSub(x2,z2) \ + addSub(x3,z3) \ + integerMulLeg(b0,x2,z3) \ + integerMulLeg(b1,x3,z2) \ + reduceFromDoubleLeg(t0,b0) \ + reduceFromDoubleLeg(t1,b1) \ + addSub(t0,t1) \ + cselect(x2,x3,regMove) \ + cselect(z2,z3,regMove) \ + integerSqrLeg(b0,t0) \ + integerSqrLeg(b1,t1) \ + reduceFromDoubleLeg(x3,b0) \ + reduceFromDoubleLeg(z3,b1) \ + integerMulLeg(b0,x1,z3) \ + reduceFromDoubleLeg(z3,b0) \ + integerSqrLeg(b0,x2) \ + integerSqrLeg(b1,z2) \ + reduceFromDoubleLeg(x2,b0) \ + reduceFromDoubleLeg(z2,b1) \ + subtraction(t0,x2,z2) \ + multiplyA24Leg(t1,t0) \ + additionLeg(t1,t1,z2) \ + integerMulLeg(b0,x2,z2) \ + integerMulLeg(b1,t0,t1) \ + reduceFromDoubleLeg(x2,b0) \ + reduceFromDoubleLeg(z2,b1) + +#define ladderStepBmi2Adx \ + addSub(x2,z2) \ + addSub(x3,z3) \ + integerMulAdx(b0,x2,z3) \ + integerMulAdx(b1,x3,z2) \ + reduceFromDoubleAdx(t0,b0) \ + reduceFromDoubleAdx(t1,b1) \ + addSub(t0,t1) \ + cselect(x2,x3,regMove) \ + cselect(z2,z3,regMove) \ + integerSqrAdx(b0,t0) \ + integerSqrAdx(b1,t1) \ + reduceFromDoubleAdx(x3,b0) \ + reduceFromDoubleAdx(z3,b1) \ + integerMulAdx(b0,x1,z3) \ + reduceFromDoubleAdx(z3,b0) \ + integerSqrAdx(b0,x2) \ + integerSqrAdx(b1,z2) \ + reduceFromDoubleAdx(x2,b0) \ + reduceFromDoubleAdx(z2,b1) \ + subtraction(t0,x2,z2) \ + multiplyA24Adx(t1,t0) \ + additionAdx(t1,t1,z2) \ + integerMulAdx(b0,x2,z2) \ + integerMulAdx(b1,t0,t1) \ + reduceFromDoubleAdx(x2,b0) \ + reduceFromDoubleAdx(z2,b1) + +#define difAddLeg \ + addSub(x1,z1) \ + integerMulLeg(b0,z1,ui) \ + reduceFromDoubleLeg(z1,b0) \ + addSub(x1,z1) \ + integerSqrLeg(b0,x1) \ + integerSqrLeg(b1,z1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) \ + integerMulLeg(b0,x1,z2) \ + integerMulLeg(b1,z1,x2) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) + +#define difAddBmi2Adx \ + addSub(x1,z1) \ + integerMulAdx(b0,z1,ui) \ + reduceFromDoubleAdx(z1,b0) \ + addSub(x1,z1) \ + integerSqrAdx(b0,x1) \ + integerSqrAdx(b1,z1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) \ + integerMulAdx(b0,x1,z2) \ + integerMulAdx(b1,z1,x2) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) + +#define doubleLeg \ + addSub(x1,z1) \ + integerSqrLeg(b0,x1) \ + integerSqrLeg(b1,z1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) \ + subtraction(t0,x1,z1) \ + multiplyA24Leg(t1,t0) \ + additionLeg(t1,t1,z1) \ + integerMulLeg(b0,x1,z1) \ + integerMulLeg(b1,t0,t1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) + +#define doubleBmi2Adx \ + addSub(x1,z1) \ + integerSqrAdx(b0,x1) \ + integerSqrAdx(b1,z1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) \ + subtraction(t0,x1,z1) \ + multiplyA24Adx(t1,t0) \ + additionAdx(t1,t1,z1) \ + integerMulAdx(b0,x1,z1) \ + integerMulAdx(b1,t0,t1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s new file mode 100644 index 00000000..b7723185 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s @@ -0,0 +1,156 @@ +// +build amd64 + +#include "textflag.h" + +// Depends on circl/math/fp25519 package +#include "../../math/fp25519/fp_amd64.h" +#include "curve_amd64.h" + +// CTE_A24 is (A+2)/4 from Curve25519 +#define CTE_A24 121666 + +#define Size 32 + +// multiplyA24Leg multiplies x times CTE_A24 and stores in z +// Uses: AX, DX, R8-R13, FLAGS +// Instr: x86_64, cmov +#define multiplyA24Leg(z,x) \ + MOVL $CTE_A24, AX; MULQ 0+x; MOVQ AX, R8; MOVQ DX, R9; \ + MOVL $CTE_A24, AX; MULQ 8+x; MOVQ AX, R12; MOVQ DX, R10; \ + MOVL $CTE_A24, AX; MULQ 16+x; MOVQ AX, R13; MOVQ DX, R11; \ + MOVL $CTE_A24, AX; MULQ 24+x; \ + ADDQ R12, R9; \ + ADCQ R13, R10; \ + ADCQ AX, R11; \ + ADCQ $0, DX; \ + MOVL $38, AX; /* 2*C = 38 = 2^256 MOD 2^255-19*/ \ + IMULQ AX, DX; \ + ADDQ DX, R8; \ + ADCQ $0, R9; MOVQ R9, 8+z; \ + ADCQ $0, R10; MOVQ R10, 16+z; \ + ADCQ $0, R11; MOVQ R11, 24+z; \ + MOVQ $0, DX; \ + CMOVQCS AX, DX; \ + ADDQ DX, R8; MOVQ R8, 0+z; + +// multiplyA24Adx multiplies x times CTE_A24 and stores in z +// Uses: AX, DX, R8-R12, FLAGS +// Instr: x86_64, cmov, bmi2 +#define multiplyA24Adx(z,x) \ + MOVQ $CTE_A24, DX; \ + MULXQ 0+x, R8, R10; \ + MULXQ 8+x, R9, R11; ADDQ R10, R9; \ + MULXQ 16+x, R10, AX; ADCQ R11, R10; \ + MULXQ 24+x, R11, R12; ADCQ AX, R11; \ + ;;;;;;;;;;;;;;;;;;;;; ADCQ $0, R12; \ + MOVL $38, DX; /* 2*C = 38 = 2^256 MOD 2^255-19*/ \ + IMULQ DX, R12; \ + ADDQ R12, R8; \ + ADCQ $0, R9; MOVQ R9, 8+z; \ + ADCQ $0, R10; MOVQ R10, 16+z; \ + ADCQ $0, R11; MOVQ R11, 24+z; \ + MOVQ $0, R12; \ + CMOVQCS DX, R12; \ + ADDQ R12, R8; MOVQ R8, 0+z; + +#define mulA24Legacy \ + multiplyA24Leg(0(DI),0(SI)) +#define mulA24Bmi2Adx \ + multiplyA24Adx(0(DI),0(SI)) + +// func mulA24Amd64(z, x *fp255.Elt) +TEXT ·mulA24Amd64(SB),NOSPLIT,$0-16 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + CHECK_BMI2ADX(LMA24, mulA24Legacy, mulA24Bmi2Adx) + + +// func ladderStepAmd64(w *[5]fp255.Elt, b uint) +// ladderStepAmd64 calculates a point addition and doubling as follows: +// (x2,z2) = 2*(x2,z2) and (x3,z3) = (x2,z2)+(x3,z3) using as a difference (x1,-). +// work = (x1,x2,z2,x3,z3) are five fp255.Elt of 32 bytes. +// stack = (t0,t1) are two fp.Elt of fp.Size bytes, and +// (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +TEXT ·ladderStepAmd64(SB),NOSPLIT,$192-16 + // Parameters + #define regWork DI + #define regMove SI + #define x1 0*Size(regWork) + #define x2 1*Size(regWork) + #define z2 2*Size(regWork) + #define x3 3*Size(regWork) + #define z3 4*Size(regWork) + // Local variables + #define t0 0*Size(SP) + #define t1 1*Size(SP) + #define b0 2*Size(SP) + #define b1 4*Size(SP) + MOVQ w+0(FP), regWork + MOVQ b+8(FP), regMove + CHECK_BMI2ADX(LLADSTEP, ladderStepLeg, ladderStepBmi2Adx) + #undef regWork + #undef regMove + #undef x1 + #undef x2 + #undef z2 + #undef x3 + #undef z3 + #undef t0 + #undef t1 + #undef b0 + #undef b1 + +// func diffAddAmd64(w *[5]fp255.Elt, b uint) +// diffAddAmd64 calculates a differential point addition using a precomputed point. +// (x1,z1) = (x1,z1)+(mu) using a difference point (x2,z2) +// w = (mu,x1,z1,x2,z2) are five fp.Elt, and +// stack = (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +TEXT ·diffAddAmd64(SB),NOSPLIT,$128-16 + // Parameters + #define regWork DI + #define regSwap SI + #define ui 0*Size(regWork) + #define x1 1*Size(regWork) + #define z1 2*Size(regWork) + #define x2 3*Size(regWork) + #define z2 4*Size(regWork) + // Local variables + #define b0 0*Size(SP) + #define b1 2*Size(SP) + MOVQ w+0(FP), regWork + MOVQ b+8(FP), regSwap + cswap(x1,x2,regSwap) + cswap(z1,z2,regSwap) + CHECK_BMI2ADX(LDIFADD, difAddLeg, difAddBmi2Adx) + #undef regWork + #undef regSwap + #undef ui + #undef x1 + #undef z1 + #undef x2 + #undef z2 + #undef b0 + #undef b1 + +// func doubleAmd64(x, z *fp255.Elt) +// doubleAmd64 calculates a point doubling (x1,z1) = 2*(x1,z1). +// stack = (t0,t1) are two fp.Elt of fp.Size bytes, and +// (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +TEXT ·doubleAmd64(SB),NOSPLIT,$192-16 + // Parameters + #define x1 0(DI) + #define z1 0(SI) + // Local variables + #define t0 0*Size(SP) + #define t1 1*Size(SP) + #define b0 2*Size(SP) + #define b1 4*Size(SP) + MOVQ x+0(FP), DI + MOVQ z+8(FP), SI + CHECK_BMI2ADX(LDOUB,doubleLeg,doubleBmi2Adx) + #undef x1 + #undef z1 + #undef t0 + #undef t1 + #undef b0 + #undef b1 diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_generic.go b/vendor/github.com/cloudflare/circl/dh/x25519/curve_generic.go new file mode 100644 index 00000000..dae67ea3 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_generic.go @@ -0,0 +1,85 @@ +package x25519 + +import ( + "encoding/binary" + "math/bits" + + fp "github.com/cloudflare/circl/math/fp25519" +) + +func doubleGeneric(x, z *fp.Elt) { + t0, t1 := &fp.Elt{}, &fp.Elt{} + fp.AddSub(x, z) + fp.Sqr(x, x) + fp.Sqr(z, z) + fp.Sub(t0, x, z) + mulA24Generic(t1, t0) + fp.Add(t1, t1, z) + fp.Mul(x, x, z) + fp.Mul(z, t0, t1) +} + +func diffAddGeneric(w *[5]fp.Elt, b uint) { + mu, x1, z1, x2, z2 := &w[0], &w[1], &w[2], &w[3], &w[4] + fp.Cswap(x1, x2, b) + fp.Cswap(z1, z2, b) + fp.AddSub(x1, z1) + fp.Mul(z1, z1, mu) + fp.AddSub(x1, z1) + fp.Sqr(x1, x1) + fp.Sqr(z1, z1) + fp.Mul(x1, x1, z2) + fp.Mul(z1, z1, x2) +} + +func ladderStepGeneric(w *[5]fp.Elt, b uint) { + x1, x2, z2, x3, z3 := &w[0], &w[1], &w[2], &w[3], &w[4] + t0 := &fp.Elt{} + t1 := &fp.Elt{} + fp.AddSub(x2, z2) + fp.AddSub(x3, z3) + fp.Mul(t0, x2, z3) + fp.Mul(t1, x3, z2) + fp.AddSub(t0, t1) + fp.Cmov(x2, x3, b) + fp.Cmov(z2, z3, b) + fp.Sqr(x3, t0) + fp.Sqr(z3, t1) + fp.Mul(z3, x1, z3) + fp.Sqr(x2, x2) + fp.Sqr(z2, z2) + fp.Sub(t0, x2, z2) + mulA24Generic(t1, t0) + fp.Add(t1, t1, z2) + fp.Mul(x2, x2, z2) + fp.Mul(z2, t0, t1) +} + +func mulA24Generic(z, x *fp.Elt) { + const A24 = 121666 + const n = 8 + var xx [4]uint64 + for i := range xx { + xx[i] = binary.LittleEndian.Uint64(x[i*n : (i+1)*n]) + } + + h0, l0 := bits.Mul64(xx[0], A24) + h1, l1 := bits.Mul64(xx[1], A24) + h2, l2 := bits.Mul64(xx[2], A24) + h3, l3 := bits.Mul64(xx[3], A24) + + var c3 uint64 + l1, c0 := bits.Add64(h0, l1, 0) + l2, c1 := bits.Add64(h1, l2, c0) + l3, c2 := bits.Add64(h2, l3, c1) + l4, _ := bits.Add64(h3, 0, c2) + _, l4 = bits.Mul64(l4, 38) + l0, c0 = bits.Add64(l0, l4, 0) + xx[1], c1 = bits.Add64(l1, 0, c0) + xx[2], c2 = bits.Add64(l2, 0, c1) + xx[3], c3 = bits.Add64(l3, 0, c2) + xx[0], _ = bits.Add64(l0, (-c3)&38, 0) + for i := range xx { + binary.LittleEndian.PutUint64(z[i*n:(i+1)*n], xx[i]) + } +} diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_noasm.go b/vendor/github.com/cloudflare/circl/dh/x25519/curve_noasm.go new file mode 100644 index 00000000..07fab97d --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_noasm.go @@ -0,0 +1,11 @@ +//go:build !amd64 || purego +// +build !amd64 purego + +package x25519 + +import fp "github.com/cloudflare/circl/math/fp25519" + +func double(x, z *fp.Elt) { doubleGeneric(x, z) } +func diffAdd(w *[5]fp.Elt, b uint) { diffAddGeneric(w, b) } +func ladderStep(w *[5]fp.Elt, b uint) { ladderStepGeneric(w, b) } +func mulA24(z, x *fp.Elt) { mulA24Generic(z, x) } diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/doc.go b/vendor/github.com/cloudflare/circl/dh/x25519/doc.go new file mode 100644 index 00000000..3ce102d1 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/doc.go @@ -0,0 +1,19 @@ +/* +Package x25519 provides Diffie-Hellman functions as specified in RFC-7748. + +Validation of public keys. + +The Diffie-Hellman function, as described in RFC-7748 [1], works for any +public key. However, if a different protocol requires contributory +behaviour [2,3], then the public keys must be validated against low-order +points [3,4]. To do that, the Shared function performs this validation +internally and returns false when the public key is invalid (i.e., it +is a low-order point). + +References: + - [1] RFC7748 by Langley, Hamburg, Turner (https://rfc-editor.org/rfc/rfc7748.txt) + - [2] Curve25519 by Bernstein (https://cr.yp.to/ecdh.html) + - [3] Bernstein (https://cr.yp.to/ecdh.html#validate) + - [4] Cremers&Jackson (https://eprint.iacr.org/2019/526) +*/ +package x25519 diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/key.go b/vendor/github.com/cloudflare/circl/dh/x25519/key.go new file mode 100644 index 00000000..bbd37ddd --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/key.go @@ -0,0 +1,47 @@ +package x25519 + +import ( + "crypto/subtle" + + fp "github.com/cloudflare/circl/math/fp25519" +) + +// Size is the length in bytes of a X25519 key. +const Size = 32 + +// Key represents a X25519 key. +type Key [Size]byte + +func (k *Key) clamp(in *Key) *Key { + *k = *in + k[0] &= 248 + k[31] = (k[31] & 127) | 64 + return k +} + +// isValidPubKey verifies if the public key is not a low-order point. +func (k *Key) isValidPubKey() bool { + fp.Modp((*fp.Elt)(k)) + isLowOrder := false + for _, P := range lowOrderPoints { + isLowOrder = isLowOrder || subtle.ConstantTimeCompare(P[:], k[:]) != 0 + } + return !isLowOrder +} + +// KeyGen obtains a public key given a secret key. +func KeyGen(public, secret *Key) { + ladderJoye(public.clamp(secret)) +} + +// Shared calculates Alice's shared key from Alice's secret key and Bob's +// public key returning true on success. A failure case happens when the public +// key is a low-order point, thus the shared key is all-zeros and the function +// returns false. +func Shared(shared, secret, public *Key) bool { + validPk := *public + validPk[31] &= (1 << (255 % 8)) - 1 + ok := validPk.isValidPubKey() + ladderMontgomery(shared.clamp(secret), &validPk) + return ok +} diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/table.go b/vendor/github.com/cloudflare/circl/dh/x25519/table.go new file mode 100644 index 00000000..28c8c4ac --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x25519/table.go @@ -0,0 +1,268 @@ +package x25519 + +import "github.com/cloudflare/circl/math/fp25519" + +// tableGenerator contains the set of points: +// +// t[i] = (xi+1)/(xi-1), +// +// where (xi,yi) = 2^iG and G is the generator point +// Size = (256)*(256/8) = 8192 bytes. +var tableGenerator = [256 * fp25519.Size]byte{ + /* (2^ 0)P */ 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5f, + /* (2^ 1)P */ 0x96, 0xfe, 0xaa, 0x16, 0xf4, 0x20, 0x82, 0x6b, 0x34, 0x6a, 0x56, 0x4f, 0x2b, 0xeb, 0xeb, 0x82, 0x0f, 0x95, 0xa5, 0x75, 0xb0, 0xa5, 0xa9, 0xd5, 0xf4, 0x88, 0x24, 0x4b, 0xcf, 0xb2, 0x42, 0x51, + /* (2^ 2)P */ 0x0c, 0x68, 0x69, 0x00, 0x75, 0xbc, 0xae, 0x6a, 0x41, 0x9c, 0xf9, 0xa0, 0x20, 0x78, 0xcf, 0x89, 0xf4, 0xd0, 0x56, 0x3b, 0x18, 0xd9, 0x58, 0x2a, 0xa4, 0x11, 0x60, 0xe3, 0x80, 0xca, 0x5a, 0x4b, + /* (2^ 3)P */ 0x5d, 0x74, 0x29, 0x8c, 0x34, 0x32, 0x91, 0x32, 0xd7, 0x2f, 0x64, 0xe1, 0x16, 0xe6, 0xa2, 0xf4, 0x34, 0xbc, 0x67, 0xff, 0x03, 0xbb, 0x45, 0x1e, 0x4a, 0x9b, 0x2a, 0xf4, 0xd0, 0x12, 0x69, 0x30, + /* (2^ 4)P */ 0x54, 0x71, 0xaf, 0xe6, 0x07, 0x65, 0x88, 0xff, 0x2f, 0xc8, 0xee, 0xdf, 0x13, 0x0e, 0xf5, 0x04, 0xce, 0xb5, 0xba, 0x2a, 0xe8, 0x2f, 0x51, 0xaa, 0x22, 0xf2, 0xd5, 0x68, 0x1a, 0x25, 0x4e, 0x17, + /* (2^ 5)P */ 0x98, 0x88, 0x02, 0x82, 0x0d, 0x70, 0x96, 0xcf, 0xc5, 0x02, 0x2c, 0x0a, 0x37, 0xe3, 0x43, 0x17, 0xaa, 0x6e, 0xe8, 0xb4, 0x98, 0xec, 0x9e, 0x37, 0x2e, 0x48, 0xe0, 0x51, 0x8a, 0x88, 0x59, 0x0c, + /* (2^ 6)P */ 0x89, 0xd1, 0xb5, 0x99, 0xd6, 0xf1, 0xcb, 0xfb, 0x84, 0xdc, 0x9f, 0x8e, 0xd5, 0xf0, 0xae, 0xac, 0x14, 0x76, 0x1f, 0x23, 0x06, 0x0d, 0xc2, 0xc1, 0x72, 0xf9, 0x74, 0xa2, 0x8d, 0x21, 0x38, 0x29, + /* (2^ 7)P */ 0x18, 0x7f, 0x1d, 0xff, 0xbe, 0x49, 0xaf, 0xf6, 0xc2, 0xc9, 0x7a, 0x38, 0x22, 0x1c, 0x54, 0xcc, 0x6b, 0xc5, 0x15, 0x40, 0xef, 0xc9, 0xfc, 0x96, 0xa9, 0x13, 0x09, 0x69, 0x7c, 0x62, 0xc1, 0x69, + /* (2^ 8)P */ 0x0e, 0xdb, 0x33, 0x47, 0x2f, 0xfd, 0x86, 0x7a, 0xe9, 0x7d, 0x08, 0x9e, 0xf2, 0xc4, 0xb8, 0xfd, 0x29, 0xa2, 0xa2, 0x8e, 0x1a, 0x4b, 0x5e, 0x09, 0x79, 0x7a, 0xb3, 0x29, 0xc8, 0xa7, 0xd7, 0x1a, + /* (2^ 9)P */ 0xc0, 0xa0, 0x7e, 0xd1, 0xca, 0x89, 0x2d, 0x34, 0x51, 0x20, 0xed, 0xcc, 0xa6, 0xdd, 0xbe, 0x67, 0x74, 0x2f, 0xb4, 0x2b, 0xbf, 0x31, 0xca, 0x19, 0xbb, 0xac, 0x80, 0x49, 0xc8, 0xb4, 0xf7, 0x3d, + /* (2^ 10)P */ 0x83, 0xd8, 0x0a, 0xc8, 0x4d, 0x44, 0xc6, 0xa8, 0x85, 0xab, 0xe3, 0x66, 0x03, 0x44, 0x1e, 0xb9, 0xd8, 0xf6, 0x64, 0x01, 0xa0, 0xcd, 0x15, 0xc2, 0x68, 0xe6, 0x47, 0xf2, 0x6e, 0x7c, 0x86, 0x3d, + /* (2^ 11)P */ 0x8c, 0x65, 0x3e, 0xcc, 0x2b, 0x58, 0xdd, 0xc7, 0x28, 0x55, 0x0e, 0xee, 0x48, 0x47, 0x2c, 0xfd, 0x71, 0x4f, 0x9f, 0xcc, 0x95, 0x9b, 0xfd, 0xa0, 0xdf, 0x5d, 0x67, 0xb0, 0x71, 0xd8, 0x29, 0x75, + /* (2^ 12)P */ 0x78, 0xbd, 0x3c, 0x2d, 0xb4, 0x68, 0xf5, 0xb8, 0x82, 0xda, 0xf3, 0x91, 0x1b, 0x01, 0x33, 0x12, 0x62, 0x3b, 0x7c, 0x4a, 0xcd, 0x6c, 0xce, 0x2d, 0x03, 0x86, 0x49, 0x9e, 0x8e, 0xfc, 0xe7, 0x75, + /* (2^ 13)P */ 0xec, 0xb6, 0xd0, 0xfc, 0xf1, 0x13, 0x4f, 0x2f, 0x45, 0x7a, 0xff, 0x29, 0x1f, 0xca, 0xa8, 0xf1, 0x9b, 0xe2, 0x81, 0x29, 0xa7, 0xc1, 0x49, 0xc2, 0x6a, 0xb5, 0x83, 0x8c, 0xbb, 0x0d, 0xbe, 0x6e, + /* (2^ 14)P */ 0x22, 0xb2, 0x0b, 0x17, 0x8d, 0xfa, 0x14, 0x71, 0x5f, 0x93, 0x93, 0xbf, 0xd5, 0xdc, 0xa2, 0x65, 0x9a, 0x97, 0x9c, 0xb5, 0x68, 0x1f, 0xc4, 0xbd, 0x89, 0x92, 0xce, 0xa2, 0x79, 0xef, 0x0e, 0x2f, + /* (2^ 15)P */ 0xce, 0x37, 0x3c, 0x08, 0x0c, 0xbf, 0xec, 0x42, 0x22, 0x63, 0x49, 0xec, 0x09, 0xbc, 0x30, 0x29, 0x0d, 0xac, 0xfe, 0x9c, 0xc1, 0xb0, 0x94, 0xf2, 0x80, 0xbb, 0xfa, 0xed, 0x4b, 0xaa, 0x80, 0x37, + /* (2^ 16)P */ 0x29, 0xd9, 0xea, 0x7c, 0x3e, 0x7d, 0xc1, 0x56, 0xc5, 0x22, 0x57, 0x2e, 0xeb, 0x4b, 0xcb, 0xe7, 0x5a, 0xe1, 0xbf, 0x2d, 0x73, 0x31, 0xe9, 0x0c, 0xf8, 0x52, 0x10, 0x62, 0xc7, 0x83, 0xb8, 0x41, + /* (2^ 17)P */ 0x50, 0x53, 0xd2, 0xc3, 0xa0, 0x5c, 0xf7, 0xdb, 0x51, 0xe3, 0xb1, 0x6e, 0x08, 0xbe, 0x36, 0x29, 0x12, 0xb2, 0xa9, 0xb4, 0x3c, 0xe0, 0x36, 0xc9, 0xaa, 0x25, 0x22, 0x32, 0x82, 0xbf, 0x45, 0x1d, + /* (2^ 18)P */ 0xc5, 0x4c, 0x02, 0x6a, 0x03, 0xb1, 0x1a, 0xe8, 0x72, 0x9a, 0x4c, 0x30, 0x1c, 0x20, 0x12, 0xe2, 0xfc, 0xb1, 0x32, 0x68, 0xba, 0x3f, 0xd7, 0xc5, 0x81, 0x95, 0x83, 0x4d, 0x5a, 0xdb, 0xff, 0x20, + /* (2^ 19)P */ 0xad, 0x0f, 0x5d, 0xbe, 0x67, 0xd3, 0x83, 0xa2, 0x75, 0x44, 0x16, 0x8b, 0xca, 0x25, 0x2b, 0x6c, 0x2e, 0xf2, 0xaa, 0x7c, 0x46, 0x35, 0x49, 0x9d, 0x49, 0xff, 0x85, 0xee, 0x8e, 0x40, 0x66, 0x51, + /* (2^ 20)P */ 0x61, 0xe3, 0xb4, 0xfa, 0xa2, 0xba, 0x67, 0x3c, 0xef, 0x5c, 0xf3, 0x7e, 0xc6, 0x33, 0xe4, 0xb3, 0x1c, 0x9b, 0x15, 0x41, 0x92, 0x72, 0x59, 0x52, 0x33, 0xab, 0xb0, 0xd5, 0x92, 0x18, 0x62, 0x6a, + /* (2^ 21)P */ 0xcb, 0xcd, 0x55, 0x75, 0x38, 0x4a, 0xb7, 0x20, 0x3f, 0x92, 0x08, 0x12, 0x0e, 0xa1, 0x2a, 0x53, 0xd1, 0x1d, 0x28, 0x62, 0x77, 0x7b, 0xa1, 0xea, 0xbf, 0x44, 0x5c, 0xf0, 0x43, 0x34, 0xab, 0x61, + /* (2^ 22)P */ 0xf8, 0xde, 0x24, 0x23, 0x42, 0x6c, 0x7a, 0x25, 0x7f, 0xcf, 0xe3, 0x17, 0x10, 0x6c, 0x1c, 0x13, 0x57, 0xa2, 0x30, 0xf6, 0x39, 0x87, 0x75, 0x23, 0x80, 0x85, 0xa7, 0x01, 0x7a, 0x40, 0x5a, 0x29, + /* (2^ 23)P */ 0xd9, 0xa8, 0x5d, 0x6d, 0x24, 0x43, 0xc4, 0xf8, 0x5d, 0xfa, 0x52, 0x0c, 0x45, 0x75, 0xd7, 0x19, 0x3d, 0xf8, 0x1b, 0x73, 0x92, 0xfc, 0xfc, 0x2a, 0x00, 0x47, 0x2b, 0x1b, 0xe8, 0xc8, 0x10, 0x7d, + /* (2^ 24)P */ 0x0b, 0xa2, 0xba, 0x70, 0x1f, 0x27, 0xe0, 0xc8, 0x57, 0x39, 0xa6, 0x7c, 0x86, 0x48, 0x37, 0x99, 0xbb, 0xd4, 0x7e, 0xcb, 0xb3, 0xef, 0x12, 0x54, 0x75, 0x29, 0xe6, 0x73, 0x61, 0xd3, 0x96, 0x31, + /* (2^ 25)P */ 0xfc, 0xdf, 0xc7, 0x41, 0xd1, 0xca, 0x5b, 0xde, 0x48, 0xc8, 0x95, 0xb3, 0xd2, 0x8c, 0xcc, 0x47, 0xcb, 0xf3, 0x1a, 0xe1, 0x42, 0xd9, 0x4c, 0xa3, 0xc2, 0xce, 0x4e, 0xd0, 0xf2, 0xdb, 0x56, 0x02, + /* (2^ 26)P */ 0x7f, 0x66, 0x0e, 0x4b, 0xe9, 0xb7, 0x5a, 0x87, 0x10, 0x0d, 0x85, 0xc0, 0x83, 0xdd, 0xd4, 0xca, 0x9f, 0xc7, 0x72, 0x4e, 0x8f, 0x2e, 0xf1, 0x47, 0x9b, 0xb1, 0x85, 0x8c, 0xbb, 0x87, 0x1a, 0x5f, + /* (2^ 27)P */ 0xb8, 0x51, 0x7f, 0x43, 0xb6, 0xd0, 0xe9, 0x7a, 0x65, 0x90, 0x87, 0x18, 0x55, 0xce, 0xc7, 0x12, 0xee, 0x7a, 0xf7, 0x5c, 0xfe, 0x09, 0xde, 0x2a, 0x27, 0x56, 0x2c, 0x7d, 0x2f, 0x5a, 0xa0, 0x23, + /* (2^ 28)P */ 0x9a, 0x16, 0x7c, 0xf1, 0x28, 0xe1, 0x08, 0x59, 0x2d, 0x85, 0xd0, 0x8a, 0xdd, 0x98, 0x74, 0xf7, 0x64, 0x2f, 0x10, 0xab, 0xce, 0xc4, 0xb4, 0x74, 0x45, 0x98, 0x13, 0x10, 0xdd, 0xba, 0x3a, 0x18, + /* (2^ 29)P */ 0xac, 0xaa, 0x92, 0xaa, 0x8d, 0xba, 0x65, 0xb1, 0x05, 0x67, 0x38, 0x99, 0x95, 0xef, 0xc5, 0xd5, 0xd1, 0x40, 0xfc, 0xf8, 0x0c, 0x8f, 0x2f, 0xbe, 0x14, 0x45, 0x20, 0xee, 0x35, 0xe6, 0x01, 0x27, + /* (2^ 30)P */ 0x14, 0x65, 0x15, 0x20, 0x00, 0xa8, 0x9f, 0x62, 0xce, 0xc1, 0xa8, 0x64, 0x87, 0x86, 0x23, 0xf2, 0x0e, 0x06, 0x3f, 0x0b, 0xff, 0x4f, 0x89, 0x5b, 0xfa, 0xa3, 0x08, 0xf7, 0x4c, 0x94, 0xd9, 0x60, + /* (2^ 31)P */ 0x1f, 0x20, 0x7a, 0x1c, 0x1a, 0x00, 0xea, 0xae, 0x63, 0xce, 0xe2, 0x3e, 0x63, 0x6a, 0xf1, 0xeb, 0xe1, 0x07, 0x7a, 0x4c, 0x59, 0x09, 0x77, 0x6f, 0xcb, 0x08, 0x02, 0x0d, 0x15, 0x58, 0xb9, 0x79, + /* (2^ 32)P */ 0xe7, 0x10, 0xd4, 0x01, 0x53, 0x5e, 0xb5, 0x24, 0x4d, 0xc8, 0xfd, 0xf3, 0xdf, 0x4e, 0xa3, 0xe3, 0xd8, 0x32, 0x40, 0x90, 0xe4, 0x68, 0x87, 0xd8, 0xec, 0xae, 0x3a, 0x7b, 0x42, 0x84, 0x13, 0x13, + /* (2^ 33)P */ 0x14, 0x4f, 0x23, 0x86, 0x12, 0xe5, 0x05, 0x84, 0x29, 0xc5, 0xb4, 0xad, 0x39, 0x47, 0xdc, 0x14, 0xfd, 0x4f, 0x63, 0x50, 0xb2, 0xb5, 0xa2, 0xb8, 0x93, 0xff, 0xa7, 0xd8, 0x4a, 0xa9, 0xe2, 0x2f, + /* (2^ 34)P */ 0xdd, 0xfa, 0x43, 0xe8, 0xef, 0x57, 0x5c, 0xec, 0x18, 0x99, 0xbb, 0xf0, 0x40, 0xce, 0x43, 0x28, 0x05, 0x63, 0x3d, 0xcf, 0xd6, 0x61, 0xb5, 0xa4, 0x7e, 0x77, 0xfb, 0xe8, 0xbd, 0x29, 0x36, 0x74, + /* (2^ 35)P */ 0x8f, 0x73, 0xaf, 0xbb, 0x46, 0xdd, 0x3e, 0x34, 0x51, 0xa6, 0x01, 0xb1, 0x28, 0x18, 0x98, 0xed, 0x7a, 0x79, 0x2c, 0x88, 0x0b, 0x76, 0x01, 0xa4, 0x30, 0x87, 0xc8, 0x8d, 0xe2, 0x23, 0xc2, 0x1f, + /* (2^ 36)P */ 0x0e, 0xba, 0x0f, 0xfc, 0x91, 0x4e, 0x60, 0x48, 0xa4, 0x6f, 0x2c, 0x05, 0x8f, 0xf7, 0x37, 0xb6, 0x9c, 0x23, 0xe9, 0x09, 0x3d, 0xac, 0xcc, 0x91, 0x7c, 0x68, 0x7a, 0x43, 0xd4, 0xee, 0xf7, 0x23, + /* (2^ 37)P */ 0x00, 0xd8, 0x9b, 0x8d, 0x11, 0xb1, 0x73, 0x51, 0xa7, 0xd4, 0x89, 0x31, 0xb6, 0x41, 0xd6, 0x29, 0x86, 0xc5, 0xbb, 0x88, 0x79, 0x17, 0xbf, 0xfd, 0xf5, 0x1d, 0xd8, 0xca, 0x4f, 0x89, 0x59, 0x29, + /* (2^ 38)P */ 0x99, 0xc8, 0xbb, 0xb4, 0xf3, 0x8e, 0xbc, 0xae, 0xb9, 0x92, 0x69, 0xb2, 0x5a, 0x99, 0x48, 0x41, 0xfb, 0x2c, 0xf9, 0x34, 0x01, 0x0b, 0xe2, 0x24, 0xe8, 0xde, 0x05, 0x4a, 0x89, 0x58, 0xd1, 0x40, + /* (2^ 39)P */ 0xf6, 0x76, 0xaf, 0x85, 0x11, 0x0b, 0xb0, 0x46, 0x79, 0x7a, 0x18, 0x73, 0x78, 0xc7, 0xba, 0x26, 0x5f, 0xff, 0x8f, 0xab, 0x95, 0xbf, 0xc0, 0x3d, 0xd7, 0x24, 0x55, 0x94, 0xd8, 0x8b, 0x60, 0x2a, + /* (2^ 40)P */ 0x02, 0x63, 0x44, 0xbd, 0x88, 0x95, 0x44, 0x26, 0x9c, 0x43, 0x88, 0x03, 0x1c, 0xc2, 0x4b, 0x7c, 0xb2, 0x11, 0xbd, 0x83, 0xf3, 0xa4, 0x98, 0x8e, 0xb9, 0x76, 0xd8, 0xc9, 0x7b, 0x8d, 0x21, 0x26, + /* (2^ 41)P */ 0x8a, 0x17, 0x7c, 0x99, 0x42, 0x15, 0x08, 0xe3, 0x6f, 0x60, 0xb6, 0x6f, 0xa8, 0x29, 0x2d, 0x3c, 0x74, 0x93, 0x27, 0xfa, 0x36, 0x77, 0x21, 0x5c, 0xfa, 0xb1, 0xfe, 0x4a, 0x73, 0x05, 0xde, 0x7d, + /* (2^ 42)P */ 0xab, 0x2b, 0xd4, 0x06, 0x39, 0x0e, 0xf1, 0x3b, 0x9c, 0x64, 0x80, 0x19, 0x3e, 0x80, 0xf7, 0xe4, 0x7a, 0xbf, 0x95, 0x95, 0xf8, 0x3b, 0x05, 0xe6, 0x30, 0x55, 0x24, 0xda, 0x38, 0xaf, 0x4f, 0x39, + /* (2^ 43)P */ 0xf4, 0x28, 0x69, 0x89, 0x58, 0xfb, 0x8e, 0x7a, 0x3c, 0x11, 0x6a, 0xcc, 0xe9, 0x78, 0xc7, 0xfb, 0x6f, 0x59, 0xaf, 0x30, 0xe3, 0x0c, 0x67, 0x72, 0xf7, 0x6c, 0x3d, 0x1d, 0xa8, 0x22, 0xf2, 0x48, + /* (2^ 44)P */ 0xa7, 0xca, 0x72, 0x0d, 0x41, 0xce, 0x1f, 0xf0, 0x95, 0x55, 0x3b, 0x21, 0xc7, 0xec, 0x20, 0x5a, 0x83, 0x14, 0xfa, 0xc1, 0x65, 0x11, 0xc2, 0x7b, 0x41, 0xa7, 0xa8, 0x1d, 0xe3, 0x9a, 0xf8, 0x07, + /* (2^ 45)P */ 0xf9, 0x0f, 0x83, 0xc6, 0xb4, 0xc2, 0xd2, 0x05, 0x93, 0x62, 0x31, 0xc6, 0x0f, 0x33, 0x3e, 0xd4, 0x04, 0xa9, 0xd3, 0x96, 0x0a, 0x59, 0xa5, 0xa5, 0xb6, 0x33, 0x53, 0xa6, 0x91, 0xdb, 0x5e, 0x70, + /* (2^ 46)P */ 0xf7, 0xa5, 0xb9, 0x0b, 0x5e, 0xe1, 0x8e, 0x04, 0x5d, 0xaf, 0x0a, 0x9e, 0xca, 0xcf, 0x40, 0x32, 0x0b, 0xa4, 0xc4, 0xed, 0xce, 0x71, 0x4b, 0x8f, 0x6d, 0x4a, 0x54, 0xde, 0xa3, 0x0d, 0x1c, 0x62, + /* (2^ 47)P */ 0x91, 0x40, 0x8c, 0xa0, 0x36, 0x28, 0x87, 0x92, 0x45, 0x14, 0xc9, 0x10, 0xb0, 0x75, 0x83, 0xce, 0x94, 0x63, 0x27, 0x4f, 0x52, 0xeb, 0x72, 0x8a, 0x35, 0x36, 0xc8, 0x7e, 0xfa, 0xfc, 0x67, 0x26, + /* (2^ 48)P */ 0x2a, 0x75, 0xe8, 0x45, 0x33, 0x17, 0x4c, 0x7f, 0xa5, 0x79, 0x70, 0xee, 0xfe, 0x47, 0x1b, 0x06, 0x34, 0xff, 0x86, 0x9f, 0xfa, 0x9a, 0xdd, 0x25, 0x9c, 0xc8, 0x5d, 0x42, 0xf5, 0xce, 0x80, 0x37, + /* (2^ 49)P */ 0xe9, 0xb4, 0x3b, 0x51, 0x5a, 0x03, 0x46, 0x1a, 0xda, 0x5a, 0x57, 0xac, 0x79, 0xf3, 0x1e, 0x3e, 0x50, 0x4b, 0xa2, 0x5f, 0x1c, 0x5f, 0x8c, 0xc7, 0x22, 0x9f, 0xfd, 0x34, 0x76, 0x96, 0x1a, 0x32, + /* (2^ 50)P */ 0xfa, 0x27, 0x6e, 0x82, 0xb8, 0x07, 0x67, 0x94, 0xd0, 0x6f, 0x50, 0x4c, 0xd6, 0x84, 0xca, 0x3d, 0x36, 0x14, 0xe9, 0x75, 0x80, 0x21, 0x89, 0xc1, 0x84, 0x84, 0x3b, 0x9b, 0x16, 0x84, 0x92, 0x6d, + /* (2^ 51)P */ 0xdf, 0x2d, 0x3f, 0x38, 0x40, 0xe8, 0x67, 0x3a, 0x75, 0x9b, 0x4f, 0x0c, 0xa3, 0xc9, 0xee, 0x33, 0x47, 0xef, 0x83, 0xa7, 0x6f, 0xc8, 0xc7, 0x3e, 0xc4, 0xfb, 0xc9, 0xba, 0x9f, 0x44, 0xec, 0x26, + /* (2^ 52)P */ 0x7d, 0x9e, 0x9b, 0xa0, 0xcb, 0x38, 0x0f, 0x5c, 0x8c, 0x47, 0xa3, 0x62, 0xc7, 0x8c, 0x16, 0x81, 0x1c, 0x12, 0xfc, 0x06, 0xd3, 0xb0, 0x23, 0x3e, 0xdd, 0xdc, 0xef, 0xa5, 0xa0, 0x8a, 0x23, 0x5a, + /* (2^ 53)P */ 0xff, 0x43, 0xea, 0xc4, 0x21, 0x61, 0xa2, 0x1b, 0xb5, 0x32, 0x88, 0x7c, 0x7f, 0xc7, 0xf8, 0x36, 0x9a, 0xf9, 0xdc, 0x0a, 0x0b, 0xea, 0xfb, 0x88, 0xf9, 0xeb, 0x5b, 0xc2, 0x8e, 0x93, 0xa9, 0x5c, + /* (2^ 54)P */ 0xa0, 0xcd, 0xfc, 0x51, 0x5e, 0x6a, 0x43, 0xd5, 0x3b, 0x89, 0xcd, 0xc2, 0x97, 0x47, 0xbc, 0x1d, 0x08, 0x4a, 0x22, 0xd3, 0x65, 0x6a, 0x34, 0x19, 0x66, 0xf4, 0x9a, 0x9b, 0xe4, 0x34, 0x50, 0x0f, + /* (2^ 55)P */ 0x6e, 0xb9, 0xe0, 0xa1, 0x67, 0x39, 0x3c, 0xf2, 0x88, 0x4d, 0x7a, 0x86, 0xfa, 0x08, 0x8b, 0xe5, 0x79, 0x16, 0x34, 0xa7, 0xc6, 0xab, 0x2f, 0xfb, 0x46, 0x69, 0x02, 0xb6, 0x1e, 0x38, 0x75, 0x2a, + /* (2^ 56)P */ 0xac, 0x20, 0x94, 0xc1, 0xe4, 0x3b, 0x0a, 0xc8, 0xdc, 0xb6, 0xf2, 0x81, 0xc6, 0xf6, 0xb1, 0x66, 0x88, 0x33, 0xe9, 0x61, 0x67, 0x03, 0xf7, 0x7c, 0xc4, 0xa4, 0x60, 0xa6, 0xd8, 0xbb, 0xab, 0x25, + /* (2^ 57)P */ 0x98, 0x51, 0xfd, 0x14, 0xba, 0x12, 0xea, 0x91, 0xa9, 0xff, 0x3c, 0x4a, 0xfc, 0x50, 0x49, 0x68, 0x28, 0xad, 0xf5, 0x30, 0x21, 0x84, 0x26, 0xf8, 0x41, 0xa4, 0x01, 0x53, 0xf7, 0x88, 0xa9, 0x3e, + /* (2^ 58)P */ 0x6f, 0x8c, 0x5f, 0x69, 0x9a, 0x10, 0x78, 0xc9, 0xf3, 0xc3, 0x30, 0x05, 0x4a, 0xeb, 0x46, 0x17, 0x95, 0x99, 0x45, 0xb4, 0x77, 0x6d, 0x4d, 0x44, 0xc7, 0x5c, 0x4e, 0x05, 0x8c, 0x2b, 0x95, 0x75, + /* (2^ 59)P */ 0xaa, 0xd6, 0xf4, 0x15, 0x79, 0x3f, 0x70, 0xa3, 0xd8, 0x47, 0x26, 0x2f, 0x20, 0x46, 0xc3, 0x66, 0x4b, 0x64, 0x1d, 0x81, 0xdf, 0x69, 0x14, 0xd0, 0x1f, 0xd7, 0xa5, 0x81, 0x7d, 0xa4, 0xfe, 0x77, + /* (2^ 60)P */ 0x81, 0xa3, 0x7c, 0xf5, 0x9e, 0x52, 0xe9, 0xc5, 0x1a, 0x88, 0x2f, 0xce, 0xb9, 0xb4, 0xee, 0x6e, 0xd6, 0x9b, 0x00, 0xe8, 0x28, 0x1a, 0xe9, 0xb6, 0xec, 0x3f, 0xfc, 0x9a, 0x3e, 0xbe, 0x80, 0x4b, + /* (2^ 61)P */ 0xc5, 0xd2, 0xae, 0x26, 0xc5, 0x73, 0x37, 0x7e, 0x9d, 0xa4, 0xc9, 0x53, 0xb4, 0xfc, 0x4a, 0x1b, 0x4d, 0xb2, 0xff, 0xba, 0xd7, 0xbd, 0x20, 0xa9, 0x0e, 0x40, 0x2d, 0x12, 0x9f, 0x69, 0x54, 0x7c, + /* (2^ 62)P */ 0xc8, 0x4b, 0xa9, 0x4f, 0xe1, 0xc8, 0x46, 0xef, 0x5e, 0xed, 0x52, 0x29, 0xce, 0x74, 0xb0, 0xe0, 0xd5, 0x85, 0xd8, 0xdb, 0xe1, 0x50, 0xa4, 0xbe, 0x2c, 0x71, 0x0f, 0x32, 0x49, 0x86, 0xb6, 0x61, + /* (2^ 63)P */ 0xd1, 0xbd, 0xcc, 0x09, 0x73, 0x5f, 0x48, 0x8a, 0x2d, 0x1a, 0x4d, 0x7d, 0x0d, 0x32, 0x06, 0xbd, 0xf4, 0xbe, 0x2d, 0x32, 0x73, 0x29, 0x23, 0x25, 0x70, 0xf7, 0x17, 0x8c, 0x75, 0xc4, 0x5d, 0x44, + /* (2^ 64)P */ 0x3c, 0x93, 0xc8, 0x7c, 0x17, 0x34, 0x04, 0xdb, 0x9f, 0x05, 0xea, 0x75, 0x21, 0xe8, 0x6f, 0xed, 0x34, 0xdb, 0x53, 0xc0, 0xfd, 0xbe, 0xfe, 0x1e, 0x99, 0xaf, 0x5d, 0xc6, 0x67, 0xe8, 0xdb, 0x4a, + /* (2^ 65)P */ 0xdf, 0x09, 0x06, 0xa9, 0xa2, 0x71, 0xcd, 0x3a, 0x50, 0x40, 0xd0, 0x6d, 0x85, 0x91, 0xe9, 0xe5, 0x3c, 0xc2, 0x57, 0x81, 0x68, 0x9b, 0xc6, 0x1e, 0x4d, 0xfe, 0x5c, 0x88, 0xf6, 0x27, 0x74, 0x69, + /* (2^ 66)P */ 0x51, 0xa8, 0xe1, 0x65, 0x9b, 0x7b, 0xbe, 0xd7, 0xdd, 0x36, 0xc5, 0x22, 0xd5, 0x28, 0x3d, 0xa0, 0x45, 0xb6, 0xd2, 0x8f, 0x65, 0x9d, 0x39, 0x28, 0xe1, 0x41, 0x26, 0x7c, 0xe1, 0xb7, 0xe5, 0x49, + /* (2^ 67)P */ 0xa4, 0x57, 0x04, 0x70, 0x98, 0x3a, 0x8c, 0x6f, 0x78, 0x67, 0xbb, 0x5e, 0xa2, 0xf0, 0x78, 0x50, 0x0f, 0x96, 0x82, 0xc3, 0xcb, 0x3c, 0x3c, 0xd1, 0xb1, 0x84, 0xdf, 0xa7, 0x58, 0x32, 0x00, 0x2e, + /* (2^ 68)P */ 0x1c, 0x6a, 0x29, 0xe6, 0x9b, 0xf3, 0xd1, 0x8a, 0xb2, 0xbf, 0x5f, 0x2a, 0x65, 0xaa, 0xee, 0xc1, 0xcb, 0xf3, 0x26, 0xfd, 0x73, 0x06, 0xee, 0x33, 0xcc, 0x2c, 0x9d, 0xa6, 0x73, 0x61, 0x25, 0x59, + /* (2^ 69)P */ 0x41, 0xfc, 0x18, 0x4e, 0xaa, 0x07, 0xea, 0x41, 0x1e, 0xa5, 0x87, 0x7c, 0x52, 0x19, 0xfc, 0xd9, 0x6f, 0xca, 0x31, 0x58, 0x80, 0xcb, 0xaa, 0xbd, 0x4f, 0x69, 0x16, 0xc9, 0x2d, 0x65, 0x5b, 0x44, + /* (2^ 70)P */ 0x15, 0x23, 0x17, 0xf2, 0xa7, 0xa3, 0x92, 0xce, 0x64, 0x99, 0x1b, 0xe1, 0x2d, 0x28, 0xdc, 0x1e, 0x4a, 0x31, 0x4c, 0xe0, 0xaf, 0x3a, 0x82, 0xa1, 0x86, 0xf5, 0x7c, 0x43, 0x94, 0x2d, 0x0a, 0x79, + /* (2^ 71)P */ 0x09, 0xe0, 0xf6, 0x93, 0xfb, 0x47, 0xc4, 0x71, 0x76, 0x52, 0x84, 0x22, 0x67, 0xa5, 0x22, 0x89, 0x69, 0x51, 0x4f, 0x20, 0x3b, 0x90, 0x70, 0xbf, 0xfe, 0x19, 0xa3, 0x1b, 0x89, 0x89, 0x7a, 0x2f, + /* (2^ 72)P */ 0x0c, 0x14, 0xe2, 0x77, 0xb5, 0x8e, 0xa0, 0x02, 0xf4, 0xdc, 0x7b, 0x42, 0xd4, 0x4e, 0x9a, 0xed, 0xd1, 0x3c, 0x32, 0xe4, 0x44, 0xec, 0x53, 0x52, 0x5b, 0x35, 0xe9, 0x14, 0x3c, 0x36, 0x88, 0x3e, + /* (2^ 73)P */ 0x8c, 0x0b, 0x11, 0x77, 0x42, 0xc1, 0x66, 0xaa, 0x90, 0x33, 0xa2, 0x10, 0x16, 0x39, 0xe0, 0x1a, 0xa2, 0xc2, 0x3f, 0xc9, 0x12, 0xbd, 0x30, 0x20, 0xab, 0xc7, 0x55, 0x95, 0x57, 0x41, 0xe1, 0x3e, + /* (2^ 74)P */ 0x41, 0x7d, 0x6e, 0x6d, 0x3a, 0xde, 0x14, 0x92, 0xfe, 0x7e, 0xf1, 0x07, 0x86, 0xd8, 0xcd, 0x3c, 0x17, 0x12, 0xe1, 0xf8, 0x88, 0x12, 0x4f, 0x67, 0xd0, 0x93, 0x9f, 0x32, 0x0f, 0x25, 0x82, 0x56, + /* (2^ 75)P */ 0x6e, 0x39, 0x2e, 0x6d, 0x13, 0x0b, 0xf0, 0x6c, 0xbf, 0xde, 0x14, 0x10, 0x6f, 0xf8, 0x4c, 0x6e, 0x83, 0x4e, 0xcc, 0xbf, 0xb5, 0xb1, 0x30, 0x59, 0xb6, 0x16, 0xba, 0x8a, 0xb4, 0x69, 0x70, 0x04, + /* (2^ 76)P */ 0x93, 0x07, 0xb2, 0x69, 0xab, 0xe4, 0x4c, 0x0d, 0x9e, 0xfb, 0xd0, 0x97, 0x1a, 0xb9, 0x4d, 0xb2, 0x1d, 0xd0, 0x00, 0x4e, 0xf5, 0x50, 0xfa, 0xcd, 0xb5, 0xdd, 0x8b, 0x36, 0x85, 0x10, 0x1b, 0x22, + /* (2^ 77)P */ 0xd2, 0xd8, 0xe3, 0xb1, 0x68, 0x94, 0xe5, 0xe7, 0x93, 0x2f, 0x12, 0xbd, 0x63, 0x65, 0xc5, 0x53, 0x09, 0x3f, 0x66, 0xe0, 0x03, 0xa9, 0xe8, 0xee, 0x42, 0x3d, 0xbe, 0xcb, 0x62, 0xa6, 0xef, 0x61, + /* (2^ 78)P */ 0x2a, 0xab, 0x6e, 0xde, 0xdd, 0xdd, 0xf8, 0x2c, 0x31, 0xf2, 0x35, 0x14, 0xd5, 0x0a, 0xf8, 0x9b, 0x73, 0x49, 0xf0, 0xc9, 0xce, 0xda, 0xea, 0x5d, 0x27, 0x9b, 0xd2, 0x41, 0x5d, 0x5b, 0x27, 0x29, + /* (2^ 79)P */ 0x4f, 0xf1, 0xeb, 0x95, 0x08, 0x0f, 0xde, 0xcf, 0xa7, 0x05, 0x49, 0x05, 0x6b, 0xb9, 0xaa, 0xb9, 0xfd, 0x20, 0xc4, 0xa1, 0xd9, 0x0d, 0xe8, 0xca, 0xc7, 0xbb, 0x73, 0x16, 0x2f, 0xbf, 0x63, 0x0a, + /* (2^ 80)P */ 0x8c, 0xbc, 0x8f, 0x95, 0x11, 0x6e, 0x2f, 0x09, 0xad, 0x2f, 0x82, 0x04, 0xe8, 0x81, 0x2a, 0x67, 0x17, 0x25, 0xd5, 0x60, 0x15, 0x35, 0xc8, 0xca, 0xf8, 0x92, 0xf1, 0xc8, 0x22, 0x77, 0x3f, 0x6f, + /* (2^ 81)P */ 0xb7, 0x94, 0xe8, 0xc2, 0xcc, 0x90, 0xba, 0xf8, 0x0d, 0x9f, 0xff, 0x38, 0xa4, 0x57, 0x75, 0x2c, 0x59, 0x23, 0xe5, 0x5a, 0x85, 0x1d, 0x4d, 0x89, 0x69, 0x3d, 0x74, 0x7b, 0x15, 0x22, 0xe1, 0x68, + /* (2^ 82)P */ 0xf3, 0x19, 0xb9, 0xcf, 0x70, 0x55, 0x7e, 0xd8, 0xb9, 0x8d, 0x79, 0x95, 0xcd, 0xde, 0x2c, 0x3f, 0xce, 0xa2, 0xc0, 0x10, 0x47, 0x15, 0x21, 0x21, 0xb2, 0xc5, 0x6d, 0x24, 0x15, 0xa1, 0x66, 0x3c, + /* (2^ 83)P */ 0x72, 0xcb, 0x4e, 0x29, 0x62, 0xc5, 0xed, 0xcb, 0x16, 0x0b, 0x28, 0x6a, 0xc3, 0x43, 0x71, 0xba, 0x67, 0x8b, 0x07, 0xd4, 0xef, 0xc2, 0x10, 0x96, 0x1e, 0x4b, 0x6a, 0x94, 0x5d, 0x73, 0x44, 0x61, + /* (2^ 84)P */ 0x50, 0x33, 0x5b, 0xd7, 0x1e, 0x11, 0x6f, 0x53, 0x1b, 0xd8, 0x41, 0x20, 0x8c, 0xdb, 0x11, 0x02, 0x3c, 0x41, 0x10, 0x0e, 0x00, 0xb1, 0x3c, 0xf9, 0x76, 0x88, 0x9e, 0x03, 0x3c, 0xfd, 0x9d, 0x14, + /* (2^ 85)P */ 0x5b, 0x15, 0x63, 0x6b, 0xe4, 0xdd, 0x79, 0xd4, 0x76, 0x79, 0x83, 0x3c, 0xe9, 0x15, 0x6e, 0xb6, 0x38, 0xe0, 0x13, 0x1f, 0x3b, 0xe4, 0xfd, 0xda, 0x35, 0x0b, 0x4b, 0x2e, 0x1a, 0xda, 0xaf, 0x5f, + /* (2^ 86)P */ 0x81, 0x75, 0x19, 0x17, 0xdf, 0xbb, 0x00, 0x36, 0xc2, 0xd2, 0x3c, 0xbe, 0x0b, 0x05, 0x72, 0x39, 0x86, 0xbe, 0xd5, 0xbd, 0x6d, 0x90, 0x38, 0x59, 0x0f, 0x86, 0x9b, 0x3f, 0xe4, 0xe5, 0xfc, 0x34, + /* (2^ 87)P */ 0x02, 0x4d, 0xd1, 0x42, 0xcd, 0xa4, 0xa8, 0x75, 0x65, 0xdf, 0x41, 0x34, 0xc5, 0xab, 0x8d, 0x82, 0xd3, 0x31, 0xe1, 0xd2, 0xed, 0xab, 0xdc, 0x33, 0x5f, 0xd2, 0x14, 0xb8, 0x6f, 0xd7, 0xba, 0x3e, + /* (2^ 88)P */ 0x0f, 0xe1, 0x70, 0x6f, 0x56, 0x6f, 0x90, 0xd4, 0x5a, 0x0f, 0x69, 0x51, 0xaa, 0xf7, 0x12, 0x5d, 0xf2, 0xfc, 0xce, 0x76, 0x6e, 0xb1, 0xad, 0x45, 0x99, 0x29, 0x23, 0xad, 0xae, 0x68, 0xf7, 0x01, + /* (2^ 89)P */ 0xbd, 0xfe, 0x48, 0x62, 0x7b, 0xc7, 0x6c, 0x2b, 0xfd, 0xaf, 0x3a, 0xec, 0x28, 0x06, 0xd3, 0x3c, 0x6a, 0x48, 0xef, 0xd4, 0x80, 0x0b, 0x1c, 0xce, 0x23, 0x6c, 0xf6, 0xa6, 0x2e, 0xff, 0x3b, 0x4c, + /* (2^ 90)P */ 0x5f, 0xeb, 0xea, 0x4a, 0x09, 0xc4, 0x2e, 0x3f, 0xa7, 0x2c, 0x37, 0x6e, 0x28, 0x9b, 0xb1, 0x61, 0x1d, 0x70, 0x2a, 0xde, 0x66, 0xa9, 0xef, 0x5e, 0xef, 0xe3, 0x55, 0xde, 0x65, 0x05, 0xb2, 0x23, + /* (2^ 91)P */ 0x57, 0x85, 0xd5, 0x79, 0x52, 0xca, 0x01, 0xe3, 0x4f, 0x87, 0xc2, 0x27, 0xce, 0xd4, 0xb2, 0x07, 0x67, 0x1d, 0xcf, 0x9d, 0x8a, 0xcd, 0x32, 0xa5, 0x56, 0xff, 0x2b, 0x3f, 0xe2, 0xfe, 0x52, 0x2a, + /* (2^ 92)P */ 0x3d, 0x66, 0xd8, 0x7c, 0xb3, 0xef, 0x24, 0x86, 0x94, 0x75, 0xbd, 0xff, 0x20, 0xac, 0xc7, 0xbb, 0x45, 0x74, 0xd3, 0x82, 0x9c, 0x5e, 0xb8, 0x57, 0x66, 0xec, 0xa6, 0x86, 0xcb, 0x52, 0x30, 0x7b, + /* (2^ 93)P */ 0x1e, 0xe9, 0x25, 0x25, 0xad, 0xf0, 0x82, 0x34, 0xa0, 0xdc, 0x8e, 0xd2, 0x43, 0x80, 0xb6, 0x2c, 0x3a, 0x00, 0x1b, 0x2e, 0x05, 0x6d, 0x4f, 0xaf, 0x0a, 0x1b, 0x78, 0x29, 0x25, 0x8c, 0x5f, 0x18, + /* (2^ 94)P */ 0xd6, 0xe0, 0x0c, 0xd8, 0x5b, 0xde, 0x41, 0xaa, 0xd6, 0xe9, 0x53, 0x68, 0x41, 0xb2, 0x07, 0x94, 0x3a, 0x4c, 0x7f, 0x35, 0x6e, 0xc3, 0x3e, 0x56, 0xce, 0x7b, 0x29, 0x0e, 0xdd, 0xb8, 0xc4, 0x4c, + /* (2^ 95)P */ 0x0e, 0x73, 0xb8, 0xff, 0x52, 0x1a, 0xfc, 0xa2, 0x37, 0x8e, 0x05, 0x67, 0x6e, 0xf1, 0x11, 0x18, 0xe1, 0x4e, 0xdf, 0xcd, 0x66, 0xa3, 0xf9, 0x10, 0x99, 0xf0, 0xb9, 0xa0, 0xc4, 0xa0, 0xf4, 0x72, + /* (2^ 96)P */ 0xa7, 0x4e, 0x3f, 0x66, 0x6f, 0xc0, 0x16, 0x8c, 0xba, 0x0f, 0x97, 0x4e, 0xf7, 0x3a, 0x3b, 0x69, 0x45, 0xc3, 0x9e, 0xd6, 0xf1, 0xe7, 0x02, 0x21, 0x89, 0x80, 0x8a, 0x96, 0xbc, 0x3c, 0xa5, 0x0b, + /* (2^ 97)P */ 0x37, 0x55, 0xa1, 0xfe, 0xc7, 0x9d, 0x3d, 0xca, 0x93, 0x64, 0x53, 0x51, 0xbb, 0x24, 0x68, 0x4c, 0xb1, 0x06, 0x40, 0x84, 0x14, 0x63, 0x88, 0xb9, 0x60, 0xcc, 0x54, 0xb4, 0x2a, 0xa7, 0xd2, 0x40, + /* (2^ 98)P */ 0x75, 0x09, 0x57, 0x12, 0xb7, 0xa1, 0x36, 0x59, 0x57, 0xa6, 0xbd, 0xde, 0x48, 0xd6, 0xb9, 0x91, 0xea, 0x30, 0x43, 0xb6, 0x4b, 0x09, 0x44, 0x33, 0xd0, 0x51, 0xee, 0x12, 0x0d, 0xa1, 0x6b, 0x00, + /* (2^ 99)P */ 0x58, 0x5d, 0xde, 0xf5, 0x68, 0x84, 0x22, 0x19, 0xb0, 0x05, 0xcc, 0x38, 0x4c, 0x2f, 0xb1, 0x0e, 0x90, 0x19, 0x60, 0xd5, 0x9d, 0x9f, 0x03, 0xa1, 0x0b, 0x0e, 0xff, 0x4f, 0xce, 0xd4, 0x02, 0x45, + /* (2^100)P */ 0x89, 0xc1, 0x37, 0x68, 0x10, 0x54, 0x20, 0xeb, 0x3c, 0xb9, 0xd3, 0x6d, 0x4c, 0x54, 0xf6, 0xd0, 0x4f, 0xd7, 0x16, 0xc4, 0x64, 0x70, 0x72, 0x40, 0xf0, 0x2e, 0x50, 0x4b, 0x11, 0xc6, 0x15, 0x6e, + /* (2^101)P */ 0x6b, 0xa7, 0xb1, 0xcf, 0x98, 0xa3, 0xf2, 0x4d, 0xb1, 0xf6, 0xf2, 0x19, 0x74, 0x6c, 0x25, 0x11, 0x43, 0x60, 0x6e, 0x06, 0x62, 0x79, 0x49, 0x4a, 0x44, 0x5b, 0x35, 0x41, 0xab, 0x3a, 0x5b, 0x70, + /* (2^102)P */ 0xd8, 0xb1, 0x97, 0xd7, 0x36, 0xf5, 0x5e, 0x36, 0xdb, 0xf0, 0xdd, 0x22, 0xd6, 0x6b, 0x07, 0x00, 0x88, 0x5a, 0x57, 0xe0, 0xb0, 0x33, 0xbf, 0x3b, 0x4d, 0xca, 0xe4, 0xc8, 0x05, 0xaa, 0x77, 0x37, + /* (2^103)P */ 0x5f, 0xdb, 0x78, 0x55, 0xc8, 0x45, 0x27, 0x39, 0xe2, 0x5a, 0xae, 0xdb, 0x49, 0x41, 0xda, 0x6f, 0x67, 0x98, 0xdc, 0x8a, 0x0b, 0xb0, 0xf0, 0xb1, 0xa3, 0x1d, 0x6f, 0xd3, 0x37, 0x34, 0x96, 0x09, + /* (2^104)P */ 0x53, 0x38, 0xdc, 0xa5, 0x90, 0x4e, 0x82, 0x7e, 0xbd, 0x5c, 0x13, 0x1f, 0x64, 0xf6, 0xb5, 0xcc, 0xcc, 0x8f, 0xce, 0x87, 0x6c, 0xd8, 0x36, 0x67, 0x9f, 0x24, 0x04, 0x66, 0xe2, 0x3c, 0x5f, 0x62, + /* (2^105)P */ 0x3f, 0xf6, 0x02, 0x95, 0x05, 0xc8, 0x8a, 0xaf, 0x69, 0x14, 0x35, 0x2e, 0x0a, 0xe7, 0x05, 0x0c, 0x05, 0x63, 0x4b, 0x76, 0x9c, 0x2e, 0x29, 0x35, 0xc3, 0x3a, 0xe2, 0xc7, 0x60, 0x43, 0x39, 0x1a, + /* (2^106)P */ 0x64, 0x32, 0x18, 0x51, 0x32, 0xd5, 0xc6, 0xd5, 0x4f, 0xb7, 0xc2, 0x43, 0xbd, 0x5a, 0x06, 0x62, 0x9b, 0x3f, 0x97, 0x3b, 0xd0, 0xf5, 0xfb, 0xb5, 0x5e, 0x6e, 0x20, 0x61, 0x36, 0xda, 0xa3, 0x13, + /* (2^107)P */ 0xe5, 0x94, 0x5d, 0x72, 0x37, 0x58, 0xbd, 0xc6, 0xc5, 0x16, 0x50, 0x20, 0x12, 0x09, 0xe3, 0x18, 0x68, 0x3c, 0x03, 0x70, 0x15, 0xce, 0x88, 0x20, 0x87, 0x79, 0x83, 0x5c, 0x49, 0x1f, 0xba, 0x7f, + /* (2^108)P */ 0x9d, 0x07, 0xf9, 0xf2, 0x23, 0x74, 0x8c, 0x5a, 0xc5, 0x3f, 0x02, 0x34, 0x7b, 0x15, 0x35, 0x17, 0x51, 0xb3, 0xfa, 0xd2, 0x9a, 0xb4, 0xf9, 0xe4, 0x3c, 0xe3, 0x78, 0xc8, 0x72, 0xff, 0x91, 0x66, + /* (2^109)P */ 0x3e, 0xff, 0x5e, 0xdc, 0xde, 0x2a, 0x2c, 0x12, 0xf4, 0x6c, 0x95, 0xd8, 0xf1, 0x4b, 0xdd, 0xf8, 0xda, 0x5b, 0x9e, 0x9e, 0x5d, 0x20, 0x86, 0xeb, 0x43, 0xc7, 0x75, 0xd9, 0xb9, 0x92, 0x9b, 0x04, + /* (2^110)P */ 0x5a, 0xc0, 0xf6, 0xb0, 0x30, 0x97, 0x37, 0xa5, 0x53, 0xa5, 0xf3, 0xc6, 0xac, 0xff, 0xa0, 0x72, 0x6d, 0xcd, 0x0d, 0xb2, 0x34, 0x2c, 0x03, 0xb0, 0x4a, 0x16, 0xd5, 0x88, 0xbc, 0x9d, 0x0e, 0x47, + /* (2^111)P */ 0x47, 0xc0, 0x37, 0xa2, 0x0c, 0xf1, 0x9c, 0xb1, 0xa2, 0x81, 0x6c, 0x1f, 0x71, 0x66, 0x54, 0xb6, 0x43, 0x0b, 0xd8, 0x6d, 0xd1, 0x1b, 0x32, 0xb3, 0x8e, 0xbe, 0x5f, 0x0c, 0x60, 0x4f, 0xc1, 0x48, + /* (2^112)P */ 0x03, 0xc8, 0xa6, 0x4a, 0x26, 0x1c, 0x45, 0x66, 0xa6, 0x7d, 0xfa, 0xa4, 0x04, 0x39, 0x6e, 0xb6, 0x95, 0x83, 0x12, 0xb3, 0xb0, 0x19, 0x5f, 0xd4, 0x10, 0xbc, 0xc9, 0xc3, 0x27, 0x26, 0x60, 0x31, + /* (2^113)P */ 0x0d, 0xe1, 0xe4, 0x32, 0x48, 0xdc, 0x20, 0x31, 0xf7, 0x17, 0xc7, 0x56, 0x67, 0xc4, 0x20, 0xeb, 0x94, 0x02, 0x28, 0x67, 0x3f, 0x2e, 0xf5, 0x00, 0x09, 0xc5, 0x30, 0x47, 0xc1, 0x4f, 0x6d, 0x56, + /* (2^114)P */ 0x06, 0x72, 0x83, 0xfd, 0x40, 0x5d, 0x3a, 0x7e, 0x7a, 0x54, 0x59, 0x71, 0xdc, 0x26, 0xe9, 0xc1, 0x95, 0x60, 0x8d, 0xa6, 0xfb, 0x30, 0x67, 0x21, 0xa7, 0xce, 0x69, 0x3f, 0x84, 0xc3, 0xe8, 0x22, + /* (2^115)P */ 0x2b, 0x4b, 0x0e, 0x93, 0xe8, 0x74, 0xd0, 0x33, 0x16, 0x58, 0xd1, 0x84, 0x0e, 0x35, 0xe4, 0xb6, 0x65, 0x23, 0xba, 0xd6, 0x6a, 0xc2, 0x34, 0x55, 0xf3, 0xf3, 0xf1, 0x89, 0x2f, 0xc1, 0x73, 0x77, + /* (2^116)P */ 0xaa, 0x62, 0x79, 0xa5, 0x4d, 0x40, 0xba, 0x8c, 0x56, 0xce, 0x99, 0x19, 0xa8, 0x97, 0x98, 0x5b, 0xfc, 0x92, 0x16, 0x12, 0x2f, 0x86, 0x8e, 0x50, 0x91, 0xc2, 0x93, 0xa0, 0x7f, 0x90, 0x81, 0x3a, + /* (2^117)P */ 0x10, 0xa5, 0x25, 0x47, 0xff, 0xd0, 0xde, 0x0d, 0x03, 0xc5, 0x3f, 0x67, 0x10, 0xcc, 0xd8, 0x10, 0x89, 0x4e, 0x1f, 0x9f, 0x1c, 0x15, 0x9d, 0x5b, 0x4c, 0xa4, 0x09, 0xcb, 0xd5, 0xc1, 0xa5, 0x32, + /* (2^118)P */ 0xfb, 0x41, 0x05, 0xb9, 0x42, 0xa4, 0x0a, 0x1e, 0xdb, 0x85, 0xb4, 0xc1, 0x7c, 0xeb, 0x85, 0x5f, 0xe5, 0xf2, 0x9d, 0x8a, 0xce, 0x95, 0xe5, 0xbe, 0x36, 0x22, 0x42, 0x22, 0xc7, 0x96, 0xe4, 0x25, + /* (2^119)P */ 0xb9, 0xe5, 0x0f, 0xcd, 0x46, 0x3c, 0xdf, 0x5e, 0x88, 0x33, 0xa4, 0xd2, 0x7e, 0x5a, 0xe7, 0x34, 0x52, 0xe3, 0x61, 0xd7, 0x11, 0xde, 0x88, 0xe4, 0x5c, 0x54, 0x85, 0xa0, 0x01, 0x8a, 0x87, 0x0e, + /* (2^120)P */ 0x04, 0xbb, 0x21, 0xe0, 0x77, 0x3c, 0x49, 0xba, 0x9a, 0x89, 0xdf, 0xc7, 0x43, 0x18, 0x4d, 0x2b, 0x67, 0x0d, 0xe8, 0x7a, 0x48, 0x7a, 0xa3, 0x9e, 0x94, 0x17, 0xe4, 0x11, 0x80, 0x95, 0xa9, 0x67, + /* (2^121)P */ 0x65, 0xb0, 0x97, 0x66, 0x1a, 0x05, 0x58, 0x4b, 0xd4, 0xa6, 0x6b, 0x8d, 0x7d, 0x3f, 0xe3, 0x47, 0xc1, 0x46, 0xca, 0x83, 0xd4, 0xa8, 0x4d, 0xbb, 0x0d, 0xdb, 0xc2, 0x81, 0xa1, 0xca, 0xbe, 0x68, + /* (2^122)P */ 0xa5, 0x9a, 0x98, 0x0b, 0xe9, 0x80, 0x89, 0x8d, 0x9b, 0xc9, 0x93, 0x2c, 0x4a, 0xb1, 0x5e, 0xf9, 0xa2, 0x73, 0x6e, 0x79, 0xc4, 0xc7, 0xc6, 0x51, 0x69, 0xb5, 0xef, 0xb5, 0x63, 0x83, 0x22, 0x6e, + /* (2^123)P */ 0xc8, 0x24, 0xd6, 0x2d, 0xb0, 0xc0, 0xbb, 0xc6, 0xee, 0x70, 0x81, 0xec, 0x7d, 0xb4, 0x7e, 0x77, 0xa9, 0xaf, 0xcf, 0x04, 0xa0, 0x15, 0xde, 0x3c, 0x9b, 0xbf, 0x60, 0x71, 0x08, 0xbc, 0xc6, 0x1d, + /* (2^124)P */ 0x02, 0x40, 0xc3, 0xee, 0x43, 0xe0, 0x07, 0x2e, 0x7f, 0xdc, 0x68, 0x7a, 0x67, 0xfc, 0xe9, 0x18, 0x9a, 0x5b, 0xd1, 0x8b, 0x18, 0x03, 0xda, 0xd8, 0x53, 0x82, 0x56, 0x00, 0xbb, 0xc3, 0xfb, 0x48, + /* (2^125)P */ 0xe1, 0x4c, 0x65, 0xfb, 0x4c, 0x7d, 0x54, 0x57, 0xad, 0xe2, 0x58, 0xa0, 0x82, 0x5b, 0x56, 0xd3, 0x78, 0x44, 0x15, 0xbf, 0x0b, 0xaf, 0x3e, 0xf6, 0x18, 0xbb, 0xdf, 0x14, 0xf1, 0x1e, 0x53, 0x47, + /* (2^126)P */ 0x87, 0xc5, 0x78, 0x42, 0x0a, 0x63, 0xec, 0xe1, 0xf3, 0x83, 0x8e, 0xca, 0x46, 0xd5, 0x07, 0x55, 0x2b, 0x0c, 0xdc, 0x3a, 0xc6, 0x35, 0xe1, 0x85, 0x4e, 0x84, 0x82, 0x56, 0xa8, 0xef, 0xa7, 0x0a, + /* (2^127)P */ 0x15, 0xf6, 0xe1, 0xb3, 0xa8, 0x1b, 0x69, 0x72, 0xfa, 0x3f, 0xbe, 0x1f, 0x70, 0xe9, 0xb4, 0x32, 0x68, 0x78, 0xbb, 0x39, 0x2e, 0xd9, 0xb6, 0x97, 0xe8, 0x39, 0x2e, 0xa0, 0xde, 0x53, 0xfe, 0x2c, + /* (2^128)P */ 0xb0, 0x52, 0xcd, 0x85, 0xcd, 0x92, 0x73, 0x68, 0x31, 0x98, 0xe2, 0x10, 0xc9, 0x66, 0xff, 0x27, 0x06, 0x2d, 0x83, 0xa9, 0x56, 0x45, 0x13, 0x97, 0xa0, 0xf8, 0x84, 0x0a, 0x36, 0xb0, 0x9b, 0x26, + /* (2^129)P */ 0x5c, 0xf8, 0x43, 0x76, 0x45, 0x55, 0x6e, 0x70, 0x1b, 0x7d, 0x59, 0x9b, 0x8c, 0xa4, 0x34, 0x37, 0x72, 0xa4, 0xef, 0xc6, 0xe8, 0x91, 0xee, 0x7a, 0xe0, 0xd9, 0xa9, 0x98, 0xc1, 0xab, 0xd6, 0x5c, + /* (2^130)P */ 0x1a, 0xe4, 0x3c, 0xcb, 0x06, 0xde, 0x04, 0x0e, 0x38, 0xe1, 0x02, 0x34, 0x89, 0xeb, 0xc6, 0xd8, 0x72, 0x37, 0x6e, 0x68, 0xbb, 0x59, 0x46, 0x90, 0xc8, 0xa8, 0x6b, 0x74, 0x71, 0xc3, 0x15, 0x72, + /* (2^131)P */ 0xd9, 0xa2, 0xe4, 0xea, 0x7e, 0xa9, 0x12, 0xfd, 0xc5, 0xf2, 0x94, 0x63, 0x51, 0xb7, 0x14, 0x95, 0x94, 0xf2, 0x08, 0x92, 0x80, 0xd5, 0x6f, 0x26, 0xb9, 0x26, 0x9a, 0x61, 0x85, 0x70, 0x84, 0x5c, + /* (2^132)P */ 0xea, 0x94, 0xd6, 0xfe, 0x10, 0x54, 0x98, 0x52, 0x54, 0xd2, 0x2e, 0x4a, 0x93, 0x5b, 0x90, 0x3c, 0x67, 0xe4, 0x3b, 0x2d, 0x69, 0x47, 0xbb, 0x10, 0xe1, 0xe9, 0xe5, 0x69, 0x2d, 0x3d, 0x3b, 0x06, + /* (2^133)P */ 0xeb, 0x7d, 0xa5, 0xdd, 0xee, 0x26, 0x27, 0x47, 0x91, 0x18, 0xf4, 0x10, 0xae, 0xc4, 0xb6, 0xef, 0x14, 0x76, 0x30, 0x7b, 0x91, 0x41, 0x16, 0x2b, 0x7c, 0x5b, 0xf4, 0xc4, 0x4f, 0x55, 0x7c, 0x11, + /* (2^134)P */ 0x12, 0x88, 0x9d, 0x8f, 0x11, 0xf3, 0x7c, 0xc0, 0x39, 0x79, 0x01, 0x50, 0x20, 0xd8, 0xdb, 0x01, 0x27, 0x28, 0x1b, 0x17, 0xf4, 0x03, 0xe8, 0xd7, 0xea, 0x25, 0xd2, 0x87, 0x74, 0xe8, 0x15, 0x10, + /* (2^135)P */ 0x4d, 0xcc, 0x3a, 0xd2, 0xfe, 0xe3, 0x8d, 0xc5, 0x2d, 0xbe, 0xa7, 0x94, 0xc2, 0x91, 0xdb, 0x50, 0x57, 0xf4, 0x9c, 0x1c, 0x3d, 0xd4, 0x94, 0x0b, 0x4a, 0x52, 0x37, 0x6e, 0xfa, 0x40, 0x16, 0x6b, + /* (2^136)P */ 0x09, 0x0d, 0xda, 0x5f, 0x6c, 0x34, 0x2f, 0x69, 0x51, 0x31, 0x4d, 0xfa, 0x59, 0x1c, 0x0b, 0x20, 0x96, 0xa2, 0x77, 0x07, 0x76, 0x6f, 0xc4, 0xb8, 0xcf, 0xfb, 0xfd, 0x3f, 0x5f, 0x39, 0x38, 0x4b, + /* (2^137)P */ 0x71, 0xd6, 0x54, 0xbe, 0x00, 0x5e, 0xd2, 0x18, 0xa6, 0xab, 0xc8, 0xbe, 0x82, 0x05, 0xd5, 0x60, 0x82, 0xb9, 0x78, 0x3b, 0x26, 0x8f, 0xad, 0x87, 0x32, 0x04, 0xda, 0x9c, 0x4e, 0xf6, 0xfd, 0x50, + /* (2^138)P */ 0xf0, 0xdc, 0x78, 0xc5, 0xaa, 0x67, 0xf5, 0x90, 0x3b, 0x13, 0xa3, 0xf2, 0x0e, 0x9b, 0x1e, 0xef, 0x71, 0xde, 0xd9, 0x42, 0x92, 0xba, 0xeb, 0x0e, 0xc7, 0x01, 0x31, 0xf0, 0x9b, 0x3c, 0x47, 0x15, + /* (2^139)P */ 0x95, 0x80, 0xb7, 0x56, 0xae, 0xe8, 0x77, 0x7c, 0x8e, 0x07, 0x6f, 0x6e, 0x66, 0xe7, 0x78, 0xb6, 0x1f, 0xba, 0x48, 0x53, 0x61, 0xb9, 0xa0, 0x2d, 0x0b, 0x3f, 0x73, 0xff, 0xc1, 0x31, 0xf9, 0x7c, + /* (2^140)P */ 0x6c, 0x36, 0x0a, 0x0a, 0xf5, 0x57, 0xb3, 0x26, 0x32, 0xd7, 0x87, 0x2b, 0xf4, 0x8c, 0x70, 0xe9, 0xc0, 0xb2, 0x1c, 0xf9, 0xa5, 0xee, 0x3a, 0xc1, 0x4c, 0xbb, 0x43, 0x11, 0x99, 0x0c, 0xd9, 0x35, + /* (2^141)P */ 0xdc, 0xd9, 0xa0, 0xa9, 0x04, 0xc4, 0xc1, 0x47, 0x51, 0xd2, 0x72, 0x19, 0x45, 0x58, 0x9e, 0x65, 0x31, 0x8c, 0xb3, 0x73, 0xc4, 0xa8, 0x75, 0x38, 0x24, 0x1f, 0x56, 0x79, 0xd3, 0x9e, 0xbd, 0x1f, + /* (2^142)P */ 0x8d, 0xc2, 0x1e, 0xd4, 0x6f, 0xbc, 0xfa, 0x11, 0xca, 0x2d, 0x2a, 0xcd, 0xe3, 0xdf, 0xf8, 0x7e, 0x95, 0x45, 0x40, 0x8c, 0x5d, 0x3b, 0xe7, 0x72, 0x27, 0x2f, 0xb7, 0x54, 0x49, 0xfa, 0x35, 0x61, + /* (2^143)P */ 0x9c, 0xb6, 0x24, 0xde, 0xa2, 0x32, 0xfc, 0xcc, 0x88, 0x5d, 0x09, 0x1f, 0x8c, 0x69, 0x55, 0x3f, 0x29, 0xf9, 0xc3, 0x5a, 0xed, 0x50, 0x33, 0xbe, 0xeb, 0x7e, 0x47, 0xca, 0x06, 0xf8, 0x9b, 0x5e, + /* (2^144)P */ 0x68, 0x9f, 0x30, 0x3c, 0xb6, 0x8f, 0xce, 0xe9, 0xf4, 0xf9, 0xe1, 0x65, 0x35, 0xf6, 0x76, 0x53, 0xf1, 0x93, 0x63, 0x5a, 0xb3, 0xcf, 0xaf, 0xd1, 0x06, 0x35, 0x62, 0xe5, 0xed, 0xa1, 0x32, 0x66, + /* (2^145)P */ 0x4c, 0xed, 0x2d, 0x0c, 0x39, 0x6c, 0x7d, 0x0b, 0x1f, 0xcb, 0x04, 0xdf, 0x81, 0x32, 0xcb, 0x56, 0xc7, 0xc3, 0xec, 0x49, 0x12, 0x5a, 0x30, 0x66, 0x2a, 0xa7, 0x8c, 0xa3, 0x60, 0x8b, 0x58, 0x5d, + /* (2^146)P */ 0x2d, 0xf4, 0xe5, 0xe8, 0x78, 0xbf, 0xec, 0xa6, 0xec, 0x3e, 0x8a, 0x3c, 0x4b, 0xb4, 0xee, 0x86, 0x04, 0x16, 0xd2, 0xfb, 0x48, 0x9c, 0x21, 0xec, 0x31, 0x67, 0xc3, 0x17, 0xf5, 0x1a, 0xaf, 0x1a, + /* (2^147)P */ 0xe7, 0xbd, 0x69, 0x67, 0x83, 0xa2, 0x06, 0xc3, 0xdb, 0x2a, 0x1e, 0x2b, 0x62, 0x80, 0x82, 0x20, 0xa6, 0x94, 0xff, 0xfb, 0x1f, 0xf5, 0x27, 0x80, 0x6b, 0xf2, 0x24, 0x11, 0xce, 0xa1, 0xcf, 0x76, + /* (2^148)P */ 0xb6, 0xab, 0x22, 0x24, 0x56, 0x00, 0xeb, 0x18, 0xc3, 0x29, 0x8c, 0x8f, 0xd5, 0xc4, 0x77, 0xf3, 0x1a, 0x56, 0x31, 0xf5, 0x07, 0xc2, 0xbb, 0x4d, 0x27, 0x8a, 0x12, 0x82, 0xf0, 0xb7, 0x53, 0x02, + /* (2^149)P */ 0xe0, 0x17, 0x2c, 0xb6, 0x1c, 0x09, 0x1f, 0x3d, 0xa9, 0x28, 0x46, 0xd6, 0xab, 0xe1, 0x60, 0x48, 0x53, 0x42, 0x9d, 0x30, 0x36, 0x74, 0xd1, 0x52, 0x76, 0xe5, 0xfa, 0x3e, 0xe1, 0x97, 0x6f, 0x35, + /* (2^150)P */ 0x5b, 0x53, 0x50, 0xa1, 0x1a, 0xe1, 0x51, 0xd3, 0xcc, 0x78, 0xd8, 0x1d, 0xbb, 0x45, 0x6b, 0x3e, 0x98, 0x2c, 0xd9, 0xbe, 0x28, 0x61, 0x77, 0x0c, 0xb8, 0x85, 0x28, 0x03, 0x93, 0xae, 0x34, 0x1d, + /* (2^151)P */ 0xc3, 0xa4, 0x5b, 0xa8, 0x8c, 0x48, 0xa0, 0x4b, 0xce, 0xe6, 0x9c, 0x3c, 0xc3, 0x48, 0x53, 0x98, 0x70, 0xa7, 0xbd, 0x97, 0x6f, 0x4c, 0x12, 0x66, 0x4a, 0x12, 0x54, 0x06, 0x29, 0xa0, 0x81, 0x0f, + /* (2^152)P */ 0xfd, 0x86, 0x9b, 0x56, 0xa6, 0x9c, 0xd0, 0x9e, 0x2d, 0x9a, 0xaf, 0x18, 0xfd, 0x09, 0x10, 0x81, 0x0a, 0xc2, 0xd8, 0x93, 0x3f, 0xd0, 0x08, 0xff, 0x6b, 0xf2, 0xae, 0x9f, 0x19, 0x48, 0xa1, 0x52, + /* (2^153)P */ 0x73, 0x1b, 0x8d, 0x2d, 0xdc, 0xf9, 0x03, 0x3e, 0x70, 0x1a, 0x96, 0x73, 0x18, 0x80, 0x05, 0x42, 0x70, 0x59, 0xa3, 0x41, 0xf0, 0x87, 0xd9, 0xc0, 0x49, 0xd5, 0xc0, 0xa1, 0x15, 0x1f, 0xaa, 0x07, + /* (2^154)P */ 0x24, 0x72, 0xd2, 0x8c, 0xe0, 0x6c, 0xd4, 0xdf, 0x39, 0x42, 0x4e, 0x93, 0x4f, 0x02, 0x0a, 0x6d, 0x59, 0x7b, 0x89, 0x99, 0x63, 0x7a, 0x8a, 0x80, 0xa2, 0x95, 0x3d, 0xe1, 0xe9, 0x56, 0x45, 0x0a, + /* (2^155)P */ 0x45, 0x30, 0xc1, 0xe9, 0x1f, 0x99, 0x1a, 0xd2, 0xb8, 0x51, 0x77, 0xfe, 0x48, 0x85, 0x0e, 0x9b, 0x35, 0x00, 0xf3, 0x4b, 0xcb, 0x43, 0xa6, 0x5d, 0x21, 0xf7, 0x40, 0x39, 0xd6, 0x28, 0xdb, 0x77, + /* (2^156)P */ 0x11, 0x90, 0xdc, 0x4a, 0x61, 0xeb, 0x5e, 0xfc, 0xeb, 0x11, 0xc4, 0xe8, 0x9a, 0x41, 0x29, 0x52, 0x74, 0xcf, 0x1d, 0x7d, 0x78, 0xe7, 0xc3, 0x9e, 0xb5, 0x4c, 0x6e, 0x21, 0x3e, 0x05, 0x0d, 0x34, + /* (2^157)P */ 0xb4, 0xf2, 0x8d, 0xb4, 0x39, 0xaf, 0xc7, 0xca, 0x94, 0x0a, 0xa1, 0x71, 0x28, 0xec, 0xfa, 0xc0, 0xed, 0x75, 0xa5, 0x5c, 0x24, 0x69, 0x0a, 0x14, 0x4c, 0x3a, 0x27, 0x34, 0x71, 0xc3, 0xf1, 0x0c, + /* (2^158)P */ 0xa5, 0xb8, 0x24, 0xc2, 0x6a, 0x30, 0xee, 0xc8, 0xb0, 0x30, 0x49, 0xcb, 0x7c, 0xee, 0xea, 0x57, 0x4f, 0xe7, 0xcb, 0xaa, 0xbd, 0x06, 0xe8, 0xa1, 0x7d, 0x65, 0xeb, 0x2e, 0x74, 0x62, 0x9a, 0x7d, + /* (2^159)P */ 0x30, 0x48, 0x6c, 0x54, 0xef, 0xb6, 0xb6, 0x9e, 0x2e, 0x6e, 0xb3, 0xdd, 0x1f, 0xca, 0x5c, 0x88, 0x05, 0x71, 0x0d, 0xef, 0x83, 0xf3, 0xb9, 0xe6, 0x12, 0x04, 0x2e, 0x9d, 0xef, 0x4f, 0x65, 0x58, + /* (2^160)P */ 0x26, 0x8e, 0x0e, 0xbe, 0xff, 0xc4, 0x05, 0xa9, 0x6e, 0x81, 0x31, 0x9b, 0xdf, 0xe5, 0x2d, 0x94, 0xe1, 0x88, 0x2e, 0x80, 0x3f, 0x72, 0x7d, 0x49, 0x8d, 0x40, 0x2f, 0x60, 0xea, 0x4d, 0x68, 0x30, + /* (2^161)P */ 0x34, 0xcb, 0xe6, 0xa3, 0x78, 0xa2, 0xe5, 0x21, 0xc4, 0x1d, 0x15, 0x5b, 0x6f, 0x6e, 0xfb, 0xae, 0x15, 0xca, 0x77, 0x9d, 0x04, 0x8e, 0x0b, 0xb3, 0x81, 0x89, 0xb9, 0x53, 0xcf, 0xc9, 0xc3, 0x28, + /* (2^162)P */ 0x2a, 0xdd, 0x6c, 0x55, 0x21, 0xb7, 0x7f, 0x28, 0x74, 0x22, 0x02, 0x97, 0xa8, 0x7c, 0x31, 0x0d, 0x58, 0x32, 0x54, 0x3a, 0x42, 0xc7, 0x68, 0x74, 0x2f, 0x64, 0xb5, 0x4e, 0x46, 0x11, 0x7f, 0x4a, + /* (2^163)P */ 0xa6, 0x3a, 0x19, 0x4d, 0x77, 0xa4, 0x37, 0xa2, 0xa1, 0x29, 0x21, 0xa9, 0x6e, 0x98, 0x65, 0xd8, 0x88, 0x1a, 0x7c, 0xf8, 0xec, 0x15, 0xc5, 0x24, 0xeb, 0xf5, 0x39, 0x5f, 0x57, 0x03, 0x40, 0x60, + /* (2^164)P */ 0x27, 0x9b, 0x0a, 0x57, 0x89, 0xf1, 0xb9, 0x47, 0x78, 0x4b, 0x5e, 0x46, 0xde, 0xce, 0x98, 0x2b, 0x20, 0x5c, 0xb8, 0xdb, 0x51, 0xf5, 0x6d, 0x02, 0x01, 0x19, 0xe2, 0x47, 0x10, 0xd9, 0xfc, 0x74, + /* (2^165)P */ 0xa3, 0xbf, 0xc1, 0x23, 0x0a, 0xa9, 0xe2, 0x13, 0xf6, 0x19, 0x85, 0x47, 0x4e, 0x07, 0xb0, 0x0c, 0x44, 0xcf, 0xf6, 0x3a, 0xbe, 0xcb, 0xf1, 0x5f, 0xbe, 0x2d, 0x81, 0xbe, 0x38, 0x54, 0xfe, 0x67, + /* (2^166)P */ 0xb0, 0x05, 0x0f, 0xa4, 0x4f, 0xf6, 0x3c, 0xd1, 0x87, 0x37, 0x28, 0x32, 0x2f, 0xfb, 0x4d, 0x05, 0xea, 0x2a, 0x0d, 0x7f, 0x5b, 0x91, 0x73, 0x41, 0x4e, 0x0d, 0x61, 0x1f, 0x4f, 0x14, 0x2f, 0x48, + /* (2^167)P */ 0x34, 0x82, 0x7f, 0xb4, 0x01, 0x02, 0x21, 0xf6, 0x90, 0xb9, 0x70, 0x9e, 0x92, 0xe1, 0x0a, 0x5d, 0x7c, 0x56, 0x49, 0xb0, 0x55, 0xf4, 0xd7, 0xdc, 0x01, 0x6f, 0x91, 0xf0, 0xf1, 0xd0, 0x93, 0x7e, + /* (2^168)P */ 0xfa, 0xb4, 0x7d, 0x8a, 0xf1, 0xcb, 0x79, 0xdd, 0x2f, 0xc6, 0x74, 0x6f, 0xbf, 0x91, 0x83, 0xbe, 0xbd, 0x91, 0x82, 0x4b, 0xd1, 0x45, 0x71, 0x02, 0x05, 0x17, 0xbf, 0x2c, 0xea, 0x73, 0x5a, 0x58, + /* (2^169)P */ 0xb2, 0x0d, 0x8a, 0x92, 0x3e, 0xa0, 0x5c, 0x48, 0xe7, 0x57, 0x28, 0x74, 0xa5, 0x01, 0xfc, 0x10, 0xa7, 0x51, 0xd5, 0xd6, 0xdb, 0x2e, 0x48, 0x2f, 0x8a, 0xdb, 0x8f, 0x04, 0xb5, 0x33, 0x04, 0x0f, + /* (2^170)P */ 0x47, 0x62, 0xdc, 0xd7, 0x8d, 0x2e, 0xda, 0x60, 0x9a, 0x81, 0xd4, 0x8c, 0xd3, 0xc9, 0xb4, 0x88, 0x97, 0x66, 0xf6, 0x01, 0xc0, 0x3a, 0x03, 0x13, 0x75, 0x7d, 0x36, 0x3b, 0xfe, 0x24, 0x3b, 0x27, + /* (2^171)P */ 0xd4, 0xb9, 0xb3, 0x31, 0x6a, 0xf6, 0xe8, 0xc6, 0xd5, 0x49, 0xdf, 0x94, 0xa4, 0x14, 0x15, 0x28, 0xa7, 0x3d, 0xb2, 0xc8, 0xdf, 0x6f, 0x72, 0xd1, 0x48, 0xe5, 0xde, 0x03, 0xd1, 0xe7, 0x3a, 0x4b, + /* (2^172)P */ 0x7e, 0x9d, 0x4b, 0xce, 0x19, 0x6e, 0x25, 0xc6, 0x1c, 0xc6, 0xe3, 0x86, 0xf1, 0x5c, 0x5c, 0xff, 0x45, 0xc1, 0x8e, 0x4b, 0xa3, 0x3c, 0xc6, 0xac, 0x74, 0x65, 0xe6, 0xfe, 0x88, 0x18, 0x62, 0x74, + /* (2^173)P */ 0x1e, 0x0a, 0x29, 0x45, 0x96, 0x40, 0x6f, 0x95, 0x2e, 0x96, 0x3a, 0x26, 0xe3, 0xf8, 0x0b, 0xef, 0x7b, 0x64, 0xc2, 0x5e, 0xeb, 0x50, 0x6a, 0xed, 0x02, 0x75, 0xca, 0x9d, 0x3a, 0x28, 0x94, 0x06, + /* (2^174)P */ 0xd1, 0xdc, 0xa2, 0x43, 0x36, 0x96, 0x9b, 0x76, 0x53, 0x53, 0xfc, 0x09, 0xea, 0xc8, 0xb7, 0x42, 0xab, 0x7e, 0x39, 0x13, 0xee, 0x2a, 0x00, 0x4f, 0x3a, 0xd6, 0xb7, 0x19, 0x2c, 0x5e, 0x00, 0x63, + /* (2^175)P */ 0xea, 0x3b, 0x02, 0x63, 0xda, 0x36, 0x67, 0xca, 0xb7, 0x99, 0x2a, 0xb1, 0x6d, 0x7f, 0x6c, 0x96, 0xe1, 0xc5, 0x37, 0xc5, 0x90, 0x93, 0xe0, 0xac, 0xee, 0x89, 0xaa, 0xa1, 0x63, 0x60, 0x69, 0x0b, + /* (2^176)P */ 0xe5, 0x56, 0x8c, 0x28, 0x97, 0x3e, 0xb0, 0xeb, 0xe8, 0x8b, 0x8c, 0x93, 0x9f, 0x9f, 0x2a, 0x43, 0x71, 0x7f, 0x71, 0x5b, 0x3d, 0xa9, 0xa5, 0xa6, 0x97, 0x9d, 0x8f, 0xe1, 0xc3, 0xb4, 0x5f, 0x1a, + /* (2^177)P */ 0xce, 0xcd, 0x60, 0x1c, 0xad, 0xe7, 0x94, 0x1c, 0xa0, 0xc4, 0x02, 0xfc, 0x43, 0x2a, 0x20, 0xee, 0x20, 0x6a, 0xc4, 0x67, 0xd8, 0xe4, 0xaf, 0x8d, 0x58, 0x7b, 0xc2, 0x8a, 0x3c, 0x26, 0x10, 0x0a, + /* (2^178)P */ 0x4a, 0x2a, 0x43, 0xe4, 0xdf, 0xa9, 0xde, 0xd0, 0xc5, 0x77, 0x92, 0xbe, 0x7b, 0xf8, 0x6a, 0x85, 0x1a, 0xc7, 0x12, 0xc2, 0xac, 0x72, 0x84, 0xce, 0x91, 0x1e, 0xbb, 0x9b, 0x6d, 0x1b, 0x15, 0x6f, + /* (2^179)P */ 0x6a, 0xd5, 0xee, 0x7c, 0x52, 0x6c, 0x77, 0x26, 0xec, 0xfa, 0xf8, 0xfb, 0xb7, 0x1c, 0x21, 0x7d, 0xcc, 0x09, 0x46, 0xfd, 0xa6, 0x66, 0xae, 0x37, 0x42, 0x0c, 0x77, 0xd2, 0x02, 0xb7, 0x81, 0x1f, + /* (2^180)P */ 0x92, 0x83, 0xc5, 0xea, 0x57, 0xb0, 0xb0, 0x2f, 0x9d, 0x4e, 0x74, 0x29, 0xfe, 0x89, 0xdd, 0xe1, 0xf8, 0xb4, 0xbe, 0x17, 0xeb, 0xf8, 0x64, 0xc9, 0x1e, 0xd4, 0xa2, 0xc9, 0x73, 0x10, 0x57, 0x29, + /* (2^181)P */ 0x54, 0xe2, 0xc0, 0x81, 0x89, 0xa1, 0x48, 0xa9, 0x30, 0x28, 0xb2, 0x65, 0x9b, 0x36, 0xf6, 0x2d, 0xc6, 0xd3, 0xcf, 0x5f, 0xd7, 0xb2, 0x3e, 0xa3, 0x1f, 0xa0, 0x99, 0x41, 0xec, 0xd6, 0x8c, 0x07, + /* (2^182)P */ 0x2f, 0x0d, 0x90, 0xad, 0x41, 0x4a, 0x58, 0x4a, 0x52, 0x4c, 0xc7, 0xe2, 0x78, 0x2b, 0x14, 0x32, 0x78, 0xc9, 0x31, 0x84, 0x33, 0xe8, 0xc4, 0x68, 0xc2, 0x9f, 0x68, 0x08, 0x90, 0xea, 0x69, 0x7f, + /* (2^183)P */ 0x65, 0x82, 0xa3, 0x46, 0x1e, 0xc8, 0xf2, 0x52, 0xfd, 0x32, 0xa8, 0x04, 0x2d, 0x07, 0x78, 0xfd, 0x94, 0x9e, 0x35, 0x25, 0xfa, 0xd5, 0xd7, 0x8c, 0xd2, 0x29, 0xcc, 0x54, 0x74, 0x1b, 0xe7, 0x4d, + /* (2^184)P */ 0xc9, 0x6a, 0xda, 0x1e, 0xad, 0x60, 0xeb, 0x42, 0x3a, 0x9c, 0xc0, 0xdb, 0xdf, 0x37, 0xad, 0x0a, 0x91, 0xc1, 0x3c, 0xe3, 0x71, 0x4b, 0x00, 0x81, 0x3c, 0x80, 0x22, 0x51, 0x34, 0xbe, 0xe6, 0x44, + /* (2^185)P */ 0xdb, 0x20, 0x19, 0xba, 0x88, 0x83, 0xfe, 0x03, 0x08, 0xb0, 0x0d, 0x15, 0x32, 0x7c, 0xd5, 0xf5, 0x29, 0x0c, 0xf6, 0x1a, 0x28, 0xc4, 0xc8, 0x49, 0xee, 0x1a, 0x70, 0xde, 0x18, 0xb5, 0xed, 0x21, + /* (2^186)P */ 0x99, 0xdc, 0x06, 0x8f, 0x41, 0x3e, 0xb6, 0x7f, 0xb8, 0xd7, 0x66, 0xc1, 0x99, 0x0d, 0x46, 0xa4, 0x83, 0x0a, 0x52, 0xce, 0x48, 0x52, 0xdd, 0x24, 0x58, 0x83, 0x92, 0x2b, 0x71, 0xad, 0xc3, 0x5e, + /* (2^187)P */ 0x0f, 0x93, 0x17, 0xbd, 0x5f, 0x2a, 0x02, 0x15, 0xe3, 0x70, 0x25, 0xd8, 0x77, 0x4a, 0xf6, 0xa4, 0x12, 0x37, 0x78, 0x15, 0x69, 0x8d, 0xbc, 0x12, 0xbb, 0x0a, 0x62, 0xfc, 0xc0, 0x94, 0x81, 0x49, + /* (2^188)P */ 0x82, 0x6c, 0x68, 0x55, 0xd2, 0xd9, 0xa2, 0x38, 0xf0, 0x21, 0x3e, 0x19, 0xd9, 0x6b, 0x5c, 0x78, 0x84, 0x54, 0x4a, 0xb2, 0x1a, 0xc8, 0xd5, 0xe4, 0x89, 0x09, 0xe2, 0xb2, 0x60, 0x78, 0x30, 0x56, + /* (2^189)P */ 0xc4, 0x74, 0x4d, 0x8b, 0xf7, 0x55, 0x9d, 0x42, 0x31, 0x01, 0x35, 0x43, 0x46, 0x83, 0xf1, 0x22, 0xff, 0x1f, 0xc7, 0x98, 0x45, 0xc2, 0x60, 0x1e, 0xef, 0x83, 0x99, 0x97, 0x14, 0xf0, 0xf2, 0x59, + /* (2^190)P */ 0x44, 0x4a, 0x49, 0xeb, 0x56, 0x7d, 0xa4, 0x46, 0x8e, 0xa1, 0x36, 0xd6, 0x54, 0xa8, 0x22, 0x3e, 0x3b, 0x1c, 0x49, 0x74, 0x52, 0xe1, 0x46, 0xb3, 0xe7, 0xcd, 0x90, 0x53, 0x4e, 0xfd, 0xea, 0x2c, + /* (2^191)P */ 0x75, 0x66, 0x0d, 0xbe, 0x38, 0x85, 0x8a, 0xba, 0x23, 0x8e, 0x81, 0x50, 0xbb, 0x74, 0x90, 0x4b, 0xc3, 0x04, 0xd3, 0x85, 0x90, 0xb8, 0xda, 0xcb, 0xc4, 0x92, 0x61, 0xe5, 0xe0, 0x4f, 0xa2, 0x61, + /* (2^192)P */ 0xcb, 0x5b, 0x52, 0xdb, 0xe6, 0x15, 0x76, 0xcb, 0xca, 0xe4, 0x67, 0xa5, 0x35, 0x8c, 0x7d, 0xdd, 0x69, 0xdd, 0xfc, 0xca, 0x3a, 0x15, 0xb4, 0xe6, 0x66, 0x97, 0x3c, 0x7f, 0x09, 0x8e, 0x66, 0x2d, + /* (2^193)P */ 0xf0, 0x5e, 0xe5, 0x5c, 0x26, 0x7e, 0x7e, 0xa5, 0x67, 0xb9, 0xd4, 0x7c, 0x52, 0x4e, 0x9f, 0x5d, 0xe5, 0xd1, 0x2f, 0x49, 0x06, 0x36, 0xc8, 0xfb, 0xae, 0xf7, 0xc3, 0xb7, 0xbe, 0x52, 0x0d, 0x09, + /* (2^194)P */ 0x7c, 0x4d, 0x7b, 0x1e, 0x5a, 0x51, 0xb9, 0x09, 0xc0, 0x44, 0xda, 0x99, 0x25, 0x6a, 0x26, 0x1f, 0x04, 0x55, 0xc5, 0xe2, 0x48, 0x95, 0xc4, 0xa1, 0xcc, 0x15, 0x6f, 0x12, 0x87, 0x42, 0xf0, 0x7e, + /* (2^195)P */ 0x15, 0xef, 0x30, 0xbd, 0x9d, 0x65, 0xd1, 0xfe, 0x7b, 0x27, 0xe0, 0xc4, 0xee, 0xb9, 0x4a, 0x8b, 0x91, 0x32, 0xdf, 0xa5, 0x36, 0x62, 0x4d, 0x88, 0x88, 0xf7, 0x5c, 0xbf, 0xa6, 0x6e, 0xd9, 0x1f, + /* (2^196)P */ 0x9a, 0x0d, 0x19, 0x1f, 0x98, 0x61, 0xa1, 0x42, 0xc1, 0x52, 0x60, 0x7e, 0x50, 0x49, 0xd8, 0x61, 0xd5, 0x2c, 0x5a, 0x28, 0xbf, 0x13, 0xe1, 0x9f, 0xd8, 0x85, 0xad, 0xdb, 0x76, 0xd6, 0x22, 0x7c, + /* (2^197)P */ 0x7d, 0xd2, 0xfb, 0x2b, 0xed, 0x70, 0xe7, 0x82, 0xa5, 0xf5, 0x96, 0xe9, 0xec, 0xb2, 0x05, 0x4c, 0x50, 0x01, 0x90, 0xb0, 0xc2, 0xa9, 0x40, 0xcd, 0x64, 0xbf, 0xd9, 0x13, 0x92, 0x31, 0x95, 0x58, + /* (2^198)P */ 0x08, 0x2e, 0xea, 0x3f, 0x70, 0x5d, 0xcc, 0xe7, 0x8c, 0x18, 0xe2, 0x58, 0x12, 0x49, 0x0c, 0xb5, 0xf0, 0x5b, 0x20, 0x48, 0xaa, 0x0b, 0xe3, 0xcc, 0x62, 0x2d, 0xa3, 0xcf, 0x9c, 0x65, 0x7c, 0x53, + /* (2^199)P */ 0x88, 0xc0, 0xcf, 0x98, 0x3a, 0x62, 0xb6, 0x37, 0xa4, 0xac, 0xd6, 0xa4, 0x1f, 0xed, 0x9b, 0xfe, 0xb0, 0xd1, 0xa8, 0x56, 0x8e, 0x9b, 0xd2, 0x04, 0x75, 0x95, 0x51, 0x0b, 0xc4, 0x71, 0x5f, 0x72, + /* (2^200)P */ 0xe6, 0x9c, 0x33, 0xd0, 0x9c, 0xf8, 0xc7, 0x28, 0x8b, 0xc1, 0xdd, 0x69, 0x44, 0xb1, 0x67, 0x83, 0x2c, 0x65, 0xa1, 0xa6, 0x83, 0xda, 0x3a, 0x88, 0x17, 0x6c, 0x4d, 0x03, 0x74, 0x19, 0x5f, 0x58, + /* (2^201)P */ 0x88, 0x91, 0xb1, 0xf1, 0x66, 0xb2, 0xcf, 0x89, 0x17, 0x52, 0xc3, 0xe7, 0x63, 0x48, 0x3b, 0xe6, 0x6a, 0x52, 0xc0, 0xb4, 0xa6, 0x9d, 0x8c, 0xd8, 0x35, 0x46, 0x95, 0xf0, 0x9d, 0x5c, 0x03, 0x3e, + /* (2^202)P */ 0x9d, 0xde, 0x45, 0xfb, 0x12, 0x54, 0x9d, 0xdd, 0x0d, 0xf4, 0xcf, 0xe4, 0x32, 0x45, 0x68, 0xdd, 0x1c, 0x67, 0x1d, 0x15, 0x9b, 0x99, 0x5c, 0x4b, 0x90, 0xf6, 0xe7, 0x11, 0xc8, 0x2c, 0x8c, 0x2d, + /* (2^203)P */ 0x40, 0x5d, 0x05, 0x90, 0x1d, 0xbe, 0x54, 0x7f, 0x40, 0xaf, 0x4a, 0x46, 0xdf, 0xc5, 0x64, 0xa4, 0xbe, 0x17, 0xe9, 0xf0, 0x24, 0x96, 0x97, 0x33, 0x30, 0x6b, 0x35, 0x27, 0xc5, 0x8d, 0x01, 0x2c, + /* (2^204)P */ 0xd4, 0xb3, 0x30, 0xe3, 0x24, 0x50, 0x41, 0xa5, 0xd3, 0x52, 0x16, 0x69, 0x96, 0x3d, 0xff, 0x73, 0xf1, 0x59, 0x9b, 0xef, 0xc4, 0x42, 0xec, 0x94, 0x5a, 0x8e, 0xd0, 0x18, 0x16, 0x20, 0x47, 0x07, + /* (2^205)P */ 0x53, 0x1c, 0x41, 0xca, 0x8a, 0xa4, 0x6c, 0x4d, 0x19, 0x61, 0xa6, 0xcf, 0x2f, 0x5f, 0x41, 0x66, 0xff, 0x27, 0xe2, 0x51, 0x00, 0xd4, 0x4d, 0x9c, 0xeb, 0xf7, 0x02, 0x9a, 0xc0, 0x0b, 0x81, 0x59, + /* (2^206)P */ 0x1d, 0x10, 0xdc, 0xb3, 0x71, 0xb1, 0x7e, 0x2a, 0x8e, 0xf6, 0xfe, 0x9f, 0xb9, 0x5a, 0x1c, 0x44, 0xea, 0x59, 0xb3, 0x93, 0x9b, 0x5c, 0x02, 0x32, 0x2f, 0x11, 0x9d, 0x1e, 0xa7, 0xe0, 0x8c, 0x5e, + /* (2^207)P */ 0xfd, 0x03, 0x95, 0x42, 0x92, 0xcb, 0xcc, 0xbf, 0x55, 0x5d, 0x09, 0x2f, 0x75, 0xba, 0x71, 0xd2, 0x1e, 0x09, 0x2d, 0x97, 0x5e, 0xad, 0x5e, 0x34, 0xba, 0x03, 0x31, 0xa8, 0x11, 0xdf, 0xc8, 0x18, + /* (2^208)P */ 0x4c, 0x0f, 0xed, 0x9a, 0x9a, 0x94, 0xcd, 0x90, 0x7e, 0xe3, 0x60, 0x66, 0xcb, 0xf4, 0xd1, 0xc5, 0x0b, 0x2e, 0xc5, 0x56, 0x2d, 0xc5, 0xca, 0xb8, 0x0d, 0x8e, 0x80, 0xc5, 0x00, 0xe4, 0x42, 0x6e, + /* (2^209)P */ 0x23, 0xfd, 0xae, 0xee, 0x66, 0x69, 0xb4, 0xa3, 0xca, 0xcd, 0x9e, 0xe3, 0x0b, 0x1f, 0x4f, 0x0c, 0x1d, 0xa5, 0x83, 0xd6, 0xc9, 0xc8, 0x9d, 0x18, 0x1b, 0x35, 0x09, 0x4c, 0x05, 0x7f, 0xf2, 0x51, + /* (2^210)P */ 0x82, 0x06, 0x32, 0x2a, 0xcd, 0x7c, 0x48, 0x4c, 0x96, 0x1c, 0xdf, 0xb3, 0x5b, 0xa9, 0x7e, 0x58, 0xe8, 0xb8, 0x5c, 0x55, 0x9e, 0xf7, 0xcc, 0xc8, 0x3d, 0xd7, 0x06, 0xa2, 0x29, 0xc8, 0x7d, 0x54, + /* (2^211)P */ 0x06, 0x9b, 0xc3, 0x80, 0xcd, 0xa6, 0x22, 0xb8, 0xc6, 0xd4, 0x00, 0x20, 0x73, 0x54, 0x6d, 0xe9, 0x4d, 0x3b, 0x46, 0x91, 0x6f, 0x5b, 0x53, 0x28, 0x1d, 0x6e, 0x48, 0xe2, 0x60, 0x46, 0x8f, 0x22, + /* (2^212)P */ 0xbf, 0x3a, 0x8d, 0xde, 0x38, 0x95, 0x79, 0x98, 0x6e, 0xca, 0xeb, 0x45, 0x00, 0x33, 0xd8, 0x8c, 0x38, 0xe7, 0x21, 0x82, 0x00, 0x2a, 0x95, 0x79, 0xbb, 0xd2, 0x5c, 0x53, 0xa7, 0xe1, 0x22, 0x43, + /* (2^213)P */ 0x1c, 0x80, 0xd1, 0x19, 0x18, 0xc1, 0x14, 0xb1, 0xc7, 0x5e, 0x3f, 0x4f, 0xd8, 0xe4, 0x16, 0x20, 0x4c, 0x0f, 0x26, 0x09, 0xf4, 0x2d, 0x0e, 0xdd, 0x66, 0x72, 0x5f, 0xae, 0xc0, 0x62, 0xc3, 0x5e, + /* (2^214)P */ 0xee, 0xb4, 0xb2, 0xb8, 0x18, 0x2b, 0x46, 0xc0, 0xfb, 0x1a, 0x4d, 0x27, 0x50, 0xd9, 0xc8, 0x7c, 0xd2, 0x02, 0x6b, 0x43, 0x05, 0x71, 0x5f, 0xf2, 0xd3, 0xcc, 0xf9, 0xbf, 0xdc, 0xf8, 0xbb, 0x43, + /* (2^215)P */ 0xdf, 0xe9, 0x39, 0xa0, 0x67, 0x17, 0xad, 0xb6, 0x83, 0x35, 0x9d, 0xf6, 0xa8, 0x4d, 0x71, 0xb0, 0xf5, 0x31, 0x29, 0xb4, 0x18, 0xfa, 0x55, 0x5e, 0x61, 0x09, 0xc6, 0x33, 0x8f, 0x55, 0xd5, 0x4e, + /* (2^216)P */ 0xdd, 0xa5, 0x47, 0xc6, 0x01, 0x79, 0xe3, 0x1f, 0x57, 0xd3, 0x81, 0x80, 0x1f, 0xdf, 0x3d, 0x59, 0xa6, 0xd7, 0x3f, 0x81, 0xfd, 0xa4, 0x49, 0x02, 0x61, 0xaf, 0x9c, 0x4e, 0x27, 0xca, 0xac, 0x69, + /* (2^217)P */ 0xc9, 0x21, 0x07, 0x33, 0xea, 0xa3, 0x7b, 0x04, 0xa0, 0x1e, 0x7e, 0x0e, 0xc2, 0x3f, 0x42, 0x83, 0x60, 0x4a, 0x31, 0x01, 0xaf, 0xc0, 0xf4, 0x1d, 0x27, 0x95, 0x28, 0x89, 0xab, 0x2d, 0xa6, 0x09, + /* (2^218)P */ 0x00, 0xcb, 0xc6, 0x9c, 0xa4, 0x25, 0xb3, 0xa5, 0xb6, 0x6c, 0xb5, 0x54, 0xc6, 0x5d, 0x4b, 0xe9, 0xa0, 0x94, 0xc9, 0xad, 0x79, 0x87, 0xe2, 0x3b, 0xad, 0x4a, 0x3a, 0xba, 0xf8, 0xe8, 0x96, 0x42, + /* (2^219)P */ 0xab, 0x1e, 0x45, 0x1e, 0x76, 0x89, 0x86, 0x32, 0x4a, 0x59, 0x59, 0xff, 0x8b, 0x59, 0x4d, 0x2e, 0x4a, 0x08, 0xa7, 0xd7, 0x53, 0x68, 0xb9, 0x49, 0xa8, 0x20, 0x14, 0x60, 0x19, 0xa3, 0x80, 0x49, + /* (2^220)P */ 0x42, 0x2c, 0x55, 0x2f, 0xe1, 0xb9, 0x65, 0x95, 0x96, 0xfe, 0x00, 0x71, 0xdb, 0x18, 0x53, 0x8a, 0xd7, 0xd0, 0xad, 0x43, 0x4d, 0x0b, 0xc9, 0x05, 0xda, 0x4e, 0x5d, 0x6a, 0xd6, 0x4c, 0x8b, 0x53, + /* (2^221)P */ 0x9f, 0x03, 0x9f, 0xe8, 0xc3, 0x4f, 0xe9, 0xf4, 0x45, 0x80, 0x61, 0x6f, 0xf2, 0x9a, 0x2c, 0x59, 0x50, 0x95, 0x4b, 0xfd, 0xb5, 0x6e, 0xa3, 0x08, 0x19, 0x14, 0xed, 0xc2, 0xf6, 0xfa, 0xff, 0x25, + /* (2^222)P */ 0x54, 0xd3, 0x79, 0xcc, 0x59, 0x44, 0x43, 0x34, 0x6b, 0x47, 0xd5, 0xb1, 0xb4, 0xbf, 0xec, 0xee, 0x99, 0x5d, 0x61, 0x61, 0xa0, 0x34, 0xeb, 0xdd, 0x73, 0xb7, 0x64, 0xeb, 0xcc, 0xce, 0x29, 0x51, + /* (2^223)P */ 0x20, 0x35, 0x99, 0x94, 0x58, 0x21, 0x43, 0xee, 0x3b, 0x0b, 0x4c, 0xf1, 0x7c, 0x9c, 0x2f, 0x77, 0xd5, 0xda, 0xbe, 0x06, 0xe3, 0xfc, 0xe2, 0xd2, 0x97, 0x6a, 0xf0, 0x46, 0xb5, 0x42, 0x5f, 0x71, + /* (2^224)P */ 0x1a, 0x5f, 0x5b, 0xda, 0xce, 0xcd, 0x4e, 0x43, 0xa9, 0x41, 0x97, 0xa4, 0x15, 0x71, 0xa1, 0x0d, 0x2e, 0xad, 0xed, 0x73, 0x7c, 0xd7, 0x0b, 0x68, 0x41, 0x90, 0xdd, 0x4e, 0x35, 0x02, 0x7c, 0x48, + /* (2^225)P */ 0xc4, 0xd9, 0x0e, 0xa7, 0xf3, 0xef, 0xef, 0xb8, 0x02, 0xe3, 0x57, 0xe8, 0xa3, 0x2a, 0xa3, 0x56, 0xa0, 0xa5, 0xa2, 0x48, 0xbd, 0x68, 0x3a, 0xdf, 0x44, 0xc4, 0x76, 0x31, 0xb7, 0x50, 0xf6, 0x07, + /* (2^226)P */ 0xb1, 0xcc, 0xe0, 0x26, 0x16, 0x9b, 0x8b, 0xe3, 0x36, 0xfb, 0x09, 0x8b, 0xc1, 0x53, 0xe0, 0x79, 0x64, 0x49, 0xf9, 0xc9, 0x19, 0x03, 0xd9, 0x56, 0xc4, 0xf5, 0x9f, 0xac, 0xe7, 0x41, 0xa9, 0x1c, + /* (2^227)P */ 0xbb, 0xa0, 0x2f, 0x16, 0x29, 0xdf, 0xc4, 0x49, 0x05, 0x33, 0xb3, 0x82, 0x32, 0xcf, 0x88, 0x84, 0x7d, 0x43, 0xbb, 0xca, 0x14, 0xda, 0xdf, 0x95, 0x86, 0xad, 0xd5, 0x64, 0x82, 0xf7, 0x91, 0x33, + /* (2^228)P */ 0x5d, 0x09, 0xb5, 0xe2, 0x6a, 0xe0, 0x9a, 0x72, 0x46, 0xa9, 0x59, 0x32, 0xd7, 0x58, 0x8a, 0xd5, 0xed, 0x21, 0x39, 0xd1, 0x62, 0x42, 0x83, 0xe9, 0x92, 0xb5, 0x4b, 0xa5, 0xfa, 0xda, 0xfe, 0x27, + /* (2^229)P */ 0xbb, 0x48, 0xad, 0x29, 0xb8, 0xc5, 0x9d, 0xa9, 0x60, 0xe2, 0x9e, 0x49, 0x42, 0x57, 0x02, 0x5f, 0xfd, 0x13, 0x75, 0x5d, 0xcd, 0x8e, 0x2c, 0x80, 0x38, 0xd9, 0x6d, 0x3f, 0xef, 0xb3, 0xce, 0x78, + /* (2^230)P */ 0x94, 0x5d, 0x13, 0x8a, 0x4f, 0xf4, 0x42, 0xc3, 0xa3, 0xdd, 0x8c, 0x82, 0x44, 0xdb, 0x9e, 0x7b, 0xe7, 0xcf, 0x37, 0x05, 0x1a, 0xd1, 0x36, 0x94, 0xc8, 0xb4, 0x1a, 0xec, 0x64, 0xb1, 0x64, 0x50, + /* (2^231)P */ 0xfc, 0xb2, 0x7e, 0xd3, 0xcf, 0xec, 0x20, 0x70, 0xfc, 0x25, 0x0d, 0xd9, 0x3e, 0xea, 0x31, 0x1f, 0x34, 0xbb, 0xa1, 0xdf, 0x7b, 0x0d, 0x93, 0x1b, 0x44, 0x30, 0x11, 0x48, 0x7a, 0x46, 0x44, 0x53, + /* (2^232)P */ 0xfb, 0x6d, 0x5e, 0xf2, 0x70, 0x31, 0x07, 0x70, 0xc8, 0x4c, 0x11, 0x50, 0x1a, 0xdc, 0x85, 0xe3, 0x00, 0x4f, 0xfc, 0xc8, 0x8a, 0x69, 0x48, 0x23, 0xd8, 0x40, 0xdd, 0x84, 0x52, 0xa5, 0x77, 0x2a, + /* (2^233)P */ 0xe4, 0x6c, 0x8c, 0xc9, 0xe0, 0xaf, 0x06, 0xfe, 0xe4, 0xd6, 0xdf, 0xdd, 0x96, 0xdf, 0x35, 0xc2, 0xd3, 0x1e, 0xbf, 0x33, 0x1e, 0xd0, 0x28, 0x14, 0xaf, 0xbd, 0x00, 0x93, 0xec, 0x68, 0x57, 0x78, + /* (2^234)P */ 0x3b, 0xb6, 0xde, 0x91, 0x7a, 0xe5, 0x02, 0x97, 0x80, 0x8b, 0xce, 0xe5, 0xbf, 0xb8, 0xbd, 0x61, 0xac, 0x58, 0x1d, 0x3d, 0x6f, 0x42, 0x5b, 0x64, 0xbc, 0x57, 0xa5, 0x27, 0x22, 0xa8, 0x04, 0x48, + /* (2^235)P */ 0x01, 0x26, 0x4d, 0xb4, 0x8a, 0x04, 0x57, 0x8e, 0x35, 0x69, 0x3a, 0x4b, 0x1a, 0x50, 0xd6, 0x68, 0x93, 0xc2, 0xe1, 0xf9, 0xc3, 0x9e, 0x9c, 0xc3, 0xe2, 0x63, 0xde, 0xd4, 0x57, 0xf2, 0x72, 0x41, + /* (2^236)P */ 0x01, 0x64, 0x0c, 0x33, 0x50, 0xb4, 0x68, 0xd3, 0x91, 0x23, 0x8f, 0x41, 0x17, 0x30, 0x0d, 0x04, 0x0d, 0xd9, 0xb7, 0x90, 0x60, 0xbb, 0x34, 0x2c, 0x1f, 0xd5, 0xdf, 0x8f, 0x22, 0x49, 0xf6, 0x16, + /* (2^237)P */ 0xf5, 0x8e, 0x92, 0x2b, 0x8e, 0x81, 0xa6, 0xbe, 0x72, 0x1e, 0xc1, 0xcd, 0x91, 0xcf, 0x8c, 0xe2, 0xcd, 0x36, 0x7a, 0xe7, 0x68, 0xaa, 0x4a, 0x59, 0x0f, 0xfd, 0x7f, 0x6c, 0x80, 0x34, 0x30, 0x31, + /* (2^238)P */ 0x65, 0xbd, 0x49, 0x22, 0xac, 0x27, 0x9d, 0x8a, 0x12, 0x95, 0x8e, 0x01, 0x64, 0xb4, 0xa3, 0x19, 0xc7, 0x7e, 0xb3, 0x52, 0xf3, 0xcf, 0x6c, 0xc2, 0x21, 0x7b, 0x79, 0x1d, 0x34, 0x68, 0x6f, 0x05, + /* (2^239)P */ 0x27, 0x23, 0xfd, 0x7e, 0x75, 0xd6, 0x79, 0x5e, 0x15, 0xfe, 0x3a, 0x55, 0xb6, 0xbc, 0xbd, 0xfa, 0x60, 0x5a, 0xaf, 0x6e, 0x2c, 0x22, 0xe7, 0xd3, 0x3b, 0x74, 0xae, 0x4d, 0x6d, 0xc7, 0x46, 0x70, + /* (2^240)P */ 0x55, 0x4a, 0x8d, 0xb1, 0x72, 0xe8, 0x0b, 0x66, 0x96, 0x14, 0x4e, 0x57, 0x18, 0x25, 0x99, 0x19, 0xbb, 0xdc, 0x2b, 0x30, 0x3a, 0x05, 0x03, 0xc1, 0x8e, 0x8e, 0x21, 0x0b, 0x80, 0xe9, 0xd8, 0x3e, + /* (2^241)P */ 0x3e, 0xe0, 0x75, 0xfa, 0x39, 0x92, 0x0b, 0x7b, 0x83, 0xc0, 0x33, 0x46, 0x68, 0xfb, 0xe9, 0xef, 0x93, 0x77, 0x1a, 0x39, 0xbe, 0x5f, 0xa3, 0x98, 0x34, 0xfe, 0xd0, 0xe2, 0x0f, 0x51, 0x65, 0x60, + /* (2^242)P */ 0x0c, 0xad, 0xab, 0x48, 0x85, 0x66, 0xcb, 0x55, 0x27, 0xe5, 0x87, 0xda, 0x48, 0x45, 0x58, 0xb4, 0xdd, 0xc1, 0x07, 0x01, 0xea, 0xec, 0x43, 0x2c, 0x35, 0xde, 0x72, 0x93, 0x80, 0x28, 0x60, 0x52, + /* (2^243)P */ 0x1f, 0x3b, 0x21, 0xf9, 0x6a, 0xc5, 0x15, 0x34, 0xdb, 0x98, 0x7e, 0x01, 0x4d, 0x1a, 0xee, 0x5b, 0x9b, 0x70, 0xcf, 0xb5, 0x05, 0xb1, 0xf6, 0x13, 0xb6, 0x9a, 0xb2, 0x82, 0x34, 0x0e, 0xf2, 0x5f, + /* (2^244)P */ 0x90, 0x6c, 0x2e, 0xcc, 0x75, 0x9c, 0xa2, 0x0a, 0x06, 0xe2, 0x70, 0x3a, 0xca, 0x73, 0x7d, 0xfc, 0x15, 0xc5, 0xb5, 0xc4, 0x8f, 0xc3, 0x9f, 0x89, 0x07, 0xc2, 0xff, 0x24, 0xb1, 0x86, 0x03, 0x25, + /* (2^245)P */ 0x56, 0x2b, 0x3d, 0xae, 0xd5, 0x28, 0xea, 0x54, 0xce, 0x60, 0xde, 0xd6, 0x9d, 0x14, 0x13, 0x99, 0xc1, 0xd6, 0x06, 0x8f, 0xc5, 0x4f, 0x69, 0x16, 0xc7, 0x8f, 0x01, 0xeb, 0x75, 0x39, 0xb2, 0x46, + /* (2^246)P */ 0xe2, 0xb4, 0xb7, 0xb4, 0x0f, 0x6a, 0x0a, 0x47, 0xde, 0x53, 0x72, 0x8f, 0x5a, 0x47, 0x92, 0x5d, 0xdb, 0x3a, 0xbd, 0x2f, 0xb5, 0xe5, 0xee, 0xab, 0x68, 0x69, 0x80, 0xa0, 0x01, 0x08, 0xa2, 0x7f, + /* (2^247)P */ 0xd2, 0x14, 0x77, 0x9f, 0xf1, 0xfa, 0xf3, 0x76, 0xc3, 0x60, 0x46, 0x2f, 0xc1, 0x40, 0xe8, 0xb3, 0x4e, 0x74, 0x12, 0xf2, 0x8d, 0xcd, 0xb4, 0x0f, 0xd2, 0x2d, 0x3a, 0x1d, 0x25, 0x5a, 0x06, 0x4b, + /* (2^248)P */ 0x4a, 0xcd, 0x77, 0x3d, 0x38, 0xde, 0xeb, 0x5c, 0xb1, 0x9c, 0x2c, 0x88, 0xdf, 0x39, 0xdf, 0x6a, 0x59, 0xf7, 0x9a, 0xb0, 0x2e, 0x24, 0xdd, 0xa2, 0x22, 0x64, 0x5f, 0x0e, 0xe5, 0xc0, 0x47, 0x31, + /* (2^249)P */ 0xdb, 0x50, 0x13, 0x1d, 0x10, 0xa5, 0x4c, 0x16, 0x62, 0xc9, 0x3f, 0xc3, 0x79, 0x34, 0xd1, 0xf8, 0x08, 0xda, 0xe5, 0x13, 0x4d, 0xce, 0x40, 0xe6, 0xba, 0xf8, 0x61, 0x50, 0xc4, 0xe0, 0xde, 0x4b, + /* (2^250)P */ 0xc9, 0xb1, 0xed, 0xa4, 0xc1, 0x6d, 0xc4, 0xd7, 0x8a, 0xd9, 0x7f, 0x43, 0xb6, 0xd7, 0x14, 0x55, 0x0b, 0xc0, 0xa1, 0xb2, 0x6b, 0x2f, 0x94, 0x58, 0x0e, 0x71, 0x70, 0x1d, 0xab, 0xb2, 0xff, 0x2d, + /* (2^251)P */ 0x68, 0x6d, 0x8b, 0xc1, 0x2f, 0xcf, 0xdf, 0xcc, 0x67, 0x61, 0x80, 0xb7, 0xa8, 0xcb, 0xeb, 0xa8, 0xe3, 0x37, 0x29, 0x5e, 0xf9, 0x97, 0x06, 0x98, 0x8c, 0x6e, 0x12, 0xd0, 0x1c, 0xba, 0xfb, 0x02, + /* (2^252)P */ 0x65, 0x45, 0xff, 0xad, 0x60, 0xc3, 0x98, 0xcb, 0x19, 0x15, 0xdb, 0x4b, 0xd2, 0x01, 0x71, 0x44, 0xd5, 0x15, 0xfb, 0x75, 0x74, 0xc8, 0xc4, 0x98, 0x7d, 0xa2, 0x22, 0x6e, 0x6d, 0xc7, 0xf8, 0x05, + /* (2^253)P */ 0x94, 0xf4, 0xb9, 0xfe, 0xdf, 0xe5, 0x69, 0xab, 0x75, 0x6b, 0x40, 0x18, 0x9d, 0xc7, 0x09, 0xae, 0x1d, 0x2d, 0xa4, 0x94, 0xfb, 0x45, 0x9b, 0x19, 0x84, 0xfa, 0x2a, 0xae, 0xeb, 0x0a, 0x71, 0x79, + /* (2^254)P */ 0xdf, 0xd2, 0x34, 0xf3, 0xa7, 0xed, 0xad, 0xa6, 0xb4, 0x57, 0x2a, 0xaf, 0x51, 0x9c, 0xde, 0x7b, 0xa8, 0xea, 0xdc, 0x86, 0x4f, 0xc6, 0x8f, 0xa9, 0x7b, 0xd0, 0x0e, 0xc2, 0x35, 0x03, 0xbe, 0x6b, + /* (2^255)P */ 0x44, 0x43, 0x98, 0x53, 0xbe, 0xdc, 0x7f, 0x66, 0xa8, 0x49, 0x59, 0x00, 0x1c, 0xbc, 0x72, 0x07, 0x8e, 0xd6, 0xbe, 0x4e, 0x9f, 0xa4, 0x07, 0xba, 0xbf, 0x30, 0xdf, 0xba, 0x85, 0xb0, 0xa7, 0x1f, +} diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve.go b/vendor/github.com/cloudflare/circl/dh/x448/curve.go new file mode 100644 index 00000000..d59564e4 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve.go @@ -0,0 +1,104 @@ +package x448 + +import ( + fp "github.com/cloudflare/circl/math/fp448" +) + +// ladderJoye calculates a fixed-point multiplication with the generator point. +// The algorithm is the right-to-left Joye's ladder as described +// in "How to precompute a ladder" in SAC'2017. +func ladderJoye(k *Key) { + w := [5]fp.Elt{} // [mu,x1,z1,x2,z2] order must be preserved. + w[1] = fp.Elt{ // x1 = S + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } + fp.SetOne(&w[2]) // z1 = 1 + w[3] = fp.Elt{ // x2 = G-S + 0x20, 0x27, 0x9d, 0xc9, 0x7d, 0x19, 0xb1, 0xac, + 0xf8, 0xba, 0x69, 0x1c, 0xff, 0x33, 0xac, 0x23, + 0x51, 0x1b, 0xce, 0x3a, 0x64, 0x65, 0xbd, 0xf1, + 0x23, 0xf8, 0xc1, 0x84, 0x9d, 0x45, 0x54, 0x29, + 0x67, 0xb9, 0x81, 0x1c, 0x03, 0xd1, 0xcd, 0xda, + 0x7b, 0xeb, 0xff, 0x1a, 0x88, 0x03, 0xcf, 0x3a, + 0x42, 0x44, 0x32, 0x01, 0x25, 0xb7, 0xfa, 0xf0, + } + fp.SetOne(&w[4]) // z2 = 1 + + const n = 448 + const h = 2 + swap := uint(1) + for s := 0; s < n-h; s++ { + i := (s + h) / 8 + j := (s + h) % 8 + bit := uint((k[i] >> uint(j)) & 1) + copy(w[0][:], tableGenerator[s*Size:(s+1)*Size]) + diffAdd(&w, swap^bit) + swap = bit + } + for s := 0; s < h; s++ { + double(&w[1], &w[2]) + } + toAffine((*[fp.Size]byte)(k), &w[1], &w[2]) +} + +// ladderMontgomery calculates a generic scalar point multiplication +// The algorithm implemented is the left-to-right Montgomery's ladder. +func ladderMontgomery(k, xP *Key) { + w := [5]fp.Elt{} // [x1, x2, z2, x3, z3] order must be preserved. + w[0] = *(*fp.Elt)(xP) // x1 = xP + fp.SetOne(&w[1]) // x2 = 1 + w[3] = *(*fp.Elt)(xP) // x3 = xP + fp.SetOne(&w[4]) // z3 = 1 + + move := uint(0) + for s := 448 - 1; s >= 0; s-- { + i := s / 8 + j := s % 8 + bit := uint((k[i] >> uint(j)) & 1) + ladderStep(&w, move^bit) + move = bit + } + toAffine((*[fp.Size]byte)(k), &w[1], &w[2]) +} + +func toAffine(k *[fp.Size]byte, x, z *fp.Elt) { + fp.Inv(z, z) + fp.Mul(x, x, z) + _ = fp.ToBytes(k[:], x) +} + +var lowOrderPoints = [3]fp.Elt{ + { /* (0,_,1) point of order 2 on Curve448 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + { /* (1,_,1) a point of order 4 on the twist of Curve448 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + { /* (-1,_,1) point of order 4 on Curve448 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, +} diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.go b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.go new file mode 100644 index 00000000..a0622666 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.go @@ -0,0 +1,30 @@ +//go:build amd64 && !purego +// +build amd64,!purego + +package x448 + +import ( + fp "github.com/cloudflare/circl/math/fp448" + "golang.org/x/sys/cpu" +) + +var hasBmi2Adx = cpu.X86.HasBMI2 && cpu.X86.HasADX + +var _ = hasBmi2Adx + +func double(x, z *fp.Elt) { doubleAmd64(x, z) } +func diffAdd(w *[5]fp.Elt, b uint) { diffAddAmd64(w, b) } +func ladderStep(w *[5]fp.Elt, b uint) { ladderStepAmd64(w, b) } +func mulA24(z, x *fp.Elt) { mulA24Amd64(z, x) } + +//go:noescape +func doubleAmd64(x, z *fp.Elt) + +//go:noescape +func diffAddAmd64(w *[5]fp.Elt, b uint) + +//go:noescape +func ladderStepAmd64(w *[5]fp.Elt, b uint) + +//go:noescape +func mulA24Amd64(z, x *fp.Elt) diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.h b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.h new file mode 100644 index 00000000..8c1ae4d0 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.h @@ -0,0 +1,111 @@ +#define ladderStepLeg \ + addSub(x2,z2) \ + addSub(x3,z3) \ + integerMulLeg(b0,x2,z3) \ + integerMulLeg(b1,x3,z2) \ + reduceFromDoubleLeg(t0,b0) \ + reduceFromDoubleLeg(t1,b1) \ + addSub(t0,t1) \ + cselect(x2,x3,regMove) \ + cselect(z2,z3,regMove) \ + integerSqrLeg(b0,t0) \ + integerSqrLeg(b1,t1) \ + reduceFromDoubleLeg(x3,b0) \ + reduceFromDoubleLeg(z3,b1) \ + integerMulLeg(b0,x1,z3) \ + reduceFromDoubleLeg(z3,b0) \ + integerSqrLeg(b0,x2) \ + integerSqrLeg(b1,z2) \ + reduceFromDoubleLeg(x2,b0) \ + reduceFromDoubleLeg(z2,b1) \ + subtraction(t0,x2,z2) \ + multiplyA24Leg(t1,t0) \ + additionLeg(t1,t1,z2) \ + integerMulLeg(b0,x2,z2) \ + integerMulLeg(b1,t0,t1) \ + reduceFromDoubleLeg(x2,b0) \ + reduceFromDoubleLeg(z2,b1) + +#define ladderStepBmi2Adx \ + addSub(x2,z2) \ + addSub(x3,z3) \ + integerMulAdx(b0,x2,z3) \ + integerMulAdx(b1,x3,z2) \ + reduceFromDoubleAdx(t0,b0) \ + reduceFromDoubleAdx(t1,b1) \ + addSub(t0,t1) \ + cselect(x2,x3,regMove) \ + cselect(z2,z3,regMove) \ + integerSqrAdx(b0,t0) \ + integerSqrAdx(b1,t1) \ + reduceFromDoubleAdx(x3,b0) \ + reduceFromDoubleAdx(z3,b1) \ + integerMulAdx(b0,x1,z3) \ + reduceFromDoubleAdx(z3,b0) \ + integerSqrAdx(b0,x2) \ + integerSqrAdx(b1,z2) \ + reduceFromDoubleAdx(x2,b0) \ + reduceFromDoubleAdx(z2,b1) \ + subtraction(t0,x2,z2) \ + multiplyA24Adx(t1,t0) \ + additionAdx(t1,t1,z2) \ + integerMulAdx(b0,x2,z2) \ + integerMulAdx(b1,t0,t1) \ + reduceFromDoubleAdx(x2,b0) \ + reduceFromDoubleAdx(z2,b1) + +#define difAddLeg \ + addSub(x1,z1) \ + integerMulLeg(b0,z1,ui) \ + reduceFromDoubleLeg(z1,b0) \ + addSub(x1,z1) \ + integerSqrLeg(b0,x1) \ + integerSqrLeg(b1,z1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) \ + integerMulLeg(b0,x1,z2) \ + integerMulLeg(b1,z1,x2) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) + +#define difAddBmi2Adx \ + addSub(x1,z1) \ + integerMulAdx(b0,z1,ui) \ + reduceFromDoubleAdx(z1,b0) \ + addSub(x1,z1) \ + integerSqrAdx(b0,x1) \ + integerSqrAdx(b1,z1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) \ + integerMulAdx(b0,x1,z2) \ + integerMulAdx(b1,z1,x2) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) + +#define doubleLeg \ + addSub(x1,z1) \ + integerSqrLeg(b0,x1) \ + integerSqrLeg(b1,z1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) \ + subtraction(t0,x1,z1) \ + multiplyA24Leg(t1,t0) \ + additionLeg(t1,t1,z1) \ + integerMulLeg(b0,x1,z1) \ + integerMulLeg(b1,t0,t1) \ + reduceFromDoubleLeg(x1,b0) \ + reduceFromDoubleLeg(z1,b1) + +#define doubleBmi2Adx \ + addSub(x1,z1) \ + integerSqrAdx(b0,x1) \ + integerSqrAdx(b1,z1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) \ + subtraction(t0,x1,z1) \ + multiplyA24Adx(t1,t0) \ + additionAdx(t1,t1,z1) \ + integerMulAdx(b0,x1,z1) \ + integerMulAdx(b1,t0,t1) \ + reduceFromDoubleAdx(x1,b0) \ + reduceFromDoubleAdx(z1,b1) diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s new file mode 100644 index 00000000..810aa9e6 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s @@ -0,0 +1,193 @@ +// +build amd64 + +#include "textflag.h" + +// Depends on circl/math/fp448 package +#include "../../math/fp448/fp_amd64.h" +#include "curve_amd64.h" + +// CTE_A24 is (A+2)/4 from Curve448 +#define CTE_A24 39082 + +#define Size 56 + +// multiplyA24Leg multiplies x times CTE_A24 and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64, cmov, adx +#define multiplyA24Leg(z,x) \ + MOVQ $CTE_A24, R15; \ + MOVQ 0+x, AX; MULQ R15; MOVQ AX, R8; ;;;;;;;;;;;; MOVQ DX, R9; \ + MOVQ 8+x, AX; MULQ R15; ADDQ AX, R9; ADCQ $0, DX; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R15; ADDQ AX, R10; ADCQ $0, DX; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R15; ADDQ AX, R11; ADCQ $0, DX; MOVQ DX, R12; \ + MOVQ 32+x, AX; MULQ R15; ADDQ AX, R12; ADCQ $0, DX; MOVQ DX, R13; \ + MOVQ 40+x, AX; MULQ R15; ADDQ AX, R13; ADCQ $0, DX; MOVQ DX, R14; \ + MOVQ 48+x, AX; MULQ R15; ADDQ AX, R14; ADCQ $0, DX; \ + MOVQ DX, AX; \ + SHLQ $32, AX; \ + ADDQ DX, R8; MOVQ $0, DX; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ AX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + ADCQ $0, DX; \ + MOVQ DX, AX; \ + SHLQ $32, AX; \ + ADDQ DX, R8; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ AX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + MOVQ R8, 0+z; \ + MOVQ R9, 8+z; \ + MOVQ R10, 16+z; \ + MOVQ R11, 24+z; \ + MOVQ R12, 32+z; \ + MOVQ R13, 40+z; \ + MOVQ R14, 48+z; + +// multiplyA24Adx multiplies x times CTE_A24 and stores in z +// Uses: AX, DX, R8-R14, FLAGS +// Instr: x86_64, bmi2 +#define multiplyA24Adx(z,x) \ + MOVQ $CTE_A24, DX; \ + MULXQ 0+x, R8, R9; \ + MULXQ 8+x, AX, R10; ADDQ AX, R9; \ + MULXQ 16+x, AX, R11; ADCQ AX, R10; \ + MULXQ 24+x, AX, R12; ADCQ AX, R11; \ + MULXQ 32+x, AX, R13; ADCQ AX, R12; \ + MULXQ 40+x, AX, R14; ADCQ AX, R13; \ + MULXQ 48+x, AX, DX; ADCQ AX, R14; \ + ;;;;;;;;;;;;;;;;;;;; ADCQ $0, DX; \ + MOVQ DX, AX; \ + SHLQ $32, AX; \ + ADDQ DX, R8; MOVQ $0, DX; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ AX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + ADCQ $0, DX; \ + MOVQ DX, AX; \ + SHLQ $32, AX; \ + ADDQ DX, R8; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ AX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + MOVQ R8, 0+z; \ + MOVQ R9, 8+z; \ + MOVQ R10, 16+z; \ + MOVQ R11, 24+z; \ + MOVQ R12, 32+z; \ + MOVQ R13, 40+z; \ + MOVQ R14, 48+z; + +#define mulA24Legacy \ + multiplyA24Leg(0(DI),0(SI)) +#define mulA24Bmi2Adx \ + multiplyA24Adx(0(DI),0(SI)) + +// func mulA24Amd64(z, x *fp448.Elt) +TEXT ·mulA24Amd64(SB),NOSPLIT,$0-16 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + CHECK_BMI2ADX(LMA24, mulA24Legacy, mulA24Bmi2Adx) + +// func ladderStepAmd64(w *[5]fp448.Elt, b uint) +// ladderStepAmd64 calculates a point addition and doubling as follows: +// (x2,z2) = 2*(x2,z2) and (x3,z3) = (x2,z2)+(x3,z3) using as a difference (x1,-). +// w = {x1,x2,z2,x3,z4} are five fp255.Elt of 56 bytes. +// stack = (t0,t1) are two fp.Elt of fp.Size bytes, and +// (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +TEXT ·ladderStepAmd64(SB),NOSPLIT,$336-16 + // Parameters + #define regWork DI + #define regMove SI + #define x1 0*Size(regWork) + #define x2 1*Size(regWork) + #define z2 2*Size(regWork) + #define x3 3*Size(regWork) + #define z3 4*Size(regWork) + // Local variables + #define t0 0*Size(SP) + #define t1 1*Size(SP) + #define b0 2*Size(SP) + #define b1 4*Size(SP) + MOVQ w+0(FP), regWork + MOVQ b+8(FP), regMove + CHECK_BMI2ADX(LLADSTEP, ladderStepLeg, ladderStepBmi2Adx) + #undef regWork + #undef regMove + #undef x1 + #undef x2 + #undef z2 + #undef x3 + #undef z3 + #undef t0 + #undef t1 + #undef b0 + #undef b1 + +// func diffAddAmd64(work *[5]fp.Elt, swap uint) +// diffAddAmd64 calculates a differential point addition using a precomputed point. +// (x1,z1) = (x1,z1)+(mu) using a difference point (x2,z2) +// work = {mu,x1,z1,x2,z2} are five fp448.Elt of 56 bytes, and +// stack = (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +// This is Equation 7 at https://eprint.iacr.org/2017/264. +TEXT ·diffAddAmd64(SB),NOSPLIT,$224-16 + // Parameters + #define regWork DI + #define regSwap SI + #define ui 0*Size(regWork) + #define x1 1*Size(regWork) + #define z1 2*Size(regWork) + #define x2 3*Size(regWork) + #define z2 4*Size(regWork) + // Local variables + #define b0 0*Size(SP) + #define b1 2*Size(SP) + MOVQ w+0(FP), regWork + MOVQ b+8(FP), regSwap + cswap(x1,x2,regSwap) + cswap(z1,z2,regSwap) + CHECK_BMI2ADX(LDIFADD, difAddLeg, difAddBmi2Adx) + #undef regWork + #undef regSwap + #undef ui + #undef x1 + #undef z1 + #undef x2 + #undef z2 + #undef b0 + #undef b1 + +// func doubleAmd64(x, z *fp448.Elt) +// doubleAmd64 calculates a point doubling (x1,z1) = 2*(x1,z1). +// stack = (t0,t1) are two fp.Elt of fp.Size bytes, and +// (b0,b1) are two-double precision fp.Elt of 2*fp.Size bytes. +TEXT ·doubleAmd64(SB),NOSPLIT,$336-16 + // Parameters + #define x1 0(DI) + #define z1 0(SI) + // Local variables + #define t0 0*Size(SP) + #define t1 1*Size(SP) + #define b0 2*Size(SP) + #define b1 4*Size(SP) + MOVQ x+0(FP), DI + MOVQ z+8(FP), SI + CHECK_BMI2ADX(LDOUB,doubleLeg,doubleBmi2Adx) + #undef x1 + #undef z1 + #undef t0 + #undef t1 + #undef b0 + #undef b1 diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_generic.go b/vendor/github.com/cloudflare/circl/dh/x448/curve_generic.go new file mode 100644 index 00000000..b0b65ccf --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_generic.go @@ -0,0 +1,100 @@ +package x448 + +import ( + "encoding/binary" + "math/bits" + + "github.com/cloudflare/circl/math/fp448" +) + +func doubleGeneric(x, z *fp448.Elt) { + t0, t1 := &fp448.Elt{}, &fp448.Elt{} + fp448.AddSub(x, z) + fp448.Sqr(x, x) + fp448.Sqr(z, z) + fp448.Sub(t0, x, z) + mulA24Generic(t1, t0) + fp448.Add(t1, t1, z) + fp448.Mul(x, x, z) + fp448.Mul(z, t0, t1) +} + +func diffAddGeneric(w *[5]fp448.Elt, b uint) { + mu, x1, z1, x2, z2 := &w[0], &w[1], &w[2], &w[3], &w[4] + fp448.Cswap(x1, x2, b) + fp448.Cswap(z1, z2, b) + fp448.AddSub(x1, z1) + fp448.Mul(z1, z1, mu) + fp448.AddSub(x1, z1) + fp448.Sqr(x1, x1) + fp448.Sqr(z1, z1) + fp448.Mul(x1, x1, z2) + fp448.Mul(z1, z1, x2) +} + +func ladderStepGeneric(w *[5]fp448.Elt, b uint) { + x1, x2, z2, x3, z3 := &w[0], &w[1], &w[2], &w[3], &w[4] + t0 := &fp448.Elt{} + t1 := &fp448.Elt{} + fp448.AddSub(x2, z2) + fp448.AddSub(x3, z3) + fp448.Mul(t0, x2, z3) + fp448.Mul(t1, x3, z2) + fp448.AddSub(t0, t1) + fp448.Cmov(x2, x3, b) + fp448.Cmov(z2, z3, b) + fp448.Sqr(x3, t0) + fp448.Sqr(z3, t1) + fp448.Mul(z3, x1, z3) + fp448.Sqr(x2, x2) + fp448.Sqr(z2, z2) + fp448.Sub(t0, x2, z2) + mulA24Generic(t1, t0) + fp448.Add(t1, t1, z2) + fp448.Mul(x2, x2, z2) + fp448.Mul(z2, t0, t1) +} + +func mulA24Generic(z, x *fp448.Elt) { + const A24 = 39082 + const n = 8 + var xx [7]uint64 + for i := range xx { + xx[i] = binary.LittleEndian.Uint64(x[i*n : (i+1)*n]) + } + h0, l0 := bits.Mul64(xx[0], A24) + h1, l1 := bits.Mul64(xx[1], A24) + h2, l2 := bits.Mul64(xx[2], A24) + h3, l3 := bits.Mul64(xx[3], A24) + h4, l4 := bits.Mul64(xx[4], A24) + h5, l5 := bits.Mul64(xx[5], A24) + h6, l6 := bits.Mul64(xx[6], A24) + + l1, c0 := bits.Add64(h0, l1, 0) + l2, c1 := bits.Add64(h1, l2, c0) + l3, c2 := bits.Add64(h2, l3, c1) + l4, c3 := bits.Add64(h3, l4, c2) + l5, c4 := bits.Add64(h4, l5, c3) + l6, c5 := bits.Add64(h5, l6, c4) + l7, _ := bits.Add64(h6, 0, c5) + + l0, c0 = bits.Add64(l0, l7, 0) + l1, c1 = bits.Add64(l1, 0, c0) + l2, c2 = bits.Add64(l2, 0, c1) + l3, c3 = bits.Add64(l3, l7<<32, c2) + l4, c4 = bits.Add64(l4, 0, c3) + l5, c5 = bits.Add64(l5, 0, c4) + l6, l7 = bits.Add64(l6, 0, c5) + + xx[0], c0 = bits.Add64(l0, l7, 0) + xx[1], c1 = bits.Add64(l1, 0, c0) + xx[2], c2 = bits.Add64(l2, 0, c1) + xx[3], c3 = bits.Add64(l3, l7<<32, c2) + xx[4], c4 = bits.Add64(l4, 0, c3) + xx[5], c5 = bits.Add64(l5, 0, c4) + xx[6], _ = bits.Add64(l6, 0, c5) + + for i := range xx { + binary.LittleEndian.PutUint64(z[i*n:(i+1)*n], xx[i]) + } +} diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_noasm.go b/vendor/github.com/cloudflare/circl/dh/x448/curve_noasm.go new file mode 100644 index 00000000..3755b7c8 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_noasm.go @@ -0,0 +1,11 @@ +//go:build !amd64 || purego +// +build !amd64 purego + +package x448 + +import fp "github.com/cloudflare/circl/math/fp448" + +func double(x, z *fp.Elt) { doubleGeneric(x, z) } +func diffAdd(w *[5]fp.Elt, b uint) { diffAddGeneric(w, b) } +func ladderStep(w *[5]fp.Elt, b uint) { ladderStepGeneric(w, b) } +func mulA24(z, x *fp.Elt) { mulA24Generic(z, x) } diff --git a/vendor/github.com/cloudflare/circl/dh/x448/doc.go b/vendor/github.com/cloudflare/circl/dh/x448/doc.go new file mode 100644 index 00000000..c02904fe --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/doc.go @@ -0,0 +1,19 @@ +/* +Package x448 provides Diffie-Hellman functions as specified in RFC-7748. + +Validation of public keys. + +The Diffie-Hellman function, as described in RFC-7748 [1], works for any +public key. However, if a different protocol requires contributory +behaviour [2,3], then the public keys must be validated against low-order +points [3,4]. To do that, the Shared function performs this validation +internally and returns false when the public key is invalid (i.e., it +is a low-order point). + +References: + - [1] RFC7748 by Langley, Hamburg, Turner (https://rfc-editor.org/rfc/rfc7748.txt) + - [2] Curve25519 by Bernstein (https://cr.yp.to/ecdh.html) + - [3] Bernstein (https://cr.yp.to/ecdh.html#validate) + - [4] Cremers&Jackson (https://eprint.iacr.org/2019/526) +*/ +package x448 diff --git a/vendor/github.com/cloudflare/circl/dh/x448/key.go b/vendor/github.com/cloudflare/circl/dh/x448/key.go new file mode 100644 index 00000000..82a07a2b --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/key.go @@ -0,0 +1,46 @@ +package x448 + +import ( + "crypto/subtle" + + fp "github.com/cloudflare/circl/math/fp448" +) + +// Size is the length in bytes of a X448 key. +const Size = 56 + +// Key represents a X448 key. +type Key [Size]byte + +func (k *Key) clamp(in *Key) *Key { + *k = *in + k[0] &= 252 + k[55] |= 128 + return k +} + +// isValidPubKey verifies if the public key is not a low-order point. +func (k *Key) isValidPubKey() bool { + fp.Modp((*fp.Elt)(k)) + isLowOrder := false + for _, P := range lowOrderPoints { + isLowOrder = isLowOrder || subtle.ConstantTimeCompare(P[:], k[:]) != 0 + } + return !isLowOrder +} + +// KeyGen obtains a public key given a secret key. +func KeyGen(public, secret *Key) { + ladderJoye(public.clamp(secret)) +} + +// Shared calculates Alice's shared key from Alice's secret key and Bob's +// public key returning true on success. A failure case happens when the public +// key is a low-order point, thus the shared key is all-zeros and the function +// returns false. +func Shared(shared, secret, public *Key) bool { + validPk := *public + ok := validPk.isValidPubKey() + ladderMontgomery(shared.clamp(secret), &validPk) + return ok +} diff --git a/vendor/github.com/cloudflare/circl/dh/x448/table.go b/vendor/github.com/cloudflare/circl/dh/x448/table.go new file mode 100644 index 00000000..eef53c30 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/dh/x448/table.go @@ -0,0 +1,460 @@ +package x448 + +import fp "github.com/cloudflare/circl/math/fp448" + +// tableGenerator contains the set of points: +// +// t[i] = (xi+1)/(xi-1), +// +// where (xi,yi) = 2^iG and G is the generator point +// Size = (448)*(448/8) = 25088 bytes. +var tableGenerator = [448 * fp.Size]byte{ + /* (2^ 0)P */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + /* (2^ 1)P */ 0x37, 0xfa, 0xaa, 0x0d, 0x86, 0xa6, 0x24, 0xe9, 0x6c, 0x95, 0x08, 0x34, 0xba, 0x1a, 0x81, 0x3a, 0xae, 0x01, 0xa5, 0xa7, 0x05, 0x85, 0x96, 0x00, 0x06, 0x5a, 0xd7, 0xff, 0xee, 0x8e, 0x8f, 0x94, 0xd2, 0xdc, 0xd7, 0xfc, 0xe7, 0xe5, 0x99, 0x1d, 0x05, 0x46, 0x43, 0xe8, 0xbc, 0x12, 0xb7, 0xeb, 0x30, 0x5e, 0x7a, 0x85, 0x68, 0xed, 0x9d, 0x28, + /* (2^ 2)P */ 0xf1, 0x7d, 0x08, 0x2b, 0x32, 0x4a, 0x62, 0x80, 0x36, 0xe7, 0xa4, 0x76, 0x5a, 0x2a, 0x1e, 0xf7, 0x9e, 0x3c, 0x40, 0x46, 0x9a, 0x1b, 0x61, 0xc1, 0xbf, 0x1a, 0x1b, 0xae, 0x91, 0x80, 0xa3, 0x76, 0x6c, 0xd4, 0x8f, 0xa4, 0xee, 0x26, 0x39, 0x23, 0xa4, 0x80, 0xf4, 0x66, 0x92, 0xe4, 0xe1, 0x18, 0x76, 0xc5, 0xe2, 0x19, 0x87, 0xd5, 0xc3, 0xe8, + /* (2^ 3)P */ 0xfb, 0xc9, 0xf0, 0x07, 0xf2, 0x93, 0xd8, 0x50, 0x36, 0xed, 0xfb, 0xbd, 0xb2, 0xd3, 0xfc, 0xdf, 0xd5, 0x2a, 0x6e, 0x26, 0x09, 0xce, 0xd4, 0x07, 0x64, 0x9f, 0x40, 0x74, 0xad, 0x98, 0x2f, 0x1c, 0xb6, 0xdc, 0x2d, 0x42, 0xff, 0xbf, 0x97, 0xd8, 0xdb, 0xef, 0x99, 0xca, 0x73, 0x99, 0x1a, 0x04, 0x3b, 0x56, 0x2c, 0x1f, 0x87, 0x9d, 0x9f, 0x03, + /* (2^ 4)P */ 0x4c, 0x35, 0x97, 0xf7, 0x81, 0x2c, 0x84, 0xa6, 0xe0, 0xcb, 0xce, 0x37, 0x4c, 0x21, 0x1c, 0x67, 0xfa, 0xab, 0x18, 0x4d, 0xef, 0xd0, 0xf0, 0x44, 0xa9, 0xfb, 0xc0, 0x8e, 0xda, 0x57, 0xa1, 0xd8, 0xeb, 0x87, 0xf4, 0x17, 0xea, 0x66, 0x0f, 0x16, 0xea, 0xcd, 0x5f, 0x3e, 0x88, 0xea, 0x09, 0x68, 0x40, 0xdf, 0x43, 0xcc, 0x54, 0x61, 0x58, 0xaa, + /* (2^ 5)P */ 0x8d, 0xe7, 0x59, 0xd7, 0x5e, 0x63, 0x37, 0xa7, 0x3f, 0xd1, 0x49, 0x85, 0x01, 0xdd, 0x5e, 0xb3, 0xe6, 0x29, 0xcb, 0x25, 0x93, 0xdd, 0x08, 0x96, 0x83, 0x52, 0x76, 0x85, 0xf5, 0x5d, 0x02, 0xbf, 0xe9, 0x6d, 0x15, 0x27, 0xc1, 0x09, 0xd1, 0x14, 0x4d, 0x6e, 0xe8, 0xaf, 0x59, 0x58, 0x34, 0x9d, 0x2a, 0x99, 0x85, 0x26, 0xbe, 0x4b, 0x1e, 0xb9, + /* (2^ 6)P */ 0x8d, 0xce, 0x94, 0xe2, 0x18, 0x56, 0x0d, 0x82, 0x8e, 0xdf, 0x85, 0x01, 0x8f, 0x93, 0x3c, 0xc6, 0xbd, 0x61, 0xfb, 0xf4, 0x22, 0xc5, 0x16, 0x87, 0xd1, 0xb1, 0x9e, 0x09, 0xc5, 0x83, 0x2e, 0x4a, 0x07, 0x88, 0xee, 0xe0, 0x29, 0x8d, 0x2e, 0x1f, 0x88, 0xad, 0xfd, 0x18, 0x93, 0xb7, 0xed, 0x42, 0x86, 0x78, 0xf0, 0xb8, 0x70, 0xbe, 0x01, 0x67, + /* (2^ 7)P */ 0xdf, 0x62, 0x2d, 0x94, 0xc7, 0x35, 0x23, 0xda, 0x27, 0xbb, 0x2b, 0xdb, 0x30, 0x80, 0x68, 0x16, 0xa3, 0xae, 0xd7, 0xd2, 0xa7, 0x7c, 0xbf, 0x6a, 0x1d, 0x83, 0xde, 0x96, 0x0a, 0x43, 0xb6, 0x30, 0x37, 0xd6, 0xee, 0x63, 0x59, 0x9a, 0xbf, 0xa3, 0x30, 0x6c, 0xaf, 0x0c, 0xee, 0x3d, 0xcb, 0x35, 0x4b, 0x55, 0x5f, 0x84, 0x85, 0xcb, 0x4f, 0x1e, + /* (2^ 8)P */ 0x9d, 0x04, 0x68, 0x89, 0xa4, 0xa9, 0x0d, 0x87, 0xc1, 0x70, 0xf1, 0xeb, 0xfb, 0x47, 0x0a, 0xf0, 0xde, 0x67, 0xb7, 0x94, 0xcd, 0x36, 0x43, 0xa5, 0x49, 0x43, 0x67, 0xc3, 0xee, 0x3c, 0x6b, 0xec, 0xd0, 0x1a, 0xf4, 0xad, 0xef, 0x06, 0x4a, 0xe8, 0x46, 0x24, 0xd7, 0x93, 0xbf, 0xf0, 0xe3, 0x81, 0x61, 0xec, 0xea, 0x64, 0xfe, 0x67, 0xeb, 0xc7, + /* (2^ 9)P */ 0x95, 0x45, 0x79, 0xcf, 0x2c, 0xfd, 0x9b, 0xfe, 0x84, 0x46, 0x4b, 0x8f, 0xa1, 0xcf, 0xc3, 0x04, 0x94, 0x78, 0xdb, 0xc9, 0xa6, 0x01, 0x75, 0xa4, 0xb4, 0x93, 0x72, 0x43, 0xa7, 0x7d, 0xda, 0x31, 0x38, 0x54, 0xab, 0x4e, 0x3f, 0x89, 0xa6, 0xab, 0x57, 0xc0, 0x16, 0x65, 0xdb, 0x92, 0x96, 0xe4, 0xc8, 0xae, 0xe7, 0x4c, 0x7a, 0xeb, 0xbb, 0x5a, + /* (2^ 10)P */ 0xbe, 0xfe, 0x86, 0xc3, 0x97, 0xe0, 0x6a, 0x18, 0x20, 0x21, 0xca, 0x22, 0x55, 0xa1, 0xeb, 0xf5, 0x74, 0xe5, 0xc9, 0x59, 0xa7, 0x92, 0x65, 0x15, 0x08, 0x71, 0xd1, 0x09, 0x7e, 0x83, 0xfc, 0xbc, 0x5a, 0x93, 0x38, 0x0d, 0x43, 0x42, 0xfd, 0x76, 0x30, 0xe8, 0x63, 0x60, 0x09, 0x8d, 0x6c, 0xd3, 0xf8, 0x56, 0x3d, 0x68, 0x47, 0xab, 0xa0, 0x1d, + /* (2^ 11)P */ 0x38, 0x50, 0x1c, 0xb1, 0xac, 0x88, 0x8f, 0x38, 0xe3, 0x69, 0xe6, 0xfc, 0x4f, 0x8f, 0xe1, 0x9b, 0xb1, 0x1a, 0x09, 0x39, 0x19, 0xdf, 0xcd, 0x98, 0x7b, 0x64, 0x42, 0xf6, 0x11, 0xea, 0xc7, 0xe8, 0x92, 0x65, 0x00, 0x2c, 0x75, 0xb5, 0x94, 0x1e, 0x5b, 0xa6, 0x66, 0x81, 0x77, 0xf3, 0x39, 0x94, 0xac, 0xbd, 0xe4, 0x2a, 0x66, 0x84, 0x9c, 0x60, + /* (2^ 12)P */ 0xb5, 0xb6, 0xd9, 0x03, 0x67, 0xa4, 0xa8, 0x0a, 0x4a, 0x2b, 0x9d, 0xfa, 0x13, 0xe1, 0x99, 0x25, 0x4a, 0x5c, 0x67, 0xb9, 0xb2, 0xb7, 0xdd, 0x1e, 0xaf, 0xeb, 0x63, 0x41, 0xb6, 0xb9, 0xa0, 0x87, 0x0a, 0xe0, 0x06, 0x07, 0xaa, 0x97, 0xf8, 0xf9, 0x38, 0x4f, 0xdf, 0x0c, 0x40, 0x7c, 0xc3, 0x98, 0xa9, 0x74, 0xf1, 0x5d, 0xda, 0xd1, 0xc0, 0x0a, + /* (2^ 13)P */ 0xf2, 0x0a, 0xab, 0xab, 0x94, 0x50, 0xf0, 0xa3, 0x6f, 0xc6, 0x66, 0xba, 0xa6, 0xdc, 0x44, 0xdd, 0xd6, 0x08, 0xf4, 0xd3, 0xed, 0xb1, 0x40, 0x93, 0xee, 0xf6, 0xb8, 0x8e, 0xb4, 0x7c, 0xb9, 0x82, 0xc9, 0x9d, 0x45, 0x3b, 0x8e, 0x10, 0xcb, 0x70, 0x1e, 0xba, 0x3c, 0x62, 0x50, 0xda, 0xa9, 0x93, 0xb5, 0xd7, 0xd0, 0x6f, 0x29, 0x52, 0x95, 0xae, + /* (2^ 14)P */ 0x14, 0x68, 0x69, 0x23, 0xa8, 0x44, 0x87, 0x9e, 0x22, 0x91, 0xe8, 0x92, 0xdf, 0xf7, 0xae, 0xba, 0x1c, 0x96, 0xe1, 0xc3, 0x94, 0xed, 0x6c, 0x95, 0xae, 0x96, 0xa7, 0x15, 0x9f, 0xf1, 0x17, 0x11, 0x92, 0x42, 0xd5, 0xcd, 0x18, 0xe7, 0xa9, 0xb5, 0x2f, 0xcd, 0xde, 0x6c, 0xc9, 0x7d, 0xfc, 0x7e, 0xbd, 0x7f, 0x10, 0x3d, 0x01, 0x00, 0x8d, 0x95, + /* (2^ 15)P */ 0x3b, 0x76, 0x72, 0xae, 0xaf, 0x84, 0xf2, 0xf7, 0xd1, 0x6d, 0x13, 0x9c, 0x47, 0xe1, 0xb7, 0xa3, 0x19, 0x16, 0xee, 0x75, 0x45, 0xf6, 0x1a, 0x7b, 0x78, 0x49, 0x79, 0x05, 0x86, 0xf0, 0x7f, 0x9f, 0xfc, 0xc4, 0xbd, 0x86, 0xf3, 0x41, 0xa7, 0xfe, 0x01, 0xd5, 0x67, 0x16, 0x10, 0x5b, 0xa5, 0x16, 0xf3, 0x7f, 0x60, 0xce, 0xd2, 0x0c, 0x8e, 0x4b, + /* (2^ 16)P */ 0x4a, 0x07, 0x99, 0x4a, 0x0f, 0x74, 0x91, 0x14, 0x68, 0xb9, 0x48, 0xb7, 0x44, 0x77, 0x9b, 0x4a, 0xe0, 0x68, 0x0e, 0x43, 0x4d, 0x98, 0x98, 0xbf, 0xa8, 0x3a, 0xb7, 0x6d, 0x2a, 0x9a, 0x77, 0x5f, 0x62, 0xf5, 0x6b, 0x4a, 0xb7, 0x7d, 0xe5, 0x09, 0x6b, 0xc0, 0x8b, 0x9c, 0x88, 0x37, 0x33, 0xf2, 0x41, 0xac, 0x22, 0x1f, 0xcf, 0x3b, 0x82, 0x34, + /* (2^ 17)P */ 0x00, 0xc3, 0x78, 0x42, 0x32, 0x2e, 0xdc, 0xda, 0xb1, 0x96, 0x21, 0xa4, 0xe4, 0xbb, 0xe9, 0x9d, 0xbb, 0x0f, 0x93, 0xed, 0x26, 0x3d, 0xb5, 0xdb, 0x94, 0x31, 0x37, 0x07, 0xa2, 0xb2, 0xd5, 0x99, 0x0d, 0x93, 0xe1, 0xce, 0x3f, 0x0b, 0x96, 0x82, 0x47, 0xfe, 0x60, 0x6f, 0x8f, 0x61, 0x88, 0xd7, 0x05, 0x95, 0x0b, 0x46, 0x06, 0xb7, 0x32, 0x06, + /* (2^ 18)P */ 0x44, 0xf5, 0x34, 0xdf, 0x2f, 0x9c, 0x5d, 0x9f, 0x53, 0x5c, 0x42, 0x8f, 0xc9, 0xdc, 0xd8, 0x40, 0xa2, 0xe7, 0x6a, 0x4a, 0x05, 0xf7, 0x86, 0x77, 0x2b, 0xae, 0x37, 0xed, 0x48, 0xfb, 0xf7, 0x62, 0x7c, 0x17, 0x59, 0x92, 0x41, 0x61, 0x93, 0x38, 0x30, 0xd1, 0xef, 0x54, 0x54, 0x03, 0x17, 0x57, 0x91, 0x15, 0x11, 0x33, 0xb5, 0xfa, 0xfb, 0x17, + /* (2^ 19)P */ 0x29, 0xbb, 0xd4, 0xb4, 0x9c, 0xf1, 0x72, 0x94, 0xce, 0x6a, 0x29, 0xa8, 0x89, 0x18, 0x19, 0xf7, 0xb7, 0xcc, 0xee, 0x9a, 0x02, 0xe3, 0xc0, 0xb1, 0xe0, 0xee, 0x83, 0x78, 0xb4, 0x9e, 0x07, 0x87, 0xdf, 0xb0, 0x82, 0x26, 0x4e, 0xa4, 0x0c, 0x33, 0xaf, 0x40, 0x59, 0xb6, 0xdd, 0x52, 0x45, 0xf0, 0xb4, 0xf6, 0xe8, 0x4e, 0x4e, 0x79, 0x1a, 0x5d, + /* (2^ 20)P */ 0x27, 0x33, 0x4d, 0x4c, 0x6b, 0x4f, 0x75, 0xb1, 0xbc, 0x1f, 0xab, 0x5b, 0x2b, 0xf0, 0x1c, 0x57, 0x86, 0xdd, 0xfd, 0x60, 0xb0, 0x8c, 0xe7, 0x9a, 0xe5, 0x5c, 0xeb, 0x11, 0x3a, 0xda, 0x22, 0x25, 0x99, 0x06, 0x8d, 0xf4, 0xaf, 0x29, 0x7a, 0xc9, 0xe5, 0xd2, 0x16, 0x9e, 0xd4, 0x63, 0x1d, 0x64, 0xa6, 0x47, 0x96, 0x37, 0x6f, 0x93, 0x2c, 0xcc, + /* (2^ 21)P */ 0xc1, 0x94, 0x74, 0x86, 0x75, 0xf2, 0x91, 0x58, 0x23, 0x85, 0x63, 0x76, 0x54, 0xc7, 0xb4, 0x8c, 0xbc, 0x4e, 0xc4, 0xa7, 0xba, 0xa0, 0x55, 0x26, 0x71, 0xd5, 0x33, 0x72, 0xc9, 0xad, 0x1e, 0xf9, 0x5d, 0x78, 0x70, 0x93, 0x4e, 0x85, 0xfc, 0x39, 0x06, 0x73, 0x76, 0xff, 0xe8, 0x64, 0x69, 0x42, 0x45, 0xb2, 0x69, 0xb5, 0x32, 0xe7, 0x2c, 0xde, + /* (2^ 22)P */ 0xde, 0x16, 0xd8, 0x33, 0x49, 0x32, 0xe9, 0x0e, 0x3a, 0x60, 0xee, 0x2e, 0x24, 0x75, 0xe3, 0x9c, 0x92, 0x07, 0xdb, 0xad, 0x92, 0xf5, 0x11, 0xdf, 0xdb, 0xb0, 0x17, 0x5c, 0xd6, 0x1a, 0x70, 0x00, 0xb7, 0xe2, 0x18, 0xec, 0xdc, 0xc2, 0x02, 0x93, 0xb3, 0xc8, 0x3f, 0x4f, 0x1b, 0x96, 0xe6, 0x33, 0x8c, 0xfb, 0xcc, 0xa5, 0x4e, 0xe8, 0xe7, 0x11, + /* (2^ 23)P */ 0x05, 0x7a, 0x74, 0x52, 0xf8, 0xdf, 0x0d, 0x7c, 0x6a, 0x1a, 0x4e, 0x9a, 0x02, 0x1d, 0xae, 0x77, 0xf8, 0x8e, 0xf9, 0xa2, 0x38, 0x54, 0x50, 0xb2, 0x2c, 0x08, 0x9d, 0x9b, 0x9f, 0xfb, 0x2b, 0x06, 0xde, 0x9d, 0xc2, 0x03, 0x0b, 0x22, 0x2b, 0x10, 0x5b, 0x3a, 0x73, 0x29, 0x8e, 0x3e, 0x37, 0x08, 0x2c, 0x3b, 0xf8, 0x80, 0xc1, 0x66, 0x1e, 0x98, + /* (2^ 24)P */ 0xd8, 0xd6, 0x3e, 0xcd, 0x63, 0x8c, 0x2b, 0x41, 0x81, 0xc0, 0x0c, 0x06, 0x87, 0xd6, 0xe7, 0x92, 0xfe, 0xf1, 0x0c, 0x4a, 0x84, 0x5b, 0xaf, 0x40, 0x53, 0x6f, 0x60, 0xd6, 0x6b, 0x76, 0x4b, 0xc2, 0xad, 0xc9, 0xb6, 0xb6, 0x6a, 0xa2, 0xb3, 0xf5, 0xf5, 0xc2, 0x55, 0x83, 0xb2, 0xd3, 0xe9, 0x41, 0x6c, 0x63, 0x51, 0xb8, 0x81, 0x74, 0xc8, 0x2c, + /* (2^ 25)P */ 0xb2, 0xaf, 0x1c, 0xee, 0x07, 0xb0, 0x58, 0xa8, 0x2c, 0x6a, 0xc9, 0x2d, 0x62, 0x28, 0x75, 0x0c, 0x40, 0xb6, 0x11, 0x33, 0x96, 0x80, 0x28, 0x6d, 0xd5, 0x9e, 0x87, 0x90, 0x01, 0x66, 0x1d, 0x1c, 0xf8, 0xb4, 0x92, 0xac, 0x38, 0x18, 0x05, 0xc2, 0x4c, 0x4b, 0x54, 0x7d, 0x80, 0x46, 0x87, 0x2d, 0x99, 0x8e, 0x70, 0x80, 0x69, 0x71, 0x8b, 0xed, + /* (2^ 26)P */ 0x37, 0xa7, 0x6b, 0x71, 0x36, 0x75, 0x8e, 0xff, 0x0f, 0x42, 0xda, 0x5a, 0x46, 0xa6, 0x97, 0x79, 0x7e, 0x30, 0xb3, 0x8f, 0xc7, 0x3a, 0xa0, 0xcb, 0x1d, 0x9c, 0x78, 0x77, 0x36, 0xc2, 0xe7, 0xf4, 0x2f, 0x29, 0x07, 0xb1, 0x07, 0xfd, 0xed, 0x1b, 0x39, 0x77, 0x06, 0x38, 0x77, 0x0f, 0x50, 0x31, 0x12, 0xbf, 0x92, 0xbf, 0x72, 0x79, 0x54, 0xa9, + /* (2^ 27)P */ 0xbd, 0x4d, 0x46, 0x6b, 0x1a, 0x80, 0x46, 0x2d, 0xed, 0xfd, 0x64, 0x6d, 0x94, 0xbc, 0x4a, 0x6e, 0x0c, 0x12, 0xf6, 0x12, 0xab, 0x54, 0x88, 0xd3, 0x85, 0xac, 0x51, 0xae, 0x6f, 0xca, 0xc4, 0xb7, 0xec, 0x22, 0x54, 0x6d, 0x80, 0xb2, 0x1c, 0x63, 0x33, 0x76, 0x6b, 0x8e, 0x6d, 0x59, 0xcd, 0x73, 0x92, 0x5f, 0xff, 0xad, 0x10, 0x35, 0x70, 0x5f, + /* (2^ 28)P */ 0xb3, 0x84, 0xde, 0xc8, 0x04, 0x43, 0x63, 0xfa, 0x29, 0xd9, 0xf0, 0x69, 0x65, 0x5a, 0x0c, 0xe8, 0x2e, 0x0b, 0xfe, 0xb0, 0x7a, 0x42, 0xb3, 0xc3, 0xfc, 0xe6, 0xb8, 0x92, 0x29, 0xae, 0xed, 0xec, 0xd5, 0xe8, 0x4a, 0xa1, 0xbd, 0x3b, 0xd3, 0xc0, 0x07, 0xab, 0x65, 0x65, 0x35, 0x9a, 0xa6, 0x5e, 0x78, 0x18, 0x76, 0x1c, 0x15, 0x49, 0xe6, 0x75, + /* (2^ 29)P */ 0x45, 0xb3, 0x92, 0xa9, 0xc3, 0xb8, 0x11, 0x68, 0x64, 0x3a, 0x83, 0x5d, 0xa8, 0x94, 0x6a, 0x9d, 0xaa, 0x27, 0x9f, 0x98, 0x5d, 0xc0, 0x29, 0xf0, 0xc0, 0x4b, 0x14, 0x3c, 0x05, 0xe7, 0xf8, 0xbd, 0x38, 0x22, 0x96, 0x75, 0x65, 0x5e, 0x0d, 0x3f, 0xbb, 0x6f, 0xe8, 0x3f, 0x96, 0x76, 0x9f, 0xba, 0xd9, 0x44, 0x92, 0x96, 0x22, 0xe7, 0x52, 0xe7, + /* (2^ 30)P */ 0xf4, 0xa3, 0x95, 0x90, 0x47, 0xdf, 0x7d, 0xdc, 0xf4, 0x13, 0x87, 0x67, 0x7d, 0x4f, 0x9d, 0xa0, 0x00, 0x46, 0x72, 0x08, 0xc3, 0xa2, 0x7a, 0x3e, 0xe7, 0x6d, 0x52, 0x7c, 0x11, 0x36, 0x50, 0x83, 0x89, 0x64, 0xcb, 0x1f, 0x08, 0x83, 0x46, 0xcb, 0xac, 0xa6, 0xd8, 0x9c, 0x1b, 0xe8, 0x05, 0x47, 0xc7, 0x26, 0x06, 0x83, 0x39, 0xe9, 0xb1, 0x1c, + /* (2^ 31)P */ 0x11, 0xe8, 0xc8, 0x42, 0xbf, 0x30, 0x9c, 0xa3, 0xf1, 0x85, 0x96, 0x95, 0x4f, 0x4f, 0x52, 0xa2, 0xf5, 0x8b, 0x68, 0x24, 0x16, 0xac, 0x9b, 0xa9, 0x27, 0x28, 0x0e, 0x84, 0x03, 0x46, 0x22, 0x5f, 0xf7, 0x0d, 0xa6, 0x85, 0x88, 0xc1, 0x45, 0x4b, 0x85, 0x1a, 0x10, 0x7f, 0xc9, 0x94, 0x20, 0xb0, 0x04, 0x28, 0x12, 0x30, 0xb9, 0xe6, 0x40, 0x6b, + /* (2^ 32)P */ 0xac, 0x1b, 0x57, 0xb6, 0x42, 0xdb, 0x81, 0x8d, 0x76, 0xfd, 0x9b, 0x1c, 0x29, 0x30, 0xd5, 0x3a, 0xcc, 0x53, 0xd9, 0x26, 0x7a, 0x0f, 0x9c, 0x2e, 0x79, 0xf5, 0x62, 0xeb, 0x61, 0x9d, 0x9b, 0x80, 0x39, 0xcd, 0x60, 0x2e, 0x1f, 0x08, 0x22, 0xbc, 0x19, 0xb3, 0x2a, 0x43, 0x44, 0xf2, 0x4e, 0x66, 0xf4, 0x36, 0xa6, 0xa7, 0xbc, 0xa4, 0x15, 0x7e, + /* (2^ 33)P */ 0xc1, 0x90, 0x8a, 0xde, 0xff, 0x78, 0xc3, 0x73, 0x16, 0xee, 0x76, 0xa0, 0x84, 0x60, 0x8d, 0xe6, 0x82, 0x0f, 0xde, 0x4e, 0xc5, 0x99, 0x34, 0x06, 0x90, 0x44, 0x55, 0xf8, 0x91, 0xd8, 0xe1, 0xe4, 0x2c, 0x8a, 0xde, 0x94, 0x1e, 0x78, 0x25, 0x3d, 0xfd, 0xd8, 0x59, 0x7d, 0xaf, 0x6e, 0xbe, 0x96, 0xbe, 0x3c, 0x16, 0x23, 0x0f, 0x4c, 0xa4, 0x28, + /* (2^ 34)P */ 0xba, 0x11, 0x35, 0x57, 0x03, 0xb6, 0xf4, 0x24, 0x89, 0xb8, 0x5a, 0x0d, 0x50, 0x9c, 0xaa, 0x51, 0x7f, 0xa4, 0x0e, 0xfc, 0x71, 0xb3, 0x3b, 0xf1, 0x96, 0x50, 0x23, 0x15, 0xf5, 0xf5, 0xd4, 0x23, 0xdc, 0x8b, 0x26, 0x9e, 0xae, 0xb7, 0x50, 0xcd, 0xc4, 0x25, 0xf6, 0x75, 0x40, 0x9c, 0x37, 0x79, 0x33, 0x60, 0xd4, 0x4b, 0x13, 0x32, 0xee, 0xe2, + /* (2^ 35)P */ 0x43, 0xb8, 0x56, 0x59, 0xf0, 0x68, 0x23, 0xb3, 0xea, 0x70, 0x58, 0x4c, 0x1e, 0x5a, 0x16, 0x54, 0x03, 0xb2, 0xf4, 0x73, 0xb6, 0xd9, 0x5c, 0x9c, 0x6f, 0xcf, 0x82, 0x2e, 0x54, 0x15, 0x46, 0x2c, 0xa3, 0xda, 0x4e, 0x87, 0xf5, 0x2b, 0xba, 0x91, 0xa3, 0xa0, 0x89, 0xba, 0x48, 0x2b, 0xfa, 0x64, 0x02, 0x7f, 0x78, 0x03, 0xd1, 0xe8, 0x3b, 0xe9, + /* (2^ 36)P */ 0x15, 0xa4, 0x71, 0xd4, 0x0c, 0x24, 0xe9, 0x07, 0xa1, 0x43, 0xf4, 0x7f, 0xbb, 0xa2, 0xa6, 0x6b, 0xfa, 0xb7, 0xea, 0x58, 0xd1, 0x96, 0xb0, 0x24, 0x5c, 0xc7, 0x37, 0x4e, 0x60, 0x0f, 0x40, 0xf2, 0x2f, 0x44, 0x70, 0xea, 0x80, 0x63, 0xfe, 0xfc, 0x46, 0x59, 0x12, 0x27, 0xb5, 0x27, 0xfd, 0xb7, 0x73, 0x0b, 0xca, 0x8b, 0xc2, 0xd3, 0x71, 0x08, + /* (2^ 37)P */ 0x26, 0x0e, 0xd7, 0x52, 0x6f, 0xf1, 0xf2, 0x9d, 0xb8, 0x3d, 0xbd, 0xd4, 0x75, 0x97, 0xd8, 0xbf, 0xa8, 0x86, 0x96, 0xa5, 0x80, 0xa0, 0x45, 0x75, 0xf6, 0x77, 0x71, 0xdb, 0x77, 0x96, 0x55, 0x99, 0x31, 0xd0, 0x4f, 0x34, 0xf4, 0x35, 0x39, 0x41, 0xd3, 0x7d, 0xf7, 0xe2, 0x74, 0xde, 0xbe, 0x5b, 0x1f, 0x39, 0x10, 0x21, 0xa3, 0x4d, 0x3b, 0xc8, + /* (2^ 38)P */ 0x04, 0x00, 0x2a, 0x45, 0xb2, 0xaf, 0x9b, 0x18, 0x6a, 0xeb, 0x96, 0x28, 0xa4, 0x77, 0xd0, 0x13, 0xcf, 0x17, 0x65, 0xe8, 0xc5, 0x81, 0x28, 0xad, 0x39, 0x7a, 0x0b, 0xaa, 0x55, 0x2b, 0xf3, 0xfc, 0x86, 0x40, 0xad, 0x0d, 0x1e, 0x28, 0xa2, 0x2d, 0xc5, 0xd6, 0x04, 0x15, 0xa2, 0x30, 0x3d, 0x12, 0x8e, 0xd6, 0xb5, 0xf7, 0x69, 0xbb, 0x84, 0x20, + /* (2^ 39)P */ 0xd7, 0x7a, 0x77, 0x2c, 0xfb, 0x81, 0x80, 0xe9, 0x1e, 0xc6, 0x36, 0x31, 0x79, 0xc3, 0x7c, 0xa9, 0x57, 0x6b, 0xb5, 0x70, 0xfb, 0xe4, 0xa1, 0xff, 0xfd, 0x21, 0xa5, 0x7c, 0xfa, 0x44, 0xba, 0x0d, 0x96, 0x3d, 0xc4, 0x5c, 0x39, 0x52, 0x87, 0xd7, 0x22, 0x0f, 0x52, 0x88, 0x91, 0x87, 0x96, 0xac, 0xfa, 0x3b, 0xdf, 0xdc, 0x83, 0x8c, 0x99, 0x29, + /* (2^ 40)P */ 0x98, 0x6b, 0x3a, 0x8d, 0x83, 0x17, 0xe1, 0x62, 0xd8, 0x80, 0x4c, 0x97, 0xce, 0x6b, 0xaa, 0x10, 0xa7, 0xc4, 0xe9, 0xeb, 0xa5, 0xfb, 0xc9, 0xdd, 0x2d, 0xeb, 0xfc, 0x9a, 0x71, 0xcd, 0x68, 0x6e, 0xc0, 0x35, 0x64, 0x62, 0x1b, 0x95, 0x12, 0xe8, 0x53, 0xec, 0xf0, 0xf4, 0x86, 0x86, 0x78, 0x18, 0xc4, 0xc6, 0xbc, 0x5a, 0x59, 0x8f, 0x7c, 0x7e, + /* (2^ 41)P */ 0x7f, 0xd7, 0x1e, 0xc5, 0x83, 0xdc, 0x1f, 0xbe, 0x0b, 0xcf, 0x2e, 0x01, 0x01, 0xed, 0xac, 0x17, 0x3b, 0xed, 0xa4, 0x30, 0x96, 0x0e, 0x14, 0x7e, 0x19, 0x2b, 0xa5, 0x67, 0x1e, 0xb3, 0x34, 0x03, 0xa8, 0xbb, 0x0a, 0x7d, 0x08, 0x2d, 0xd5, 0x53, 0x19, 0x6f, 0x13, 0xd5, 0xc0, 0x90, 0x8a, 0xcc, 0xc9, 0x5c, 0xab, 0x24, 0xd7, 0x03, 0xf6, 0x57, + /* (2^ 42)P */ 0x49, 0xcb, 0xb4, 0x96, 0x5f, 0xa6, 0xf8, 0x71, 0x6f, 0x59, 0xad, 0x05, 0x24, 0x2d, 0xaf, 0x67, 0xa8, 0xbe, 0x95, 0xdf, 0x0d, 0x28, 0x5a, 0x7f, 0x6e, 0x87, 0x8c, 0x6e, 0x67, 0x0c, 0xf4, 0xe0, 0x1c, 0x30, 0xc2, 0x66, 0xae, 0x20, 0xa1, 0x34, 0xec, 0x9c, 0xbc, 0xae, 0x3d, 0xa1, 0x28, 0x28, 0x95, 0x1d, 0xc9, 0x3a, 0xa8, 0xfd, 0xfc, 0xa1, + /* (2^ 43)P */ 0xe2, 0x2b, 0x9d, 0xed, 0x02, 0x99, 0x67, 0xbb, 0x2e, 0x16, 0x62, 0x05, 0x70, 0xc7, 0x27, 0xb9, 0x1c, 0x3f, 0xf2, 0x11, 0x01, 0xd8, 0x51, 0xa4, 0x18, 0x92, 0xa9, 0x5d, 0xfb, 0xa9, 0xe4, 0x42, 0xba, 0x38, 0x34, 0x1a, 0x4a, 0xc5, 0x6a, 0x37, 0xde, 0xa7, 0x0c, 0xb4, 0x7e, 0x7f, 0xde, 0xa6, 0xee, 0xcd, 0x55, 0x57, 0x05, 0x06, 0xfd, 0x5d, + /* (2^ 44)P */ 0x2f, 0x32, 0xcf, 0x2e, 0x2c, 0x7b, 0xbe, 0x9a, 0x0c, 0x57, 0x35, 0xf8, 0x87, 0xda, 0x9c, 0xec, 0x48, 0xf2, 0xbb, 0xe2, 0xda, 0x10, 0x58, 0x20, 0xc6, 0xd3, 0x87, 0xe9, 0xc7, 0x26, 0xd1, 0x9a, 0x46, 0x87, 0x90, 0xda, 0xdc, 0xde, 0xc3, 0xb3, 0xf2, 0xe8, 0x6f, 0x4a, 0xe6, 0xe8, 0x9d, 0x98, 0x36, 0x20, 0x03, 0x47, 0x15, 0x3f, 0x64, 0x59, + /* (2^ 45)P */ 0xd4, 0x71, 0x49, 0x0a, 0x67, 0x97, 0xaa, 0x3f, 0xf4, 0x1b, 0x3a, 0x6e, 0x5e, 0x17, 0xcc, 0x0a, 0x8f, 0x81, 0x6a, 0x41, 0x38, 0x77, 0x40, 0x8a, 0x11, 0x42, 0x62, 0xd2, 0x50, 0x32, 0x79, 0x78, 0x28, 0xc2, 0x2e, 0x10, 0x01, 0x94, 0x30, 0x4f, 0x7f, 0x18, 0x17, 0x56, 0x85, 0x4e, 0xad, 0xf7, 0xcb, 0x87, 0x3c, 0x3f, 0x50, 0x2c, 0xc0, 0xba, + /* (2^ 46)P */ 0xbc, 0x30, 0x8e, 0x65, 0x8e, 0x57, 0x5b, 0x38, 0x7a, 0xd4, 0x95, 0x52, 0x7a, 0x32, 0x59, 0x69, 0xcd, 0x9d, 0x47, 0x34, 0x5b, 0x55, 0xa5, 0x24, 0x60, 0xdd, 0xc0, 0xc1, 0x62, 0x73, 0x44, 0xae, 0x4c, 0x9c, 0x65, 0x55, 0x1b, 0x9d, 0x8a, 0x29, 0xb0, 0x1a, 0x52, 0xa8, 0xf1, 0xe6, 0x9a, 0xb3, 0xf6, 0xa3, 0xc9, 0x0a, 0x70, 0x7d, 0x0f, 0xee, + /* (2^ 47)P */ 0x77, 0xd3, 0xe5, 0x8e, 0xfa, 0x00, 0xeb, 0x1b, 0x7f, 0xdc, 0x68, 0x3f, 0x92, 0xbd, 0xb7, 0x0b, 0xb7, 0xb5, 0x24, 0xdf, 0xc5, 0x67, 0x53, 0xd4, 0x36, 0x79, 0xc4, 0x7b, 0x57, 0xbc, 0x99, 0x97, 0x60, 0xef, 0xe4, 0x01, 0xa1, 0xa7, 0xaa, 0x12, 0x36, 0x29, 0xb1, 0x03, 0xc2, 0x83, 0x1c, 0x2b, 0x83, 0xef, 0x2e, 0x2c, 0x23, 0x92, 0xfd, 0xd1, + /* (2^ 48)P */ 0x94, 0xef, 0x03, 0x59, 0xfa, 0x8a, 0x18, 0x76, 0xee, 0x58, 0x08, 0x4d, 0x44, 0xce, 0xf1, 0x52, 0x33, 0x49, 0xf6, 0x69, 0x71, 0xe3, 0xa9, 0xbc, 0x86, 0xe3, 0x43, 0xde, 0x33, 0x7b, 0x90, 0x8b, 0x3e, 0x7d, 0xd5, 0x4a, 0xf0, 0x23, 0x99, 0xa6, 0xea, 0x5f, 0x08, 0xe5, 0xb9, 0x49, 0x8b, 0x0d, 0x6a, 0x21, 0xab, 0x07, 0x62, 0xcd, 0xc4, 0xbe, + /* (2^ 49)P */ 0x61, 0xbf, 0x70, 0x14, 0xfa, 0x4e, 0x9e, 0x7c, 0x0c, 0xf8, 0xb2, 0x48, 0x71, 0x62, 0x83, 0xd6, 0xd1, 0xdc, 0x9c, 0x29, 0x66, 0xb1, 0x34, 0x9c, 0x8d, 0xe6, 0x88, 0xaf, 0xbe, 0xdc, 0x4d, 0xeb, 0xb0, 0xe7, 0x28, 0xae, 0xb2, 0x05, 0x56, 0xc6, 0x0e, 0x10, 0x26, 0xab, 0x2c, 0x59, 0x72, 0x03, 0x66, 0xfe, 0x8f, 0x2c, 0x51, 0x2d, 0xdc, 0xae, + /* (2^ 50)P */ 0xdc, 0x63, 0xf1, 0x8b, 0x5c, 0x65, 0x0b, 0xf1, 0xa6, 0x22, 0xe2, 0xd9, 0xdb, 0x49, 0xb1, 0x3c, 0x47, 0xc2, 0xfe, 0xac, 0x86, 0x07, 0x52, 0xec, 0xb0, 0x08, 0x69, 0xfb, 0xd1, 0x06, 0xdc, 0x48, 0x5c, 0x3d, 0xb2, 0x4d, 0xb8, 0x1a, 0x4e, 0xda, 0xb9, 0xc1, 0x2b, 0xab, 0x4b, 0x62, 0x81, 0x21, 0x9a, 0xfc, 0x3d, 0x39, 0x83, 0x11, 0x36, 0xeb, + /* (2^ 51)P */ 0x94, 0xf3, 0x17, 0xef, 0xf9, 0x60, 0x54, 0xc3, 0xd7, 0x27, 0x35, 0xc5, 0x98, 0x5e, 0xf6, 0x63, 0x6c, 0xa0, 0x4a, 0xd3, 0xa3, 0x98, 0xd9, 0x42, 0xe3, 0xf1, 0xf8, 0x81, 0x96, 0xa9, 0xea, 0x6d, 0x4b, 0x8e, 0x33, 0xca, 0x94, 0x0d, 0xa0, 0xf7, 0xbb, 0x64, 0xa3, 0x36, 0x6f, 0xdc, 0x5a, 0x94, 0x42, 0xca, 0x06, 0xb2, 0x2b, 0x9a, 0x9f, 0x71, + /* (2^ 52)P */ 0xec, 0xdb, 0xa6, 0x1f, 0xdf, 0x15, 0x36, 0xa3, 0xda, 0x8a, 0x7a, 0xb6, 0xa7, 0xe3, 0xaf, 0x52, 0xe0, 0x8d, 0xe8, 0xf2, 0x44, 0x20, 0xeb, 0xa1, 0x20, 0xc4, 0x65, 0x3c, 0x7c, 0x6c, 0x49, 0xed, 0x2f, 0x66, 0x23, 0x68, 0x61, 0x91, 0x40, 0x9f, 0x50, 0x19, 0xd1, 0x84, 0xa7, 0xe2, 0xed, 0x34, 0x37, 0xe3, 0xe4, 0x11, 0x7f, 0x87, 0x55, 0x0f, + /* (2^ 53)P */ 0xb3, 0xa1, 0x0f, 0xb0, 0x48, 0xc0, 0x4d, 0x96, 0xa7, 0xcf, 0x5a, 0x81, 0xb8, 0x4a, 0x46, 0xef, 0x0a, 0xd3, 0x40, 0x7e, 0x02, 0xe3, 0x63, 0xaa, 0x50, 0xd1, 0x2a, 0x37, 0x22, 0x4a, 0x7f, 0x4f, 0xb6, 0xf9, 0x01, 0x82, 0x78, 0x3d, 0x93, 0x14, 0x11, 0x8a, 0x90, 0x60, 0xcd, 0x45, 0x4e, 0x7b, 0x42, 0xb9, 0x3e, 0x6e, 0x68, 0x1f, 0x36, 0x41, + /* (2^ 54)P */ 0x13, 0x73, 0x0e, 0x4f, 0x79, 0x93, 0x9e, 0x29, 0x70, 0x7b, 0x4a, 0x59, 0x1a, 0x9a, 0xf4, 0x55, 0x08, 0xf0, 0xdb, 0x17, 0x58, 0xec, 0x64, 0xad, 0x7f, 0x29, 0xeb, 0x3f, 0x85, 0x4e, 0x60, 0x28, 0x98, 0x1f, 0x73, 0x4e, 0xe6, 0xa8, 0xab, 0xd5, 0xd6, 0xfc, 0xa1, 0x36, 0x6d, 0x15, 0xc6, 0x13, 0x83, 0xa0, 0xc2, 0x6e, 0xd9, 0xdb, 0xc9, 0xcc, + /* (2^ 55)P */ 0xff, 0xd8, 0x52, 0xa3, 0xdc, 0x99, 0xcf, 0x3e, 0x19, 0xb3, 0x68, 0xd0, 0xb5, 0x0d, 0xb8, 0xee, 0x3f, 0xef, 0x6e, 0xc0, 0x38, 0x28, 0x44, 0x92, 0x78, 0x91, 0x1a, 0x08, 0x78, 0x6c, 0x65, 0x24, 0xf3, 0xa2, 0x3d, 0xf2, 0xe5, 0x79, 0x62, 0x69, 0x29, 0xf4, 0x22, 0xc5, 0xdb, 0x6a, 0xae, 0xf4, 0x44, 0xa3, 0x6f, 0xc7, 0x86, 0xab, 0xef, 0xef, + /* (2^ 56)P */ 0xbf, 0x54, 0x9a, 0x09, 0x5d, 0x17, 0xd0, 0xde, 0xfb, 0xf5, 0xca, 0xff, 0x13, 0x20, 0x88, 0x82, 0x3a, 0xe2, 0xd0, 0x3b, 0xfb, 0x05, 0x76, 0xd1, 0xc0, 0x02, 0x71, 0x3b, 0x94, 0xe8, 0xc9, 0x84, 0xcf, 0xa4, 0xe9, 0x28, 0x7b, 0xf5, 0x09, 0xc3, 0x2b, 0x22, 0x40, 0xf1, 0x68, 0x24, 0x24, 0x7d, 0x9f, 0x6e, 0xcd, 0xfe, 0xb0, 0x19, 0x61, 0xf5, + /* (2^ 57)P */ 0xe8, 0x63, 0x51, 0xb3, 0x95, 0x6b, 0x7b, 0x74, 0x92, 0x52, 0x45, 0xa4, 0xed, 0xea, 0x0e, 0x0d, 0x2b, 0x01, 0x1e, 0x2c, 0xbc, 0x91, 0x06, 0x69, 0xdb, 0x1f, 0xb5, 0x77, 0x1d, 0x56, 0xf5, 0xb4, 0x02, 0x80, 0x49, 0x56, 0x12, 0xce, 0x86, 0x05, 0xc9, 0xd9, 0xae, 0xf3, 0x6d, 0xe6, 0x3f, 0x40, 0x52, 0xe9, 0x49, 0x2b, 0x31, 0x06, 0x86, 0x14, + /* (2^ 58)P */ 0xf5, 0x09, 0x3b, 0xd2, 0xff, 0xdf, 0x11, 0xa5, 0x1c, 0x99, 0xe8, 0x1b, 0xa4, 0x2c, 0x7d, 0x8e, 0xc8, 0xf7, 0x03, 0x46, 0xfa, 0xb6, 0xde, 0x73, 0x91, 0x7e, 0x5a, 0x7a, 0xd7, 0x9a, 0x5b, 0x80, 0x24, 0x62, 0x5e, 0x92, 0xf1, 0xa3, 0x45, 0xa3, 0x43, 0x92, 0x8a, 0x2a, 0x5b, 0x0c, 0xb4, 0xc8, 0xad, 0x1c, 0xb6, 0x6c, 0x5e, 0x81, 0x18, 0x91, + /* (2^ 59)P */ 0x96, 0xb3, 0xca, 0x2b, 0xe3, 0x7a, 0x59, 0x72, 0x17, 0x74, 0x29, 0x21, 0xe7, 0x78, 0x07, 0xad, 0xda, 0xb6, 0xcd, 0xf9, 0x27, 0x4d, 0xc8, 0xf2, 0x98, 0x22, 0xca, 0xf2, 0x33, 0x74, 0x7a, 0xdd, 0x1e, 0x71, 0xec, 0xe3, 0x3f, 0xe2, 0xa2, 0xd2, 0x38, 0x75, 0xb0, 0xd0, 0x0a, 0xcf, 0x7d, 0x36, 0xdc, 0x49, 0x38, 0x25, 0x34, 0x4f, 0x20, 0x9a, + /* (2^ 60)P */ 0x2b, 0x6e, 0x04, 0x0d, 0x4f, 0x3d, 0x3b, 0x24, 0xf6, 0x4e, 0x5e, 0x0a, 0xbd, 0x48, 0x96, 0xba, 0x81, 0x8f, 0x39, 0x82, 0x13, 0xe6, 0x72, 0xf3, 0x0f, 0xb6, 0x94, 0xf4, 0xc5, 0x90, 0x74, 0x91, 0xa8, 0xf2, 0xc9, 0xca, 0x9a, 0x4d, 0x98, 0xf2, 0xdf, 0x52, 0x4e, 0x97, 0x2f, 0xeb, 0x84, 0xd3, 0xaf, 0xc2, 0xcc, 0xfb, 0x4c, 0x26, 0x4b, 0xe4, + /* (2^ 61)P */ 0x12, 0x9e, 0xfb, 0x9d, 0x78, 0x79, 0x99, 0xdd, 0xb3, 0x0b, 0x2e, 0x56, 0x41, 0x8e, 0x3f, 0x39, 0xb8, 0x97, 0x89, 0x53, 0x9b, 0x8a, 0x3c, 0x40, 0x9d, 0xa4, 0x6c, 0x2e, 0x31, 0x71, 0xc6, 0x0a, 0x41, 0xd4, 0x95, 0x06, 0x5e, 0xc1, 0xab, 0xc2, 0x14, 0xc4, 0xc7, 0x15, 0x08, 0x3a, 0xad, 0x7a, 0xb4, 0x62, 0xa3, 0x0c, 0x90, 0xf4, 0x47, 0x08, + /* (2^ 62)P */ 0x7f, 0xec, 0x09, 0x82, 0xf5, 0x94, 0x09, 0x93, 0x32, 0xd3, 0xdc, 0x56, 0x80, 0x7b, 0x5b, 0x22, 0x80, 0x6a, 0x96, 0x72, 0xb1, 0xc2, 0xd9, 0xa1, 0x8b, 0x66, 0x42, 0x16, 0xe2, 0x07, 0xb3, 0x2d, 0xf1, 0x75, 0x35, 0x72, 0xc7, 0x98, 0xbe, 0x63, 0x3b, 0x20, 0x75, 0x05, 0xc1, 0x3e, 0x31, 0x5a, 0xf7, 0xaa, 0xae, 0x4b, 0xdb, 0x1d, 0xd0, 0x74, + /* (2^ 63)P */ 0x36, 0x5c, 0x74, 0xe6, 0x5d, 0x59, 0x3f, 0x15, 0x4b, 0x4d, 0x4e, 0x67, 0x41, 0xfe, 0x98, 0x1f, 0x49, 0x76, 0x91, 0x0f, 0x9b, 0xf4, 0xaf, 0x86, 0xaf, 0x66, 0x19, 0xed, 0x46, 0xf1, 0x05, 0x9a, 0xcc, 0xd1, 0x14, 0x1f, 0x82, 0x12, 0x8e, 0xe6, 0xf4, 0xc3, 0x42, 0x5c, 0x4e, 0x33, 0x93, 0xbe, 0x30, 0xe7, 0x64, 0xa9, 0x35, 0x00, 0x4d, 0xf9, + /* (2^ 64)P */ 0x1f, 0xc1, 0x1e, 0xb7, 0xe3, 0x7c, 0xfa, 0xa3, 0x6b, 0x76, 0xaf, 0x9c, 0x05, 0x85, 0x4a, 0xa9, 0xfb, 0xe3, 0x7e, 0xf2, 0x49, 0x56, 0xdc, 0x2f, 0x57, 0x10, 0xba, 0x37, 0xb2, 0x62, 0xf5, 0x6b, 0xe5, 0x8f, 0x0a, 0x87, 0xd1, 0x6a, 0xcb, 0x9d, 0x07, 0xd0, 0xf6, 0x38, 0x99, 0x2c, 0x61, 0x4a, 0x4e, 0xd8, 0xd2, 0x88, 0x29, 0x99, 0x11, 0x95, + /* (2^ 65)P */ 0x6f, 0xdc, 0xd5, 0xd6, 0xd6, 0xa7, 0x4c, 0x46, 0x93, 0x65, 0x62, 0x23, 0x95, 0x32, 0x9c, 0xde, 0x40, 0x41, 0x68, 0x2c, 0x18, 0x4e, 0x5a, 0x8c, 0xc0, 0xc5, 0xc5, 0xea, 0x5c, 0x45, 0x0f, 0x60, 0x78, 0x39, 0xb6, 0x36, 0x23, 0x12, 0xbc, 0x21, 0x9a, 0xf8, 0x91, 0xac, 0xc4, 0x70, 0xdf, 0x85, 0x8e, 0x3c, 0xec, 0x22, 0x04, 0x98, 0xa8, 0xaa, + /* (2^ 66)P */ 0xcc, 0x52, 0x10, 0x5b, 0x4b, 0x6c, 0xc5, 0xfa, 0x3e, 0xd4, 0xf8, 0x1c, 0x04, 0x14, 0x48, 0x33, 0xd9, 0xfc, 0x5f, 0xb0, 0xa5, 0x48, 0x8c, 0x45, 0x8a, 0xee, 0x3e, 0xa7, 0xc1, 0x2e, 0x34, 0xca, 0xf6, 0xc9, 0xeb, 0x10, 0xbb, 0xe1, 0x59, 0x84, 0x25, 0xe8, 0x81, 0x70, 0xc0, 0x09, 0x42, 0xa7, 0x3b, 0x0d, 0x33, 0x00, 0xb5, 0x77, 0xbe, 0x25, + /* (2^ 67)P */ 0xcd, 0x1f, 0xbc, 0x7d, 0xef, 0xe5, 0xca, 0x91, 0xaf, 0xa9, 0x59, 0x6a, 0x09, 0xca, 0xd6, 0x1b, 0x3d, 0x55, 0xde, 0xa2, 0x6a, 0x80, 0xd6, 0x95, 0x47, 0xe4, 0x5f, 0x68, 0x54, 0x08, 0xdf, 0x29, 0xba, 0x2a, 0x02, 0x84, 0xe8, 0xe9, 0x00, 0x77, 0x99, 0x36, 0x03, 0xf6, 0x4a, 0x3e, 0x21, 0x81, 0x7d, 0xb8, 0xa4, 0x8a, 0xa2, 0x05, 0xef, 0xbc, + /* (2^ 68)P */ 0x7c, 0x59, 0x5f, 0x66, 0xd9, 0xb7, 0x83, 0x43, 0x8a, 0xa1, 0x8d, 0x51, 0x70, 0xba, 0xf2, 0x9b, 0x95, 0xc0, 0x4b, 0x4c, 0xa0, 0x14, 0xd3, 0xa4, 0x5d, 0x4a, 0x37, 0x36, 0x97, 0x31, 0x1e, 0x12, 0xe7, 0xbb, 0x08, 0x67, 0xa5, 0x23, 0xd7, 0xfb, 0x97, 0xd8, 0x6a, 0x03, 0xb1, 0xf8, 0x7f, 0xda, 0x58, 0xd9, 0x3f, 0x73, 0x4a, 0x53, 0xe1, 0x7b, + /* (2^ 69)P */ 0x55, 0x83, 0x98, 0x78, 0x6c, 0x56, 0x5e, 0xed, 0xf7, 0x23, 0x3e, 0x4c, 0x7d, 0x09, 0x2d, 0x09, 0x9c, 0x58, 0x8b, 0x32, 0xca, 0xfe, 0xbf, 0x47, 0x03, 0xeb, 0x4d, 0xe7, 0xeb, 0x9c, 0x83, 0x05, 0x68, 0xaa, 0x80, 0x89, 0x44, 0xf9, 0xd4, 0xdc, 0xdb, 0xb1, 0xdb, 0x77, 0xac, 0xf9, 0x2a, 0xae, 0x35, 0xac, 0x74, 0xb5, 0x95, 0x62, 0x18, 0x85, + /* (2^ 70)P */ 0xab, 0x82, 0x7e, 0x10, 0xd7, 0xe6, 0x57, 0xd1, 0x66, 0x12, 0x31, 0x9c, 0x9c, 0xa6, 0x27, 0x59, 0x71, 0x2e, 0xeb, 0xa0, 0x68, 0xc5, 0x87, 0x51, 0xf4, 0xca, 0x3f, 0x98, 0x56, 0xb0, 0x89, 0xb1, 0xc7, 0x7b, 0x46, 0xb3, 0xae, 0x36, 0xf2, 0xee, 0x15, 0x1a, 0x60, 0xf4, 0x50, 0x76, 0x4f, 0xc4, 0x53, 0x0d, 0x36, 0x4d, 0x31, 0xb1, 0x20, 0x51, + /* (2^ 71)P */ 0xf7, 0x1d, 0x8c, 0x1b, 0x5e, 0xe5, 0x02, 0x6f, 0xc5, 0xa5, 0xe0, 0x5f, 0xc6, 0xb6, 0x63, 0x43, 0xaf, 0x3c, 0x19, 0x6c, 0xf4, 0xaf, 0xa4, 0x33, 0xb1, 0x0a, 0x37, 0x3d, 0xd9, 0x4d, 0xe2, 0x29, 0x24, 0x26, 0x94, 0x7c, 0x02, 0xe4, 0xe2, 0xf2, 0xbe, 0xbd, 0xac, 0x1b, 0x48, 0xb8, 0xdd, 0xe9, 0x0d, 0x9a, 0x50, 0x1a, 0x98, 0x71, 0x6e, 0xdc, + /* (2^ 72)P */ 0x9f, 0x40, 0xb1, 0xb3, 0x66, 0x28, 0x6c, 0xfe, 0xa6, 0x7d, 0xf8, 0x3e, 0xb8, 0xf3, 0xde, 0x52, 0x76, 0x52, 0xa3, 0x92, 0x98, 0x23, 0xab, 0x4f, 0x88, 0x97, 0xfc, 0x22, 0xe1, 0x6b, 0x67, 0xcd, 0x13, 0x95, 0xda, 0x65, 0xdd, 0x3b, 0x67, 0x3f, 0x5f, 0x4c, 0xf2, 0x8a, 0xad, 0x98, 0xa7, 0x94, 0x24, 0x45, 0x87, 0x11, 0x7c, 0x75, 0x79, 0x85, + /* (2^ 73)P */ 0x70, 0xbf, 0xf9, 0x3b, 0xa9, 0x44, 0x57, 0x72, 0x96, 0xc9, 0xa4, 0x98, 0x65, 0xbf, 0x87, 0xb3, 0x3a, 0x39, 0x12, 0xde, 0xe5, 0x39, 0x01, 0x4f, 0xf7, 0xc0, 0x71, 0x52, 0x36, 0x85, 0xb3, 0x18, 0xf8, 0x14, 0xc0, 0x6d, 0xae, 0x9e, 0x4f, 0xb0, 0x72, 0x87, 0xac, 0x5c, 0xd1, 0x6c, 0x41, 0x6c, 0x90, 0x9d, 0x22, 0x81, 0xe4, 0x2b, 0xea, 0xe5, + /* (2^ 74)P */ 0xfc, 0xea, 0x1a, 0x65, 0xd9, 0x49, 0x6a, 0x39, 0xb5, 0x96, 0x72, 0x7b, 0x32, 0xf1, 0xd0, 0xe9, 0x45, 0xd9, 0x31, 0x55, 0xc7, 0x34, 0xe9, 0x5a, 0xec, 0x73, 0x0b, 0x03, 0xc4, 0xb3, 0xe6, 0xc9, 0x5e, 0x0a, 0x17, 0xfe, 0x53, 0x66, 0x7f, 0x21, 0x18, 0x74, 0x54, 0x1b, 0xc9, 0x49, 0x16, 0xd2, 0x48, 0xaf, 0x5b, 0x47, 0x7b, 0xeb, 0xaa, 0xc9, + /* (2^ 75)P */ 0x47, 0x04, 0xf5, 0x5a, 0x87, 0x77, 0x9e, 0x21, 0x34, 0x4e, 0x83, 0x88, 0xaf, 0x02, 0x1d, 0xb0, 0x5a, 0x1d, 0x1d, 0x7d, 0x8d, 0x2c, 0xd3, 0x8d, 0x63, 0xa9, 0x45, 0xfb, 0x15, 0x6d, 0x86, 0x45, 0xcd, 0x38, 0x0e, 0xf7, 0x37, 0x79, 0xed, 0x6d, 0x5a, 0xbc, 0x32, 0xcc, 0x66, 0xf1, 0x3a, 0xb2, 0x87, 0x6f, 0x70, 0x71, 0xd9, 0xf2, 0xfa, 0x7b, + /* (2^ 76)P */ 0x68, 0x07, 0xdc, 0x61, 0x40, 0xe4, 0xec, 0x32, 0xc8, 0xbe, 0x66, 0x30, 0x54, 0x80, 0xfd, 0x13, 0x7a, 0xef, 0xae, 0xed, 0x2e, 0x00, 0x6d, 0x3f, 0xbd, 0xfc, 0x91, 0x24, 0x53, 0x7f, 0x63, 0x9d, 0x2e, 0xe3, 0x76, 0xe0, 0xf3, 0xe1, 0x8f, 0x7a, 0xc4, 0x77, 0x0c, 0x91, 0xc0, 0xc2, 0x18, 0x6b, 0x04, 0xad, 0xb6, 0x70, 0x9a, 0x64, 0xc5, 0x82, + /* (2^ 77)P */ 0x7f, 0xea, 0x13, 0xd8, 0x9e, 0xfc, 0x5b, 0x06, 0xb5, 0x4f, 0xda, 0x38, 0xe0, 0x9c, 0xd2, 0x3a, 0xc1, 0x1c, 0x62, 0x70, 0x7f, 0xc6, 0x24, 0x0a, 0x47, 0x04, 0x01, 0xc4, 0x55, 0x09, 0xd1, 0x7a, 0x07, 0xba, 0xa3, 0x80, 0x4f, 0xc1, 0x65, 0x36, 0x6d, 0xc0, 0x10, 0xcf, 0x94, 0xa9, 0xa2, 0x01, 0x44, 0xd1, 0xf9, 0x1c, 0x4c, 0xfb, 0xf8, 0x99, + /* (2^ 78)P */ 0x6c, 0xb9, 0x6b, 0xee, 0x43, 0x5b, 0xb9, 0xbb, 0xee, 0x2e, 0x52, 0xc1, 0xc6, 0xb9, 0x61, 0xd2, 0x93, 0xa5, 0xaf, 0x52, 0xf4, 0xa4, 0x1a, 0x51, 0x61, 0xa7, 0xcb, 0x9e, 0xbb, 0x56, 0x65, 0xe2, 0xbf, 0x75, 0xb9, 0x9c, 0x50, 0x96, 0x60, 0x81, 0x74, 0x47, 0xc0, 0x04, 0x88, 0x71, 0x76, 0x39, 0x9a, 0xa7, 0xb1, 0x4e, 0x43, 0x15, 0xe0, 0xbb, + /* (2^ 79)P */ 0xbb, 0xce, 0xe2, 0xbb, 0xf9, 0x17, 0x0f, 0x82, 0x40, 0xad, 0x73, 0xe3, 0xeb, 0x3b, 0x06, 0x1a, 0xcf, 0x8e, 0x6e, 0x28, 0xb8, 0x26, 0xd9, 0x5b, 0xb7, 0xb3, 0xcf, 0xb4, 0x6a, 0x1c, 0xbf, 0x7f, 0xb8, 0xb5, 0x79, 0xcf, 0x45, 0x68, 0x7d, 0xc5, 0xeb, 0xf3, 0xbe, 0x39, 0x40, 0xfc, 0x07, 0x90, 0x7a, 0x62, 0xad, 0x86, 0x08, 0x71, 0x25, 0xe1, + /* (2^ 80)P */ 0x9b, 0x46, 0xac, 0xef, 0xc1, 0x4e, 0xa1, 0x97, 0x95, 0x76, 0xf9, 0x1b, 0xc2, 0xb2, 0x6a, 0x41, 0xea, 0x80, 0x3d, 0xe9, 0x08, 0x52, 0x5a, 0xe3, 0xf2, 0x08, 0xc5, 0xea, 0x39, 0x3f, 0x44, 0x71, 0x4d, 0xea, 0x0d, 0x05, 0x23, 0xe4, 0x2e, 0x3c, 0x89, 0xfe, 0x12, 0x8a, 0x95, 0x42, 0x0a, 0x68, 0xea, 0x5a, 0x28, 0x06, 0x9e, 0xe3, 0x5f, 0xe0, + /* (2^ 81)P */ 0x00, 0x61, 0x6c, 0x98, 0x9b, 0xe7, 0xb9, 0x06, 0x1c, 0xc5, 0x1b, 0xed, 0xbe, 0xc8, 0xb3, 0xea, 0x87, 0xf0, 0xc4, 0x24, 0x7d, 0xbb, 0x5d, 0xa4, 0x1d, 0x7a, 0x16, 0x00, 0x55, 0x94, 0x67, 0x78, 0xbd, 0x58, 0x02, 0x82, 0x90, 0x53, 0x76, 0xd4, 0x72, 0x99, 0x51, 0x6f, 0x7b, 0xcf, 0x80, 0x30, 0x31, 0x3b, 0x01, 0xc7, 0xc1, 0xef, 0xe6, 0x42, + /* (2^ 82)P */ 0xe2, 0x35, 0xaf, 0x4b, 0x79, 0xc6, 0x12, 0x24, 0x99, 0xc0, 0x68, 0xb0, 0x43, 0x3e, 0xe5, 0xef, 0xe2, 0x29, 0xea, 0xb8, 0xb3, 0xbc, 0x6a, 0x53, 0x2c, 0x69, 0x18, 0x5a, 0xf9, 0x15, 0xae, 0x66, 0x58, 0x18, 0xd3, 0x2d, 0x4b, 0x00, 0xfd, 0x84, 0xab, 0x4f, 0xae, 0x70, 0x6b, 0x9e, 0x9a, 0xdf, 0x83, 0xfd, 0x2e, 0x3c, 0xcf, 0xf8, 0x88, 0x5b, + /* (2^ 83)P */ 0xa4, 0x90, 0x31, 0x85, 0x13, 0xcd, 0xdf, 0x64, 0xc9, 0xa1, 0x0b, 0xe7, 0xb6, 0x73, 0x8a, 0x1b, 0x22, 0x78, 0x4c, 0xd4, 0xae, 0x48, 0x18, 0x00, 0x00, 0xa8, 0x9f, 0x06, 0xf9, 0xfb, 0x2d, 0xc3, 0xb1, 0x2a, 0xbc, 0x13, 0x99, 0x57, 0xaf, 0xf0, 0x8d, 0x61, 0x54, 0x29, 0xd5, 0xf2, 0x72, 0x00, 0x96, 0xd1, 0x85, 0x12, 0x8a, 0xf0, 0x23, 0xfb, + /* (2^ 84)P */ 0x69, 0xc7, 0xdb, 0xd9, 0x92, 0x75, 0x08, 0x9b, 0xeb, 0xa5, 0x93, 0xd1, 0x1a, 0xf4, 0xf5, 0xaf, 0xe6, 0xc4, 0x4a, 0x0d, 0x35, 0x26, 0x39, 0x9d, 0xd3, 0x17, 0x3e, 0xae, 0x2d, 0xbf, 0x73, 0x9f, 0xb7, 0x74, 0x91, 0xd1, 0xd8, 0x5c, 0x14, 0xf9, 0x75, 0xdf, 0xeb, 0xc2, 0x22, 0xd8, 0x14, 0x8d, 0x86, 0x23, 0x4d, 0xd1, 0x2d, 0xdb, 0x6b, 0x42, + /* (2^ 85)P */ 0x8c, 0xda, 0xc6, 0xf8, 0x71, 0xba, 0x2b, 0x06, 0x78, 0xae, 0xcc, 0x3a, 0xe3, 0xe3, 0xa1, 0x8b, 0xe2, 0x34, 0x6d, 0x28, 0x9e, 0x46, 0x13, 0x4d, 0x9e, 0xa6, 0x73, 0x49, 0x65, 0x79, 0x88, 0xb9, 0x3a, 0xd1, 0x6d, 0x2f, 0x48, 0x2b, 0x0a, 0x7f, 0x58, 0x20, 0x37, 0xf4, 0x0e, 0xbb, 0x4a, 0x95, 0x58, 0x0c, 0x88, 0x30, 0xc4, 0x74, 0xdd, 0xfd, + /* (2^ 86)P */ 0x6d, 0x13, 0x4e, 0x89, 0x2d, 0xa9, 0xa3, 0xed, 0x09, 0xe3, 0x0e, 0x71, 0x3e, 0x4a, 0xab, 0x90, 0xde, 0x03, 0xeb, 0x56, 0x46, 0x60, 0x06, 0xf5, 0x71, 0xe5, 0xee, 0x9b, 0xef, 0xff, 0xc4, 0x2c, 0x9f, 0x37, 0x48, 0x45, 0x94, 0x12, 0x41, 0x81, 0x15, 0x70, 0x91, 0x99, 0x5e, 0x56, 0x6b, 0xf4, 0xa6, 0xc9, 0xf5, 0x69, 0x9d, 0x78, 0x37, 0x57, + /* (2^ 87)P */ 0xf3, 0x51, 0x57, 0x7e, 0x43, 0x6f, 0xc6, 0x67, 0x59, 0x0c, 0xcf, 0x94, 0xe6, 0x3d, 0xb5, 0x07, 0xc9, 0x77, 0x48, 0xc9, 0x68, 0x0d, 0x98, 0x36, 0x62, 0x35, 0x38, 0x1c, 0xf5, 0xc5, 0xec, 0x66, 0x78, 0xfe, 0x47, 0xab, 0x26, 0xd6, 0x44, 0xb6, 0x06, 0x0f, 0x89, 0xe3, 0x19, 0x40, 0x1a, 0xe7, 0xd8, 0x65, 0x55, 0xf7, 0x1a, 0xfc, 0xa3, 0x0e, + /* (2^ 88)P */ 0x0e, 0x30, 0xa6, 0xb7, 0x58, 0x60, 0x62, 0x2a, 0x6c, 0x13, 0xa8, 0x14, 0x9b, 0xb8, 0xf2, 0x70, 0xd8, 0xb1, 0x71, 0x88, 0x8c, 0x18, 0x31, 0x25, 0x93, 0x90, 0xb4, 0xc7, 0x49, 0xd8, 0xd4, 0xdb, 0x1e, 0x1e, 0x7f, 0xaa, 0xba, 0xc9, 0xf2, 0x5d, 0xa9, 0x3a, 0x43, 0xb4, 0x5c, 0xee, 0x7b, 0xc7, 0x97, 0xb7, 0x66, 0xd7, 0x23, 0xd9, 0x22, 0x59, + /* (2^ 89)P */ 0x28, 0x19, 0xa6, 0xf9, 0x89, 0x20, 0x78, 0xd4, 0x6d, 0xcb, 0x79, 0x8f, 0x61, 0x6f, 0xb2, 0x5c, 0x4f, 0xa6, 0x54, 0x84, 0x95, 0x24, 0x36, 0x64, 0xcb, 0x39, 0xe7, 0x8f, 0x97, 0x9c, 0x5c, 0x3c, 0xfb, 0x51, 0x11, 0x01, 0x17, 0xdb, 0xc9, 0x9b, 0x51, 0x03, 0x9a, 0xe9, 0xe5, 0x24, 0x1e, 0xf5, 0xda, 0xe0, 0x48, 0x02, 0x23, 0xd0, 0x2c, 0x81, + /* (2^ 90)P */ 0x42, 0x1b, 0xe4, 0x91, 0x85, 0x2a, 0x0c, 0xd2, 0x28, 0x66, 0x57, 0x9e, 0x33, 0x8d, 0x25, 0x71, 0x10, 0x65, 0x76, 0xa2, 0x8c, 0x21, 0x86, 0x81, 0x15, 0xc2, 0x27, 0xeb, 0x54, 0x2d, 0x4f, 0x6c, 0xe6, 0xd6, 0x24, 0x9c, 0x1a, 0x12, 0xb8, 0x81, 0xe2, 0x0a, 0xf3, 0xd3, 0xf0, 0xd3, 0xe1, 0x74, 0x1f, 0x9b, 0x11, 0x47, 0xd0, 0xcf, 0xb6, 0x54, + /* (2^ 91)P */ 0x26, 0x45, 0xa2, 0x10, 0xd4, 0x2d, 0xae, 0xc0, 0xb0, 0xe8, 0x86, 0xb3, 0xc7, 0xea, 0x70, 0x87, 0x61, 0xb5, 0xa5, 0x55, 0xbe, 0x88, 0x1d, 0x7a, 0xd9, 0x6f, 0xeb, 0x83, 0xe2, 0x44, 0x7f, 0x98, 0x04, 0xd6, 0x50, 0x9d, 0xa7, 0x86, 0x66, 0x09, 0x63, 0xe1, 0xed, 0x72, 0xb1, 0xe4, 0x1d, 0x3a, 0xfd, 0x47, 0xce, 0x1c, 0xaa, 0x3b, 0x8f, 0x1b, + /* (2^ 92)P */ 0xf4, 0x3c, 0x4a, 0xb6, 0xc2, 0x9c, 0xe0, 0x2e, 0xb7, 0x38, 0xea, 0x61, 0x35, 0x97, 0x10, 0x90, 0xae, 0x22, 0x48, 0xb3, 0xa9, 0xc6, 0x7a, 0xbb, 0x23, 0xf2, 0xf8, 0x1b, 0xa7, 0xa1, 0x79, 0xcc, 0xc4, 0xf8, 0x08, 0x76, 0x8a, 0x5a, 0x1c, 0x1b, 0xc5, 0x33, 0x91, 0xa9, 0xb8, 0xb9, 0xd3, 0xf8, 0x49, 0xcd, 0xe5, 0x82, 0x43, 0xf7, 0xca, 0x68, + /* (2^ 93)P */ 0x38, 0xba, 0xae, 0x44, 0xfe, 0x57, 0x64, 0x56, 0x7c, 0x0e, 0x9c, 0xca, 0xff, 0xa9, 0x82, 0xbb, 0x38, 0x4a, 0xa7, 0xf7, 0x47, 0xab, 0xbe, 0x6d, 0x23, 0x0b, 0x8a, 0xed, 0xc2, 0xb9, 0x8f, 0xf1, 0xec, 0x91, 0x44, 0x73, 0x64, 0xba, 0xd5, 0x8f, 0x37, 0x38, 0x0d, 0xd5, 0xf8, 0x73, 0x57, 0xb6, 0xc2, 0x45, 0xdc, 0x25, 0xb2, 0xb6, 0xea, 0xd9, + /* (2^ 94)P */ 0xbf, 0xe9, 0x1a, 0x40, 0x4d, 0xcc, 0xe6, 0x1d, 0x70, 0x1a, 0x65, 0xcc, 0x34, 0x2c, 0x37, 0x2c, 0x2d, 0x6b, 0x6d, 0xe5, 0x2f, 0x19, 0x9e, 0xe4, 0xe1, 0xaa, 0xd4, 0xab, 0x54, 0xf4, 0xa8, 0xe4, 0x69, 0x2d, 0x8e, 0x4d, 0xd7, 0xac, 0xb0, 0x5b, 0xfe, 0xe3, 0x26, 0x07, 0xc3, 0xf8, 0x1b, 0x43, 0xa8, 0x1d, 0x64, 0xa5, 0x25, 0x88, 0xbb, 0x77, + /* (2^ 95)P */ 0x92, 0xcd, 0x6e, 0xa0, 0x79, 0x04, 0x18, 0xf4, 0x11, 0x58, 0x48, 0xb5, 0x3c, 0x7b, 0xd1, 0xcc, 0xd3, 0x14, 0x2c, 0xa0, 0xdd, 0x04, 0x44, 0x11, 0xb3, 0x6d, 0x2f, 0x0d, 0xf5, 0x2a, 0x75, 0x5d, 0x1d, 0xda, 0x86, 0x8d, 0x7d, 0x6b, 0x32, 0x68, 0xb6, 0x6c, 0x64, 0x9e, 0xde, 0x80, 0x88, 0xce, 0x08, 0xbf, 0x0b, 0xe5, 0x8e, 0x4f, 0x1d, 0xfb, + /* (2^ 96)P */ 0xaf, 0xe8, 0x85, 0xbf, 0x7f, 0x37, 0x8d, 0x66, 0x7c, 0xd5, 0xd3, 0x96, 0xa5, 0x81, 0x67, 0x95, 0xff, 0x48, 0xde, 0xde, 0xd7, 0x7a, 0x46, 0x34, 0xb1, 0x13, 0x70, 0x29, 0xed, 0x87, 0x90, 0xb0, 0x40, 0x2c, 0xa6, 0x43, 0x6e, 0xb6, 0xbc, 0x48, 0x8a, 0xc1, 0xae, 0xb8, 0xd4, 0xe2, 0xc0, 0x32, 0xb2, 0xa6, 0x2a, 0x8f, 0xb5, 0x16, 0x9e, 0xc3, + /* (2^ 97)P */ 0xff, 0x4d, 0xd2, 0xd6, 0x74, 0xef, 0x2c, 0x96, 0xc1, 0x11, 0xa8, 0xb8, 0xfe, 0x94, 0x87, 0x3e, 0xa0, 0xfb, 0x57, 0xa3, 0xfc, 0x7a, 0x7e, 0x6a, 0x59, 0x6c, 0x54, 0xbb, 0xbb, 0xa2, 0x25, 0x38, 0x1b, 0xdf, 0x5d, 0x7b, 0x94, 0x14, 0xde, 0x07, 0x6e, 0xd3, 0xab, 0x02, 0x26, 0x74, 0x16, 0x12, 0xdf, 0x2e, 0x2a, 0xa7, 0xb0, 0xe8, 0x29, 0xc0, + /* (2^ 98)P */ 0x6a, 0x38, 0x0b, 0xd3, 0xba, 0x45, 0x23, 0xe0, 0x04, 0x3b, 0x83, 0x39, 0xc5, 0x11, 0xe6, 0xcf, 0x39, 0x0a, 0xb3, 0xb0, 0x3b, 0x27, 0x29, 0x63, 0x1c, 0xf3, 0x00, 0xe6, 0xd2, 0x55, 0x21, 0x1f, 0x84, 0x97, 0x9f, 0x01, 0x49, 0x43, 0x30, 0x5f, 0xe0, 0x1d, 0x24, 0xc4, 0x4e, 0xa0, 0x2b, 0x0b, 0x12, 0x55, 0xc3, 0x27, 0xae, 0x08, 0x83, 0x7c, + /* (2^ 99)P */ 0x5d, 0x1a, 0xb7, 0xa9, 0xf5, 0xfd, 0xec, 0xad, 0xb7, 0x87, 0x02, 0x5f, 0x0d, 0x30, 0x4d, 0xe2, 0x65, 0x87, 0xa4, 0x41, 0x45, 0x1d, 0x67, 0xe0, 0x30, 0x5c, 0x13, 0x87, 0xf6, 0x2e, 0x08, 0xc1, 0xc7, 0x12, 0x45, 0xc8, 0x9b, 0xad, 0xb8, 0xd5, 0x57, 0xbb, 0x5c, 0x48, 0x3a, 0xe1, 0x91, 0x5e, 0xf6, 0x4d, 0x8a, 0x63, 0x75, 0x69, 0x0c, 0x01, + /* (2^100)P */ 0x8f, 0x53, 0x2d, 0xa0, 0x71, 0x3d, 0xfc, 0x45, 0x10, 0x96, 0xcf, 0x56, 0xf9, 0xbb, 0x40, 0x3c, 0x86, 0x52, 0x76, 0xbe, 0x84, 0xf9, 0xa6, 0x9d, 0x3d, 0x27, 0xbe, 0xb4, 0x00, 0x49, 0x94, 0xf5, 0x5d, 0xe1, 0x62, 0x85, 0x66, 0xe5, 0xb8, 0x20, 0x2c, 0x09, 0x7d, 0x9d, 0x3d, 0x6e, 0x74, 0x39, 0xab, 0xad, 0xa0, 0x90, 0x97, 0x5f, 0xbb, 0xa7, + /* (2^101)P */ 0xdb, 0x2d, 0x99, 0x08, 0x16, 0x46, 0x83, 0x7a, 0xa8, 0xea, 0x3d, 0x28, 0x5b, 0x49, 0xfc, 0xb9, 0x6d, 0x00, 0x9e, 0x54, 0x4f, 0x47, 0x64, 0x9b, 0x58, 0x4d, 0x07, 0x0c, 0x6f, 0x29, 0x56, 0x0b, 0x00, 0x14, 0x85, 0x96, 0x41, 0x04, 0xb9, 0x5c, 0xa4, 0xf6, 0x16, 0x73, 0x6a, 0xc7, 0x62, 0x0c, 0x65, 0x2f, 0x93, 0xbf, 0xf7, 0xb9, 0xb7, 0xf1, + /* (2^102)P */ 0xeb, 0x6d, 0xb3, 0x46, 0x32, 0xd2, 0xcb, 0x08, 0x94, 0x14, 0xbf, 0x3f, 0xc5, 0xcb, 0x5f, 0x9f, 0x8a, 0x89, 0x0c, 0x1b, 0x45, 0xad, 0x4c, 0x50, 0xb4, 0xe1, 0xa0, 0x6b, 0x11, 0x92, 0xaf, 0x1f, 0x00, 0xcc, 0xe5, 0x13, 0x7e, 0xe4, 0x2e, 0xa0, 0x57, 0xf3, 0xa7, 0x84, 0x79, 0x7a, 0xc2, 0xb7, 0xb7, 0xfc, 0x5d, 0xa5, 0xa9, 0x64, 0xcc, 0xd8, + /* (2^103)P */ 0xa9, 0xc4, 0x12, 0x8b, 0x34, 0x78, 0x3e, 0x38, 0xfd, 0x3f, 0x87, 0xfa, 0x88, 0x94, 0xd5, 0xd9, 0x7f, 0xeb, 0x58, 0xff, 0xb9, 0x45, 0xdb, 0xa1, 0xed, 0x22, 0x28, 0x1d, 0x00, 0x6d, 0x79, 0x85, 0x7a, 0x75, 0x5d, 0xf0, 0xb1, 0x9e, 0x47, 0x28, 0x8c, 0x62, 0xdf, 0xfb, 0x4c, 0x7b, 0xc5, 0x1a, 0x42, 0x95, 0xef, 0x9a, 0xb7, 0x27, 0x7e, 0xda, + /* (2^104)P */ 0xca, 0xd5, 0xc0, 0x17, 0xa1, 0x66, 0x79, 0x9c, 0x2a, 0xb7, 0x0a, 0xfe, 0x62, 0xe4, 0x26, 0x78, 0x90, 0xa7, 0xcb, 0xb0, 0x4f, 0x6d, 0xf9, 0x8f, 0xf7, 0x7d, 0xac, 0xb8, 0x78, 0x1f, 0x41, 0xea, 0x97, 0x1e, 0x62, 0x97, 0x43, 0x80, 0x58, 0x80, 0xb6, 0x69, 0x7d, 0xee, 0x16, 0xd2, 0xa1, 0x81, 0xd7, 0xb1, 0x27, 0x03, 0x48, 0xda, 0xab, 0xec, + /* (2^105)P */ 0x5b, 0xed, 0x40, 0x8e, 0x8c, 0xc1, 0x66, 0x90, 0x7f, 0x0c, 0xb2, 0xfc, 0xbd, 0x16, 0xac, 0x7d, 0x4c, 0x6a, 0xf9, 0xae, 0xe7, 0x4e, 0x11, 0x12, 0xe9, 0xbe, 0x17, 0x09, 0xc6, 0xc1, 0x5e, 0xb5, 0x7b, 0x50, 0x5c, 0x27, 0xfb, 0x80, 0xab, 0x01, 0xfa, 0x5b, 0x9b, 0x75, 0x16, 0x6e, 0xb2, 0x5c, 0x8c, 0x2f, 0xa5, 0x6a, 0x1a, 0x68, 0xa6, 0x90, + /* (2^106)P */ 0x75, 0xfe, 0xb6, 0x96, 0x96, 0x87, 0x4c, 0x12, 0xa9, 0xd1, 0xd8, 0x03, 0xa3, 0xc1, 0x15, 0x96, 0xe8, 0xa0, 0x75, 0x82, 0xa0, 0x6d, 0xea, 0x54, 0xdc, 0x5f, 0x0d, 0x7e, 0xf6, 0x70, 0xb5, 0xdc, 0x7a, 0xf6, 0xc4, 0xd4, 0x21, 0x49, 0xf5, 0xd4, 0x14, 0x6d, 0x48, 0x1d, 0x7c, 0x99, 0x42, 0xdf, 0x78, 0x6b, 0x9d, 0xb9, 0x30, 0x3c, 0xd0, 0x29, + /* (2^107)P */ 0x85, 0xd6, 0xd8, 0xf3, 0x91, 0x74, 0xdd, 0xbd, 0x72, 0x96, 0x10, 0xe4, 0x76, 0x02, 0x5a, 0x72, 0x67, 0xd3, 0x17, 0x72, 0x14, 0x9a, 0x20, 0x5b, 0x0f, 0x8d, 0xed, 0x6d, 0x4e, 0xe3, 0xd9, 0x82, 0xc2, 0x99, 0xee, 0x39, 0x61, 0x69, 0x8a, 0x24, 0x01, 0x92, 0x15, 0xe7, 0xfc, 0xf9, 0x4d, 0xac, 0xf1, 0x30, 0x49, 0x01, 0x0b, 0x6e, 0x0f, 0x20, + /* (2^108)P */ 0xd8, 0x25, 0x94, 0x5e, 0x43, 0x29, 0xf5, 0xcc, 0xe8, 0xe3, 0x55, 0x41, 0x3c, 0x9f, 0x58, 0x5b, 0x00, 0xeb, 0xc5, 0xdf, 0xcf, 0xfb, 0xfd, 0x6e, 0x92, 0xec, 0x99, 0x30, 0xd6, 0x05, 0xdd, 0x80, 0x7a, 0x5d, 0x6d, 0x16, 0x85, 0xd8, 0x9d, 0x43, 0x65, 0xd8, 0x2c, 0x33, 0x2f, 0x5c, 0x41, 0xea, 0xb7, 0x95, 0x77, 0xf2, 0x9e, 0x59, 0x09, 0xe8, + /* (2^109)P */ 0x00, 0xa0, 0x03, 0x80, 0xcd, 0x60, 0xe5, 0x17, 0xd4, 0x15, 0x99, 0xdd, 0x4f, 0xbf, 0x66, 0xb8, 0xc0, 0xf5, 0xf9, 0xfc, 0x6d, 0x42, 0x18, 0x34, 0x1c, 0x7d, 0x5b, 0xb5, 0x09, 0xd0, 0x99, 0x57, 0x81, 0x0b, 0x62, 0xb3, 0xa2, 0xf9, 0x0b, 0xae, 0x95, 0xb8, 0xc2, 0x3b, 0x0d, 0x5b, 0x00, 0xf1, 0xed, 0xbc, 0x05, 0x9d, 0x61, 0xbc, 0x73, 0x9d, + /* (2^110)P */ 0xd4, 0xdb, 0x29, 0xe5, 0x85, 0xe9, 0xc6, 0x89, 0x2a, 0xa8, 0x54, 0xab, 0xb3, 0x7f, 0x88, 0xc0, 0x4d, 0xe0, 0xd1, 0x74, 0x6e, 0xa3, 0xa7, 0x39, 0xd5, 0xcc, 0xa1, 0x8a, 0xcb, 0x5b, 0x34, 0xad, 0x92, 0xb4, 0xd8, 0xd5, 0x17, 0xf6, 0x77, 0x18, 0x9e, 0xaf, 0x45, 0x3b, 0x03, 0xe2, 0xf8, 0x52, 0x60, 0xdc, 0x15, 0x20, 0x9e, 0xdf, 0xd8, 0x5d, + /* (2^111)P */ 0x02, 0xc1, 0xac, 0x1a, 0x15, 0x8e, 0x6c, 0xf5, 0x1e, 0x1e, 0xba, 0x7e, 0xc2, 0xda, 0x7d, 0x02, 0xda, 0x43, 0xae, 0x04, 0x70, 0x28, 0x54, 0x78, 0x94, 0xf5, 0x4f, 0x07, 0x84, 0x8f, 0xed, 0xaa, 0xc0, 0xb8, 0xcd, 0x7f, 0x7e, 0x33, 0xa3, 0xbe, 0x21, 0x29, 0xc8, 0x56, 0x34, 0xc0, 0x76, 0x87, 0x8f, 0xc7, 0x73, 0x58, 0x90, 0x16, 0xfc, 0xd6, + /* (2^112)P */ 0xb8, 0x3f, 0xe1, 0xdf, 0x3a, 0x91, 0x25, 0x0c, 0xf6, 0x47, 0xa8, 0x89, 0xc4, 0xc6, 0x61, 0xec, 0x86, 0x2c, 0xfd, 0xbe, 0xa4, 0x6f, 0xc2, 0xd4, 0x46, 0x19, 0x70, 0x5d, 0x09, 0x02, 0x86, 0xd3, 0x4b, 0xe9, 0x16, 0x7b, 0xf0, 0x0d, 0x6c, 0xff, 0x91, 0x05, 0xbf, 0x55, 0xb4, 0x00, 0x8d, 0xe5, 0x6d, 0x68, 0x20, 0x90, 0x12, 0xb5, 0x5c, 0x32, + /* (2^113)P */ 0x80, 0x45, 0xc8, 0x51, 0x87, 0xba, 0x1c, 0x5c, 0xcf, 0x5f, 0x4b, 0x3c, 0x9e, 0x3b, 0x36, 0xd2, 0x26, 0xa2, 0x7f, 0xab, 0xb7, 0xbf, 0xda, 0x68, 0x23, 0x8f, 0xc3, 0xa0, 0xfd, 0xad, 0xf1, 0x56, 0x3b, 0xd0, 0x75, 0x2b, 0x44, 0x61, 0xd8, 0xf4, 0xf1, 0x05, 0x49, 0x53, 0x07, 0xee, 0x47, 0xef, 0xc0, 0x7c, 0x9d, 0xe4, 0x15, 0x88, 0xc5, 0x47, + /* (2^114)P */ 0x2d, 0xb5, 0x09, 0x80, 0xb9, 0xd3, 0xd8, 0xfe, 0x4c, 0xd2, 0xa6, 0x6e, 0xd3, 0x75, 0xcf, 0xb0, 0x99, 0xcb, 0x50, 0x8d, 0xe9, 0x67, 0x9b, 0x20, 0xe8, 0x57, 0xd8, 0x14, 0x85, 0x73, 0x6a, 0x74, 0xe0, 0x99, 0xf0, 0x6b, 0x6e, 0x59, 0x30, 0x31, 0x33, 0x96, 0x5f, 0xa1, 0x0c, 0x1b, 0xf4, 0xca, 0x09, 0xe1, 0x9b, 0xb5, 0xcf, 0x6d, 0x0b, 0xeb, + /* (2^115)P */ 0x1a, 0xde, 0x50, 0xa9, 0xac, 0x3e, 0x10, 0x43, 0x4f, 0x82, 0x4f, 0xc0, 0xfe, 0x3f, 0x33, 0xd2, 0x64, 0x86, 0x50, 0xa9, 0x51, 0x76, 0x5e, 0x50, 0x97, 0x6c, 0x73, 0x8d, 0x77, 0xa3, 0x75, 0x03, 0xbc, 0xc9, 0xfb, 0x50, 0xd9, 0x6d, 0x16, 0xad, 0x5d, 0x32, 0x3d, 0xac, 0x44, 0xdf, 0x51, 0xf7, 0x19, 0xd4, 0x0b, 0x57, 0x78, 0x0b, 0x81, 0x4e, + /* (2^116)P */ 0x32, 0x24, 0xf1, 0x6c, 0x55, 0x62, 0x1d, 0xb3, 0x1f, 0xda, 0xfa, 0x6a, 0x8f, 0x98, 0x01, 0x16, 0xde, 0x44, 0x50, 0x0d, 0x2e, 0x6c, 0x0b, 0xa2, 0xd3, 0x74, 0x0e, 0xa9, 0xbf, 0x8d, 0xa9, 0xc8, 0xc8, 0x2f, 0x62, 0xc1, 0x35, 0x5e, 0xfd, 0x3a, 0xb3, 0x83, 0x2d, 0xee, 0x4e, 0xfd, 0x5c, 0x5e, 0xad, 0x85, 0xa5, 0x10, 0xb5, 0x4f, 0x34, 0xa7, + /* (2^117)P */ 0xd1, 0x58, 0x6f, 0xe6, 0x54, 0x2c, 0xc2, 0xcd, 0xcf, 0x83, 0xdc, 0x88, 0x0c, 0xb9, 0xb4, 0x62, 0x18, 0x89, 0x65, 0x28, 0xe9, 0x72, 0x4b, 0x65, 0xcf, 0xd6, 0x90, 0x88, 0xd7, 0x76, 0x17, 0x4f, 0x74, 0x64, 0x1e, 0xcb, 0xd3, 0xf5, 0x4b, 0xaa, 0x2e, 0x4d, 0x2d, 0x7c, 0x13, 0x1f, 0xfd, 0xd9, 0x60, 0x83, 0x7e, 0xda, 0x64, 0x1c, 0xdc, 0x9f, + /* (2^118)P */ 0xad, 0xef, 0xac, 0x1b, 0xc1, 0x30, 0x5a, 0x15, 0xc9, 0x1f, 0xac, 0xf1, 0xca, 0x44, 0x95, 0x95, 0xea, 0xf2, 0x22, 0xe7, 0x8d, 0x25, 0xf0, 0xff, 0xd8, 0x71, 0xf7, 0xf8, 0x8f, 0x8f, 0xcd, 0xf4, 0x1e, 0xfe, 0x6c, 0x68, 0x04, 0xb8, 0x78, 0xa1, 0x5f, 0xa6, 0x5d, 0x5e, 0xf9, 0x8d, 0xea, 0x80, 0xcb, 0xf3, 0x17, 0xa6, 0x03, 0xc9, 0x38, 0xd5, + /* (2^119)P */ 0x79, 0x14, 0x31, 0xc3, 0x38, 0xe5, 0xaa, 0xbf, 0x17, 0xa3, 0x04, 0x4e, 0x80, 0x59, 0x9c, 0x9f, 0x19, 0x39, 0xe4, 0x2d, 0x23, 0x54, 0x4a, 0x7f, 0x3e, 0xf3, 0xd9, 0xc7, 0xba, 0x6c, 0x8f, 0x6b, 0xfa, 0x34, 0xb5, 0x23, 0x17, 0x1d, 0xff, 0x1d, 0xea, 0x1f, 0xd7, 0xba, 0x61, 0xb2, 0xe0, 0x38, 0x6a, 0xe9, 0xcf, 0x48, 0x5d, 0x6a, 0x10, 0x9c, + /* (2^120)P */ 0xc8, 0xbb, 0x13, 0x1c, 0x3f, 0x3c, 0x34, 0xfd, 0xac, 0x37, 0x52, 0x44, 0x25, 0xa8, 0xde, 0x1d, 0x63, 0xf4, 0x81, 0x9a, 0xbe, 0x0b, 0x74, 0x2e, 0xc8, 0x51, 0x16, 0xd3, 0xac, 0x4a, 0xaf, 0xe2, 0x5f, 0x3a, 0x89, 0x32, 0xd1, 0x9b, 0x7c, 0x90, 0x0d, 0xac, 0xdc, 0x8b, 0x73, 0x45, 0x45, 0x97, 0xb1, 0x90, 0x2c, 0x1b, 0x31, 0xca, 0xb1, 0x94, + /* (2^121)P */ 0x07, 0x28, 0xdd, 0x10, 0x14, 0xa5, 0x95, 0x7e, 0xf3, 0xe4, 0xd4, 0x14, 0xb4, 0x7e, 0x76, 0xdb, 0x42, 0xd6, 0x94, 0x3e, 0xeb, 0x44, 0x64, 0x88, 0x0d, 0xec, 0xc1, 0x21, 0xf0, 0x79, 0xe0, 0x83, 0x67, 0x55, 0x53, 0xc2, 0xf6, 0xc5, 0xc5, 0x89, 0x39, 0xe8, 0x42, 0xd0, 0x17, 0xbd, 0xff, 0x35, 0x59, 0x0e, 0xc3, 0x06, 0x86, 0xd4, 0x64, 0xcf, + /* (2^122)P */ 0x91, 0xa8, 0xdb, 0x57, 0x9b, 0xe2, 0x96, 0x31, 0x10, 0x6e, 0xd7, 0x9a, 0x97, 0xb3, 0xab, 0xb5, 0x15, 0x66, 0xbe, 0xcc, 0x6d, 0x9a, 0xac, 0x06, 0xb3, 0x0d, 0xaa, 0x4b, 0x9c, 0x96, 0x79, 0x6c, 0x34, 0xee, 0x9e, 0x53, 0x4d, 0x6e, 0xbd, 0x88, 0x02, 0xbf, 0x50, 0x54, 0x12, 0x5d, 0x01, 0x02, 0x46, 0xc6, 0x74, 0x02, 0x8c, 0x24, 0xae, 0xb1, + /* (2^123)P */ 0xf5, 0x22, 0xea, 0xac, 0x7d, 0x9c, 0x33, 0x8a, 0xa5, 0x36, 0x79, 0x6a, 0x4f, 0xa4, 0xdc, 0xa5, 0x73, 0x64, 0xc4, 0x6f, 0x43, 0x02, 0x3b, 0x94, 0x66, 0xd2, 0x4b, 0x4f, 0xf6, 0x45, 0x33, 0x5d, 0x10, 0x33, 0x18, 0x1e, 0xa3, 0xfc, 0xf7, 0xd2, 0xb8, 0xc8, 0xa7, 0xe0, 0x76, 0x8a, 0xcd, 0xff, 0x4f, 0x99, 0x34, 0x47, 0x84, 0x91, 0x96, 0x9f, + /* (2^124)P */ 0x8a, 0x48, 0x3b, 0x48, 0x4a, 0xbc, 0xac, 0xe2, 0x80, 0xd6, 0xd2, 0x35, 0xde, 0xd0, 0x56, 0x42, 0x33, 0xb3, 0x56, 0x5a, 0xcd, 0xb8, 0x3d, 0xb5, 0x25, 0xc1, 0xed, 0xff, 0x87, 0x0b, 0x79, 0xff, 0xf2, 0x62, 0xe1, 0x76, 0xc6, 0xa2, 0x0f, 0xa8, 0x9b, 0x0d, 0xcc, 0x3f, 0x3d, 0x35, 0x27, 0x8d, 0x0b, 0x74, 0xb0, 0xc3, 0x78, 0x8c, 0xcc, 0xc8, + /* (2^125)P */ 0xfc, 0x9a, 0x0c, 0xa8, 0x49, 0x42, 0xb8, 0xdf, 0xcf, 0xb3, 0x19, 0xa6, 0x64, 0x57, 0xfe, 0xe8, 0xf8, 0xa6, 0x4b, 0x86, 0xa1, 0xd5, 0x83, 0x7f, 0x14, 0x99, 0x18, 0x0c, 0x7d, 0x5b, 0xf7, 0x3d, 0xf9, 0x4b, 0x79, 0xb1, 0x86, 0x30, 0xb4, 0x5e, 0x6a, 0xe8, 0x9d, 0xfa, 0x8a, 0x41, 0xc4, 0x30, 0xfc, 0x56, 0x74, 0x14, 0x42, 0xc8, 0x96, 0x0e, + /* (2^126)P */ 0xdf, 0x66, 0xec, 0xbc, 0x44, 0xdb, 0x19, 0xce, 0xd4, 0xb5, 0x49, 0x40, 0x07, 0x49, 0xe0, 0x3a, 0x61, 0x10, 0xfb, 0x7d, 0xba, 0xb1, 0xe0, 0x28, 0x5b, 0x99, 0x59, 0x96, 0xa2, 0xee, 0xe0, 0x23, 0x37, 0x39, 0x1f, 0xe6, 0x57, 0x9f, 0xf8, 0xf8, 0xdc, 0x74, 0xf6, 0x8f, 0x4f, 0x5e, 0x51, 0xa4, 0x12, 0xac, 0xbe, 0xe4, 0xf3, 0xd1, 0xf0, 0x24, + /* (2^127)P */ 0x1e, 0x3e, 0x9a, 0x5f, 0xdf, 0x9f, 0xd6, 0x4e, 0x8a, 0x28, 0xc3, 0xcd, 0x96, 0x9d, 0x57, 0xc7, 0x61, 0x81, 0x90, 0xff, 0xae, 0xb1, 0x4f, 0xc2, 0x96, 0x8b, 0x1a, 0x18, 0xf4, 0x50, 0xcb, 0x31, 0xe1, 0x57, 0xf4, 0x90, 0xa8, 0xea, 0xac, 0xe7, 0x61, 0x98, 0xb6, 0x15, 0xc1, 0x7b, 0x29, 0xa4, 0xc3, 0x18, 0xef, 0xb9, 0xd8, 0xdf, 0xf6, 0xac, + /* (2^128)P */ 0xca, 0xa8, 0x6c, 0xf1, 0xb4, 0xca, 0xfe, 0x31, 0xee, 0x48, 0x38, 0x8b, 0x0e, 0xbb, 0x7a, 0x30, 0xaa, 0xf9, 0xee, 0x27, 0x53, 0x24, 0xdc, 0x2e, 0x15, 0xa6, 0x48, 0x8f, 0xa0, 0x7e, 0xf1, 0xdc, 0x93, 0x87, 0x39, 0xeb, 0x7f, 0x38, 0x92, 0x92, 0x4c, 0x29, 0xe9, 0x57, 0xd8, 0x59, 0xfc, 0xe9, 0x9c, 0x44, 0xc0, 0x65, 0xcf, 0xac, 0x4b, 0xdc, + /* (2^129)P */ 0xa3, 0xd0, 0x37, 0x8f, 0x86, 0x2f, 0xc6, 0x47, 0x55, 0x46, 0x65, 0x26, 0x4b, 0x91, 0xe2, 0x18, 0x5c, 0x4f, 0x23, 0xc1, 0x37, 0x29, 0xb9, 0xc1, 0x27, 0xc5, 0x3c, 0xbf, 0x7e, 0x23, 0xdb, 0x73, 0x99, 0xbd, 0x1b, 0xb2, 0x31, 0x68, 0x3a, 0xad, 0xb7, 0xb0, 0x10, 0xc5, 0xe5, 0x11, 0x51, 0xba, 0xa7, 0x60, 0x66, 0x54, 0xf0, 0x08, 0xd7, 0x69, + /* (2^130)P */ 0x89, 0x41, 0x79, 0xcc, 0xeb, 0x0a, 0xf5, 0x4b, 0xa3, 0x4c, 0xce, 0x52, 0xb0, 0xa7, 0xe4, 0x41, 0x75, 0x7d, 0x04, 0xbb, 0x09, 0x4c, 0x50, 0x9f, 0xdf, 0xea, 0x74, 0x61, 0x02, 0xad, 0xb4, 0x9d, 0xb7, 0x05, 0xb9, 0xea, 0xeb, 0x91, 0x35, 0xe7, 0x49, 0xea, 0xd3, 0x4f, 0x3c, 0x60, 0x21, 0x7a, 0xde, 0xc7, 0xe2, 0x5a, 0xee, 0x8e, 0x93, 0xc7, + /* (2^131)P */ 0x00, 0xe8, 0xed, 0xd0, 0xb3, 0x0d, 0xaf, 0xb2, 0xde, 0x2c, 0xf6, 0x00, 0xe2, 0xea, 0x6d, 0xf8, 0x0e, 0xd9, 0x67, 0x59, 0xa9, 0x50, 0xbb, 0x17, 0x8f, 0xff, 0xb1, 0x9f, 0x17, 0xb6, 0xf2, 0xb5, 0xba, 0x80, 0xf7, 0x0f, 0xba, 0xd5, 0x09, 0x43, 0xaa, 0x4e, 0x3a, 0x67, 0x6a, 0x89, 0x9b, 0x18, 0x65, 0x35, 0xf8, 0x3a, 0x49, 0x91, 0x30, 0x51, + /* (2^132)P */ 0x8d, 0x25, 0xe9, 0x0e, 0x7d, 0x50, 0x76, 0xe4, 0x58, 0x7e, 0xb9, 0x33, 0xe6, 0x65, 0x90, 0xc2, 0x50, 0x9d, 0x50, 0x2e, 0x11, 0xad, 0xd5, 0x43, 0x52, 0x32, 0x41, 0x4f, 0x7b, 0xb6, 0xa0, 0xec, 0x81, 0x75, 0x36, 0x7c, 0x77, 0x85, 0x59, 0x70, 0xe4, 0xf9, 0xef, 0x66, 0x8d, 0x35, 0xc8, 0x2a, 0x6e, 0x5b, 0xc6, 0x0d, 0x0b, 0x29, 0x60, 0x68, + /* (2^133)P */ 0xf8, 0xce, 0xb0, 0x3a, 0x56, 0x7d, 0x51, 0x9a, 0x25, 0x73, 0xea, 0xdd, 0xe4, 0xe0, 0x0e, 0xf0, 0x07, 0xc0, 0x31, 0x00, 0x73, 0x35, 0xd0, 0x39, 0xc4, 0x9b, 0xb7, 0x95, 0xe0, 0x62, 0x70, 0x36, 0x0b, 0xcb, 0xa0, 0x42, 0xde, 0x51, 0xcf, 0x41, 0xe0, 0xb8, 0xb4, 0xc0, 0xe5, 0x46, 0x99, 0x9f, 0x02, 0x7f, 0x14, 0x8c, 0xc1, 0x4e, 0xef, 0xe8, + /* (2^134)P */ 0x10, 0x01, 0x57, 0x0a, 0xbe, 0x8b, 0x18, 0xc8, 0xca, 0x00, 0x28, 0x77, 0x4a, 0x9a, 0xc7, 0x55, 0x2a, 0xcc, 0x0c, 0x7b, 0xb9, 0xe9, 0xc8, 0x97, 0x7c, 0x02, 0xe3, 0x09, 0x2f, 0x62, 0x30, 0xb8, 0x40, 0x09, 0x65, 0xe9, 0x55, 0x63, 0xb5, 0x07, 0xca, 0x9f, 0x00, 0xdf, 0x9d, 0x5c, 0xc7, 0xee, 0x57, 0xa5, 0x90, 0x15, 0x1e, 0x22, 0xa0, 0x12, + /* (2^135)P */ 0x71, 0x2d, 0xc9, 0xef, 0x27, 0xb9, 0xd8, 0x12, 0x43, 0x6b, 0xa8, 0xce, 0x3b, 0x6d, 0x6e, 0x91, 0x43, 0x23, 0xbc, 0x32, 0xb3, 0xbf, 0xe1, 0xc7, 0x39, 0xcf, 0x7c, 0x42, 0x4c, 0xb1, 0x30, 0xe2, 0xdd, 0x69, 0x06, 0xe5, 0xea, 0xf0, 0x2a, 0x16, 0x50, 0x71, 0xca, 0x92, 0xdf, 0xc1, 0xcc, 0xec, 0xe6, 0x54, 0x07, 0xf3, 0x18, 0x8d, 0xd8, 0x29, + /* (2^136)P */ 0x98, 0x51, 0x48, 0x8f, 0xfa, 0x2e, 0x5e, 0x67, 0xb0, 0xc6, 0x17, 0x12, 0xb6, 0x7d, 0xc9, 0xad, 0x81, 0x11, 0xad, 0x0c, 0x1c, 0x2d, 0x45, 0xdf, 0xac, 0x66, 0xbd, 0x08, 0x6f, 0x7c, 0xc7, 0x06, 0x6e, 0x19, 0x08, 0x39, 0x64, 0xd7, 0xe4, 0xd1, 0x11, 0x5f, 0x1c, 0xf4, 0x67, 0xc3, 0x88, 0x6a, 0xe6, 0x07, 0xa3, 0x83, 0xd7, 0xfd, 0x2a, 0xf9, + /* (2^137)P */ 0x87, 0xed, 0xeb, 0xd9, 0xdf, 0xff, 0x43, 0x8b, 0xaa, 0x20, 0x58, 0xb0, 0xb4, 0x6b, 0x14, 0xb8, 0x02, 0xc5, 0x40, 0x20, 0x22, 0xbb, 0xf7, 0xb4, 0xf3, 0x05, 0x1e, 0x4d, 0x94, 0xff, 0xe3, 0xc5, 0x22, 0x82, 0xfe, 0xaf, 0x90, 0x42, 0x98, 0x6b, 0x76, 0x8b, 0x3e, 0x89, 0x3f, 0x42, 0x2a, 0xa7, 0x26, 0x00, 0xda, 0x5c, 0xa2, 0x2b, 0xec, 0xdd, + /* (2^138)P */ 0x5c, 0x21, 0x16, 0x0d, 0x46, 0xb8, 0xd0, 0xa7, 0x88, 0xe7, 0x25, 0xcb, 0x3e, 0x50, 0x73, 0x61, 0xe7, 0xaf, 0x5a, 0x3f, 0x47, 0x8b, 0x3d, 0x97, 0x79, 0x2c, 0xe6, 0x6d, 0x95, 0x74, 0x65, 0x70, 0x36, 0xfd, 0xd1, 0x9e, 0x13, 0x18, 0x63, 0xb1, 0x2d, 0x0b, 0xb5, 0x36, 0x3e, 0xe7, 0x35, 0x42, 0x3b, 0xe6, 0x1f, 0x4d, 0x9d, 0x59, 0xa2, 0x43, + /* (2^139)P */ 0x8c, 0x0c, 0x7c, 0x24, 0x9e, 0xe0, 0xf8, 0x05, 0x1c, 0x9e, 0x1f, 0x31, 0xc0, 0x70, 0xb3, 0xfb, 0x4e, 0xf8, 0x0a, 0x57, 0xb7, 0x49, 0xb5, 0x73, 0xa1, 0x5f, 0x9b, 0x6a, 0x07, 0x6c, 0x87, 0x71, 0x87, 0xd4, 0xbe, 0x98, 0x1e, 0x98, 0xee, 0x52, 0xc1, 0x7b, 0x95, 0x0f, 0x28, 0x32, 0x36, 0x28, 0xd0, 0x3a, 0x0f, 0x7d, 0x2a, 0xa9, 0x62, 0xb9, + /* (2^140)P */ 0x97, 0xe6, 0x18, 0x77, 0xf9, 0x34, 0xac, 0xbc, 0xe0, 0x62, 0x9f, 0x42, 0xde, 0xbd, 0x2f, 0xf7, 0x1f, 0xb7, 0x14, 0x52, 0x8a, 0x79, 0xb2, 0x3f, 0xd2, 0x95, 0x71, 0x01, 0xe8, 0xaf, 0x8c, 0xa4, 0xa4, 0xa7, 0x27, 0xf3, 0x5c, 0xdf, 0x3e, 0x57, 0x7a, 0xf1, 0x76, 0x49, 0xe6, 0x42, 0x3f, 0x8f, 0x1e, 0x63, 0x4a, 0x65, 0xb5, 0x41, 0xf5, 0x02, + /* (2^141)P */ 0x72, 0x85, 0xc5, 0x0b, 0xe1, 0x47, 0x64, 0x02, 0xc5, 0x4d, 0x81, 0x69, 0xb2, 0xcf, 0x0f, 0x6c, 0xd4, 0x6d, 0xd0, 0xc7, 0xb4, 0x1c, 0xd0, 0x32, 0x59, 0x89, 0xe2, 0xe0, 0x96, 0x8b, 0x12, 0x98, 0xbf, 0x63, 0x7a, 0x4c, 0x76, 0x7e, 0x58, 0x17, 0x8f, 0x5b, 0x0a, 0x59, 0x65, 0x75, 0xbc, 0x61, 0x1f, 0xbe, 0xc5, 0x6e, 0x0a, 0x57, 0x52, 0x70, + /* (2^142)P */ 0x92, 0x1c, 0x77, 0xbb, 0x62, 0x02, 0x6c, 0x25, 0x9c, 0x66, 0x07, 0x83, 0xab, 0xcc, 0x80, 0x5d, 0xd2, 0x76, 0x0c, 0xa4, 0xc5, 0xb4, 0x8a, 0x68, 0x23, 0x31, 0x32, 0x29, 0x8a, 0x47, 0x92, 0x12, 0x80, 0xb3, 0xfa, 0x18, 0xe4, 0x8d, 0xc0, 0x4d, 0xfe, 0x97, 0x5f, 0x72, 0x41, 0xb5, 0x5c, 0x7a, 0xbd, 0xf0, 0xcf, 0x5e, 0x97, 0xaa, 0x64, 0x32, + /* (2^143)P */ 0x35, 0x3f, 0x75, 0xc1, 0x7a, 0x75, 0x7e, 0xa9, 0xc6, 0x0b, 0x4e, 0x32, 0x62, 0xec, 0xe3, 0x5c, 0xfb, 0x01, 0x43, 0xb6, 0xd4, 0x5b, 0x75, 0xd2, 0xee, 0x7f, 0x5d, 0x23, 0x2b, 0xb3, 0x54, 0x34, 0x4c, 0xd3, 0xb4, 0x32, 0x84, 0x81, 0xb5, 0x09, 0x76, 0x19, 0xda, 0x58, 0xda, 0x7c, 0xdb, 0x2e, 0xdd, 0x4c, 0x8e, 0xdd, 0x5d, 0x89, 0x10, 0x10, + /* (2^144)P */ 0x57, 0x25, 0x6a, 0x08, 0x37, 0x92, 0xa8, 0xdf, 0x24, 0xef, 0x8f, 0x33, 0x34, 0x52, 0xa4, 0x4c, 0xf0, 0x77, 0x9f, 0x69, 0x77, 0xd5, 0x8f, 0xd2, 0x9a, 0xb3, 0xb6, 0x1d, 0x2d, 0xa6, 0xf7, 0x1f, 0xda, 0xd7, 0xcb, 0x75, 0x11, 0xc3, 0x6b, 0xc0, 0x38, 0xb1, 0xd5, 0x2d, 0x96, 0x84, 0x16, 0xfa, 0x26, 0xb9, 0xcc, 0x3f, 0x16, 0x47, 0x23, 0x74, + /* (2^145)P */ 0x9b, 0x61, 0x2a, 0x1c, 0xdd, 0x39, 0xa5, 0xfa, 0x1c, 0x7d, 0x63, 0x50, 0xca, 0xe6, 0x9d, 0xfa, 0xb7, 0xc4, 0x4c, 0x6a, 0x97, 0x5f, 0x36, 0x4e, 0x47, 0xdd, 0x17, 0xf7, 0xf9, 0x19, 0xce, 0x75, 0x17, 0xad, 0xce, 0x2a, 0xf3, 0xfe, 0x27, 0x8f, 0x3e, 0x48, 0xc0, 0x60, 0x87, 0x24, 0x19, 0xae, 0x59, 0xe4, 0x5a, 0x00, 0x2a, 0xba, 0xa2, 0x1f, + /* (2^146)P */ 0x26, 0x88, 0x42, 0x60, 0x9f, 0x6e, 0x2c, 0x7c, 0x39, 0x0f, 0x47, 0x6a, 0x0e, 0x02, 0xbb, 0x4b, 0x34, 0x29, 0x55, 0x18, 0x36, 0xcf, 0x3b, 0x47, 0xf1, 0x2e, 0xfc, 0x6e, 0x94, 0xff, 0xe8, 0x6b, 0x06, 0xd2, 0xba, 0x77, 0x5e, 0x60, 0xd7, 0x19, 0xef, 0x02, 0x9d, 0x3a, 0xc2, 0xb7, 0xa9, 0xd8, 0x57, 0xee, 0x7e, 0x2b, 0xf2, 0x6d, 0x28, 0xda, + /* (2^147)P */ 0xdf, 0xd9, 0x92, 0x11, 0x98, 0x23, 0xe2, 0x45, 0x2f, 0x74, 0x70, 0xee, 0x0e, 0x55, 0x65, 0x79, 0x86, 0x38, 0x17, 0x92, 0x85, 0x87, 0x99, 0x50, 0xd9, 0x7c, 0xdb, 0xa1, 0x10, 0xec, 0x30, 0xb7, 0x40, 0xa3, 0x23, 0x9b, 0x0e, 0x27, 0x49, 0x29, 0x03, 0x94, 0xff, 0x53, 0xdc, 0xd7, 0xed, 0x49, 0xa9, 0x5a, 0x3b, 0xee, 0xd7, 0xc7, 0x65, 0xaf, + /* (2^148)P */ 0xa0, 0xbd, 0xbe, 0x03, 0xee, 0x0c, 0xbe, 0x32, 0x00, 0x7b, 0x52, 0xcb, 0x92, 0x29, 0xbf, 0xa0, 0xc6, 0xd9, 0xd2, 0xd6, 0x15, 0xe8, 0x3a, 0x75, 0x61, 0x65, 0x56, 0xae, 0xad, 0x3c, 0x2a, 0x64, 0x14, 0x3f, 0x8e, 0xc1, 0x2d, 0x0c, 0x8d, 0x20, 0xdb, 0x58, 0x4b, 0xe5, 0x40, 0x15, 0x4b, 0xdc, 0xa8, 0xbd, 0xef, 0x08, 0xa7, 0xd1, 0xf4, 0xb0, + /* (2^149)P */ 0xa9, 0x0f, 0x05, 0x94, 0x66, 0xac, 0x1f, 0x65, 0x3f, 0xe1, 0xb8, 0xe1, 0x34, 0x5e, 0x1d, 0x8f, 0xe3, 0x93, 0x03, 0x15, 0xff, 0xb6, 0x65, 0xb6, 0x6e, 0xc0, 0x2f, 0xd4, 0x2e, 0xb9, 0x2c, 0x13, 0x3c, 0x99, 0x1c, 0xb5, 0x87, 0xba, 0x79, 0xcb, 0xf0, 0x18, 0x06, 0x86, 0x04, 0x14, 0x25, 0x09, 0xcd, 0x1c, 0x14, 0xda, 0x35, 0xd0, 0x38, 0x3b, + /* (2^150)P */ 0x1b, 0x04, 0xa3, 0x27, 0xb4, 0xd3, 0x37, 0x48, 0x1e, 0x8f, 0x69, 0xd3, 0x5a, 0x2f, 0x20, 0x02, 0x36, 0xbe, 0x06, 0x7b, 0x6b, 0x6c, 0x12, 0x5b, 0x80, 0x74, 0x44, 0xe6, 0xf8, 0xf5, 0x95, 0x59, 0x29, 0xab, 0x51, 0x47, 0x83, 0x28, 0xe0, 0xad, 0xde, 0xaa, 0xd3, 0xb1, 0x1a, 0xcb, 0xa3, 0xcd, 0x8b, 0x6a, 0xb1, 0xa7, 0x0a, 0xd1, 0xf9, 0xbe, + /* (2^151)P */ 0xce, 0x2f, 0x85, 0xca, 0x74, 0x6d, 0x49, 0xb8, 0xce, 0x80, 0x44, 0xe0, 0xda, 0x5b, 0xcf, 0x2f, 0x79, 0x74, 0xfe, 0xb4, 0x2c, 0x99, 0x20, 0x6e, 0x09, 0x04, 0xfb, 0x6d, 0x57, 0x5b, 0x95, 0x0c, 0x45, 0xda, 0x4f, 0x7f, 0x63, 0xcc, 0x85, 0x5a, 0x67, 0x50, 0x68, 0x71, 0xb4, 0x67, 0xb1, 0x2e, 0xc1, 0x1c, 0xdc, 0xff, 0x2a, 0x7c, 0x10, 0x5e, + /* (2^152)P */ 0xa6, 0xde, 0xf3, 0xd4, 0x22, 0x30, 0x24, 0x9e, 0x0b, 0x30, 0x54, 0x59, 0x7e, 0xa2, 0xeb, 0x89, 0x54, 0x65, 0x3e, 0x40, 0xd1, 0xde, 0xe6, 0xee, 0x4d, 0xbf, 0x5e, 0x40, 0x1d, 0xee, 0x4f, 0x68, 0xd9, 0xa7, 0x2f, 0xb3, 0x64, 0xb3, 0xf5, 0xc8, 0xd3, 0xaa, 0x70, 0x70, 0x3d, 0xef, 0xd3, 0x95, 0x54, 0xdb, 0x3e, 0x94, 0x95, 0x92, 0x1f, 0x45, + /* (2^153)P */ 0x22, 0x80, 0x1d, 0x9d, 0x96, 0xa5, 0x78, 0x6f, 0xe0, 0x1e, 0x1b, 0x66, 0x42, 0xc8, 0xae, 0x9e, 0x46, 0x45, 0x08, 0x41, 0xdf, 0x80, 0xae, 0x6f, 0xdb, 0x15, 0x5a, 0x21, 0x31, 0x7a, 0xd0, 0xf2, 0x54, 0x15, 0x88, 0xd3, 0x0f, 0x7f, 0x14, 0x5a, 0x14, 0x97, 0xab, 0xf4, 0x58, 0x6a, 0x9f, 0xea, 0x74, 0xe5, 0x6b, 0x90, 0x59, 0x2b, 0x48, 0xd9, + /* (2^154)P */ 0x12, 0x24, 0x04, 0xf5, 0x50, 0xc2, 0x8c, 0xb0, 0x7c, 0x46, 0x98, 0xd5, 0x24, 0xad, 0xf6, 0x72, 0xdc, 0x82, 0x1a, 0x60, 0xc1, 0xeb, 0x48, 0xef, 0x7f, 0x6e, 0xe6, 0xcc, 0xdb, 0x7b, 0xae, 0xbe, 0x5e, 0x1e, 0x5c, 0xe6, 0x0a, 0x70, 0xdf, 0xa4, 0xa3, 0x85, 0x1b, 0x1b, 0x7f, 0x72, 0xb9, 0x96, 0x6f, 0xdc, 0x03, 0x76, 0x66, 0xfb, 0xa0, 0x33, + /* (2^155)P */ 0x37, 0x40, 0xbb, 0xbc, 0x68, 0x58, 0x86, 0xca, 0xbb, 0xa5, 0x24, 0x76, 0x3d, 0x48, 0xd1, 0xad, 0xb4, 0xa8, 0xcf, 0xc3, 0xb6, 0xa8, 0xba, 0x1a, 0x3a, 0xbe, 0x33, 0x75, 0x04, 0x5c, 0x13, 0x8c, 0x0d, 0x70, 0x8d, 0xa6, 0x4e, 0x2a, 0xeb, 0x17, 0x3c, 0x22, 0xdd, 0x3e, 0x96, 0x40, 0x11, 0x9e, 0x4e, 0xae, 0x3d, 0xf8, 0x91, 0xd7, 0x50, 0xc8, + /* (2^156)P */ 0xd8, 0xca, 0xde, 0x19, 0xcf, 0x00, 0xe4, 0x73, 0x18, 0x7f, 0x9b, 0x9f, 0xf4, 0x5b, 0x49, 0x49, 0x99, 0xdc, 0xa4, 0x46, 0x21, 0xb5, 0xd7, 0x3e, 0xb7, 0x47, 0x1b, 0xa9, 0x9f, 0x4c, 0x69, 0x7d, 0xec, 0x33, 0xd6, 0x1c, 0x51, 0x7f, 0x47, 0x74, 0x7a, 0x6c, 0xf3, 0xd2, 0x2e, 0xbf, 0xdf, 0x6c, 0x9e, 0x77, 0x3b, 0x34, 0xf6, 0x73, 0x80, 0xed, + /* (2^157)P */ 0x16, 0xfb, 0x16, 0xc3, 0xc2, 0x83, 0xe4, 0xf4, 0x03, 0x7f, 0x52, 0xb0, 0x67, 0x51, 0x7b, 0x24, 0x5a, 0x51, 0xd3, 0xb6, 0x4e, 0x59, 0x76, 0xcd, 0x08, 0x7b, 0x1d, 0x7a, 0x9c, 0x65, 0xae, 0xce, 0xaa, 0xd2, 0x1c, 0x85, 0x66, 0x68, 0x06, 0x15, 0xa8, 0x06, 0xe6, 0x16, 0x37, 0xf4, 0x49, 0x9e, 0x0f, 0x50, 0x37, 0xb1, 0xb2, 0x93, 0x70, 0x43, + /* (2^158)P */ 0x18, 0x3a, 0x16, 0xe5, 0x8d, 0xc8, 0x35, 0xd6, 0x7b, 0x09, 0xec, 0x61, 0x5f, 0x5c, 0x2a, 0x19, 0x96, 0x2e, 0xc3, 0xfd, 0xab, 0xe6, 0x23, 0xae, 0xab, 0xc5, 0xcb, 0xb9, 0x7b, 0x2d, 0x34, 0x51, 0xb9, 0x41, 0x9e, 0x7d, 0xca, 0xda, 0x25, 0x45, 0x14, 0xb0, 0xc7, 0x4d, 0x26, 0x2b, 0xfe, 0x43, 0xb0, 0x21, 0x5e, 0xfa, 0xdc, 0x7c, 0xf9, 0x5a, + /* (2^159)P */ 0x94, 0xad, 0x42, 0x17, 0xf5, 0xcd, 0x1c, 0x0d, 0xf6, 0x41, 0xd2, 0x55, 0xbb, 0x50, 0xf1, 0xc6, 0xbc, 0xa6, 0xc5, 0x3a, 0xfd, 0x9b, 0x75, 0x3e, 0xf6, 0x1a, 0xa7, 0xb2, 0x6e, 0x64, 0x12, 0xdc, 0x3c, 0xe5, 0xf6, 0xfc, 0x3b, 0xfa, 0x43, 0x81, 0xd4, 0xa5, 0xee, 0xf5, 0x9c, 0x47, 0x2f, 0xd0, 0x9c, 0xde, 0xa1, 0x48, 0x91, 0x9a, 0x34, 0xc1, + /* (2^160)P */ 0x37, 0x1b, 0xb3, 0x88, 0xc9, 0x98, 0x4e, 0xfb, 0x84, 0x4f, 0x2b, 0x0a, 0xb6, 0x8f, 0x35, 0x15, 0xcd, 0x61, 0x7a, 0x5f, 0x5c, 0xa0, 0xca, 0x23, 0xa0, 0x93, 0x1f, 0xcc, 0x3c, 0x39, 0x3a, 0x24, 0xa7, 0x49, 0xad, 0x8d, 0x59, 0xcc, 0x94, 0x5a, 0x16, 0xf5, 0x70, 0xe8, 0x52, 0x1e, 0xee, 0x20, 0x30, 0x17, 0x7e, 0xf0, 0x4c, 0x93, 0x06, 0x5a, + /* (2^161)P */ 0x81, 0xba, 0x3b, 0xd7, 0x3e, 0xb4, 0x32, 0x3a, 0x22, 0x39, 0x2a, 0xfc, 0x19, 0xd9, 0xd2, 0xf6, 0xc5, 0x79, 0x6c, 0x0e, 0xde, 0xda, 0x01, 0xff, 0x52, 0xfb, 0xb6, 0x95, 0x4e, 0x7a, 0x10, 0xb8, 0x06, 0x86, 0x3c, 0xcd, 0x56, 0xd6, 0x15, 0xbf, 0x6e, 0x3e, 0x4f, 0x35, 0x5e, 0xca, 0xbc, 0xa5, 0x95, 0xa2, 0xdf, 0x2d, 0x1d, 0xaf, 0x59, 0xf9, + /* (2^162)P */ 0x69, 0xe5, 0xe2, 0xfa, 0xc9, 0x7f, 0xdd, 0x09, 0xf5, 0x6b, 0x4e, 0x2e, 0xbe, 0xb4, 0xbf, 0x3e, 0xb2, 0xf2, 0x81, 0x30, 0xe1, 0x07, 0xa8, 0x0d, 0x2b, 0xd2, 0x5a, 0x55, 0xbe, 0x4b, 0x86, 0x5d, 0xb0, 0x5e, 0x7c, 0x8f, 0xc1, 0x3c, 0x81, 0x4c, 0xf7, 0x6d, 0x7d, 0xe6, 0x4f, 0x8a, 0x85, 0xc2, 0x2f, 0x28, 0xef, 0x8c, 0x69, 0xc2, 0xc2, 0x1a, + /* (2^163)P */ 0xd9, 0xe4, 0x0e, 0x1e, 0xc2, 0xf7, 0x2f, 0x9f, 0xa1, 0x40, 0xfe, 0x46, 0x16, 0xaf, 0x2e, 0xd1, 0xec, 0x15, 0x9b, 0x61, 0x92, 0xce, 0xfc, 0x10, 0x43, 0x1d, 0x00, 0xf6, 0xbe, 0x20, 0x80, 0x80, 0x6f, 0x3c, 0x16, 0x94, 0x59, 0xba, 0x03, 0x53, 0x6e, 0xb6, 0xdd, 0x25, 0x7b, 0x86, 0xbf, 0x96, 0xf4, 0x2f, 0xa1, 0x96, 0x8d, 0xf9, 0xb3, 0x29, + /* (2^164)P */ 0x3b, 0x04, 0x60, 0x6e, 0xce, 0xab, 0xd2, 0x63, 0x18, 0x53, 0x88, 0x16, 0x4a, 0x6a, 0xab, 0x72, 0x03, 0x68, 0xa5, 0xd4, 0x0d, 0xb2, 0x82, 0x81, 0x1f, 0x2b, 0x5c, 0x75, 0xe8, 0xd2, 0x1d, 0x7f, 0xe7, 0x1b, 0x35, 0x02, 0xde, 0xec, 0xbd, 0xcb, 0xc7, 0x01, 0xd3, 0x95, 0x61, 0xfe, 0xb2, 0x7a, 0x66, 0x09, 0x4c, 0x6d, 0xfd, 0x39, 0xf7, 0x52, + /* (2^165)P */ 0x42, 0xc1, 0x5f, 0xf8, 0x35, 0x52, 0xc1, 0xfe, 0xc5, 0x11, 0x80, 0x1c, 0x11, 0x46, 0x31, 0x11, 0xbe, 0xd0, 0xc4, 0xb6, 0x07, 0x13, 0x38, 0xa0, 0x8d, 0x65, 0xf0, 0x56, 0x9e, 0x16, 0xbf, 0x9d, 0xcd, 0x51, 0x34, 0xf9, 0x08, 0x48, 0x7b, 0x76, 0x0c, 0x7b, 0x30, 0x07, 0xa8, 0x76, 0xaf, 0xa3, 0x29, 0x38, 0xb0, 0x58, 0xde, 0x72, 0x4b, 0x45, + /* (2^166)P */ 0xd4, 0x16, 0xa7, 0xc0, 0xb4, 0x9f, 0xdf, 0x1a, 0x37, 0xc8, 0x35, 0xed, 0xc5, 0x85, 0x74, 0x64, 0x09, 0x22, 0xef, 0xe9, 0x0c, 0xaf, 0x12, 0x4c, 0x9e, 0xf8, 0x47, 0x56, 0xe0, 0x7f, 0x4e, 0x24, 0x6b, 0x0c, 0xe7, 0xad, 0xc6, 0x47, 0x1d, 0xa4, 0x0d, 0x86, 0x89, 0x65, 0xe8, 0x5f, 0x71, 0xc7, 0xe9, 0xcd, 0xec, 0x6c, 0x62, 0xc7, 0xe3, 0xb3, + /* (2^167)P */ 0xb5, 0xea, 0x86, 0xe3, 0x15, 0x18, 0x3f, 0x6d, 0x7b, 0x05, 0x95, 0x15, 0x53, 0x26, 0x1c, 0xeb, 0xbe, 0x7e, 0x16, 0x42, 0x4b, 0xa2, 0x3d, 0xdd, 0x0e, 0xff, 0xba, 0x67, 0xb5, 0xae, 0x7a, 0x17, 0xde, 0x23, 0xad, 0x14, 0xcc, 0xd7, 0xaf, 0x57, 0x01, 0xe0, 0xdd, 0x48, 0xdd, 0xd7, 0xe3, 0xdf, 0xe9, 0x2d, 0xda, 0x67, 0xa4, 0x9f, 0x29, 0x04, + /* (2^168)P */ 0x16, 0x53, 0xe6, 0x9c, 0x4e, 0xe5, 0x1e, 0x70, 0x81, 0x25, 0x02, 0x9b, 0x47, 0x6d, 0xd2, 0x08, 0x73, 0xbe, 0x0a, 0xf1, 0x7b, 0xeb, 0x24, 0xeb, 0x38, 0x23, 0x5c, 0xb6, 0x3e, 0xce, 0x1e, 0xe3, 0xbc, 0x82, 0x35, 0x1f, 0xaf, 0x3a, 0x3a, 0xe5, 0x4e, 0xc1, 0xca, 0xbf, 0x47, 0xb4, 0xbb, 0xbc, 0x5f, 0xea, 0xc6, 0xca, 0xf3, 0xa0, 0xa2, 0x73, + /* (2^169)P */ 0xef, 0xa4, 0x7a, 0x4e, 0xe4, 0xc7, 0xb6, 0x43, 0x2e, 0xa5, 0xe4, 0xa5, 0xba, 0x1e, 0xa5, 0xfe, 0x9e, 0xce, 0xa9, 0x80, 0x04, 0xcb, 0x4f, 0xd8, 0x74, 0x05, 0x48, 0xfa, 0x99, 0x11, 0x5d, 0x97, 0x3b, 0x07, 0x0d, 0xdd, 0xe6, 0xb1, 0x74, 0x87, 0x1a, 0xd3, 0x26, 0xb7, 0x8f, 0xe1, 0x63, 0x3d, 0xec, 0x53, 0x93, 0xb0, 0x81, 0x78, 0x34, 0xa4, + /* (2^170)P */ 0xe1, 0xe7, 0xd4, 0x58, 0x9d, 0x0e, 0x8b, 0x65, 0x66, 0x37, 0x16, 0x48, 0x6f, 0xaa, 0x42, 0x37, 0x77, 0xad, 0xb1, 0x56, 0x48, 0xdf, 0x65, 0x36, 0x30, 0xb8, 0x00, 0x12, 0xd8, 0x32, 0x28, 0x7f, 0xc1, 0x71, 0xeb, 0x93, 0x0f, 0x48, 0x04, 0xe1, 0x5a, 0x6a, 0x96, 0xc1, 0xca, 0x89, 0x6d, 0x1b, 0x82, 0x4c, 0x18, 0x6d, 0x55, 0x4b, 0xea, 0xfd, + /* (2^171)P */ 0x62, 0x1a, 0x53, 0xb4, 0xb1, 0xbe, 0x6f, 0x15, 0x18, 0x88, 0xd4, 0x66, 0x61, 0xc7, 0x12, 0x69, 0x02, 0xbd, 0x03, 0x23, 0x2b, 0xef, 0xf9, 0x54, 0xa4, 0x85, 0xa8, 0xe3, 0xb7, 0xbd, 0xa9, 0xa3, 0xf3, 0x2a, 0xdd, 0xf1, 0xd4, 0x03, 0x0f, 0xa9, 0xa1, 0xd8, 0xa3, 0xcd, 0xb2, 0x71, 0x90, 0x4b, 0x35, 0x62, 0xf2, 0x2f, 0xce, 0x67, 0x1f, 0xaa, + /* (2^172)P */ 0x9e, 0x1e, 0xcd, 0x43, 0x7e, 0x87, 0x37, 0x94, 0x3a, 0x97, 0x4c, 0x7e, 0xee, 0xc9, 0x37, 0x85, 0xf1, 0xd9, 0x4f, 0xbf, 0xf9, 0x6f, 0x39, 0x9a, 0x39, 0x87, 0x2e, 0x25, 0x84, 0x42, 0xc3, 0x80, 0xcb, 0x07, 0x22, 0xae, 0x30, 0xd5, 0x50, 0xa1, 0x23, 0xcc, 0x31, 0x81, 0x9d, 0xf1, 0x30, 0xd9, 0x2b, 0x73, 0x41, 0x16, 0x50, 0xab, 0x2d, 0xa2, + /* (2^173)P */ 0xa4, 0x69, 0x4f, 0xa1, 0x4e, 0xb9, 0xbf, 0x14, 0xe8, 0x2b, 0x04, 0x93, 0xb7, 0x6e, 0x9f, 0x7d, 0x73, 0x0a, 0xc5, 0x14, 0xb8, 0xde, 0x8c, 0xc1, 0xfe, 0xc0, 0xa7, 0xa4, 0xcc, 0x42, 0x42, 0x81, 0x15, 0x65, 0x8a, 0x80, 0xb9, 0xde, 0x1f, 0x60, 0x33, 0x0e, 0xcb, 0xfc, 0xe0, 0xdb, 0x83, 0xa1, 0xe5, 0xd0, 0x16, 0x86, 0x2c, 0xe2, 0x87, 0xed, + /* (2^174)P */ 0x7a, 0xc0, 0xeb, 0x6b, 0xf6, 0x0d, 0x4c, 0x6d, 0x1e, 0xdb, 0xab, 0xe7, 0x19, 0x45, 0xc6, 0xe3, 0xb2, 0x06, 0xbb, 0xbc, 0x70, 0x99, 0x83, 0x33, 0xeb, 0x28, 0xc8, 0x77, 0xf6, 0x4d, 0x01, 0xb7, 0x59, 0xa0, 0xd2, 0xb3, 0x2a, 0x72, 0x30, 0xe7, 0x11, 0x39, 0xb6, 0x41, 0x29, 0x65, 0x5a, 0x14, 0xb9, 0x86, 0x08, 0xe0, 0x7d, 0x32, 0x8c, 0xf0, + /* (2^175)P */ 0x5c, 0x11, 0x30, 0x9e, 0x05, 0x27, 0xf5, 0x45, 0x0f, 0xb3, 0xc9, 0x75, 0xc3, 0xd7, 0xe1, 0x82, 0x3b, 0x8e, 0x87, 0x23, 0x00, 0x15, 0x19, 0x07, 0xd9, 0x21, 0x53, 0xc7, 0xf1, 0xa3, 0xbf, 0x70, 0x64, 0x15, 0x18, 0xca, 0x23, 0x9e, 0xd3, 0x08, 0xc3, 0x2a, 0x8b, 0xe5, 0x83, 0x04, 0x89, 0x14, 0xfd, 0x28, 0x25, 0x1c, 0xe3, 0x26, 0xa7, 0x22, + /* (2^176)P */ 0xdc, 0xd4, 0x75, 0x60, 0x99, 0x94, 0xea, 0x09, 0x8e, 0x8a, 0x3c, 0x1b, 0xf9, 0xbd, 0x33, 0x0d, 0x51, 0x3d, 0x12, 0x6f, 0x4e, 0x72, 0xe0, 0x17, 0x20, 0xe9, 0x75, 0xe6, 0x3a, 0xb2, 0x13, 0x83, 0x4e, 0x7a, 0x08, 0x9e, 0xd1, 0x04, 0x5f, 0x6b, 0x42, 0x0b, 0x76, 0x2a, 0x2d, 0x77, 0x53, 0x6c, 0x65, 0x6d, 0x8e, 0x25, 0x3c, 0xb6, 0x8b, 0x69, + /* (2^177)P */ 0xb9, 0x49, 0x28, 0xd0, 0xdc, 0x6c, 0x8f, 0x4c, 0xc9, 0x14, 0x8a, 0x38, 0xa3, 0xcb, 0xc4, 0x9d, 0x53, 0xcf, 0xe9, 0xe3, 0xcf, 0xe0, 0xb1, 0xf2, 0x1b, 0x4c, 0x7f, 0x83, 0x2a, 0x7a, 0xe9, 0x8b, 0x3b, 0x86, 0x61, 0x30, 0xe9, 0x99, 0xbd, 0xba, 0x19, 0x6e, 0x65, 0x2a, 0x12, 0x3e, 0x9c, 0xa8, 0xaf, 0xc3, 0xcf, 0xf8, 0x1f, 0x77, 0x86, 0xea, + /* (2^178)P */ 0x30, 0xde, 0xe7, 0xff, 0x54, 0xf7, 0xa2, 0x59, 0xf6, 0x0b, 0xfb, 0x7a, 0xf2, 0x39, 0xf0, 0xdb, 0x39, 0xbc, 0xf0, 0xfa, 0x60, 0xeb, 0x6b, 0x4f, 0x47, 0x17, 0xc8, 0x00, 0x65, 0x6d, 0x25, 0x1c, 0xd0, 0x48, 0x56, 0x53, 0x45, 0x11, 0x30, 0x02, 0x49, 0x20, 0x27, 0xac, 0xf2, 0x4c, 0xac, 0x64, 0x3d, 0x52, 0xb8, 0x89, 0xe0, 0x93, 0x16, 0x0f, + /* (2^179)P */ 0x84, 0x09, 0xba, 0x40, 0xb2, 0x2f, 0xa3, 0xa8, 0xc2, 0xba, 0x46, 0x33, 0x05, 0x9d, 0x62, 0xad, 0xa1, 0x3c, 0x33, 0xef, 0x0d, 0xeb, 0xf0, 0x77, 0x11, 0x5a, 0xb0, 0x21, 0x9c, 0xdf, 0x55, 0x24, 0x25, 0x35, 0x51, 0x61, 0x92, 0xf0, 0xb1, 0xce, 0xf5, 0xd4, 0x7b, 0x6c, 0x21, 0x9d, 0x56, 0x52, 0xf8, 0xa1, 0x4c, 0xe9, 0x27, 0x55, 0xac, 0x91, + /* (2^180)P */ 0x03, 0x3e, 0x30, 0xd2, 0x0a, 0xfa, 0x7d, 0x82, 0x3d, 0x1f, 0x8b, 0xcb, 0xb6, 0x04, 0x5c, 0xcc, 0x8b, 0xda, 0xe2, 0x68, 0x74, 0x08, 0x8c, 0x44, 0x83, 0x57, 0x6d, 0x6f, 0x80, 0xb0, 0x7e, 0xa9, 0x82, 0x91, 0x7b, 0x4c, 0x37, 0x97, 0xd1, 0x63, 0xd1, 0xbd, 0x45, 0xe6, 0x8a, 0x86, 0xd6, 0x89, 0x54, 0xfd, 0xd2, 0xb1, 0xd7, 0x54, 0xad, 0xaf, + /* (2^181)P */ 0x8b, 0x33, 0x62, 0x49, 0x9f, 0x63, 0xf9, 0x87, 0x42, 0x58, 0xbf, 0xb3, 0xe6, 0x68, 0x02, 0x60, 0x5c, 0x76, 0x62, 0xf7, 0x61, 0xd7, 0x36, 0x31, 0xf7, 0x9c, 0xb5, 0xe5, 0x13, 0x6c, 0xea, 0x78, 0xae, 0xcf, 0xde, 0xbf, 0xb6, 0xeb, 0x4f, 0xc8, 0x2a, 0xb4, 0x9a, 0x9f, 0xf3, 0xd1, 0x6a, 0xec, 0x0c, 0xbd, 0x85, 0x98, 0x40, 0x06, 0x1c, 0x2a, + /* (2^182)P */ 0x74, 0x3b, 0xe7, 0x81, 0xd5, 0xae, 0x54, 0x56, 0x03, 0xe8, 0x97, 0x16, 0x76, 0xcf, 0x24, 0x96, 0x96, 0x5b, 0xcc, 0x09, 0xab, 0x23, 0x6f, 0x54, 0xae, 0x8f, 0xe4, 0x12, 0xcb, 0xfd, 0xbc, 0xac, 0x93, 0x45, 0x3d, 0x68, 0x08, 0x22, 0x59, 0xc6, 0xf0, 0x47, 0x19, 0x8c, 0x79, 0x93, 0x1e, 0x0e, 0x30, 0xb0, 0x94, 0xfb, 0x17, 0x1d, 0x5a, 0x12, + /* (2^183)P */ 0x85, 0xff, 0x40, 0x18, 0x85, 0xff, 0x44, 0x37, 0x69, 0x23, 0x4d, 0x34, 0xe1, 0xeb, 0xa3, 0x1b, 0x55, 0x40, 0xc1, 0x64, 0xf4, 0xd4, 0x13, 0x0a, 0x9f, 0xb9, 0x19, 0xfc, 0x88, 0x7d, 0xc0, 0x72, 0xcf, 0x69, 0x2f, 0xd2, 0x0c, 0x82, 0x0f, 0xda, 0x08, 0xba, 0x0f, 0xaa, 0x3b, 0xe9, 0xe5, 0x83, 0x7a, 0x06, 0xe8, 0x1b, 0x38, 0x43, 0xc3, 0x54, + /* (2^184)P */ 0x14, 0xaa, 0xb3, 0x6e, 0xe6, 0x28, 0xee, 0xc5, 0x22, 0x6c, 0x7c, 0xf9, 0xa8, 0x71, 0xcc, 0xfe, 0x68, 0x7e, 0xd3, 0xb8, 0x37, 0x96, 0xca, 0x0b, 0xd9, 0xb6, 0x06, 0xa9, 0xf6, 0x71, 0xe8, 0x31, 0xf7, 0xd8, 0xf1, 0x5d, 0xab, 0xb9, 0xf0, 0x5c, 0x98, 0xcf, 0x22, 0xa2, 0x2a, 0xf6, 0xd0, 0x59, 0xf0, 0x9d, 0xd9, 0x6a, 0x4f, 0x59, 0x57, 0xad, + /* (2^185)P */ 0xd7, 0x2b, 0x3d, 0x38, 0x4c, 0x2e, 0x23, 0x4d, 0x49, 0xa2, 0x62, 0x62, 0xf9, 0x0f, 0xde, 0x08, 0xf3, 0x86, 0x71, 0xb6, 0xc7, 0xf9, 0x85, 0x9c, 0x33, 0xa1, 0xcf, 0x16, 0xaa, 0x60, 0xb9, 0xb7, 0xea, 0xed, 0x01, 0x1c, 0x59, 0xdb, 0x3f, 0x3f, 0x97, 0x2e, 0xf0, 0x09, 0x9f, 0x10, 0x85, 0x5f, 0x53, 0x39, 0xf3, 0x13, 0x40, 0x56, 0x95, 0xf9, + /* (2^186)P */ 0xb4, 0xe3, 0xda, 0xc6, 0x1f, 0x78, 0x8e, 0xac, 0xd4, 0x20, 0x1d, 0xa0, 0xbf, 0x4c, 0x09, 0x16, 0xa7, 0x30, 0xb5, 0x8d, 0x9e, 0xa1, 0x5f, 0x6d, 0x52, 0xf4, 0x71, 0xb6, 0x32, 0x2d, 0x21, 0x51, 0xc6, 0xfc, 0x2f, 0x08, 0xf4, 0x13, 0x6c, 0x55, 0xba, 0x72, 0x81, 0x24, 0x49, 0x0e, 0x4f, 0x06, 0x36, 0x39, 0x6a, 0xc5, 0x81, 0xfc, 0xeb, 0xb2, + /* (2^187)P */ 0x7d, 0x8d, 0xc8, 0x6c, 0xea, 0xb4, 0xb9, 0xe8, 0x40, 0xc9, 0x69, 0xc9, 0x30, 0x05, 0xfd, 0x34, 0x46, 0xfd, 0x94, 0x05, 0x16, 0xf5, 0x4b, 0x13, 0x3d, 0x24, 0x1a, 0xd6, 0x64, 0x2b, 0x9c, 0xe2, 0xa5, 0xd9, 0x98, 0xe0, 0xe8, 0xf4, 0xbc, 0x2c, 0xbd, 0xa2, 0x56, 0xe3, 0x9e, 0x14, 0xdb, 0xbf, 0x05, 0xbf, 0x9a, 0x13, 0x5d, 0xf7, 0x91, 0xa3, + /* (2^188)P */ 0x8b, 0xcb, 0x27, 0xf3, 0x15, 0x26, 0x05, 0x40, 0x0f, 0xa6, 0x15, 0x13, 0x71, 0x95, 0xa2, 0xc6, 0x38, 0x04, 0x67, 0xf8, 0x9a, 0x83, 0x06, 0xaa, 0x25, 0x36, 0x72, 0x01, 0x6f, 0x74, 0x5f, 0xe5, 0x6e, 0x44, 0x99, 0xce, 0x13, 0xbc, 0x82, 0xc2, 0x0d, 0xa4, 0x98, 0x50, 0x38, 0xf3, 0xa2, 0xc5, 0xe5, 0x24, 0x1f, 0x6f, 0x56, 0x3e, 0x07, 0xb2, + /* (2^189)P */ 0xbd, 0x0f, 0x32, 0x60, 0x07, 0xb1, 0xd7, 0x0b, 0x11, 0x07, 0x57, 0x02, 0x89, 0xe8, 0x8b, 0xe8, 0x5a, 0x1f, 0xee, 0x54, 0x6b, 0xff, 0xb3, 0x04, 0x07, 0x57, 0x13, 0x0b, 0x94, 0xa8, 0x4d, 0x81, 0xe2, 0x17, 0x16, 0x45, 0xd4, 0x4b, 0xf7, 0x7e, 0x64, 0x66, 0x20, 0xe8, 0x0b, 0x26, 0xfd, 0xa9, 0x8a, 0x47, 0x52, 0x89, 0x14, 0xd0, 0xd1, 0xa1, + /* (2^190)P */ 0xdc, 0x03, 0xe6, 0x20, 0x44, 0x47, 0x8f, 0x04, 0x16, 0x24, 0x22, 0xc1, 0x55, 0x5c, 0xbe, 0x43, 0xc3, 0x92, 0xc5, 0x54, 0x3d, 0x5d, 0xd1, 0x05, 0x9c, 0xc6, 0x7c, 0xbf, 0x23, 0x84, 0x1a, 0xba, 0x4f, 0x1f, 0xfc, 0xa1, 0xae, 0x1a, 0x64, 0x02, 0x51, 0xf1, 0xcb, 0x7a, 0x20, 0xce, 0xb2, 0x34, 0x3c, 0xca, 0xe0, 0xe4, 0xba, 0x22, 0xd4, 0x7b, + /* (2^191)P */ 0xca, 0xfd, 0xca, 0xd7, 0xde, 0x61, 0xae, 0xf0, 0x79, 0x0c, 0x20, 0xab, 0xbc, 0x6f, 0x4d, 0x61, 0xf0, 0xc7, 0x9c, 0x8d, 0x4b, 0x52, 0xf3, 0xb9, 0x48, 0x63, 0x0b, 0xb6, 0xd2, 0x25, 0x9a, 0x96, 0x72, 0xc1, 0x6b, 0x0c, 0xb5, 0xfb, 0x71, 0xaa, 0xad, 0x47, 0x5b, 0xe7, 0xc0, 0x0a, 0x55, 0xb2, 0xd4, 0x16, 0x2f, 0xb1, 0x01, 0xfd, 0xce, 0x27, + /* (2^192)P */ 0x64, 0x11, 0x4b, 0xab, 0x57, 0x09, 0xc6, 0x49, 0x4a, 0x37, 0xc3, 0x36, 0xc4, 0x7b, 0x81, 0x1f, 0x42, 0xed, 0xbb, 0xe0, 0xa0, 0x8d, 0x51, 0xe6, 0xca, 0x8b, 0xb9, 0xcd, 0x99, 0x2d, 0x91, 0x53, 0xa9, 0x47, 0xcb, 0x32, 0xc7, 0xa4, 0x92, 0xec, 0x46, 0x74, 0x44, 0x6d, 0x71, 0x9f, 0x6d, 0x0c, 0x69, 0xa4, 0xf8, 0xbe, 0x9f, 0x7f, 0xa0, 0xd7, + /* (2^193)P */ 0x5f, 0x33, 0xb6, 0x91, 0xc8, 0xa5, 0x3f, 0x5d, 0x7f, 0x38, 0x6e, 0x74, 0x20, 0x4a, 0xd6, 0x2b, 0x98, 0x2a, 0x41, 0x4b, 0x83, 0x64, 0x0b, 0x92, 0x7a, 0x06, 0x1e, 0xc6, 0x2c, 0xf6, 0xe4, 0x91, 0xe5, 0xb1, 0x2e, 0x6e, 0x4e, 0xa8, 0xc8, 0x14, 0x32, 0x57, 0x44, 0x1c, 0xe4, 0xb9, 0x7f, 0x54, 0x51, 0x08, 0x81, 0xaa, 0x4e, 0xce, 0xa1, 0x5d, + /* (2^194)P */ 0x5c, 0xd5, 0x9b, 0x5e, 0x7c, 0xb5, 0xb1, 0x52, 0x73, 0x00, 0x41, 0x56, 0x79, 0x08, 0x7e, 0x07, 0x28, 0x06, 0xa6, 0xfb, 0x7f, 0x69, 0xbd, 0x7a, 0x3c, 0xae, 0x9f, 0x39, 0xbb, 0x54, 0xa2, 0x79, 0xb9, 0x0e, 0x7f, 0xbb, 0xe0, 0xe6, 0xb7, 0x27, 0x64, 0x38, 0x45, 0xdb, 0x84, 0xe4, 0x61, 0x72, 0x3f, 0xe2, 0x24, 0xfe, 0x7a, 0x31, 0x9a, 0xc9, + /* (2^195)P */ 0xa1, 0xd2, 0xa4, 0xee, 0x24, 0x96, 0xe5, 0x5b, 0x79, 0x78, 0x3c, 0x7b, 0x82, 0x3b, 0x8b, 0x58, 0x0b, 0xa3, 0x63, 0x2d, 0xbc, 0x75, 0x46, 0xe8, 0x83, 0x1a, 0xc0, 0x2a, 0x92, 0x61, 0xa8, 0x75, 0x37, 0x3c, 0xbf, 0x0f, 0xef, 0x8f, 0x6c, 0x97, 0x75, 0x10, 0x05, 0x7a, 0xde, 0x23, 0xe8, 0x2a, 0x35, 0xeb, 0x41, 0x64, 0x7d, 0xcf, 0xe0, 0x52, + /* (2^196)P */ 0x4a, 0xd0, 0x49, 0x93, 0xae, 0xf3, 0x24, 0x8c, 0xe1, 0x09, 0x98, 0x45, 0xd8, 0xb9, 0xfe, 0x8e, 0x8c, 0xa8, 0x2c, 0xc9, 0x9f, 0xce, 0x01, 0xdc, 0x38, 0x11, 0xab, 0x85, 0xb9, 0xe8, 0x00, 0x51, 0xfd, 0x82, 0xe1, 0x9b, 0x4e, 0xfc, 0xb5, 0x2a, 0x0f, 0x8b, 0xda, 0x4e, 0x02, 0xca, 0xcc, 0xe3, 0x91, 0xc4, 0xe0, 0xcf, 0x7b, 0xd6, 0xe6, 0x6a, + /* (2^197)P */ 0xfe, 0x11, 0xd7, 0xaa, 0xe3, 0x0c, 0x52, 0x2e, 0x04, 0xe0, 0xe0, 0x61, 0xc8, 0x05, 0xd7, 0x31, 0x4c, 0xc3, 0x9b, 0x2d, 0xce, 0x59, 0xbe, 0x12, 0xb7, 0x30, 0x21, 0xfc, 0x81, 0xb8, 0x5e, 0x57, 0x73, 0xd0, 0xad, 0x8e, 0x9e, 0xe4, 0xeb, 0xcd, 0xcf, 0xd2, 0x0f, 0x01, 0x35, 0x16, 0xed, 0x7a, 0x43, 0x8e, 0x42, 0xdc, 0xea, 0x4c, 0xa8, 0x7c, + /* (2^198)P */ 0x37, 0x26, 0xcc, 0x76, 0x0b, 0xe5, 0x76, 0xdd, 0x3e, 0x19, 0x3c, 0xc4, 0x6c, 0x7f, 0xd0, 0x03, 0xc1, 0xb8, 0x59, 0x82, 0xca, 0x36, 0xc1, 0xe4, 0xc8, 0xb2, 0x83, 0x69, 0x9c, 0xc5, 0x9d, 0x12, 0x82, 0x1c, 0xea, 0xb2, 0x84, 0x9f, 0xf3, 0x52, 0x6b, 0xbb, 0xd8, 0x81, 0x56, 0x83, 0x04, 0x66, 0x05, 0x22, 0x49, 0x37, 0x93, 0xb1, 0xfd, 0xd5, + /* (2^199)P */ 0xaf, 0x96, 0xbf, 0x03, 0xbe, 0xe6, 0x5d, 0x78, 0x19, 0xba, 0x37, 0x46, 0x0a, 0x2b, 0x52, 0x7c, 0xd8, 0x51, 0x9e, 0x3d, 0x29, 0x42, 0xdb, 0x0e, 0x31, 0x20, 0x94, 0xf8, 0x43, 0x9a, 0x2d, 0x22, 0xd3, 0xe3, 0xa1, 0x79, 0x68, 0xfb, 0x2d, 0x7e, 0xd6, 0x79, 0xda, 0x0b, 0xc6, 0x5b, 0x76, 0x68, 0xf0, 0xfe, 0x72, 0x59, 0xbb, 0xa1, 0x9c, 0x74, + /* (2^200)P */ 0x0a, 0xd9, 0xec, 0xc5, 0xbd, 0xf0, 0xda, 0xcf, 0x82, 0xab, 0x46, 0xc5, 0x32, 0x13, 0xdc, 0x5b, 0xac, 0xc3, 0x53, 0x9a, 0x7f, 0xef, 0xa5, 0x40, 0x5a, 0x1f, 0xc1, 0x12, 0x91, 0x54, 0x83, 0x6a, 0xb0, 0x9a, 0x85, 0x4d, 0xbf, 0x36, 0x8e, 0xd3, 0xa2, 0x2b, 0xe5, 0xd6, 0xc6, 0xe1, 0x58, 0x5b, 0x82, 0x9b, 0xc8, 0xf2, 0x03, 0xba, 0xf5, 0x92, + /* (2^201)P */ 0xfb, 0x21, 0x7e, 0xde, 0xe7, 0xb4, 0xc0, 0x56, 0x86, 0x3a, 0x5b, 0x78, 0xf8, 0xf0, 0xf4, 0xe7, 0x5c, 0x00, 0xd2, 0xd7, 0xd6, 0xf8, 0x75, 0x5e, 0x0f, 0x3e, 0xd1, 0x4b, 0x77, 0xd8, 0xad, 0xb0, 0xc9, 0x8b, 0x59, 0x7d, 0x30, 0x76, 0x64, 0x7a, 0x76, 0xd9, 0x51, 0x69, 0xfc, 0xbd, 0x8e, 0xb5, 0x55, 0xe0, 0xd2, 0x07, 0x15, 0xa9, 0xf7, 0xa4, + /* (2^202)P */ 0xaa, 0x2d, 0x2f, 0x2b, 0x3c, 0x15, 0xdd, 0xcd, 0xe9, 0x28, 0x82, 0x4f, 0xa2, 0xaa, 0x31, 0x48, 0xcc, 0xfa, 0x07, 0x73, 0x8a, 0x34, 0x74, 0x0d, 0xab, 0x1a, 0xca, 0xd2, 0xbf, 0x3a, 0xdb, 0x1a, 0x5f, 0x50, 0x62, 0xf4, 0x6b, 0x83, 0x38, 0x43, 0x96, 0xee, 0x6b, 0x39, 0x1e, 0xf0, 0x17, 0x80, 0x1e, 0x9b, 0xed, 0x2b, 0x2f, 0xcc, 0x65, 0xf7, + /* (2^203)P */ 0x03, 0xb3, 0x23, 0x9c, 0x0d, 0xd1, 0xeb, 0x7e, 0x34, 0x17, 0x8a, 0x4c, 0xde, 0x54, 0x39, 0xc4, 0x11, 0x82, 0xd3, 0xa4, 0x00, 0x32, 0x95, 0x9c, 0xa6, 0x64, 0x76, 0x6e, 0xd6, 0x53, 0x27, 0xb4, 0x6a, 0x14, 0x8c, 0x54, 0xf6, 0x58, 0x9e, 0x22, 0x4a, 0x55, 0x18, 0x77, 0xd0, 0x08, 0x6b, 0x19, 0x8a, 0xb5, 0xe7, 0x19, 0xb8, 0x60, 0x92, 0xb1, + /* (2^204)P */ 0x66, 0xec, 0xf3, 0x12, 0xde, 0x67, 0x7f, 0xd4, 0x5b, 0xf6, 0x70, 0x64, 0x0a, 0xb5, 0xc2, 0xf9, 0xb3, 0x64, 0xab, 0x56, 0x46, 0xc7, 0x93, 0xc2, 0x8b, 0x2d, 0xd0, 0xd6, 0x39, 0x3b, 0x1f, 0xcd, 0xb3, 0xac, 0xcc, 0x2c, 0x27, 0x6a, 0xbc, 0xb3, 0x4b, 0xa8, 0x3c, 0x69, 0x20, 0xe2, 0x18, 0x35, 0x17, 0xe1, 0x8a, 0xd3, 0x11, 0x74, 0xaa, 0x4d, + /* (2^205)P */ 0x96, 0xc4, 0x16, 0x7e, 0xfd, 0xf5, 0xd0, 0x7d, 0x1f, 0x32, 0x1b, 0xdb, 0xa6, 0xfd, 0x51, 0x75, 0x4d, 0xd7, 0x00, 0xe5, 0x7f, 0x58, 0x5b, 0xeb, 0x4b, 0x6a, 0x78, 0xfe, 0xe5, 0xd6, 0x8f, 0x99, 0x17, 0xca, 0x96, 0x45, 0xf7, 0x52, 0xdf, 0x84, 0x06, 0x77, 0xb9, 0x05, 0x63, 0x5d, 0xe9, 0x91, 0xb1, 0x4b, 0x82, 0x5a, 0xdb, 0xd7, 0xca, 0x69, + /* (2^206)P */ 0x02, 0xd3, 0x38, 0x38, 0x87, 0xea, 0xbd, 0x9f, 0x11, 0xca, 0xf3, 0x21, 0xf1, 0x9b, 0x35, 0x97, 0x98, 0xff, 0x8e, 0x6d, 0x3d, 0xd6, 0xb2, 0xfa, 0x68, 0xcb, 0x7e, 0x62, 0x85, 0xbb, 0xc7, 0x5d, 0xee, 0x32, 0x30, 0x2e, 0x71, 0x96, 0x63, 0x43, 0x98, 0xc4, 0xa7, 0xde, 0x60, 0xb2, 0xd9, 0x43, 0x4a, 0xfa, 0x97, 0x2d, 0x5f, 0x21, 0xd4, 0xfe, + /* (2^207)P */ 0x3b, 0x20, 0x29, 0x07, 0x07, 0xb5, 0x78, 0xc3, 0xc7, 0xab, 0x56, 0xba, 0x40, 0xde, 0x1d, 0xcf, 0xc3, 0x00, 0x56, 0x21, 0x0c, 0xc8, 0x42, 0xd9, 0x0e, 0xcd, 0x02, 0x7c, 0x07, 0xb9, 0x11, 0xd7, 0x96, 0xaf, 0xff, 0xad, 0xc5, 0xba, 0x30, 0x6d, 0x82, 0x3a, 0xbf, 0xef, 0x7b, 0xf7, 0x0a, 0x74, 0xbd, 0x31, 0x0c, 0xe4, 0xec, 0x1a, 0xe5, 0xc5, + /* (2^208)P */ 0xcc, 0xf2, 0x28, 0x16, 0x12, 0xbf, 0xef, 0x85, 0xbc, 0xf7, 0xcb, 0x9f, 0xdb, 0xa8, 0xb2, 0x49, 0x53, 0x48, 0xa8, 0x24, 0xa8, 0x68, 0x8d, 0xbb, 0x21, 0x0a, 0x5a, 0xbd, 0xb2, 0x91, 0x61, 0x47, 0xc4, 0x43, 0x08, 0xa6, 0x19, 0xef, 0x8e, 0x88, 0x39, 0xc6, 0x33, 0x30, 0xf3, 0x0e, 0xc5, 0x92, 0x66, 0xd6, 0xfe, 0xc5, 0x12, 0xd9, 0x4c, 0x2d, + /* (2^209)P */ 0x30, 0x34, 0x07, 0xbf, 0x9c, 0x5a, 0x4e, 0x65, 0xf1, 0x39, 0x35, 0x38, 0xae, 0x7b, 0x55, 0xac, 0x6a, 0x92, 0x24, 0x7e, 0x50, 0xd3, 0xba, 0x78, 0x51, 0xfe, 0x4d, 0x32, 0x05, 0x11, 0xf5, 0x52, 0xf1, 0x31, 0x45, 0x39, 0x98, 0x7b, 0x28, 0x56, 0xc3, 0x5d, 0x4f, 0x07, 0x6f, 0x84, 0xb8, 0x1a, 0x58, 0x0b, 0xc4, 0x7c, 0xc4, 0x8d, 0x32, 0x8e, + /* (2^210)P */ 0x7e, 0xaf, 0x98, 0xce, 0xc5, 0x2b, 0x9d, 0xf6, 0xfa, 0x2c, 0xb6, 0x2a, 0x5a, 0x1d, 0xc0, 0x24, 0x8d, 0xa4, 0xce, 0xb1, 0x12, 0x01, 0xf9, 0x79, 0xc6, 0x79, 0x38, 0x0c, 0xd4, 0x07, 0xc9, 0xf7, 0x37, 0xa1, 0x0b, 0xfe, 0x72, 0xec, 0x5d, 0xd6, 0xb0, 0x1c, 0x70, 0xbe, 0x70, 0x01, 0x13, 0xe0, 0x86, 0x95, 0xc7, 0x2e, 0x12, 0x3b, 0xe6, 0xa6, + /* (2^211)P */ 0x24, 0x82, 0x67, 0xe0, 0x14, 0x7b, 0x56, 0x08, 0x38, 0x44, 0xdb, 0xa0, 0x3a, 0x05, 0x47, 0xb2, 0xc0, 0xac, 0xd1, 0xcc, 0x3f, 0x82, 0xb8, 0x8a, 0x88, 0xbc, 0xf5, 0x33, 0xa1, 0x35, 0x0f, 0xf6, 0xe2, 0xef, 0x6c, 0xf7, 0x37, 0x9e, 0xe8, 0x10, 0xca, 0xb0, 0x8e, 0x80, 0x86, 0x00, 0x23, 0xd0, 0x4a, 0x76, 0x9f, 0xf7, 0x2c, 0x52, 0x15, 0x0e, + /* (2^212)P */ 0x5e, 0x49, 0xe1, 0x2c, 0x9a, 0x01, 0x76, 0xa6, 0xb3, 0x07, 0x5b, 0xa4, 0x07, 0xef, 0x1d, 0xc3, 0x6a, 0xbb, 0x64, 0xbe, 0x71, 0x15, 0x6e, 0x32, 0x31, 0x46, 0x9a, 0x9e, 0x8f, 0x45, 0x73, 0xce, 0x0b, 0x94, 0x1a, 0x52, 0x07, 0xf4, 0x50, 0x30, 0x49, 0x53, 0x50, 0xfb, 0x71, 0x1f, 0x5a, 0x03, 0xa9, 0x76, 0xf2, 0x8f, 0x42, 0xff, 0xed, 0xed, + /* (2^213)P */ 0xed, 0x08, 0xdb, 0x91, 0x1c, 0xee, 0xa2, 0xb4, 0x47, 0xa2, 0xfa, 0xcb, 0x03, 0xd1, 0xff, 0x8c, 0xad, 0x64, 0x50, 0x61, 0xcd, 0xfc, 0x88, 0xa0, 0x31, 0x95, 0x30, 0xb9, 0x58, 0xdd, 0xd7, 0x43, 0xe4, 0x46, 0xc2, 0x16, 0xd9, 0x72, 0x4a, 0x56, 0x51, 0x70, 0x85, 0xf1, 0xa1, 0x80, 0x40, 0xd5, 0xba, 0x67, 0x81, 0xda, 0xcd, 0x03, 0xea, 0x51, + /* (2^214)P */ 0x42, 0x50, 0xf0, 0xef, 0x37, 0x61, 0x72, 0x85, 0xe1, 0xf1, 0xff, 0x6f, 0x3d, 0xe8, 0x7b, 0x21, 0x5c, 0xe5, 0x50, 0x03, 0xde, 0x00, 0xc1, 0xf7, 0x3a, 0x55, 0x12, 0x1c, 0x9e, 0x1e, 0xce, 0xd1, 0x2f, 0xaf, 0x05, 0x70, 0x5b, 0x47, 0xf2, 0x04, 0x7a, 0x89, 0xbc, 0x78, 0xa6, 0x65, 0x6c, 0xaa, 0x3c, 0xa2, 0x3c, 0x8b, 0x5c, 0xa9, 0x22, 0x48, + /* (2^215)P */ 0x7e, 0x8c, 0x8f, 0x2f, 0x60, 0xe3, 0x5a, 0x94, 0xd4, 0xce, 0xdd, 0x9d, 0x83, 0x3b, 0x77, 0x78, 0x43, 0x1d, 0xfd, 0x8f, 0xc8, 0xe8, 0x02, 0x90, 0xab, 0xf6, 0xc9, 0xfc, 0xf1, 0x63, 0xaa, 0x5f, 0x42, 0xf1, 0x78, 0x34, 0x64, 0x16, 0x75, 0x9c, 0x7d, 0xd0, 0xe4, 0x74, 0x5a, 0xa8, 0xfb, 0xcb, 0xac, 0x20, 0xa3, 0xc2, 0xa6, 0x20, 0xf8, 0x1b, + /* (2^216)P */ 0x00, 0x4f, 0x1e, 0x56, 0xb5, 0x34, 0xb2, 0x87, 0x31, 0xe5, 0xee, 0x8d, 0xf1, 0x41, 0x67, 0xb7, 0x67, 0x3a, 0x54, 0x86, 0x5c, 0xf0, 0x0b, 0x37, 0x2f, 0x1b, 0x92, 0x5d, 0x58, 0x93, 0xdc, 0xd8, 0x58, 0xcc, 0x9e, 0x67, 0xd0, 0x97, 0x3a, 0xaf, 0x49, 0x39, 0x2d, 0x3b, 0xd8, 0x98, 0xfb, 0x76, 0x6b, 0xe7, 0xaf, 0xc3, 0x45, 0x44, 0x53, 0x94, + /* (2^217)P */ 0x30, 0xbd, 0x90, 0x75, 0xd3, 0xbd, 0x3b, 0x58, 0x27, 0x14, 0x9f, 0x6b, 0xd4, 0x31, 0x99, 0xcd, 0xde, 0x3a, 0x21, 0x1e, 0xb4, 0x02, 0xe4, 0x33, 0x04, 0x02, 0xb0, 0x50, 0x66, 0x68, 0x90, 0xdd, 0x7b, 0x69, 0x31, 0xd9, 0xcf, 0x68, 0x73, 0xf1, 0x60, 0xdd, 0xc8, 0x1d, 0x5d, 0xe3, 0xd6, 0x5b, 0x2a, 0xa4, 0xea, 0xc4, 0x3f, 0x08, 0xcd, 0x9c, + /* (2^218)P */ 0x6b, 0x1a, 0xbf, 0x55, 0xc1, 0x1b, 0x0c, 0x05, 0x09, 0xdf, 0xf5, 0x5e, 0xa3, 0x77, 0x95, 0xe9, 0xdf, 0x19, 0xdd, 0xc7, 0x94, 0xcb, 0x06, 0x73, 0xd0, 0x88, 0x02, 0x33, 0x94, 0xca, 0x7a, 0x2f, 0x8e, 0x3d, 0x72, 0x61, 0x2d, 0x4d, 0xa6, 0x61, 0x1f, 0x32, 0x5e, 0x87, 0x53, 0x36, 0x11, 0x15, 0x20, 0xb3, 0x5a, 0x57, 0x51, 0x93, 0x20, 0xd8, + /* (2^219)P */ 0xb7, 0x56, 0xf4, 0xab, 0x7d, 0x0c, 0xfb, 0x99, 0x1a, 0x30, 0x29, 0xb0, 0x75, 0x2a, 0xf8, 0x53, 0x71, 0x23, 0xbd, 0xa7, 0xd8, 0x0a, 0xe2, 0x27, 0x65, 0xe9, 0x74, 0x26, 0x98, 0x4a, 0x69, 0x19, 0xb2, 0x4d, 0x0a, 0x17, 0x98, 0xb2, 0xa9, 0x57, 0x4e, 0xf6, 0x86, 0xc8, 0x01, 0xa4, 0xc6, 0x98, 0xad, 0x5a, 0x90, 0x2c, 0x05, 0x46, 0x64, 0xb7, + /* (2^220)P */ 0x7b, 0x91, 0xdf, 0xfc, 0xf8, 0x1c, 0x8c, 0x15, 0x9e, 0xf7, 0xd5, 0xa8, 0xe8, 0xe7, 0xe3, 0xa3, 0xb0, 0x04, 0x74, 0xfa, 0x78, 0xfb, 0x26, 0xbf, 0x67, 0x42, 0xf9, 0x8c, 0x9b, 0xb4, 0x69, 0x5b, 0x02, 0x13, 0x6d, 0x09, 0x6c, 0xd6, 0x99, 0x61, 0x7b, 0x89, 0x4a, 0x67, 0x75, 0xa3, 0x98, 0x13, 0x23, 0x1d, 0x18, 0x24, 0x0e, 0xef, 0x41, 0x79, + /* (2^221)P */ 0x86, 0x33, 0xab, 0x08, 0xcb, 0xbf, 0x1e, 0x76, 0x3c, 0x0b, 0xbd, 0x30, 0xdb, 0xe9, 0xa3, 0x35, 0x87, 0x1b, 0xe9, 0x07, 0x00, 0x66, 0x7f, 0x3b, 0x35, 0x0c, 0x8a, 0x3f, 0x61, 0xbc, 0xe0, 0xae, 0xf6, 0xcc, 0x54, 0xe1, 0x72, 0x36, 0x2d, 0xee, 0x93, 0x24, 0xf8, 0xd7, 0xc5, 0xf9, 0xcb, 0xb0, 0xe5, 0x88, 0x0d, 0x23, 0x4b, 0x76, 0x15, 0xa2, + /* (2^222)P */ 0x37, 0xdb, 0x83, 0xd5, 0x6d, 0x06, 0x24, 0x37, 0x1b, 0x15, 0x85, 0x15, 0xe2, 0xc0, 0x4e, 0x02, 0xa9, 0x6d, 0x0a, 0x3a, 0x94, 0x4a, 0x6f, 0x49, 0x00, 0x01, 0x72, 0xbb, 0x60, 0x14, 0x35, 0xae, 0xb4, 0xc6, 0x01, 0x0a, 0x00, 0x9e, 0xc3, 0x58, 0xc5, 0xd1, 0x5e, 0x30, 0x73, 0x96, 0x24, 0x85, 0x9d, 0xf0, 0xf9, 0xec, 0x09, 0xd3, 0xe7, 0x70, + /* (2^223)P */ 0xf3, 0xbd, 0x96, 0x87, 0xe9, 0x71, 0xbd, 0xd6, 0xa2, 0x45, 0xeb, 0x0a, 0xcd, 0x2c, 0xf1, 0x72, 0xa6, 0x31, 0xa9, 0x6f, 0x09, 0xa1, 0x5e, 0xdd, 0xc8, 0x8d, 0x0d, 0xbc, 0x5a, 0x8d, 0xb1, 0x2c, 0x9a, 0xcc, 0x37, 0x74, 0xc2, 0xa9, 0x4e, 0xd6, 0xc0, 0x3c, 0xa0, 0x23, 0xb0, 0xa0, 0x77, 0x14, 0x80, 0x45, 0x71, 0x6a, 0x2d, 0x41, 0xc3, 0x82, + /* (2^224)P */ 0x37, 0x44, 0xec, 0x8a, 0x3e, 0xc1, 0x0c, 0xa9, 0x12, 0x9c, 0x08, 0x88, 0xcb, 0xd9, 0xf8, 0xba, 0x00, 0xd6, 0xc3, 0xdf, 0xef, 0x7a, 0x44, 0x7e, 0x25, 0x69, 0xc9, 0xc1, 0x46, 0xe5, 0x20, 0x9e, 0xcc, 0x0b, 0x05, 0x3e, 0xf4, 0x78, 0x43, 0x0c, 0xa6, 0x2f, 0xc1, 0xfa, 0x70, 0xb2, 0x3c, 0x31, 0x7a, 0x63, 0x58, 0xab, 0x17, 0xcf, 0x4c, 0x4f, + /* (2^225)P */ 0x2b, 0x08, 0x31, 0x59, 0x75, 0x8b, 0xec, 0x0a, 0xa9, 0x79, 0x70, 0xdd, 0xf1, 0x11, 0xc3, 0x11, 0x1f, 0xab, 0x37, 0xaa, 0x26, 0xea, 0x53, 0xc4, 0x79, 0xa7, 0x91, 0x00, 0xaa, 0x08, 0x42, 0xeb, 0x8b, 0x8b, 0xe8, 0xc3, 0x2f, 0xb8, 0x78, 0x90, 0x38, 0x0e, 0x8a, 0x42, 0x0c, 0x0f, 0xbf, 0x3e, 0xf8, 0xd8, 0x07, 0xcf, 0x6a, 0x34, 0xc9, 0xfa, + /* (2^226)P */ 0x11, 0xe0, 0x76, 0x4d, 0x23, 0xc5, 0xa6, 0xcc, 0x9f, 0x9a, 0x2a, 0xde, 0x3a, 0xb5, 0x92, 0x39, 0x19, 0x8a, 0xf1, 0x8d, 0xf9, 0x4d, 0xc9, 0xb4, 0x39, 0x9f, 0x57, 0xd8, 0x72, 0xab, 0x1d, 0x61, 0x6a, 0xb2, 0xff, 0x52, 0xba, 0x54, 0x0e, 0xfb, 0x83, 0x30, 0x8a, 0xf7, 0x3b, 0xf4, 0xd8, 0xae, 0x1a, 0x94, 0x3a, 0xec, 0x63, 0xfe, 0x6e, 0x7c, + /* (2^227)P */ 0xdc, 0x70, 0x8e, 0x55, 0x44, 0xbf, 0xd2, 0x6a, 0xa0, 0x14, 0x61, 0x89, 0xd5, 0x55, 0x45, 0x3c, 0xf6, 0x40, 0x0d, 0x83, 0x85, 0x44, 0xb4, 0x62, 0x56, 0xfe, 0x60, 0xd7, 0x07, 0x1d, 0x47, 0x30, 0x3b, 0x73, 0xa4, 0xb5, 0xb7, 0xea, 0xac, 0xda, 0xf1, 0x17, 0xaa, 0x60, 0xdf, 0xe9, 0x84, 0xda, 0x31, 0x32, 0x61, 0xbf, 0xd0, 0x7e, 0x8a, 0x02, + /* (2^228)P */ 0xb9, 0x51, 0xb3, 0x89, 0x21, 0x5d, 0xa2, 0xfe, 0x79, 0x2a, 0xb3, 0x2a, 0x3b, 0xe6, 0x6f, 0x2b, 0x22, 0x03, 0xea, 0x7b, 0x1f, 0xaf, 0x85, 0xc3, 0x38, 0x55, 0x5b, 0x8e, 0xb4, 0xaa, 0x77, 0xfe, 0x03, 0x6e, 0xda, 0x91, 0x24, 0x0c, 0x48, 0x39, 0x27, 0x43, 0x16, 0xd2, 0x0a, 0x0d, 0x43, 0xa3, 0x0e, 0xca, 0x45, 0xd1, 0x7f, 0xf5, 0xd3, 0x16, + /* (2^229)P */ 0x3d, 0x32, 0x9b, 0x38, 0xf8, 0x06, 0x93, 0x78, 0x5b, 0x50, 0x2b, 0x06, 0xd8, 0x66, 0xfe, 0xab, 0x9b, 0x58, 0xc7, 0xd1, 0x4d, 0xd5, 0xf8, 0x3b, 0x10, 0x7e, 0x85, 0xde, 0x58, 0x4e, 0xdf, 0x53, 0xd9, 0x58, 0xe0, 0x15, 0x81, 0x9f, 0x1a, 0x78, 0xfc, 0x9f, 0x10, 0xc2, 0x23, 0xd6, 0x78, 0xd1, 0x9d, 0xd2, 0xd5, 0x1c, 0x53, 0xe2, 0xc9, 0x76, + /* (2^230)P */ 0x98, 0x1e, 0x38, 0x7b, 0x71, 0x18, 0x4b, 0x15, 0xaf, 0xa1, 0xa6, 0x98, 0xcb, 0x26, 0xa3, 0xc8, 0x07, 0x46, 0xda, 0x3b, 0x70, 0x65, 0xec, 0x7a, 0x2b, 0x34, 0x94, 0xa8, 0xb6, 0x14, 0xf8, 0x1a, 0xce, 0xf7, 0xc8, 0x60, 0xf3, 0x88, 0xf4, 0x33, 0x60, 0x7b, 0xd1, 0x02, 0xe7, 0xda, 0x00, 0x4a, 0xea, 0xd2, 0xfd, 0x88, 0xd2, 0x99, 0x28, 0xf3, + /* (2^231)P */ 0x28, 0x24, 0x1d, 0x26, 0xc2, 0xeb, 0x8b, 0x3b, 0xb4, 0x6b, 0xbe, 0x6b, 0x77, 0xff, 0xf3, 0x21, 0x3b, 0x26, 0x6a, 0x8c, 0x8e, 0x2a, 0x44, 0xa8, 0x01, 0x2b, 0x71, 0xea, 0x64, 0x30, 0xfd, 0xfd, 0x95, 0xcb, 0x39, 0x38, 0x48, 0xfa, 0x96, 0x97, 0x8c, 0x2f, 0x33, 0xca, 0x03, 0xe6, 0xd7, 0x94, 0x55, 0x6c, 0xc3, 0xb3, 0xa8, 0xf7, 0xae, 0x8c, + /* (2^232)P */ 0xea, 0x62, 0x8a, 0xb4, 0xeb, 0x74, 0xf7, 0xb8, 0xae, 0xc5, 0x20, 0x71, 0x06, 0xd6, 0x7c, 0x62, 0x9b, 0x69, 0x74, 0xef, 0xa7, 0x6d, 0xd6, 0x8c, 0x37, 0xb9, 0xbf, 0xcf, 0xeb, 0xe4, 0x2f, 0x04, 0x02, 0x21, 0x7d, 0x75, 0x6b, 0x92, 0x48, 0xf8, 0x70, 0xad, 0x69, 0xe2, 0xea, 0x0e, 0x88, 0x67, 0x72, 0xcc, 0x2d, 0x10, 0xce, 0x2d, 0xcf, 0x65, + /* (2^233)P */ 0x49, 0xf3, 0x57, 0x64, 0xe5, 0x5c, 0xc5, 0x65, 0x49, 0x97, 0xc4, 0x8a, 0xcc, 0xa9, 0xca, 0x94, 0x7b, 0x86, 0x88, 0xb6, 0x51, 0x27, 0x69, 0xa5, 0x0f, 0x8b, 0x06, 0x59, 0xa0, 0x94, 0xef, 0x63, 0x1a, 0x01, 0x9e, 0x4f, 0xd2, 0x5a, 0x93, 0xc0, 0x7c, 0xe6, 0x61, 0x77, 0xb6, 0xf5, 0x40, 0xd9, 0x98, 0x43, 0x5b, 0x56, 0x68, 0xe9, 0x37, 0x8f, + /* (2^234)P */ 0xee, 0x87, 0xd2, 0x05, 0x1b, 0x39, 0x89, 0x10, 0x07, 0x6d, 0xe8, 0xfd, 0x8b, 0x4d, 0xb2, 0xa7, 0x7b, 0x1e, 0xa0, 0x6c, 0x0d, 0x3d, 0x3d, 0x49, 0xba, 0x61, 0x36, 0x1f, 0xc2, 0x84, 0x4a, 0xcc, 0x87, 0xa9, 0x1b, 0x23, 0x04, 0xe2, 0x3e, 0x97, 0xe1, 0xdb, 0xd5, 0x5a, 0xe8, 0x41, 0x6b, 0xe5, 0x5a, 0xa1, 0x99, 0xe5, 0x7b, 0xa7, 0xe0, 0x3b, + /* (2^235)P */ 0xea, 0xa3, 0x6a, 0xdd, 0x77, 0x7f, 0x77, 0x41, 0xc5, 0x6a, 0xe4, 0xaf, 0x11, 0x5f, 0x88, 0xa5, 0x10, 0xee, 0xd0, 0x8c, 0x0c, 0xb4, 0xa5, 0x2a, 0xd0, 0xd8, 0x1d, 0x47, 0x06, 0xc0, 0xd5, 0xce, 0x51, 0x54, 0x9b, 0x2b, 0xe6, 0x2f, 0xe7, 0xe7, 0x31, 0x5f, 0x5c, 0x23, 0x81, 0x3e, 0x03, 0x93, 0xaa, 0x2d, 0x71, 0x84, 0xa0, 0x89, 0x32, 0xa6, + /* (2^236)P */ 0x55, 0xa3, 0x13, 0x92, 0x4e, 0x93, 0x7d, 0xec, 0xca, 0x57, 0xfb, 0x37, 0xae, 0xd2, 0x18, 0x2e, 0x54, 0x05, 0x6c, 0xd1, 0x28, 0xca, 0x90, 0x40, 0x82, 0x2e, 0x79, 0xc6, 0x5a, 0xc7, 0xdd, 0x84, 0x93, 0xdf, 0x15, 0xb8, 0x1f, 0xb1, 0xf9, 0xaf, 0x2c, 0xe5, 0x32, 0xcd, 0xc2, 0x99, 0x6d, 0xac, 0x85, 0x5c, 0x63, 0xd3, 0xe2, 0xff, 0x24, 0xda, + /* (2^237)P */ 0x2d, 0x8d, 0xfd, 0x65, 0xcc, 0xe5, 0x02, 0xa0, 0xe5, 0xb9, 0xec, 0x59, 0x09, 0x50, 0x27, 0xb7, 0x3d, 0x2a, 0x79, 0xb2, 0x76, 0x5d, 0x64, 0x95, 0xf8, 0xc5, 0xaf, 0x8a, 0x62, 0x11, 0x5c, 0x56, 0x1c, 0x05, 0x64, 0x9e, 0x5e, 0xbd, 0x54, 0x04, 0xe6, 0x9e, 0xab, 0xe6, 0x22, 0x7e, 0x42, 0x54, 0xb5, 0xa5, 0xd0, 0x8d, 0x28, 0x6b, 0x0f, 0x0b, + /* (2^238)P */ 0x2d, 0xb2, 0x8c, 0x59, 0x10, 0x37, 0x84, 0x3b, 0x9b, 0x65, 0x1b, 0x0f, 0x10, 0xf9, 0xea, 0x60, 0x1b, 0x02, 0xf5, 0xee, 0x8b, 0xe6, 0x32, 0x7d, 0x10, 0x7f, 0x5f, 0x8c, 0x72, 0x09, 0x4e, 0x1f, 0x29, 0xff, 0x65, 0xcb, 0x3e, 0x3a, 0xd2, 0x96, 0x50, 0x1e, 0xea, 0x64, 0x99, 0xb5, 0x4c, 0x7a, 0x69, 0xb8, 0x95, 0xae, 0x48, 0xc0, 0x7c, 0xb1, + /* (2^239)P */ 0xcd, 0x7c, 0x4f, 0x3e, 0xea, 0xf3, 0x90, 0xcb, 0x12, 0x76, 0xd1, 0x17, 0xdc, 0x0d, 0x13, 0x0f, 0xfd, 0x4d, 0xb5, 0x1f, 0xe4, 0xdd, 0xf2, 0x4d, 0x58, 0xea, 0xa5, 0x66, 0x92, 0xcf, 0xe5, 0x54, 0xea, 0x9b, 0x35, 0x83, 0x1a, 0x44, 0x8e, 0x62, 0x73, 0x45, 0x98, 0xa3, 0x89, 0x95, 0x52, 0x93, 0x1a, 0x8d, 0x63, 0x0f, 0xc2, 0x57, 0x3c, 0xb1, + /* (2^240)P */ 0x72, 0xb4, 0xdf, 0x51, 0xb7, 0xf6, 0x52, 0xa2, 0x14, 0x56, 0xe5, 0x0a, 0x2e, 0x75, 0x81, 0x02, 0xee, 0x93, 0x48, 0x0a, 0x92, 0x4e, 0x0c, 0x0f, 0xdf, 0x09, 0x89, 0x99, 0xf6, 0xf9, 0x22, 0xa2, 0x32, 0xf8, 0xb0, 0x76, 0x0c, 0xb2, 0x4d, 0x6e, 0xbe, 0x83, 0x35, 0x61, 0x44, 0xd2, 0x58, 0xc7, 0xdd, 0x14, 0xcf, 0xc3, 0x4b, 0x7c, 0x07, 0xee, + /* (2^241)P */ 0x8b, 0x03, 0xee, 0xcb, 0xa7, 0x2e, 0x28, 0xbd, 0x97, 0xd1, 0x4c, 0x2b, 0xd1, 0x92, 0x67, 0x5b, 0x5a, 0x12, 0xbf, 0x29, 0x17, 0xfc, 0x50, 0x09, 0x74, 0x76, 0xa2, 0xd4, 0x82, 0xfd, 0x2c, 0x0c, 0x90, 0xf7, 0xe7, 0xe5, 0x9a, 0x2c, 0x16, 0x40, 0xb9, 0x6c, 0xd9, 0xe0, 0x22, 0x9e, 0xf8, 0xdd, 0x73, 0xe4, 0x7b, 0x9e, 0xbe, 0x4f, 0x66, 0x22, + /* (2^242)P */ 0xa4, 0x10, 0xbe, 0xb8, 0x83, 0x3a, 0x77, 0x8e, 0xea, 0x0a, 0xc4, 0x97, 0x3e, 0xb6, 0x6c, 0x81, 0xd7, 0x65, 0xd9, 0xf7, 0xae, 0xe6, 0xbe, 0xab, 0x59, 0x81, 0x29, 0x4b, 0xff, 0xe1, 0x0f, 0xc3, 0x2b, 0xad, 0x4b, 0xef, 0xc4, 0x50, 0x9f, 0x88, 0x31, 0xf2, 0xde, 0x80, 0xd6, 0xf4, 0x20, 0x9c, 0x77, 0x9b, 0xbe, 0xbe, 0x08, 0xf5, 0xf0, 0x95, + /* (2^243)P */ 0x0e, 0x7c, 0x7b, 0x7c, 0xb3, 0xd8, 0x83, 0xfc, 0x8c, 0x75, 0x51, 0x74, 0x1b, 0xe1, 0x6d, 0x11, 0x05, 0x46, 0x24, 0x0d, 0xa4, 0x2b, 0x32, 0xfd, 0x2c, 0x4e, 0x21, 0xdf, 0x39, 0x6b, 0x96, 0xfc, 0xff, 0x92, 0xfc, 0x35, 0x0d, 0x9a, 0x4b, 0xc0, 0x70, 0x46, 0x32, 0x7d, 0xc0, 0xc4, 0x04, 0xe0, 0x2d, 0x83, 0xa7, 0x00, 0xc7, 0xcb, 0xb4, 0x8f, + /* (2^244)P */ 0xa9, 0x5a, 0x7f, 0x0e, 0xdd, 0x2c, 0x85, 0xaa, 0x4d, 0xac, 0xde, 0xb3, 0xb6, 0xaf, 0xe6, 0xd1, 0x06, 0x7b, 0x2c, 0xa4, 0x01, 0x19, 0x22, 0x7d, 0x78, 0xf0, 0x3a, 0xea, 0x89, 0xfe, 0x21, 0x61, 0x6d, 0xb8, 0xfe, 0xa5, 0x2a, 0xab, 0x0d, 0x7b, 0x51, 0x39, 0xb6, 0xde, 0xbc, 0xf0, 0xc5, 0x48, 0xd7, 0x09, 0x82, 0x6e, 0x66, 0x75, 0xc5, 0xcd, + /* (2^245)P */ 0xee, 0xdf, 0x2b, 0x6c, 0xa8, 0xde, 0x61, 0xe1, 0x27, 0xfa, 0x2a, 0x0f, 0x68, 0xe7, 0x7a, 0x9b, 0x13, 0xe9, 0x56, 0xd2, 0x1c, 0x3d, 0x2f, 0x3c, 0x7a, 0xf6, 0x6f, 0x45, 0xee, 0xe8, 0xf4, 0xa0, 0xa6, 0xe8, 0xa5, 0x27, 0xee, 0xf2, 0x85, 0xa9, 0xd5, 0x0e, 0xa9, 0x26, 0x60, 0xfe, 0xee, 0xc7, 0x59, 0x99, 0x5e, 0xa3, 0xdf, 0x23, 0x36, 0xd5, + /* (2^246)P */ 0x15, 0x66, 0x6f, 0xd5, 0x78, 0xa4, 0x0a, 0xf7, 0xb1, 0xe8, 0x75, 0x6b, 0x48, 0x7d, 0xa6, 0x4d, 0x3d, 0x36, 0x9b, 0xc7, 0xcc, 0x68, 0x9a, 0xfe, 0x2f, 0x39, 0x2a, 0x51, 0x31, 0x39, 0x7d, 0x73, 0x6f, 0xc8, 0x74, 0x72, 0x6f, 0x6e, 0xda, 0x5f, 0xad, 0x48, 0xc8, 0x40, 0xe1, 0x06, 0x01, 0x36, 0xa1, 0x88, 0xc8, 0x99, 0x9c, 0xd1, 0x11, 0x8f, + /* (2^247)P */ 0xab, 0xc5, 0xcb, 0xcf, 0xbd, 0x73, 0x21, 0xd0, 0x82, 0xb1, 0x2e, 0x2d, 0xd4, 0x36, 0x1b, 0xed, 0xa9, 0x8a, 0x26, 0x79, 0xc4, 0x17, 0xae, 0xe5, 0x09, 0x0a, 0x0c, 0xa4, 0x21, 0xa0, 0x6e, 0xdd, 0x62, 0x8e, 0x44, 0x62, 0xcc, 0x50, 0xff, 0x93, 0xb3, 0x9a, 0x72, 0x8c, 0x3f, 0xa1, 0xa6, 0x4d, 0x87, 0xd5, 0x1c, 0x5a, 0xc0, 0x0b, 0x1a, 0xd6, + /* (2^248)P */ 0x67, 0x36, 0x6a, 0x1f, 0x96, 0xe5, 0x80, 0x20, 0xa9, 0xe8, 0x0b, 0x0e, 0x21, 0x29, 0x3f, 0xc8, 0x0a, 0x6d, 0x27, 0x47, 0xca, 0xd9, 0x05, 0x55, 0xbf, 0x11, 0xcf, 0x31, 0x7a, 0x37, 0xc7, 0x90, 0xa9, 0xf4, 0x07, 0x5e, 0xd5, 0xc3, 0x92, 0xaa, 0x95, 0xc8, 0x23, 0x2a, 0x53, 0x45, 0xe3, 0x3a, 0x24, 0xe9, 0x67, 0x97, 0x3a, 0x82, 0xf9, 0xa6, + /* (2^249)P */ 0x92, 0x9e, 0x6d, 0x82, 0x67, 0xe9, 0xf9, 0x17, 0x96, 0x2c, 0xa7, 0xd3, 0x89, 0xf9, 0xdb, 0xd8, 0x20, 0xc6, 0x2e, 0xec, 0x4a, 0x76, 0x64, 0xbf, 0x27, 0x40, 0xe2, 0xb4, 0xdf, 0x1f, 0xa0, 0xef, 0x07, 0x80, 0xfb, 0x8e, 0x12, 0xf8, 0xb8, 0xe1, 0xc6, 0xdf, 0x7c, 0x69, 0x35, 0x5a, 0xe1, 0x8e, 0x5d, 0x69, 0x84, 0x56, 0xb6, 0x31, 0x1c, 0x0b, + /* (2^250)P */ 0xd6, 0x94, 0x5c, 0xef, 0xbb, 0x46, 0x45, 0x44, 0x5b, 0xa1, 0xae, 0x03, 0x65, 0xdd, 0xb5, 0x66, 0x88, 0x35, 0x29, 0x95, 0x16, 0x54, 0xa6, 0xf5, 0xc9, 0x78, 0x34, 0xe6, 0x0f, 0xc4, 0x2b, 0x5b, 0x79, 0x51, 0x68, 0x48, 0x3a, 0x26, 0x87, 0x05, 0x70, 0xaf, 0x8b, 0xa6, 0xc7, 0x2e, 0xb3, 0xa9, 0x10, 0x01, 0xb0, 0xb9, 0x31, 0xfd, 0xdc, 0x80, + /* (2^251)P */ 0x25, 0xf2, 0xad, 0xd6, 0x75, 0xa3, 0x04, 0x05, 0x64, 0x8a, 0x97, 0x60, 0x27, 0x2a, 0xe5, 0x6d, 0xb0, 0x73, 0xf4, 0x07, 0x2a, 0x9d, 0xe9, 0x46, 0xb4, 0x1c, 0x51, 0xf8, 0x63, 0x98, 0x7e, 0xe5, 0x13, 0x51, 0xed, 0x98, 0x65, 0x98, 0x4f, 0x8f, 0xe7, 0x7e, 0x72, 0xd7, 0x64, 0x11, 0x2f, 0xcd, 0x12, 0xf8, 0xc4, 0x63, 0x52, 0x0f, 0x7f, 0xc4, + /* (2^252)P */ 0x5c, 0xd9, 0x85, 0x63, 0xc7, 0x8a, 0x65, 0x9a, 0x25, 0x83, 0x31, 0x73, 0x49, 0xf0, 0x93, 0x96, 0x70, 0x67, 0x6d, 0xb1, 0xff, 0x95, 0x54, 0xe4, 0xf8, 0x15, 0x6c, 0x5f, 0xbd, 0xf6, 0x0f, 0x38, 0x7b, 0x68, 0x7d, 0xd9, 0x3d, 0xf0, 0xa9, 0xa0, 0xe4, 0xd1, 0xb6, 0x34, 0x6d, 0x14, 0x16, 0xc2, 0x4c, 0x30, 0x0e, 0x67, 0xd3, 0xbe, 0x2e, 0xc0, + /* (2^253)P */ 0x06, 0x6b, 0x52, 0xc8, 0x14, 0xcd, 0xae, 0x03, 0x93, 0xea, 0xc1, 0xf2, 0xf6, 0x8b, 0xc5, 0xb6, 0xdc, 0x82, 0x42, 0x29, 0x94, 0xe0, 0x25, 0x6c, 0x3f, 0x9f, 0x5d, 0xe4, 0x96, 0xf6, 0x8e, 0x3f, 0xf9, 0x72, 0xc4, 0x77, 0x60, 0x8b, 0xa4, 0xf9, 0xa8, 0xc3, 0x0a, 0x81, 0xb1, 0x97, 0x70, 0x18, 0xab, 0xea, 0x37, 0x8a, 0x08, 0xc7, 0xe2, 0x95, + /* (2^254)P */ 0x94, 0x49, 0xd9, 0x5f, 0x76, 0x72, 0x82, 0xad, 0x2d, 0x50, 0x1a, 0x7a, 0x5b, 0xe6, 0x95, 0x1e, 0x95, 0x65, 0x87, 0x1c, 0x52, 0xd7, 0x44, 0xe6, 0x9b, 0x56, 0xcd, 0x6f, 0x05, 0xff, 0x67, 0xc5, 0xdb, 0xa2, 0xac, 0xe4, 0xa2, 0x28, 0x63, 0x5f, 0xfb, 0x0c, 0x3b, 0xf1, 0x87, 0xc3, 0x36, 0x78, 0x3f, 0x77, 0xfa, 0x50, 0x85, 0xf9, 0xd7, 0x82, + /* (2^255)P */ 0x64, 0xc0, 0xe0, 0xd8, 0x2d, 0xed, 0xcb, 0x6a, 0xfd, 0xcd, 0xbc, 0x7e, 0x9f, 0xc8, 0x85, 0xe9, 0xc1, 0x7c, 0x0f, 0xe5, 0x18, 0xea, 0xd4, 0x51, 0xad, 0x59, 0x13, 0x75, 0xd9, 0x3d, 0xd4, 0x8a, 0xb2, 0xbe, 0x78, 0x52, 0x2b, 0x52, 0x94, 0x37, 0x41, 0xd6, 0xb4, 0xb6, 0x45, 0x20, 0x76, 0xe0, 0x1f, 0x31, 0xdb, 0xb1, 0xa1, 0x43, 0xf0, 0x18, + /* (2^256)P */ 0x74, 0xa9, 0xa4, 0xa9, 0xdd, 0x6e, 0x3e, 0x68, 0xe5, 0xc3, 0x2e, 0x92, 0x17, 0xa4, 0xcb, 0x80, 0xb1, 0xf0, 0x06, 0x93, 0xef, 0xe6, 0x00, 0xe6, 0x3b, 0xb1, 0x32, 0x65, 0x7b, 0x83, 0xb6, 0x8a, 0x49, 0x1b, 0x14, 0x89, 0xee, 0xba, 0xf5, 0x6a, 0x8d, 0x36, 0xef, 0xb0, 0xd8, 0xb2, 0x16, 0x99, 0x17, 0x35, 0x02, 0x16, 0x55, 0x58, 0xdd, 0x82, + /* (2^257)P */ 0x36, 0x95, 0xe8, 0xf4, 0x36, 0x42, 0xbb, 0xc5, 0x3e, 0xfa, 0x30, 0x84, 0x9e, 0x59, 0xfd, 0xd2, 0x95, 0x42, 0xf8, 0x64, 0xd9, 0xb9, 0x0e, 0x9f, 0xfa, 0xd0, 0x7b, 0x20, 0x31, 0x77, 0x48, 0x29, 0x4d, 0xd0, 0x32, 0x57, 0x56, 0x30, 0xa6, 0x17, 0x53, 0x04, 0xbf, 0x08, 0x28, 0xec, 0xb8, 0x46, 0xc1, 0x03, 0x89, 0xdc, 0xed, 0xa0, 0x35, 0x53, + /* (2^258)P */ 0xc5, 0x7f, 0x9e, 0xd8, 0xc5, 0xba, 0x5f, 0x68, 0xc8, 0x23, 0x75, 0xea, 0x0d, 0xd9, 0x5a, 0xfd, 0x61, 0x1a, 0xa3, 0x2e, 0x45, 0x63, 0x14, 0x55, 0x86, 0x21, 0x29, 0xbe, 0xef, 0x5e, 0x50, 0xe5, 0x18, 0x59, 0xe7, 0xe3, 0xce, 0x4d, 0x8c, 0x15, 0x8f, 0x89, 0x66, 0x44, 0x52, 0x3d, 0xfa, 0xc7, 0x9a, 0x59, 0x90, 0x8e, 0xc0, 0x06, 0x3f, 0xc9, + /* (2^259)P */ 0x8e, 0x04, 0xd9, 0x16, 0x50, 0x1d, 0x8c, 0x9f, 0xd5, 0xe3, 0xce, 0xfd, 0x47, 0x04, 0x27, 0x4d, 0xc2, 0xfa, 0x71, 0xd9, 0x0b, 0xb8, 0x65, 0xf4, 0x11, 0xf3, 0x08, 0xee, 0x81, 0xc8, 0x67, 0x99, 0x0b, 0x8d, 0x77, 0xa3, 0x4f, 0xb5, 0x9b, 0xdb, 0x26, 0xf1, 0x97, 0xeb, 0x04, 0x54, 0xeb, 0x80, 0x08, 0x1d, 0x1d, 0xf6, 0x3d, 0x1f, 0x5a, 0xb8, + /* (2^260)P */ 0xb7, 0x9c, 0x9d, 0xee, 0xb9, 0x5c, 0xad, 0x0d, 0x9e, 0xfd, 0x60, 0x3c, 0x27, 0x4e, 0xa2, 0x95, 0xfb, 0x64, 0x7e, 0x79, 0x64, 0x87, 0x10, 0xb4, 0x73, 0xe0, 0x9d, 0x46, 0x4d, 0x3d, 0xee, 0x83, 0xe4, 0x16, 0x88, 0x97, 0xe6, 0x4d, 0xba, 0x70, 0xb6, 0x96, 0x7b, 0xff, 0x4b, 0xc8, 0xcf, 0x72, 0x83, 0x3e, 0x5b, 0x24, 0x2e, 0x57, 0xf1, 0x82, + /* (2^261)P */ 0x30, 0x71, 0x40, 0x51, 0x4f, 0x44, 0xbb, 0xc7, 0xf0, 0x54, 0x6e, 0x9d, 0xeb, 0x15, 0xad, 0xf8, 0x61, 0x43, 0x5a, 0xef, 0xc0, 0xb1, 0x57, 0xae, 0x03, 0x40, 0xe8, 0x68, 0x6f, 0x03, 0x20, 0x4f, 0x8a, 0x51, 0x2a, 0x9e, 0xd2, 0x45, 0xaf, 0xb4, 0xf5, 0xd4, 0x95, 0x7f, 0x3d, 0x3d, 0xb7, 0xb6, 0x28, 0xc5, 0x08, 0x8b, 0x44, 0xd6, 0x3f, 0xe7, + /* (2^262)P */ 0xa9, 0x52, 0x04, 0x67, 0xcb, 0x20, 0x63, 0xf8, 0x18, 0x01, 0x44, 0x21, 0x6a, 0x8a, 0x83, 0x48, 0xd4, 0xaf, 0x23, 0x0f, 0x35, 0x8d, 0xe5, 0x5a, 0xc4, 0x7c, 0x55, 0x46, 0x19, 0x5f, 0x35, 0xe0, 0x5d, 0x97, 0x4c, 0x2d, 0x04, 0xed, 0x59, 0xd4, 0xb0, 0xb2, 0xc6, 0xe3, 0x51, 0xe1, 0x38, 0xc6, 0x30, 0x49, 0x8f, 0xae, 0x61, 0x64, 0xce, 0xa8, + /* (2^263)P */ 0x9b, 0x64, 0x83, 0x3c, 0xd3, 0xdf, 0xb9, 0x27, 0xe7, 0x5b, 0x7f, 0xeb, 0xf3, 0x26, 0xcf, 0xb1, 0x8f, 0xaf, 0x26, 0xc8, 0x48, 0xce, 0xa1, 0xac, 0x7d, 0x10, 0x34, 0x28, 0xe1, 0x1f, 0x69, 0x03, 0x64, 0x77, 0x61, 0xdd, 0x4a, 0x9b, 0x18, 0x47, 0xf8, 0xca, 0x63, 0xc9, 0x03, 0x2d, 0x20, 0x2a, 0x69, 0x6e, 0x42, 0xd0, 0xe7, 0xaa, 0xb5, 0xf3, + /* (2^264)P */ 0xea, 0x31, 0x0c, 0x57, 0x0f, 0x3e, 0xe3, 0x35, 0xd8, 0x30, 0xa5, 0x6f, 0xdd, 0x95, 0x43, 0xc6, 0x66, 0x07, 0x4f, 0x34, 0xc3, 0x7e, 0x04, 0x10, 0x2d, 0xc4, 0x1c, 0x94, 0x52, 0x2e, 0x5b, 0x9a, 0x65, 0x2f, 0x91, 0xaa, 0x4f, 0x3c, 0xdc, 0x23, 0x18, 0xe1, 0x4f, 0x85, 0xcd, 0xf4, 0x8c, 0x51, 0xf7, 0xab, 0x4f, 0xdc, 0x15, 0x5c, 0x9e, 0xc5, + /* (2^265)P */ 0x54, 0x57, 0x23, 0x17, 0xe7, 0x82, 0x2f, 0x04, 0x7d, 0xfe, 0xe7, 0x1f, 0xa2, 0x57, 0x79, 0xe9, 0x58, 0x9b, 0xbe, 0xc6, 0x16, 0x4a, 0x17, 0x50, 0x90, 0x4a, 0x34, 0x70, 0x87, 0x37, 0x01, 0x26, 0xd8, 0xa3, 0x5f, 0x07, 0x7c, 0xd0, 0x7d, 0x05, 0x8a, 0x93, 0x51, 0x2f, 0x99, 0xea, 0xcf, 0x00, 0xd8, 0xc7, 0xe6, 0x9b, 0x8c, 0x62, 0x45, 0x87, + /* (2^266)P */ 0xc3, 0xfd, 0x29, 0x66, 0xe7, 0x30, 0x29, 0x77, 0xe0, 0x0d, 0x63, 0x5b, 0xe6, 0x90, 0x1a, 0x1e, 0x99, 0xc2, 0xa7, 0xab, 0xff, 0xa7, 0xbd, 0x79, 0x01, 0x97, 0xfd, 0x27, 0x1b, 0x43, 0x2b, 0xe6, 0xfe, 0x5e, 0xf1, 0xb9, 0x35, 0x38, 0x08, 0x25, 0x55, 0x90, 0x68, 0x2e, 0xc3, 0x67, 0x39, 0x9f, 0x2b, 0x2c, 0x70, 0x48, 0x8c, 0x47, 0xee, 0x56, + /* (2^267)P */ 0xf7, 0x32, 0x70, 0xb5, 0xe6, 0x42, 0xfd, 0x0a, 0x39, 0x9b, 0x07, 0xfe, 0x0e, 0xf4, 0x47, 0xba, 0x6a, 0x3f, 0xf5, 0x2c, 0x15, 0xf3, 0x60, 0x3f, 0xb1, 0x83, 0x7b, 0x2e, 0x34, 0x58, 0x1a, 0x6e, 0x4a, 0x49, 0x05, 0x45, 0xca, 0xdb, 0x00, 0x01, 0x0c, 0x42, 0x5e, 0x60, 0x40, 0x5f, 0xd9, 0xc7, 0x3a, 0x9e, 0x1c, 0x8d, 0xab, 0x11, 0x55, 0x65, + /* (2^268)P */ 0x87, 0x40, 0xb7, 0x0d, 0xaa, 0x34, 0x89, 0x90, 0x75, 0x6d, 0xa2, 0xfe, 0x3b, 0x6d, 0x5c, 0x39, 0x98, 0x10, 0x9e, 0x15, 0xc5, 0x35, 0xa2, 0x27, 0x23, 0x0a, 0x2d, 0x60, 0xe2, 0xa8, 0x7f, 0x3e, 0x77, 0x8f, 0xcc, 0x44, 0xcc, 0x30, 0x28, 0xe2, 0xf0, 0x04, 0x8c, 0xee, 0xe4, 0x5f, 0x68, 0x8c, 0xdf, 0x70, 0xbf, 0x31, 0xee, 0x2a, 0xfc, 0xce, + /* (2^269)P */ 0x92, 0xf2, 0xa0, 0xd9, 0x58, 0x3b, 0x7c, 0x1a, 0x99, 0x46, 0x59, 0x54, 0x60, 0x06, 0x8d, 0x5e, 0xf0, 0x22, 0xa1, 0xed, 0x92, 0x8a, 0x4d, 0x76, 0x95, 0x05, 0x0b, 0xff, 0xfc, 0x9a, 0xd1, 0xcc, 0x05, 0xb9, 0x5e, 0x99, 0xe8, 0x2a, 0x76, 0x7b, 0xfd, 0xa6, 0xe2, 0xd1, 0x1a, 0xd6, 0x76, 0x9f, 0x2f, 0x0e, 0xd1, 0xa8, 0x77, 0x5a, 0x40, 0x5a, + /* (2^270)P */ 0xff, 0xf9, 0x3f, 0xa9, 0xa6, 0x6c, 0x6d, 0x03, 0x8b, 0xa7, 0x10, 0x5d, 0x3f, 0xec, 0x3e, 0x1c, 0x0b, 0x6b, 0xa2, 0x6a, 0x22, 0xa9, 0x28, 0xd0, 0x66, 0xc9, 0xc2, 0x3d, 0x47, 0x20, 0x7d, 0xa6, 0x1d, 0xd8, 0x25, 0xb5, 0xf2, 0xf9, 0x70, 0x19, 0x6b, 0xf8, 0x43, 0x36, 0xc5, 0x1f, 0xe4, 0x5a, 0x4c, 0x13, 0xe4, 0x6d, 0x08, 0x0b, 0x1d, 0xb1, + /* (2^271)P */ 0x3f, 0x20, 0x9b, 0xfb, 0xec, 0x7d, 0x31, 0xc5, 0xfc, 0x88, 0x0b, 0x30, 0xed, 0x36, 0xc0, 0x63, 0xb1, 0x7d, 0x10, 0xda, 0xb6, 0x2e, 0xad, 0xf3, 0xec, 0x94, 0xe7, 0xec, 0xb5, 0x9c, 0xfe, 0xf5, 0x35, 0xf0, 0xa2, 0x2d, 0x7f, 0xca, 0x6b, 0x67, 0x1a, 0xf6, 0xb3, 0xda, 0x09, 0x2a, 0xaa, 0xdf, 0xb1, 0xca, 0x9b, 0xfb, 0xeb, 0xb3, 0xcd, 0xc0, + /* (2^272)P */ 0xcd, 0x4d, 0x89, 0x00, 0xa4, 0x3b, 0x48, 0xf0, 0x76, 0x91, 0x35, 0xa5, 0xf8, 0xc9, 0xb6, 0x46, 0xbc, 0xf6, 0x9a, 0x45, 0x47, 0x17, 0x96, 0x80, 0x5b, 0x3a, 0x28, 0x33, 0xf9, 0x5a, 0xef, 0x43, 0x07, 0xfe, 0x3b, 0xf4, 0x8e, 0x19, 0xce, 0xd2, 0x94, 0x4b, 0x6d, 0x8e, 0x67, 0x20, 0xc7, 0x4f, 0x2f, 0x59, 0x8e, 0xe1, 0xa1, 0xa9, 0xf9, 0x0e, + /* (2^273)P */ 0xdc, 0x7b, 0xb5, 0x50, 0x2e, 0xe9, 0x7e, 0x8b, 0x78, 0xa1, 0x38, 0x96, 0x22, 0xc3, 0x61, 0x67, 0x6d, 0xc8, 0x58, 0xed, 0x41, 0x1d, 0x5d, 0x86, 0x98, 0x7f, 0x2f, 0x1b, 0x8d, 0x3e, 0xaa, 0xc1, 0xd2, 0x0a, 0xf3, 0xbf, 0x95, 0x04, 0xf3, 0x10, 0x3c, 0x2b, 0x7f, 0x90, 0x46, 0x04, 0xaa, 0x6a, 0xa9, 0x35, 0x76, 0xac, 0x49, 0xb5, 0x00, 0x45, + /* (2^274)P */ 0xb1, 0x93, 0x79, 0x84, 0x4a, 0x2a, 0x30, 0x78, 0x16, 0xaa, 0xc5, 0x74, 0x06, 0xce, 0xa5, 0xa7, 0x32, 0x86, 0xe0, 0xf9, 0x10, 0xd2, 0x58, 0x76, 0xfb, 0x66, 0x49, 0x76, 0x3a, 0x90, 0xba, 0xb5, 0xcc, 0x99, 0xcd, 0x09, 0xc1, 0x9a, 0x74, 0x23, 0xdf, 0x0c, 0xfe, 0x99, 0x52, 0x80, 0xa3, 0x7c, 0x1c, 0x71, 0x5f, 0x2c, 0x49, 0x57, 0xf4, 0xf9, + /* (2^275)P */ 0x6d, 0xbf, 0x52, 0xe6, 0x25, 0x98, 0xed, 0xcf, 0xe3, 0xbc, 0x08, 0xa2, 0x1a, 0x90, 0xae, 0xa0, 0xbf, 0x07, 0x15, 0xad, 0x0a, 0x9f, 0x3e, 0x47, 0x44, 0xc2, 0x10, 0x46, 0xa6, 0x7a, 0x9e, 0x2f, 0x57, 0xbc, 0xe2, 0xf0, 0x1d, 0xd6, 0x9a, 0x06, 0xed, 0xfc, 0x54, 0x95, 0x92, 0x15, 0xa2, 0xf7, 0x8d, 0x6b, 0xef, 0xb2, 0x05, 0xed, 0x5c, 0x63, + /* (2^276)P */ 0xbc, 0x0b, 0x27, 0x3a, 0x3a, 0xf8, 0xe1, 0x48, 0x02, 0x7e, 0x27, 0xe6, 0x81, 0x62, 0x07, 0x73, 0x74, 0xe5, 0x52, 0xd7, 0xf8, 0x26, 0xca, 0x93, 0x4d, 0x3e, 0x9b, 0x55, 0x09, 0x8e, 0xe3, 0xd7, 0xa6, 0xe3, 0xb6, 0x2a, 0xa9, 0xb3, 0xb0, 0xa0, 0x8c, 0x01, 0xbb, 0x07, 0x90, 0x78, 0x6d, 0x6d, 0xe9, 0xf0, 0x7a, 0x90, 0xbd, 0xdc, 0x0c, 0x36, + /* (2^277)P */ 0x7f, 0x20, 0x12, 0x0f, 0x40, 0x00, 0x53, 0xd8, 0x0c, 0x27, 0x47, 0x47, 0x22, 0x80, 0xfb, 0x62, 0xe4, 0xa7, 0xf7, 0xbd, 0x42, 0xa5, 0xc3, 0x2b, 0xb2, 0x7f, 0x50, 0xcc, 0xe2, 0xfb, 0xd5, 0xc0, 0x63, 0xdd, 0x24, 0x5f, 0x7c, 0x08, 0x91, 0xbf, 0x6e, 0x47, 0x44, 0xd4, 0x6a, 0xc0, 0xc3, 0x09, 0x39, 0x27, 0xdd, 0xc7, 0xca, 0x06, 0x29, 0x55, + /* (2^278)P */ 0x76, 0x28, 0x58, 0xb0, 0xd2, 0xf3, 0x0f, 0x04, 0xe9, 0xc9, 0xab, 0x66, 0x5b, 0x75, 0x51, 0xdc, 0xe5, 0x8f, 0xe8, 0x1f, 0xdb, 0x03, 0x0f, 0xb0, 0x7d, 0xf9, 0x20, 0x64, 0x89, 0xe9, 0xdc, 0xe6, 0x24, 0xc3, 0xd5, 0xd2, 0x41, 0xa6, 0xe4, 0xe3, 0xc4, 0x79, 0x7c, 0x0f, 0xa1, 0x61, 0x2f, 0xda, 0xa4, 0xc9, 0xfd, 0xad, 0x5c, 0x65, 0x6a, 0xf3, + /* (2^279)P */ 0xd5, 0xab, 0x72, 0x7a, 0x3b, 0x59, 0xea, 0xcf, 0xd5, 0x17, 0xd2, 0xb2, 0x5f, 0x2d, 0xab, 0xad, 0x9e, 0x88, 0x64, 0x55, 0x96, 0x6e, 0xf3, 0x44, 0xa9, 0x11, 0xf5, 0xf8, 0x3a, 0xf1, 0xcd, 0x79, 0x4c, 0x99, 0x6d, 0x23, 0x6a, 0xa0, 0xc2, 0x1a, 0x19, 0x45, 0xb5, 0xd8, 0x95, 0x2f, 0x49, 0xe9, 0x46, 0x39, 0x26, 0x60, 0x04, 0x15, 0x8b, 0xcc, + /* (2^280)P */ 0x66, 0x0c, 0xf0, 0x54, 0x41, 0x02, 0x91, 0xab, 0xe5, 0x85, 0x8a, 0x44, 0xa6, 0x34, 0x96, 0x32, 0xc0, 0xdf, 0x6c, 0x41, 0x39, 0xd4, 0xc6, 0xe1, 0xe3, 0x81, 0xb0, 0x4c, 0x34, 0x4f, 0xe5, 0xf4, 0x35, 0x46, 0x1f, 0xeb, 0x75, 0xfd, 0x43, 0x37, 0x50, 0x99, 0xab, 0xad, 0xb7, 0x8c, 0xa1, 0x57, 0xcb, 0xe6, 0xce, 0x16, 0x2e, 0x85, 0xcc, 0xf9, + /* (2^281)P */ 0x63, 0xd1, 0x3f, 0x9e, 0xa2, 0x17, 0x2e, 0x1d, 0x3e, 0xce, 0x48, 0x2d, 0xbb, 0x8f, 0x69, 0xc9, 0xa6, 0x3d, 0x4e, 0xfe, 0x09, 0x56, 0xb3, 0x02, 0x5f, 0x99, 0x97, 0x0c, 0x54, 0xda, 0x32, 0x97, 0x9b, 0xf4, 0x95, 0xf1, 0xad, 0xe3, 0x2b, 0x04, 0xa7, 0x9b, 0x3f, 0xbb, 0xe7, 0x87, 0x2e, 0x1f, 0x8b, 0x4b, 0x7a, 0xa4, 0x43, 0x0c, 0x0f, 0x35, + /* (2^282)P */ 0x05, 0xdc, 0xe0, 0x2c, 0xa1, 0xc1, 0xd0, 0xf1, 0x1f, 0x4e, 0xc0, 0x6c, 0x35, 0x7b, 0xca, 0x8f, 0x8b, 0x02, 0xb1, 0xf7, 0xd6, 0x2e, 0xe7, 0x93, 0x80, 0x85, 0x18, 0x88, 0x19, 0xb9, 0xb4, 0x4a, 0xbc, 0xeb, 0x5a, 0x78, 0x38, 0xed, 0xc6, 0x27, 0x2a, 0x74, 0x76, 0xf0, 0x1b, 0x79, 0x92, 0x2f, 0xd2, 0x81, 0x98, 0xdf, 0xa9, 0x50, 0x19, 0xeb, + /* (2^283)P */ 0xb5, 0xe7, 0xb4, 0x11, 0x3a, 0x81, 0xb6, 0xb4, 0xf8, 0xa2, 0xb3, 0x6c, 0xfc, 0x9d, 0xe0, 0xc0, 0xe0, 0x59, 0x7f, 0x05, 0x37, 0xef, 0x2c, 0xa9, 0x3a, 0x24, 0xac, 0x7b, 0x25, 0xa0, 0x55, 0xd2, 0x44, 0x82, 0x82, 0x6e, 0x64, 0xa3, 0x58, 0xc8, 0x67, 0xae, 0x26, 0xa7, 0x0f, 0x42, 0x63, 0xe1, 0x93, 0x01, 0x52, 0x19, 0xaf, 0x49, 0x3e, 0x33, + /* (2^284)P */ 0x05, 0x85, 0xe6, 0x66, 0xaf, 0x5f, 0xdf, 0xbf, 0x9d, 0x24, 0x62, 0x60, 0x90, 0xe2, 0x4c, 0x7d, 0x4e, 0xc3, 0x74, 0x5d, 0x4f, 0x53, 0xf3, 0x63, 0x13, 0xf4, 0x74, 0x28, 0x6b, 0x7d, 0x57, 0x0c, 0x9d, 0x84, 0xa7, 0x1a, 0xff, 0xa0, 0x79, 0xdf, 0xfc, 0x65, 0x98, 0x8e, 0x22, 0x0d, 0x62, 0x7e, 0xf2, 0x34, 0x60, 0x83, 0x05, 0x14, 0xb1, 0xc1, + /* (2^285)P */ 0x64, 0x22, 0xcc, 0xdf, 0x5c, 0xbc, 0x88, 0x68, 0x4c, 0xd9, 0xbc, 0x0e, 0xc9, 0x8b, 0xb4, 0x23, 0x52, 0xad, 0xb0, 0xb3, 0xf1, 0x17, 0xd8, 0x15, 0x04, 0x6b, 0x99, 0xf0, 0xc4, 0x7d, 0x48, 0x22, 0x4a, 0xf8, 0x6f, 0xaa, 0x88, 0x0d, 0xc5, 0x5e, 0xa9, 0x1c, 0x61, 0x3d, 0x95, 0xa9, 0x7b, 0x6a, 0x79, 0x33, 0x0a, 0x2b, 0x99, 0xe3, 0x4e, 0x48, + /* (2^286)P */ 0x6b, 0x9b, 0x6a, 0x2a, 0xf1, 0x60, 0x31, 0xb4, 0x73, 0xd1, 0x87, 0x45, 0x9c, 0x15, 0x58, 0x4b, 0x91, 0x6d, 0x94, 0x1c, 0x41, 0x11, 0x4a, 0x83, 0xec, 0xaf, 0x65, 0xbc, 0x34, 0xaa, 0x26, 0xe2, 0xaf, 0xed, 0x46, 0x05, 0x4e, 0xdb, 0xc6, 0x4e, 0x10, 0x28, 0x4e, 0x72, 0xe5, 0x31, 0xa3, 0x20, 0xd7, 0xb1, 0x96, 0x64, 0xf6, 0xce, 0x08, 0x08, + /* (2^287)P */ 0x16, 0xa9, 0x5c, 0x9f, 0x9a, 0xb4, 0xb8, 0xc8, 0x32, 0x78, 0xc0, 0x3a, 0xd9, 0x5f, 0x94, 0xac, 0x3a, 0x42, 0x1f, 0x43, 0xd6, 0x80, 0x47, 0x2c, 0xdc, 0x76, 0x27, 0xfa, 0x50, 0xe5, 0xa1, 0xe4, 0xc3, 0xcb, 0x61, 0x31, 0xe1, 0x2e, 0xde, 0x81, 0x3b, 0x77, 0x1c, 0x39, 0x3c, 0xdb, 0xda, 0x87, 0x4b, 0x84, 0x12, 0xeb, 0xdd, 0x54, 0xbf, 0xe7, + /* (2^288)P */ 0xbf, 0xcb, 0x73, 0x21, 0x3d, 0x7e, 0x13, 0x8c, 0xa6, 0x34, 0x21, 0x2b, 0xa5, 0xe4, 0x9f, 0x8e, 0x9c, 0x01, 0x9c, 0x43, 0xd9, 0xc7, 0xb9, 0xf1, 0xbe, 0x7f, 0x45, 0x51, 0x97, 0xa1, 0x8e, 0x01, 0xf8, 0xbd, 0xd2, 0xbf, 0x81, 0x3a, 0x8b, 0xab, 0xe4, 0x89, 0xb7, 0xbd, 0xf2, 0xcd, 0xa9, 0x8a, 0x8a, 0xde, 0xfb, 0x8a, 0x55, 0x12, 0x7b, 0x17, + /* (2^289)P */ 0x1b, 0x95, 0x58, 0x4d, 0xe6, 0x51, 0x31, 0x52, 0x1c, 0xd8, 0x15, 0x84, 0xb1, 0x0d, 0x36, 0x25, 0x88, 0x91, 0x46, 0x71, 0x42, 0x56, 0xe2, 0x90, 0x08, 0x9e, 0x77, 0x1b, 0xee, 0x22, 0x3f, 0xec, 0xee, 0x8c, 0x7b, 0x2e, 0x79, 0xc4, 0x6c, 0x07, 0xa1, 0x7e, 0x52, 0xf5, 0x26, 0x5c, 0x84, 0x2a, 0x50, 0x6e, 0x82, 0xb3, 0x76, 0xda, 0x35, 0x16, + /* (2^290)P */ 0x0a, 0x6f, 0x99, 0x87, 0xc0, 0x7d, 0x8a, 0xb2, 0xca, 0xae, 0xe8, 0x65, 0x98, 0x0f, 0xb3, 0x44, 0xe1, 0xdc, 0x52, 0x79, 0x75, 0xec, 0x8f, 0x95, 0x87, 0x45, 0xd1, 0x32, 0x18, 0x55, 0x15, 0xce, 0x64, 0x9b, 0x08, 0x4f, 0x2c, 0xea, 0xba, 0x1c, 0x57, 0x06, 0x63, 0xc8, 0xb1, 0xfd, 0xc5, 0x67, 0xe7, 0x1f, 0x87, 0x9e, 0xde, 0x72, 0x7d, 0xec, + /* (2^291)P */ 0x36, 0x8b, 0x4d, 0x2c, 0xc2, 0x46, 0xe8, 0x96, 0xac, 0x0b, 0x8c, 0xc5, 0x09, 0x10, 0xfc, 0xf2, 0xda, 0xea, 0x22, 0xb2, 0xd3, 0x89, 0xeb, 0xb2, 0x85, 0x0f, 0xff, 0x59, 0x50, 0x2c, 0x99, 0x5a, 0x1f, 0xec, 0x2a, 0x6f, 0xec, 0xcf, 0xe9, 0xce, 0x12, 0x6b, 0x19, 0xd8, 0xde, 0x9b, 0xce, 0x0e, 0x6a, 0xaa, 0xe1, 0x32, 0xea, 0x4c, 0xfe, 0x92, + /* (2^292)P */ 0x5f, 0x17, 0x70, 0x53, 0x26, 0x03, 0x0b, 0xab, 0xd1, 0xc1, 0x42, 0x0b, 0xab, 0x2b, 0x3d, 0x31, 0xa4, 0xd5, 0x2b, 0x5e, 0x00, 0xd5, 0x9a, 0x22, 0x34, 0xe0, 0x53, 0x3f, 0x59, 0x7f, 0x2c, 0x6d, 0x72, 0x9a, 0xa4, 0xbe, 0x3d, 0x42, 0x05, 0x1b, 0xf2, 0x7f, 0x88, 0x56, 0xd1, 0x7c, 0x7d, 0x6b, 0x9f, 0x43, 0xfe, 0x65, 0x19, 0xae, 0x9c, 0x4c, + /* (2^293)P */ 0xf3, 0x7c, 0x20, 0xa9, 0xfc, 0xf2, 0xf2, 0x3b, 0x3c, 0x57, 0x41, 0x94, 0xe5, 0xcc, 0x6a, 0x37, 0x5d, 0x09, 0xf2, 0xab, 0xc2, 0xca, 0x60, 0x38, 0x6b, 0x7a, 0xe1, 0x78, 0x2b, 0xc1, 0x1d, 0xe8, 0xfd, 0xbc, 0x3d, 0x5c, 0xa2, 0xdb, 0x49, 0x20, 0x79, 0xe6, 0x1b, 0x9b, 0x65, 0xd9, 0x6d, 0xec, 0x57, 0x1d, 0xd2, 0xe9, 0x90, 0xeb, 0x43, 0x7b, + /* (2^294)P */ 0x2a, 0x8b, 0x2e, 0x19, 0x18, 0x10, 0xb8, 0x83, 0xe7, 0x7d, 0x2d, 0x9a, 0x3a, 0xe5, 0xd1, 0xe4, 0x7c, 0x38, 0xe5, 0x59, 0x2a, 0x6e, 0xd9, 0x01, 0x29, 0x3d, 0x23, 0xf7, 0x52, 0xba, 0x61, 0x04, 0x9a, 0xde, 0xc4, 0x31, 0x50, 0xeb, 0x1b, 0xaa, 0xde, 0x39, 0x58, 0xd8, 0x1b, 0x1e, 0xfc, 0x57, 0x9a, 0x28, 0x43, 0x9e, 0x97, 0x5e, 0xaa, 0xa3, + /* (2^295)P */ 0x97, 0x0a, 0x74, 0xc4, 0x39, 0x99, 0x6b, 0x40, 0xc7, 0x3e, 0x8c, 0xa7, 0xb1, 0x4e, 0x9a, 0x59, 0x6e, 0x1c, 0xfe, 0xfc, 0x2a, 0x5e, 0x73, 0x2b, 0x8c, 0xa9, 0x71, 0xf5, 0xda, 0x6b, 0x15, 0xab, 0xf7, 0xbe, 0x2a, 0x44, 0x5f, 0xba, 0xae, 0x67, 0x93, 0xc5, 0x86, 0xc1, 0xb8, 0xdf, 0xdc, 0xcb, 0xd7, 0xff, 0xb1, 0x71, 0x7c, 0x6f, 0x88, 0xf8, + /* (2^296)P */ 0x3f, 0x89, 0xb1, 0xbf, 0x24, 0x16, 0xac, 0x56, 0xfe, 0xdf, 0x94, 0x71, 0xbf, 0xd6, 0x57, 0x0c, 0xb4, 0x77, 0x37, 0xaa, 0x2a, 0x70, 0x76, 0x49, 0xaf, 0x0c, 0x97, 0x8e, 0x78, 0x2a, 0x67, 0xc9, 0x3b, 0x3d, 0x5b, 0x01, 0x2f, 0xda, 0xd5, 0xa8, 0xde, 0x02, 0xa9, 0xac, 0x76, 0x00, 0x0b, 0x46, 0xc6, 0x2d, 0xdc, 0x08, 0xf4, 0x10, 0x2c, 0xbe, + /* (2^297)P */ 0xcb, 0x07, 0xf9, 0x91, 0xc6, 0xd5, 0x3e, 0x54, 0x63, 0xae, 0xfc, 0x10, 0xbe, 0x3a, 0x20, 0x73, 0x4e, 0x65, 0x0e, 0x2d, 0x86, 0x77, 0x83, 0x9d, 0xe2, 0x0a, 0xe9, 0xac, 0x22, 0x52, 0x76, 0xd4, 0x6e, 0xfa, 0xe0, 0x09, 0xef, 0x78, 0x82, 0x9f, 0x26, 0xf9, 0x06, 0xb5, 0xe7, 0x05, 0x0e, 0xf2, 0x46, 0x72, 0x93, 0xd3, 0x24, 0xbd, 0x87, 0x60, + /* (2^298)P */ 0x14, 0x55, 0x84, 0x7b, 0x6c, 0x60, 0x80, 0x73, 0x8c, 0xbe, 0x2d, 0xd6, 0x69, 0xd6, 0x17, 0x26, 0x44, 0x9f, 0x88, 0xa2, 0x39, 0x7c, 0x89, 0xbc, 0x6d, 0x9e, 0x46, 0xb6, 0x68, 0x66, 0xea, 0xdc, 0x31, 0xd6, 0x21, 0x51, 0x9f, 0x28, 0x28, 0xaf, 0x9e, 0x47, 0x2c, 0x4c, 0x8f, 0xf3, 0xaf, 0x1f, 0xe4, 0xab, 0xac, 0xe9, 0x0c, 0x91, 0x3a, 0x61, + /* (2^299)P */ 0xb0, 0x37, 0x55, 0x4b, 0xe9, 0xc3, 0xb1, 0xce, 0x42, 0xe6, 0xc5, 0x11, 0x7f, 0x2c, 0x11, 0xfc, 0x4e, 0x71, 0x17, 0x00, 0x74, 0x7f, 0xbf, 0x07, 0x4d, 0xfd, 0x40, 0xb2, 0x87, 0xb0, 0xef, 0x1f, 0x35, 0x2c, 0x2d, 0xd7, 0xe1, 0xe4, 0xad, 0x0e, 0x7f, 0x63, 0x66, 0x62, 0x23, 0x41, 0xf6, 0xc1, 0x14, 0xa6, 0xd7, 0xa9, 0x11, 0x56, 0x9d, 0x1b, + /* (2^300)P */ 0x02, 0x82, 0x42, 0x18, 0x4f, 0x1b, 0xc9, 0x5d, 0x78, 0x5f, 0xee, 0xed, 0x01, 0x49, 0x8f, 0xf2, 0xa0, 0xe2, 0x6e, 0xbb, 0x6b, 0x04, 0x8d, 0xb2, 0x41, 0xae, 0xc8, 0x1b, 0x59, 0x34, 0xb8, 0x2a, 0xdb, 0x1f, 0xd2, 0x52, 0xdf, 0x3f, 0x35, 0x00, 0x8b, 0x61, 0xbc, 0x97, 0xa0, 0xc4, 0x77, 0xd1, 0xe4, 0x2c, 0x59, 0x68, 0xff, 0x30, 0xf2, 0xe2, + /* (2^301)P */ 0x79, 0x08, 0xb1, 0xdb, 0x55, 0xae, 0xd0, 0xed, 0xda, 0xa0, 0xec, 0x6c, 0xae, 0x68, 0xf2, 0x0b, 0x61, 0xb3, 0xf5, 0x21, 0x69, 0x87, 0x0b, 0x03, 0xea, 0x8a, 0x15, 0xd9, 0x7e, 0xca, 0xf7, 0xcd, 0xf3, 0x33, 0xb3, 0x4c, 0x5b, 0x23, 0x4e, 0x6f, 0x90, 0xad, 0x91, 0x4b, 0x4f, 0x46, 0x37, 0xe5, 0xe8, 0xb7, 0xeb, 0xd5, 0xca, 0x34, 0x4e, 0x23, + /* (2^302)P */ 0x09, 0x02, 0xdd, 0xfd, 0x70, 0xac, 0x56, 0x80, 0x36, 0x5e, 0x49, 0xd0, 0x3f, 0xc2, 0xe0, 0xba, 0x46, 0x7f, 0x5c, 0xf7, 0xc5, 0xbd, 0xd5, 0x55, 0x7d, 0x3f, 0xd5, 0x7d, 0x06, 0xdf, 0x27, 0x20, 0x4f, 0xe9, 0x30, 0xec, 0x1b, 0xa0, 0x0c, 0xd4, 0x2c, 0xe1, 0x2b, 0x65, 0x73, 0xea, 0x75, 0x35, 0xe8, 0xe6, 0x56, 0xd6, 0x07, 0x15, 0x99, 0xdf, + /* (2^303)P */ 0x4e, 0x10, 0xb7, 0xd0, 0x63, 0x8c, 0xcf, 0x16, 0x00, 0x7c, 0x58, 0xdf, 0x86, 0xdc, 0x4e, 0xca, 0x9c, 0x40, 0x5a, 0x42, 0xfd, 0xec, 0x98, 0xa4, 0x42, 0x53, 0xae, 0x16, 0x9d, 0xfd, 0x75, 0x5a, 0x12, 0x56, 0x1e, 0xc6, 0x57, 0xcc, 0x79, 0x27, 0x96, 0x00, 0xcf, 0x80, 0x4f, 0x8a, 0x36, 0x5c, 0xbb, 0xe9, 0x12, 0xdb, 0xb6, 0x2b, 0xad, 0x96, + /* (2^304)P */ 0x92, 0x32, 0x1f, 0xfd, 0xc6, 0x02, 0x94, 0x08, 0x1b, 0x60, 0x6a, 0x9f, 0x8b, 0xd6, 0xc8, 0xad, 0xd5, 0x1b, 0x27, 0x4e, 0xa4, 0x4d, 0x4a, 0x00, 0x10, 0x5f, 0x86, 0x11, 0xf5, 0xe3, 0x14, 0x32, 0x43, 0xee, 0xb9, 0xc7, 0xab, 0xf4, 0x6f, 0xe5, 0x66, 0x0c, 0x06, 0x0d, 0x96, 0x79, 0x28, 0xaf, 0x45, 0x2b, 0x56, 0xbe, 0xe4, 0x4a, 0x52, 0xd6, + /* (2^305)P */ 0x15, 0x16, 0x69, 0xef, 0x60, 0xca, 0x82, 0x25, 0x0f, 0xc6, 0x30, 0xa0, 0x0a, 0xd1, 0x83, 0x29, 0xcd, 0xb6, 0x89, 0x6c, 0xf5, 0xb2, 0x08, 0x38, 0xe6, 0xca, 0x6b, 0x19, 0x93, 0xc6, 0x5f, 0x75, 0x8e, 0x60, 0x34, 0x23, 0xc4, 0x13, 0x17, 0x69, 0x55, 0xcc, 0x72, 0x9c, 0x2b, 0x6c, 0x80, 0xf4, 0x4b, 0x8b, 0xb6, 0x97, 0x65, 0x07, 0xb6, 0xfb, + /* (2^306)P */ 0x01, 0x99, 0x74, 0x28, 0xa6, 0x67, 0xa3, 0xe5, 0x25, 0xfb, 0xdf, 0x82, 0x93, 0xe7, 0x35, 0x74, 0xce, 0xe3, 0x15, 0x1c, 0x1d, 0x79, 0x52, 0x84, 0x08, 0x04, 0x2f, 0x5c, 0xb8, 0xcd, 0x7f, 0x89, 0xb0, 0x39, 0x93, 0x63, 0xc9, 0x5d, 0x06, 0x01, 0x59, 0xf7, 0x7e, 0xf1, 0x4c, 0x3d, 0x12, 0x8d, 0x69, 0x1d, 0xb7, 0x21, 0x5e, 0x88, 0x82, 0xa2, + /* (2^307)P */ 0x8e, 0x69, 0xaf, 0x9a, 0x41, 0x0d, 0x9d, 0xcf, 0x8e, 0x8d, 0x5c, 0x51, 0x6e, 0xde, 0x0e, 0x48, 0x23, 0x89, 0xe5, 0x37, 0x80, 0xd6, 0x9d, 0x72, 0x32, 0x26, 0x38, 0x2d, 0x63, 0xa0, 0xfa, 0xd3, 0x40, 0xc0, 0x8c, 0x68, 0x6f, 0x2b, 0x1e, 0x9a, 0x39, 0x51, 0x78, 0x74, 0x9a, 0x7b, 0x4a, 0x8f, 0x0c, 0xa0, 0x88, 0x60, 0xa5, 0x21, 0xcd, 0xc7, + /* (2^308)P */ 0x3a, 0x7f, 0x73, 0x14, 0xbf, 0x89, 0x6a, 0x4c, 0x09, 0x5d, 0xf2, 0x93, 0x20, 0x2d, 0xc4, 0x29, 0x86, 0x06, 0x95, 0xab, 0x22, 0x76, 0x4c, 0x54, 0xe1, 0x7e, 0x80, 0x6d, 0xab, 0x29, 0x61, 0x87, 0x77, 0xf6, 0xc0, 0x3e, 0xda, 0xab, 0x65, 0x7e, 0x39, 0x12, 0xa1, 0x6b, 0x42, 0xf7, 0xc5, 0x97, 0x77, 0xec, 0x6f, 0x22, 0xbe, 0x44, 0xc7, 0x03, + /* (2^309)P */ 0xa5, 0x23, 0x90, 0x41, 0xa3, 0xc5, 0x3e, 0xe0, 0xa5, 0x32, 0x49, 0x1f, 0x39, 0x78, 0xb1, 0xd8, 0x24, 0xea, 0xd4, 0x87, 0x53, 0x42, 0x51, 0xf4, 0xd9, 0x46, 0x25, 0x2f, 0x62, 0xa9, 0x90, 0x9a, 0x4a, 0x25, 0x8a, 0xd2, 0x10, 0xe7, 0x3c, 0xbc, 0x58, 0x8d, 0x16, 0x14, 0x96, 0xa4, 0x6f, 0xf8, 0x12, 0x69, 0x91, 0x73, 0xe2, 0xfa, 0xf4, 0x57, + /* (2^310)P */ 0x51, 0x45, 0x3f, 0x96, 0xdc, 0x97, 0x38, 0xa6, 0x01, 0x63, 0x09, 0xea, 0xc2, 0x13, 0x30, 0xb0, 0x00, 0xb8, 0x0a, 0xce, 0xd1, 0x8f, 0x3e, 0x69, 0x62, 0x46, 0x33, 0x9c, 0xbf, 0x4b, 0xcb, 0x0c, 0x90, 0x1c, 0x45, 0xcf, 0x37, 0x5b, 0xf7, 0x4b, 0x5e, 0x95, 0xc3, 0x28, 0x9f, 0x08, 0x83, 0x53, 0x74, 0xab, 0x0c, 0xb4, 0xc0, 0xa1, 0xbc, 0x89, + /* (2^311)P */ 0x06, 0xb1, 0x51, 0x15, 0x65, 0x60, 0x21, 0x17, 0x7a, 0x20, 0x65, 0xee, 0x12, 0x35, 0x4d, 0x46, 0xf4, 0xf8, 0xd0, 0xb1, 0xca, 0x09, 0x30, 0x08, 0x89, 0x23, 0x3b, 0xe7, 0xab, 0x8b, 0x77, 0xa6, 0xad, 0x25, 0xdd, 0xea, 0x3c, 0x7d, 0xa5, 0x24, 0xb3, 0xe8, 0xfa, 0xfb, 0xc9, 0xf2, 0x71, 0xe9, 0xfa, 0xf2, 0xdc, 0x54, 0xdd, 0x55, 0x2e, 0x2f, + /* (2^312)P */ 0x7f, 0x96, 0x96, 0xfb, 0x52, 0x86, 0xcf, 0xea, 0x62, 0x18, 0xf1, 0x53, 0x1f, 0x61, 0x2a, 0x9f, 0x8c, 0x51, 0xca, 0x2c, 0xde, 0x6d, 0xce, 0xab, 0x58, 0x32, 0x0b, 0x33, 0x9b, 0x99, 0xb4, 0x5c, 0x88, 0x2a, 0x76, 0xcc, 0x3e, 0x54, 0x1e, 0x9d, 0xa2, 0x89, 0xe4, 0x19, 0xba, 0x80, 0xc8, 0x39, 0x32, 0x7f, 0x0f, 0xc7, 0x84, 0xbb, 0x43, 0x56, + /* (2^313)P */ 0x9b, 0x07, 0xb4, 0x42, 0xa9, 0xa0, 0x78, 0x4f, 0x28, 0x70, 0x2b, 0x7e, 0x61, 0xe0, 0xdd, 0x02, 0x98, 0xfc, 0xed, 0x31, 0x80, 0xf1, 0x15, 0x52, 0x89, 0x23, 0xcd, 0x5d, 0x2b, 0xc5, 0x19, 0x32, 0xfb, 0x70, 0x50, 0x7a, 0x97, 0x6b, 0x42, 0xdb, 0xca, 0xdb, 0xc4, 0x59, 0x99, 0xe0, 0x12, 0x1f, 0x17, 0xba, 0x8b, 0xf0, 0xc4, 0x38, 0x5d, 0x27, + /* (2^314)P */ 0x29, 0x1d, 0xdc, 0x2b, 0xf6, 0x5b, 0x04, 0x61, 0x36, 0x76, 0xa0, 0x56, 0x36, 0x6e, 0xd7, 0x24, 0x4d, 0xe7, 0xef, 0x44, 0xd2, 0xd5, 0x07, 0xcd, 0xc4, 0x9d, 0x80, 0x48, 0xc3, 0x38, 0xcf, 0xd8, 0xa3, 0xdd, 0xb2, 0x5e, 0xb5, 0x70, 0x15, 0xbb, 0x36, 0x85, 0x8a, 0xd7, 0xfb, 0x56, 0x94, 0x73, 0x9c, 0x81, 0xbe, 0xb1, 0x44, 0x28, 0xf1, 0x37, + /* (2^315)P */ 0xbf, 0xcf, 0x5c, 0xd2, 0xe2, 0xea, 0xc2, 0xcd, 0x70, 0x7a, 0x9d, 0xcb, 0x81, 0xc1, 0xe9, 0xf1, 0x56, 0x71, 0x52, 0xf7, 0x1b, 0x87, 0xc6, 0xd8, 0xcc, 0xb2, 0x69, 0xf3, 0xb0, 0xbd, 0xba, 0x83, 0x12, 0x26, 0xc4, 0xce, 0x72, 0xde, 0x3b, 0x21, 0x28, 0x9e, 0x5a, 0x94, 0xf5, 0x04, 0xa3, 0xc8, 0x0f, 0x5e, 0xbc, 0x71, 0xf9, 0x0d, 0xce, 0xf5, + /* (2^316)P */ 0x93, 0x97, 0x00, 0x85, 0xf4, 0xb4, 0x40, 0xec, 0xd9, 0x2b, 0x6c, 0xd6, 0x63, 0x9e, 0x93, 0x0a, 0x5a, 0xf4, 0xa7, 0x9a, 0xe3, 0x3c, 0xf0, 0x55, 0xd1, 0x96, 0x6c, 0xf5, 0x2a, 0xce, 0xd7, 0x95, 0x72, 0xbf, 0xc5, 0x0c, 0xce, 0x79, 0xa2, 0x0a, 0x78, 0xe0, 0x72, 0xd0, 0x66, 0x28, 0x05, 0x75, 0xd3, 0x23, 0x09, 0x91, 0xed, 0x7e, 0xc4, 0xbc, + /* (2^317)P */ 0x77, 0xc2, 0x9a, 0xf7, 0xa6, 0xe6, 0x18, 0xb4, 0xe7, 0xf6, 0xda, 0xec, 0x44, 0x6d, 0xfb, 0x08, 0xee, 0x65, 0xa8, 0x92, 0x85, 0x1f, 0xba, 0x38, 0x93, 0x20, 0x5c, 0x4d, 0xd2, 0x18, 0x0f, 0x24, 0xbe, 0x1a, 0x96, 0x44, 0x7d, 0xeb, 0xb3, 0xda, 0x95, 0xf4, 0xaf, 0x6c, 0x06, 0x0f, 0x47, 0x37, 0xc8, 0x77, 0x63, 0xe1, 0x29, 0xef, 0xff, 0xa5, + /* (2^318)P */ 0x16, 0x12, 0xd9, 0x47, 0x90, 0x22, 0x9b, 0x05, 0xf2, 0xa5, 0x9a, 0xae, 0x83, 0x98, 0xb5, 0xac, 0xab, 0x29, 0xaa, 0xdc, 0x5f, 0xde, 0xcd, 0xf7, 0x42, 0xad, 0x3b, 0x96, 0xd6, 0x3e, 0x6e, 0x52, 0x47, 0xb1, 0xab, 0x51, 0xde, 0x49, 0x7c, 0x87, 0x8d, 0x86, 0xe2, 0x70, 0x13, 0x21, 0x51, 0x1c, 0x0c, 0x25, 0xc1, 0xb0, 0xe6, 0x19, 0xcf, 0x12, + /* (2^319)P */ 0xf0, 0xbc, 0x97, 0x8f, 0x4b, 0x2f, 0xd1, 0x1f, 0x8c, 0x57, 0xed, 0x3c, 0xf4, 0x26, 0x19, 0xbb, 0x60, 0xca, 0x24, 0xc5, 0xd9, 0x97, 0xe2, 0x5f, 0x76, 0x49, 0x39, 0x7e, 0x2d, 0x12, 0x21, 0x98, 0xda, 0xe6, 0xdb, 0xd2, 0xd8, 0x9f, 0x18, 0xd8, 0x83, 0x6c, 0xba, 0x89, 0x8d, 0x29, 0xfa, 0x46, 0x33, 0x8c, 0x28, 0xdf, 0x6a, 0xb3, 0x69, 0x28, + /* (2^320)P */ 0x86, 0x17, 0xbc, 0xd6, 0x7c, 0xba, 0x1e, 0x83, 0xbb, 0x84, 0xb5, 0x8c, 0xad, 0xdf, 0xa1, 0x24, 0x81, 0x70, 0x40, 0x0f, 0xad, 0xad, 0x3b, 0x23, 0xd0, 0x93, 0xa0, 0x49, 0x5c, 0x4b, 0x51, 0xbe, 0x20, 0x49, 0x4e, 0xda, 0x2d, 0xd3, 0xad, 0x1b, 0x74, 0x08, 0x41, 0xf0, 0xef, 0x19, 0xe9, 0x45, 0x5d, 0x02, 0xae, 0x26, 0x25, 0xd9, 0xd1, 0xc2, + /* (2^321)P */ 0x48, 0x81, 0x3e, 0xb2, 0x83, 0xf8, 0x4d, 0xb3, 0xd0, 0x4c, 0x75, 0xb3, 0xa0, 0x52, 0x26, 0xf2, 0xaf, 0x5d, 0x36, 0x70, 0x72, 0xd6, 0xb7, 0x88, 0x08, 0x69, 0xbd, 0x15, 0x25, 0xb1, 0x45, 0x1b, 0xb7, 0x0b, 0x5f, 0x71, 0x5d, 0x83, 0x49, 0xb9, 0x84, 0x3b, 0x7c, 0xc1, 0x50, 0x93, 0x05, 0x53, 0xe0, 0x61, 0xea, 0xc1, 0xef, 0xdb, 0x82, 0x97, + /* (2^322)P */ 0x00, 0xd5, 0xc3, 0x3a, 0x4d, 0x8a, 0x23, 0x7a, 0xef, 0xff, 0x37, 0xef, 0xf3, 0xbc, 0xa9, 0xb6, 0xae, 0xd7, 0x3a, 0x7b, 0xfd, 0x3e, 0x8e, 0x9b, 0xab, 0x44, 0x54, 0x60, 0x28, 0x6c, 0xbf, 0x15, 0x24, 0x4a, 0x56, 0x60, 0x7f, 0xa9, 0x7a, 0x28, 0x59, 0x2c, 0x8a, 0xd1, 0x7d, 0x6b, 0x00, 0xfd, 0xa5, 0xad, 0xbc, 0x19, 0x3f, 0xcb, 0x73, 0xe0, + /* (2^323)P */ 0xcf, 0x9e, 0x66, 0x06, 0x4d, 0x2b, 0xf5, 0x9c, 0xc2, 0x9d, 0x9e, 0xed, 0x5a, 0x5c, 0x2d, 0x00, 0xbf, 0x29, 0x90, 0x88, 0xe4, 0x5d, 0xfd, 0xe2, 0xf0, 0x38, 0xec, 0x4d, 0x26, 0xea, 0x54, 0xf0, 0x3c, 0x84, 0x10, 0x6a, 0xf9, 0x66, 0x9c, 0xe7, 0x21, 0xfd, 0x0f, 0xc7, 0x13, 0x50, 0x81, 0xb6, 0x50, 0xf9, 0x04, 0x7f, 0xa4, 0x37, 0x85, 0x14, + /* (2^324)P */ 0xdb, 0x87, 0x49, 0xc7, 0xa8, 0x39, 0x0c, 0x32, 0x98, 0x0c, 0xb9, 0x1a, 0x1b, 0x4d, 0xe0, 0x8a, 0x9a, 0x8e, 0x8f, 0xab, 0x5a, 0x17, 0x3d, 0x04, 0x21, 0xce, 0x3e, 0x2c, 0xf9, 0xa3, 0x97, 0xe4, 0x77, 0x95, 0x0e, 0xb6, 0xa5, 0x15, 0xad, 0x3a, 0x1e, 0x46, 0x53, 0x17, 0x09, 0x83, 0x71, 0x4e, 0x86, 0x38, 0xd5, 0x23, 0x44, 0x16, 0x8d, 0xc8, + /* (2^325)P */ 0x05, 0x5e, 0x99, 0x08, 0xbb, 0xc3, 0xc0, 0xb7, 0x6c, 0x12, 0xf2, 0xf3, 0xf4, 0x7c, 0x6a, 0x4d, 0x9e, 0xeb, 0x3d, 0xb9, 0x63, 0x94, 0xce, 0x81, 0xd8, 0x11, 0xcb, 0x55, 0x69, 0x4a, 0x20, 0x0b, 0x4c, 0x2e, 0x14, 0xb8, 0xd4, 0x6a, 0x7c, 0xf0, 0xed, 0xfc, 0x8f, 0xef, 0xa0, 0xeb, 0x6c, 0x01, 0xe2, 0xdc, 0x10, 0x22, 0xa2, 0x01, 0x85, 0x64, + /* (2^326)P */ 0x58, 0xe1, 0x9c, 0x27, 0x55, 0xc6, 0x25, 0xa6, 0x7d, 0x67, 0x88, 0x65, 0x99, 0x6c, 0xcb, 0xdb, 0x27, 0x4f, 0x44, 0x29, 0xf5, 0x4a, 0x23, 0x10, 0xbc, 0x03, 0x3f, 0x36, 0x1e, 0xef, 0xb0, 0xba, 0x75, 0xe8, 0x74, 0x5f, 0x69, 0x3e, 0x26, 0x40, 0xb4, 0x2f, 0xdc, 0x43, 0xbf, 0xa1, 0x8b, 0xbd, 0xca, 0x6e, 0xc1, 0x6e, 0x21, 0x79, 0xa0, 0xd0, + /* (2^327)P */ 0x78, 0x93, 0x4a, 0x2d, 0x22, 0x6e, 0x6e, 0x7d, 0x74, 0xd2, 0x66, 0x58, 0xce, 0x7b, 0x1d, 0x97, 0xb1, 0xf2, 0xda, 0x1c, 0x79, 0xfb, 0xba, 0xd1, 0xc0, 0xc5, 0x6e, 0xc9, 0x11, 0x89, 0xd2, 0x41, 0x8d, 0x70, 0xb9, 0xcc, 0xea, 0x6a, 0xb3, 0x45, 0xb6, 0x05, 0x2e, 0xf2, 0x17, 0xf1, 0x27, 0xb8, 0xed, 0x06, 0x1f, 0xdb, 0x9d, 0x1f, 0x69, 0x28, + /* (2^328)P */ 0x93, 0x12, 0xa8, 0x11, 0xe1, 0x92, 0x30, 0x8d, 0xac, 0xe1, 0x1c, 0x60, 0x7c, 0xed, 0x2d, 0x2e, 0xd3, 0x03, 0x5c, 0x9c, 0xc5, 0xbd, 0x64, 0x4a, 0x8c, 0xba, 0x76, 0xfe, 0xc6, 0xc1, 0xea, 0xc2, 0x4f, 0xbe, 0x70, 0x3d, 0x64, 0xcf, 0x8e, 0x18, 0xcb, 0xcd, 0x57, 0xa7, 0xf7, 0x36, 0xa9, 0x6b, 0x3e, 0xb8, 0x69, 0xee, 0x47, 0xa2, 0x7e, 0xb2, + /* (2^329)P */ 0x96, 0xaf, 0x3a, 0xf5, 0xed, 0xcd, 0xaf, 0xf7, 0x82, 0xaf, 0x59, 0x62, 0x0b, 0x36, 0x85, 0xf9, 0xaf, 0xd6, 0x38, 0xff, 0x87, 0x2e, 0x1d, 0x6c, 0x8b, 0xaf, 0x3b, 0xdf, 0x28, 0xa2, 0xd6, 0x4d, 0x80, 0x92, 0xc3, 0x0f, 0x34, 0xa8, 0xae, 0x69, 0x5d, 0x7b, 0x9d, 0xbc, 0xf5, 0xfd, 0x1d, 0xb1, 0x96, 0x55, 0x86, 0xe1, 0x5c, 0xb6, 0xac, 0xb9, + /* (2^330)P */ 0x50, 0x9e, 0x37, 0x28, 0x7d, 0xa8, 0x33, 0x63, 0xda, 0x3f, 0x20, 0x98, 0x0e, 0x09, 0xa8, 0x77, 0x3b, 0x7a, 0xfc, 0x16, 0x85, 0x44, 0x64, 0x77, 0x65, 0x68, 0x92, 0x41, 0xc6, 0x1f, 0xdf, 0x27, 0xf9, 0xec, 0xa0, 0x61, 0x22, 0xea, 0x19, 0xe7, 0x75, 0x8b, 0x4e, 0xe5, 0x0f, 0xb7, 0xf7, 0xd2, 0x53, 0xf4, 0xdd, 0x4a, 0xaa, 0x78, 0x40, 0xb7, + /* (2^331)P */ 0xd4, 0x89, 0xe3, 0x79, 0xba, 0xb6, 0xc3, 0xda, 0xe6, 0x78, 0x65, 0x7d, 0x6e, 0x22, 0x62, 0xb1, 0x3d, 0xea, 0x90, 0x84, 0x30, 0x5e, 0xd4, 0x39, 0x84, 0x78, 0xd9, 0x75, 0xd6, 0xce, 0x2a, 0x11, 0x29, 0x69, 0xa4, 0x5e, 0xaa, 0x2a, 0x98, 0x5a, 0xe5, 0x91, 0x8f, 0xb2, 0xfb, 0xda, 0x97, 0xe8, 0x83, 0x6f, 0x04, 0xb9, 0x5d, 0xaf, 0xe1, 0x9b, + /* (2^332)P */ 0x8b, 0xe4, 0xe1, 0x48, 0x9c, 0xc4, 0x83, 0x89, 0xdf, 0x65, 0xd3, 0x35, 0x55, 0x13, 0xf4, 0x1f, 0x36, 0x92, 0x33, 0x38, 0xcb, 0xed, 0x15, 0xe6, 0x60, 0x2d, 0x25, 0xf5, 0x36, 0x60, 0x3a, 0x37, 0x9b, 0x71, 0x9d, 0x42, 0xb0, 0x14, 0xc8, 0xba, 0x62, 0xa3, 0x49, 0xb0, 0x88, 0xc1, 0x72, 0x73, 0xdd, 0x62, 0x40, 0xa9, 0x62, 0x88, 0x99, 0xca, + /* (2^333)P */ 0x47, 0x7b, 0xea, 0xda, 0x46, 0x2f, 0x45, 0xc6, 0xe3, 0xb4, 0x4d, 0x8d, 0xac, 0x0b, 0x54, 0x22, 0x06, 0x31, 0x16, 0x66, 0x3e, 0xe4, 0x38, 0x12, 0xcd, 0xf3, 0xe7, 0x99, 0x37, 0xd9, 0x62, 0x24, 0x4b, 0x05, 0xf2, 0x58, 0xe6, 0x29, 0x4b, 0x0d, 0xf6, 0xc1, 0xba, 0xa0, 0x1e, 0x0f, 0xcb, 0x1f, 0xc6, 0x2b, 0x19, 0xfc, 0x82, 0x01, 0xd0, 0x86, + /* (2^334)P */ 0xa2, 0xae, 0x77, 0x20, 0xfb, 0xa8, 0x18, 0xb4, 0x61, 0xef, 0xe8, 0x52, 0x79, 0xbb, 0x86, 0x90, 0x5d, 0x2e, 0x76, 0xed, 0x66, 0x60, 0x5d, 0x00, 0xb5, 0xa4, 0x00, 0x40, 0x89, 0xec, 0xd1, 0xd2, 0x0d, 0x26, 0xb9, 0x30, 0xb2, 0xd2, 0xb8, 0xe8, 0x0e, 0x56, 0xf9, 0x67, 0x94, 0x2e, 0x62, 0xe1, 0x79, 0x48, 0x2b, 0xa9, 0xfa, 0xea, 0xdb, 0x28, + /* (2^335)P */ 0x35, 0xf1, 0xb0, 0x43, 0xbd, 0x27, 0xef, 0x18, 0x44, 0xa2, 0x04, 0xb4, 0x69, 0xa1, 0x97, 0x1f, 0x8c, 0x04, 0x82, 0x9b, 0x00, 0x6d, 0xf8, 0xbf, 0x7d, 0xc1, 0x5b, 0xab, 0xe8, 0xb2, 0x34, 0xbd, 0xaf, 0x7f, 0xb2, 0x0d, 0xf3, 0xed, 0xfc, 0x5b, 0x50, 0xee, 0xe7, 0x4a, 0x20, 0xd9, 0xf5, 0xc6, 0x9a, 0x97, 0x6d, 0x07, 0x2f, 0xb9, 0x31, 0x02, + /* (2^336)P */ 0xf9, 0x54, 0x4a, 0xc5, 0x61, 0x7e, 0x1d, 0xa6, 0x0e, 0x1a, 0xa8, 0xd3, 0x8c, 0x36, 0x7d, 0xf1, 0x06, 0xb1, 0xac, 0x93, 0xcd, 0xe9, 0x8f, 0x61, 0x6c, 0x5d, 0x03, 0x23, 0xdf, 0x85, 0x53, 0x39, 0x63, 0x5e, 0xeb, 0xf3, 0xd3, 0xd3, 0x75, 0x97, 0x9b, 0x62, 0x9b, 0x01, 0xb3, 0x19, 0xd8, 0x2b, 0x36, 0xf2, 0x2c, 0x2c, 0x6f, 0x36, 0xc6, 0x3c, + /* (2^337)P */ 0x05, 0x74, 0x43, 0x10, 0xb6, 0xb0, 0xf8, 0xbf, 0x02, 0x46, 0x9a, 0xee, 0xc1, 0xaf, 0xc1, 0xe5, 0x5a, 0x2e, 0xbb, 0xe1, 0xdc, 0xc6, 0xce, 0x51, 0x29, 0x50, 0xbf, 0x1b, 0xde, 0xff, 0xba, 0x4d, 0x8d, 0x8b, 0x7e, 0xe7, 0xbd, 0x5b, 0x8f, 0xbe, 0xe3, 0x75, 0x71, 0xff, 0x37, 0x05, 0x5a, 0x10, 0xeb, 0x54, 0x7e, 0x44, 0x72, 0x2c, 0xd4, 0xfc, + /* (2^338)P */ 0x03, 0x12, 0x1c, 0xb2, 0x08, 0x90, 0xa1, 0x2d, 0x50, 0xa0, 0xad, 0x7f, 0x8d, 0xa6, 0x97, 0xc1, 0xbd, 0xdc, 0xc3, 0xa7, 0xad, 0x31, 0xdf, 0xb8, 0x03, 0x84, 0xc3, 0xb9, 0x29, 0x3d, 0x92, 0x2e, 0xc3, 0x90, 0x07, 0xe8, 0xa7, 0xc7, 0xbc, 0x61, 0xe9, 0x3e, 0xa0, 0x35, 0xda, 0x1d, 0xab, 0x48, 0xfe, 0x50, 0xc9, 0x25, 0x59, 0x23, 0x69, 0x3f, + /* (2^339)P */ 0x8e, 0x91, 0xab, 0x6b, 0x91, 0x4f, 0x89, 0x76, 0x67, 0xad, 0xb2, 0x65, 0x9d, 0xad, 0x02, 0x36, 0xdc, 0xac, 0x96, 0x93, 0x97, 0x21, 0x14, 0xd0, 0xe8, 0x11, 0x60, 0x1e, 0xeb, 0x96, 0x06, 0xf2, 0x53, 0xf2, 0x6d, 0xb7, 0x93, 0x6f, 0x26, 0x91, 0x23, 0xe3, 0x34, 0x04, 0x92, 0x91, 0x37, 0x08, 0x50, 0xd6, 0x28, 0x09, 0x27, 0xa1, 0x0c, 0x00, + /* (2^340)P */ 0x1f, 0xbb, 0x21, 0x26, 0x33, 0xcb, 0xa4, 0xd1, 0xee, 0x85, 0xf9, 0xd9, 0x3c, 0x90, 0xc3, 0xd1, 0x26, 0xa2, 0x25, 0x93, 0x43, 0x61, 0xed, 0x91, 0x6e, 0x54, 0x03, 0x2e, 0x42, 0x9d, 0xf7, 0xa6, 0x02, 0x0f, 0x2f, 0x9c, 0x7a, 0x8d, 0x12, 0xc2, 0x18, 0xfc, 0x41, 0xff, 0x85, 0x26, 0x1a, 0x44, 0x55, 0x0b, 0x89, 0xab, 0x6f, 0x62, 0x33, 0x8c, + /* (2^341)P */ 0xe0, 0x3c, 0x5d, 0x70, 0x64, 0x87, 0x81, 0x35, 0xf2, 0x37, 0xa6, 0x24, 0x3e, 0xe0, 0x62, 0xd5, 0x71, 0xe7, 0x93, 0xfb, 0xac, 0xc3, 0xe7, 0xc7, 0x04, 0xe2, 0x70, 0xd3, 0x29, 0x5b, 0x21, 0xbf, 0xf4, 0x26, 0x5d, 0xf3, 0x95, 0xb4, 0x2a, 0x6a, 0x07, 0x55, 0xa6, 0x4b, 0x3b, 0x15, 0xf2, 0x25, 0x8a, 0x95, 0x3f, 0x63, 0x2f, 0x7a, 0x23, 0x96, + /* (2^342)P */ 0x0d, 0x3d, 0xd9, 0x13, 0xa7, 0xb3, 0x5e, 0x67, 0xf7, 0x02, 0x23, 0xee, 0x84, 0xff, 0x99, 0xda, 0xb9, 0x53, 0xf8, 0xf0, 0x0e, 0x39, 0x2f, 0x3c, 0x64, 0x34, 0xe3, 0x09, 0xfd, 0x2b, 0x33, 0xc7, 0xfe, 0x62, 0x2b, 0x84, 0xdf, 0x2b, 0xd2, 0x7c, 0x26, 0x01, 0x70, 0x66, 0x5b, 0x85, 0xc2, 0xbe, 0x88, 0x37, 0xf1, 0x30, 0xac, 0xb8, 0x76, 0xa3, + /* (2^343)P */ 0x6e, 0x01, 0xf0, 0x55, 0x35, 0xe4, 0xbd, 0x43, 0x62, 0x9d, 0xd6, 0x11, 0xef, 0x6f, 0xb8, 0x8c, 0xaa, 0x98, 0x87, 0xc6, 0x6d, 0xc4, 0xcc, 0x74, 0x92, 0x53, 0x4a, 0xdf, 0xe4, 0x08, 0x89, 0x17, 0xd0, 0x0f, 0xf4, 0x00, 0x60, 0x78, 0x08, 0x44, 0xb5, 0xda, 0x18, 0xed, 0x98, 0xc8, 0x61, 0x3d, 0x39, 0xdb, 0xcf, 0x1d, 0x49, 0x40, 0x65, 0x75, + /* (2^344)P */ 0x8e, 0x10, 0xae, 0x5f, 0x06, 0xd2, 0x95, 0xfd, 0x20, 0x16, 0x49, 0x5b, 0x57, 0xbe, 0x22, 0x8b, 0x43, 0xfb, 0xe6, 0xcc, 0x26, 0xa5, 0x5d, 0xd3, 0x68, 0xc5, 0xf9, 0x5a, 0x86, 0x24, 0x87, 0x27, 0x05, 0xfd, 0xe2, 0xff, 0xb3, 0xa3, 0x7b, 0x37, 0x59, 0xc5, 0x4e, 0x14, 0x94, 0xf9, 0x3b, 0xcb, 0x7c, 0xed, 0xca, 0x1d, 0xb2, 0xac, 0x05, 0x4a, + /* (2^345)P */ 0xf4, 0xd1, 0x81, 0xeb, 0x89, 0xbf, 0xfe, 0x1e, 0x41, 0x92, 0x29, 0xee, 0xe1, 0x43, 0xf5, 0x86, 0x1d, 0x2f, 0xbb, 0x1e, 0x84, 0x5d, 0x7b, 0x8d, 0xd5, 0xda, 0xee, 0x1e, 0x8a, 0xd0, 0x27, 0xf2, 0x60, 0x51, 0x59, 0x82, 0xf4, 0x84, 0x2b, 0x5b, 0x14, 0x2d, 0x81, 0x82, 0x3e, 0x2b, 0xb4, 0x6d, 0x51, 0x4f, 0xc5, 0xcb, 0xbf, 0x74, 0xe3, 0xb4, + /* (2^346)P */ 0x19, 0x2f, 0x22, 0xb3, 0x04, 0x5f, 0x81, 0xca, 0x05, 0x60, 0xb9, 0xaa, 0xee, 0x0e, 0x2f, 0x48, 0x38, 0xf9, 0x91, 0xb4, 0x66, 0xe4, 0x57, 0x28, 0x54, 0x10, 0xe9, 0x61, 0x9d, 0xd4, 0x90, 0x75, 0xb1, 0x39, 0x23, 0xb6, 0xfc, 0x82, 0xe0, 0xfa, 0xbb, 0x5c, 0x6e, 0xc3, 0x44, 0x13, 0x00, 0x83, 0x55, 0x9e, 0x8e, 0x10, 0x61, 0x81, 0x91, 0x04, + /* (2^347)P */ 0x5f, 0x2a, 0xd7, 0x81, 0xd9, 0x9c, 0xbb, 0x79, 0xbc, 0x62, 0x56, 0x98, 0x03, 0x5a, 0x18, 0x85, 0x2a, 0x9c, 0xd0, 0xfb, 0xd2, 0xb1, 0xaf, 0xef, 0x0d, 0x24, 0xc5, 0xfa, 0x39, 0xbb, 0x6b, 0xed, 0xa4, 0xdf, 0xe4, 0x87, 0xcd, 0x41, 0xd3, 0x72, 0x32, 0xc6, 0x28, 0x21, 0xb1, 0xba, 0x8b, 0xa3, 0x91, 0x79, 0x76, 0x22, 0x25, 0x10, 0x61, 0xd1, + /* (2^348)P */ 0x73, 0xb5, 0x32, 0x97, 0xdd, 0xeb, 0xdd, 0x22, 0x22, 0xf1, 0x33, 0x3c, 0x77, 0x56, 0x7d, 0x6b, 0x48, 0x2b, 0x05, 0x81, 0x03, 0x03, 0x91, 0x9a, 0xe3, 0x5e, 0xd4, 0xee, 0x3f, 0xf8, 0xbb, 0x50, 0x21, 0x32, 0x4c, 0x4a, 0x58, 0x49, 0xde, 0x0c, 0xde, 0x30, 0x82, 0x3d, 0x92, 0xf0, 0x6c, 0xcc, 0x32, 0x3e, 0xd2, 0x78, 0x8a, 0x6e, 0x2c, 0xd0, + /* (2^349)P */ 0xf0, 0xf7, 0xa1, 0x0b, 0xc1, 0x74, 0x85, 0xa8, 0xe9, 0xdd, 0x48, 0xa1, 0xc0, 0x16, 0xd8, 0x2b, 0x61, 0x08, 0xc2, 0x2b, 0x30, 0x26, 0x79, 0xce, 0x9e, 0xfd, 0x39, 0xd7, 0x81, 0xa4, 0x63, 0x8c, 0xd5, 0x74, 0xa0, 0x88, 0xfa, 0x03, 0x30, 0xe9, 0x7f, 0x2b, 0xc6, 0x02, 0xc9, 0x5e, 0xe4, 0xd5, 0x4d, 0x92, 0xd0, 0xf6, 0xf2, 0x5b, 0x79, 0x08, + /* (2^350)P */ 0x34, 0x89, 0x81, 0x43, 0xd1, 0x94, 0x2c, 0x10, 0x54, 0x9b, 0xa0, 0xe5, 0x44, 0xe8, 0xc2, 0x2f, 0x3e, 0x0e, 0x74, 0xae, 0xba, 0xe2, 0xac, 0x85, 0x6b, 0xd3, 0x5c, 0x97, 0xf7, 0x90, 0xf1, 0x12, 0xc0, 0x03, 0xc8, 0x1f, 0x37, 0x72, 0x8c, 0x9b, 0x9c, 0x17, 0x96, 0x9d, 0xc7, 0xbf, 0xa3, 0x3f, 0x44, 0x3d, 0x87, 0x81, 0xbd, 0x81, 0xa6, 0x5f, + /* (2^351)P */ 0xe4, 0xff, 0x78, 0x62, 0x82, 0x5b, 0x76, 0x58, 0xf5, 0x5b, 0xa6, 0xc4, 0x53, 0x11, 0x3b, 0x7b, 0xaa, 0x67, 0xf8, 0xea, 0x3b, 0x5d, 0x9a, 0x2e, 0x04, 0xeb, 0x4a, 0x24, 0xfb, 0x56, 0xf0, 0xa8, 0xd4, 0x14, 0xed, 0x0f, 0xfd, 0xc5, 0x26, 0x17, 0x2a, 0xf0, 0xb9, 0x13, 0x8c, 0xbd, 0x65, 0x14, 0x24, 0x95, 0x27, 0x12, 0x63, 0x2a, 0x09, 0x18, + /* (2^352)P */ 0xe1, 0x5c, 0xe7, 0xe0, 0x00, 0x6a, 0x96, 0xf2, 0x49, 0x6a, 0x39, 0xa5, 0xe0, 0x17, 0x79, 0x4a, 0x63, 0x07, 0x62, 0x09, 0x61, 0x1b, 0x6e, 0xa9, 0xb5, 0x62, 0xb7, 0xde, 0xdf, 0x80, 0x4c, 0x5a, 0x99, 0x73, 0x59, 0x9d, 0xfb, 0xb1, 0x5e, 0xbe, 0xb8, 0xb7, 0x63, 0x93, 0xe8, 0xad, 0x5e, 0x1f, 0xae, 0x59, 0x1c, 0xcd, 0xb4, 0xc2, 0xb3, 0x8a, + /* (2^353)P */ 0x78, 0x53, 0xa1, 0x4c, 0x70, 0x9c, 0x63, 0x7e, 0xb3, 0x12, 0x40, 0x5f, 0xbb, 0x23, 0xa7, 0xf7, 0x77, 0x96, 0x5b, 0x4d, 0x91, 0x10, 0x52, 0x85, 0x9e, 0xa5, 0x38, 0x0b, 0xfd, 0x25, 0x01, 0x4b, 0xfa, 0x4d, 0xd3, 0x3f, 0x78, 0x74, 0x42, 0xff, 0x62, 0x2d, 0x27, 0xdc, 0x9d, 0xd1, 0x29, 0x76, 0x2e, 0x78, 0xb3, 0x35, 0xfa, 0x15, 0xd5, 0x38, + /* (2^354)P */ 0x8b, 0xc7, 0x43, 0xce, 0xf0, 0x5e, 0xf1, 0x0d, 0x02, 0x38, 0xe8, 0x82, 0xc9, 0x25, 0xad, 0x2d, 0x27, 0xa4, 0x54, 0x18, 0xb2, 0x30, 0x73, 0xa4, 0x41, 0x08, 0xe4, 0x86, 0xe6, 0x8c, 0xe9, 0x2a, 0x34, 0xb3, 0xd6, 0x61, 0x8f, 0x66, 0x26, 0x08, 0xb6, 0x06, 0x33, 0xaa, 0x12, 0xac, 0x72, 0xec, 0x2e, 0x52, 0xa3, 0x25, 0x3e, 0xd7, 0x62, 0xe8, + /* (2^355)P */ 0xc4, 0xbb, 0x89, 0xc8, 0x40, 0xcc, 0x84, 0xec, 0x4a, 0xd9, 0xc4, 0x55, 0x78, 0x00, 0xcf, 0xd8, 0xe9, 0x24, 0x59, 0xdc, 0x5e, 0xf0, 0x66, 0xa1, 0x83, 0xae, 0x97, 0x18, 0xc5, 0x54, 0x27, 0xa2, 0x21, 0x52, 0x03, 0x31, 0x5b, 0x11, 0x67, 0xf6, 0x12, 0x00, 0x87, 0x2f, 0xff, 0x59, 0x70, 0x8f, 0x6d, 0x71, 0xab, 0xab, 0x24, 0xb8, 0xba, 0x35, + /* (2^356)P */ 0x69, 0x43, 0xa7, 0x14, 0x06, 0x96, 0xe9, 0xc2, 0xe3, 0x2b, 0x45, 0x22, 0xc0, 0xd0, 0x2f, 0x34, 0xd1, 0x01, 0x99, 0xfc, 0x99, 0x38, 0xa1, 0x25, 0x2e, 0x59, 0x6c, 0x27, 0xc9, 0xeb, 0x7b, 0xdc, 0x4e, 0x26, 0x68, 0xba, 0xfa, 0xec, 0x02, 0x05, 0x64, 0x80, 0x30, 0x20, 0x5c, 0x26, 0x7f, 0xaf, 0x95, 0x17, 0x3d, 0x5c, 0x9e, 0x96, 0x96, 0xaf, + /* (2^357)P */ 0xa6, 0xba, 0x21, 0x29, 0x32, 0xe2, 0x98, 0xde, 0x9b, 0x6d, 0x0b, 0x44, 0x91, 0xa8, 0x3e, 0xd4, 0xb8, 0x04, 0x6c, 0xf6, 0x04, 0x39, 0xbd, 0x52, 0x05, 0x15, 0x27, 0x78, 0x8e, 0x55, 0xac, 0x79, 0xc5, 0xe6, 0x00, 0x7f, 0x90, 0xa2, 0xdd, 0x07, 0x13, 0xe0, 0x24, 0x70, 0x5c, 0x0f, 0x4d, 0xa9, 0xf9, 0xae, 0xcb, 0x34, 0x10, 0x9d, 0x89, 0x9d, + /* (2^358)P */ 0x12, 0xe0, 0xb3, 0x9f, 0xc4, 0x96, 0x1d, 0xcf, 0xed, 0x99, 0x64, 0x28, 0x8d, 0xc7, 0x31, 0x82, 0xee, 0x5e, 0x75, 0x48, 0xff, 0x3a, 0xf2, 0x09, 0x34, 0x03, 0x93, 0x52, 0x19, 0xb2, 0xc5, 0x81, 0x93, 0x45, 0x5e, 0x59, 0x21, 0x2b, 0xec, 0x89, 0xba, 0x36, 0x6e, 0xf9, 0x82, 0x75, 0x7e, 0x82, 0x3f, 0xaa, 0xe2, 0xe3, 0x3b, 0x94, 0xfd, 0x98, + /* (2^359)P */ 0x7c, 0xdb, 0x75, 0x31, 0x61, 0xfb, 0x15, 0x28, 0x94, 0xd7, 0xc3, 0x5a, 0xa9, 0xa1, 0x0a, 0x66, 0x0f, 0x2b, 0x13, 0x3e, 0x42, 0xb5, 0x28, 0x3a, 0xca, 0x83, 0xf3, 0x61, 0x22, 0xf4, 0x40, 0xc5, 0xdf, 0xe7, 0x31, 0x9f, 0x7e, 0x51, 0x75, 0x06, 0x9d, 0x51, 0xc8, 0xe7, 0x9f, 0xc3, 0x71, 0x4f, 0x3d, 0x5b, 0xfb, 0xe9, 0x8e, 0x08, 0x40, 0x8e, + /* (2^360)P */ 0xf7, 0x31, 0xad, 0x50, 0x5d, 0x25, 0x93, 0x73, 0x68, 0xf6, 0x7c, 0x89, 0x5a, 0x3d, 0x9f, 0x9b, 0x05, 0x82, 0xe7, 0x70, 0x4b, 0x19, 0xaa, 0xcf, 0xff, 0xde, 0x50, 0x8f, 0x2f, 0x69, 0xd3, 0xf0, 0x99, 0x51, 0x6b, 0x9d, 0xb6, 0x56, 0x6f, 0xf8, 0x4c, 0x74, 0x8b, 0x4c, 0x91, 0xf9, 0xa9, 0xb1, 0x3e, 0x07, 0xdf, 0x0b, 0x27, 0x8a, 0xb1, 0xed, + /* (2^361)P */ 0xfb, 0x67, 0xd9, 0x48, 0xd2, 0xe4, 0x44, 0x9b, 0x43, 0x15, 0x8a, 0xeb, 0x00, 0x53, 0xad, 0x25, 0xc7, 0x7e, 0x19, 0x30, 0x87, 0xb7, 0xd5, 0x5f, 0x04, 0xf8, 0xaa, 0xdd, 0x57, 0xae, 0x34, 0x75, 0xe2, 0x84, 0x4b, 0x54, 0x60, 0x37, 0x95, 0xe4, 0xd3, 0xec, 0xac, 0xef, 0x47, 0x31, 0xa3, 0xc8, 0x31, 0x22, 0xdb, 0x26, 0xe7, 0x6a, 0xb5, 0xad, + /* (2^362)P */ 0x44, 0x09, 0x5c, 0x95, 0xe4, 0x72, 0x3c, 0x1a, 0xd1, 0xac, 0x42, 0x51, 0x99, 0x6f, 0xfa, 0x1f, 0xf2, 0x22, 0xbe, 0xff, 0x7b, 0x66, 0xf5, 0x6c, 0xb3, 0x66, 0xc7, 0x4d, 0x78, 0x31, 0x83, 0x80, 0xf5, 0x41, 0xe9, 0x7f, 0xbe, 0xf7, 0x23, 0x49, 0x6b, 0x84, 0x4e, 0x7e, 0x47, 0x07, 0x6e, 0x74, 0xdf, 0xe5, 0x9d, 0x9e, 0x56, 0x2a, 0xc0, 0xbc, + /* (2^363)P */ 0xac, 0x10, 0x80, 0x8c, 0x7c, 0xfa, 0x83, 0xdf, 0xb3, 0xd0, 0xc4, 0xbe, 0xfb, 0x9f, 0xac, 0xc9, 0xc3, 0x40, 0x95, 0x0b, 0x09, 0x23, 0xda, 0x63, 0x67, 0xcf, 0xe7, 0x9f, 0x7d, 0x7b, 0x6b, 0xe2, 0xe6, 0x6d, 0xdb, 0x87, 0x9e, 0xa6, 0xff, 0x6d, 0xab, 0xbd, 0xfb, 0x54, 0x84, 0x68, 0xcf, 0x89, 0xf1, 0xd0, 0xe2, 0x85, 0x61, 0xdc, 0x22, 0xd1, + /* (2^364)P */ 0xa8, 0x48, 0xfb, 0x8c, 0x6a, 0x63, 0x01, 0x72, 0x43, 0x43, 0xeb, 0x21, 0xa3, 0x00, 0x8a, 0xc0, 0x87, 0x51, 0x9e, 0x86, 0x75, 0x16, 0x79, 0xf9, 0x6b, 0x11, 0x80, 0x62, 0xc2, 0x9d, 0xb8, 0x8c, 0x30, 0x8e, 0x8d, 0x03, 0x52, 0x7e, 0x31, 0x59, 0x38, 0xf9, 0x25, 0xc7, 0x0f, 0xc7, 0xa8, 0x2b, 0x5c, 0x80, 0xfa, 0x90, 0xa2, 0x63, 0xca, 0xe7, + /* (2^365)P */ 0xf1, 0x5d, 0xb5, 0xd9, 0x20, 0x10, 0x7d, 0x0f, 0xc5, 0x50, 0x46, 0x07, 0xff, 0x02, 0x75, 0x2b, 0x4a, 0xf3, 0x39, 0x91, 0x72, 0xb7, 0xd5, 0xcc, 0x38, 0xb8, 0xe7, 0x36, 0x26, 0x5e, 0x11, 0x97, 0x25, 0xfb, 0x49, 0x68, 0xdc, 0xb4, 0x46, 0x87, 0x5c, 0xc2, 0x7f, 0xaa, 0x7d, 0x36, 0x23, 0xa6, 0xc6, 0x53, 0xec, 0xbc, 0x57, 0x47, 0xc1, 0x2b, + /* (2^366)P */ 0x25, 0x5d, 0x7d, 0x95, 0xda, 0x0b, 0x8f, 0x78, 0x1e, 0x19, 0x09, 0xfa, 0x67, 0xe0, 0xa0, 0x17, 0x24, 0x76, 0x6c, 0x30, 0x1f, 0x62, 0x3d, 0xbe, 0x45, 0x70, 0xcc, 0xb6, 0x1e, 0x68, 0x06, 0x25, 0x68, 0x16, 0x1a, 0x33, 0x3f, 0x90, 0xc7, 0x78, 0x2d, 0x98, 0x3c, 0x2f, 0xb9, 0x2d, 0x94, 0x0b, 0xfb, 0x49, 0x56, 0x30, 0xd7, 0xc1, 0xe6, 0x48, + /* (2^367)P */ 0x7a, 0xd1, 0xe0, 0x8e, 0x67, 0xfc, 0x0b, 0x50, 0x1f, 0x84, 0x98, 0xfa, 0xaf, 0xae, 0x2e, 0x31, 0x27, 0xcf, 0x3f, 0xf2, 0x6e, 0x8d, 0x81, 0x8f, 0xd2, 0x5f, 0xde, 0xd3, 0x5e, 0xe9, 0xe7, 0x13, 0x48, 0x83, 0x5a, 0x4e, 0x84, 0xd1, 0x58, 0xcf, 0x6b, 0x84, 0xdf, 0x13, 0x1d, 0x91, 0x85, 0xe8, 0xcb, 0x29, 0x79, 0xd2, 0xca, 0xac, 0x6a, 0x93, + /* (2^368)P */ 0x53, 0x82, 0xce, 0x61, 0x96, 0x88, 0x6f, 0xe1, 0x4a, 0x4c, 0x1e, 0x30, 0x73, 0xe8, 0x74, 0xde, 0x40, 0x2b, 0xe0, 0xc4, 0xb5, 0xd8, 0x7c, 0x15, 0xe7, 0xe1, 0xb1, 0xe0, 0xd6, 0x88, 0xb1, 0x6a, 0x57, 0x19, 0x6a, 0x22, 0x66, 0x57, 0xf6, 0x8d, 0xfd, 0xc0, 0xf2, 0xa3, 0x03, 0x56, 0xfb, 0x2e, 0x75, 0x5e, 0xc7, 0x8e, 0x22, 0x96, 0x5c, 0x06, + /* (2^369)P */ 0x98, 0x7e, 0xbf, 0x3e, 0xbf, 0x24, 0x9d, 0x15, 0xd3, 0xf6, 0xd3, 0xd2, 0xf0, 0x11, 0xf2, 0xdb, 0x36, 0x23, 0x38, 0xf7, 0x1d, 0x71, 0x20, 0xd2, 0x54, 0x7f, 0x1e, 0x24, 0x8f, 0xe2, 0xaa, 0xf7, 0x3f, 0x6b, 0x41, 0x4e, 0xdc, 0x0e, 0xec, 0xe8, 0x35, 0x0a, 0x08, 0x6d, 0x89, 0x5b, 0x32, 0x91, 0x01, 0xb6, 0xe0, 0x2c, 0xc6, 0xa1, 0xbe, 0xb4, + /* (2^370)P */ 0x29, 0xf2, 0x1e, 0x1c, 0xdc, 0x68, 0x8a, 0x43, 0x87, 0x2c, 0x48, 0xb3, 0x9e, 0xed, 0xd2, 0x82, 0x46, 0xac, 0x2f, 0xef, 0x93, 0x34, 0x37, 0xca, 0x64, 0x8d, 0xc9, 0x06, 0x90, 0xbb, 0x78, 0x0a, 0x3c, 0x4c, 0xcf, 0x35, 0x7a, 0x0f, 0xf7, 0xa7, 0xf4, 0x2f, 0x45, 0x69, 0x3f, 0xa9, 0x5d, 0xce, 0x7b, 0x8a, 0x84, 0xc3, 0xae, 0xf4, 0xda, 0xd5, + /* (2^371)P */ 0xca, 0xba, 0x95, 0x43, 0x05, 0x7b, 0x06, 0xd9, 0x5c, 0x0a, 0x18, 0x5f, 0x6a, 0x6a, 0xce, 0xc0, 0x3d, 0x95, 0x51, 0x0e, 0x1a, 0xbe, 0x85, 0x7a, 0xf2, 0x69, 0xec, 0xc0, 0x8c, 0xca, 0xa3, 0x32, 0x0a, 0x76, 0x50, 0xc6, 0x76, 0x61, 0x00, 0x89, 0xbf, 0x6e, 0x0f, 0x48, 0x90, 0x31, 0x93, 0xec, 0x34, 0x70, 0xf0, 0xc3, 0x8d, 0xf0, 0x0f, 0xb5, + /* (2^372)P */ 0xbe, 0x23, 0xe2, 0x18, 0x99, 0xf1, 0xed, 0x8a, 0xf6, 0xc9, 0xac, 0xb8, 0x1e, 0x9a, 0x3c, 0x15, 0xae, 0xd7, 0x6d, 0xb3, 0x04, 0xee, 0x5b, 0x0d, 0x1e, 0x79, 0xb7, 0xf9, 0xf9, 0x8d, 0xad, 0xf9, 0x8f, 0x5a, 0x6a, 0x7b, 0xd7, 0x9b, 0xca, 0x62, 0xfe, 0x9c, 0xc0, 0x6f, 0x6d, 0x9d, 0x76, 0xa3, 0x69, 0xb9, 0x4c, 0xa1, 0xc4, 0x0c, 0x76, 0xaa, + /* (2^373)P */ 0x1c, 0x06, 0xfe, 0x3f, 0x45, 0x70, 0xcd, 0x97, 0xa9, 0xa2, 0xb1, 0xd3, 0xf2, 0xa5, 0x0c, 0x49, 0x2c, 0x75, 0x73, 0x1f, 0xcf, 0x00, 0xaf, 0xd5, 0x2e, 0xde, 0x0d, 0x8f, 0x8f, 0x7c, 0xc4, 0x58, 0xce, 0xd4, 0xf6, 0x24, 0x19, 0x2e, 0xd8, 0xc5, 0x1d, 0x1a, 0x3f, 0xb8, 0x4f, 0xbc, 0x7d, 0xbd, 0x68, 0xe3, 0x81, 0x98, 0x1b, 0xa8, 0xc9, 0xd9, + /* (2^374)P */ 0x39, 0x95, 0x78, 0x24, 0x6c, 0x38, 0xe4, 0xe7, 0xd0, 0x8d, 0xb9, 0x38, 0x71, 0x5e, 0xc1, 0x62, 0x80, 0xcc, 0xcb, 0x8c, 0x97, 0xca, 0xf8, 0xb9, 0xd9, 0x9c, 0xce, 0x72, 0x7b, 0x70, 0xee, 0x5f, 0xea, 0xa2, 0xdf, 0xa9, 0x14, 0x10, 0xf9, 0x6e, 0x59, 0x9f, 0x9c, 0xe0, 0x0c, 0xb2, 0x07, 0x97, 0xcd, 0xd2, 0x89, 0x16, 0xfd, 0x9c, 0xa8, 0xa5, + /* (2^375)P */ 0x5a, 0x61, 0xf1, 0x59, 0x7c, 0x38, 0xda, 0xe2, 0x85, 0x99, 0x68, 0xe9, 0xc9, 0xf7, 0x32, 0x7e, 0xc4, 0xca, 0xb7, 0x11, 0x08, 0x69, 0x2b, 0x66, 0x02, 0xf7, 0x2e, 0x18, 0xc3, 0x8e, 0xe1, 0xf9, 0xc5, 0x19, 0x9a, 0x0a, 0x9c, 0x07, 0xba, 0xc7, 0x9c, 0x03, 0x34, 0x89, 0x99, 0x67, 0x0b, 0x16, 0x4b, 0x07, 0x36, 0x16, 0x36, 0x2c, 0xe2, 0xa1, + /* (2^376)P */ 0x70, 0x10, 0x91, 0x27, 0xa8, 0x24, 0x8e, 0x29, 0x04, 0x6f, 0x79, 0x1f, 0xd3, 0xa5, 0x68, 0xd3, 0x0b, 0x7d, 0x56, 0x4d, 0x14, 0x57, 0x7b, 0x2e, 0x00, 0x9f, 0x9a, 0xfd, 0x6c, 0x63, 0x18, 0x81, 0xdb, 0x9d, 0xb7, 0xd7, 0xa4, 0x1e, 0xe8, 0x40, 0xf1, 0x4c, 0xa3, 0x01, 0xd5, 0x4b, 0x75, 0xea, 0xdd, 0x97, 0xfd, 0x5b, 0xb2, 0x66, 0x6a, 0x24, + /* (2^377)P */ 0x72, 0x11, 0xfe, 0x73, 0x1b, 0xd3, 0xea, 0x7f, 0x93, 0x15, 0x15, 0x05, 0xfe, 0x40, 0xe8, 0x28, 0xd8, 0x50, 0x47, 0x66, 0xfa, 0xb7, 0xb5, 0x04, 0xba, 0x35, 0x1e, 0x32, 0x9f, 0x5f, 0x32, 0xba, 0x3d, 0xd1, 0xed, 0x9a, 0x76, 0xca, 0xa3, 0x3e, 0x77, 0xd8, 0xd8, 0x7c, 0x5f, 0x68, 0x42, 0xb5, 0x86, 0x7f, 0x3b, 0xc9, 0xc1, 0x89, 0x64, 0xda, + /* (2^378)P */ 0xd5, 0xd4, 0x17, 0x31, 0xfc, 0x6a, 0xfd, 0xb8, 0xe8, 0xe5, 0x3e, 0x39, 0x06, 0xe4, 0xd1, 0x90, 0x2a, 0xca, 0xf6, 0x54, 0x6c, 0x1b, 0x2f, 0x49, 0x97, 0xb1, 0x2a, 0x82, 0x43, 0x3d, 0x1f, 0x8b, 0xe2, 0x47, 0xc5, 0x24, 0xa8, 0xd5, 0x53, 0x29, 0x7d, 0xc6, 0x87, 0xa6, 0x25, 0x3a, 0x64, 0xdd, 0x71, 0x08, 0x9e, 0xcd, 0xe9, 0x45, 0xc7, 0xba, + /* (2^379)P */ 0x37, 0x72, 0x6d, 0x13, 0x7a, 0x8d, 0x04, 0x31, 0xe6, 0xe3, 0x9e, 0x36, 0x71, 0x3e, 0xc0, 0x1e, 0xe3, 0x71, 0xd3, 0x49, 0x4e, 0x4a, 0x36, 0x42, 0x68, 0x68, 0x61, 0xc7, 0x3c, 0xdb, 0x81, 0x49, 0xf7, 0x91, 0x4d, 0xea, 0x4c, 0x4f, 0x98, 0xc6, 0x7e, 0x60, 0x84, 0x4b, 0x6a, 0x37, 0xbb, 0x52, 0xf7, 0xce, 0x02, 0xe4, 0xad, 0xd1, 0x3c, 0xa7, + /* (2^380)P */ 0x51, 0x06, 0x2d, 0xf8, 0x08, 0xe8, 0xf1, 0x0c, 0xe5, 0xa9, 0xac, 0x29, 0x73, 0x3b, 0xed, 0x98, 0x5f, 0x55, 0x08, 0x38, 0x51, 0x44, 0x36, 0x5d, 0xea, 0xc3, 0xb8, 0x0e, 0xa0, 0x4f, 0xd2, 0x79, 0xe9, 0x98, 0xc3, 0xf5, 0x00, 0xb9, 0x26, 0x27, 0x42, 0xa8, 0x07, 0xc1, 0x12, 0x31, 0xc1, 0xc3, 0x3c, 0x3b, 0x7a, 0x72, 0x97, 0xc2, 0x70, 0x3a, + /* (2^381)P */ 0xf4, 0xb2, 0xba, 0x32, 0xbc, 0xa9, 0x2f, 0x87, 0xc7, 0x3c, 0x45, 0xcd, 0xae, 0xe2, 0x13, 0x6d, 0x3a, 0xf2, 0xf5, 0x66, 0x97, 0x29, 0xaf, 0x53, 0x9f, 0xda, 0xea, 0x14, 0xdf, 0x04, 0x98, 0x19, 0x95, 0x9e, 0x2a, 0x00, 0x5c, 0x9d, 0x1d, 0xf0, 0x39, 0x23, 0xff, 0xfc, 0xca, 0x36, 0xb7, 0xde, 0xdf, 0x37, 0x78, 0x52, 0x21, 0xfa, 0x19, 0x10, + /* (2^382)P */ 0x50, 0x20, 0x73, 0x74, 0x62, 0x21, 0xf2, 0xf7, 0x9b, 0x66, 0x85, 0x34, 0x74, 0xd4, 0x9d, 0x60, 0xd7, 0xbc, 0xc8, 0x46, 0x3b, 0xb8, 0x80, 0x42, 0x15, 0x0a, 0x6c, 0x35, 0x1a, 0x69, 0xf0, 0x1d, 0x4b, 0x29, 0x54, 0x5a, 0x9a, 0x48, 0xec, 0x9f, 0x37, 0x74, 0x91, 0xd0, 0xd1, 0x9e, 0x00, 0xc2, 0x76, 0x56, 0xd6, 0xa0, 0x15, 0x14, 0x83, 0x59, + /* (2^383)P */ 0xc2, 0xf8, 0x22, 0x20, 0x23, 0x07, 0xbd, 0x1d, 0x6f, 0x1e, 0x8c, 0x56, 0x06, 0x6a, 0x4b, 0x9f, 0xe2, 0xa9, 0x92, 0x46, 0x4b, 0x46, 0x59, 0xd7, 0xe1, 0xda, 0x14, 0x98, 0x07, 0x65, 0x7e, 0x28, 0x20, 0xf2, 0x9d, 0x4f, 0x36, 0x5c, 0x92, 0xe0, 0x9d, 0xfe, 0x3e, 0xda, 0xe4, 0x47, 0x19, 0x3c, 0x00, 0x7f, 0x22, 0xf2, 0x9e, 0x51, 0xae, 0x4d, + /* (2^384)P */ 0xbe, 0x8c, 0x1b, 0x10, 0xb6, 0xad, 0xcc, 0xcc, 0xd8, 0x5e, 0x21, 0xa6, 0xfb, 0xf1, 0xf6, 0xbd, 0x0a, 0x24, 0x67, 0xb4, 0x57, 0x7a, 0xbc, 0xe8, 0xe9, 0xff, 0xee, 0x0a, 0x1f, 0xee, 0xbd, 0xc8, 0x44, 0xed, 0x2b, 0xbb, 0x55, 0x1f, 0xdd, 0x7c, 0xb3, 0xeb, 0x3f, 0x63, 0xa1, 0x28, 0x91, 0x21, 0xab, 0x71, 0xc6, 0x4c, 0xd0, 0xe9, 0xb0, 0x21, + /* (2^385)P */ 0xad, 0xc9, 0x77, 0x2b, 0xee, 0x89, 0xa4, 0x7b, 0xfd, 0xf9, 0xf6, 0x14, 0xe4, 0xed, 0x1a, 0x16, 0x9b, 0x78, 0x41, 0x43, 0xa8, 0x83, 0x72, 0x06, 0x2e, 0x7c, 0xdf, 0xeb, 0x7e, 0xdd, 0xd7, 0x8b, 0xea, 0x9a, 0x2b, 0x03, 0xba, 0x57, 0xf3, 0xf1, 0xd9, 0xe5, 0x09, 0xc5, 0x98, 0x61, 0x1c, 0x51, 0x6d, 0x5d, 0x6e, 0xfb, 0x5e, 0x95, 0x9f, 0xb5, + /* (2^386)P */ 0x23, 0xe2, 0x1e, 0x95, 0xa3, 0x5e, 0x42, 0x10, 0xc7, 0xc3, 0x70, 0xbf, 0x4b, 0x6b, 0x83, 0x36, 0x93, 0xb7, 0x68, 0x47, 0x88, 0x3a, 0x10, 0x88, 0x48, 0x7f, 0x8c, 0xae, 0x54, 0x10, 0x02, 0xa4, 0x52, 0x8f, 0x8d, 0xf7, 0x26, 0x4f, 0x50, 0xc3, 0x6a, 0xe2, 0x4e, 0x3b, 0x4c, 0xb9, 0x8a, 0x14, 0x15, 0x6d, 0x21, 0x29, 0xb3, 0x6e, 0x4e, 0xd0, + /* (2^387)P */ 0x4c, 0x8a, 0x18, 0x3f, 0xb7, 0x20, 0xfd, 0x3e, 0x54, 0xca, 0x68, 0x3c, 0xea, 0x6f, 0xf4, 0x6b, 0xa2, 0xbd, 0x01, 0xbd, 0xfe, 0x08, 0xa8, 0xd8, 0xc2, 0x20, 0x36, 0x05, 0xcd, 0xe9, 0xf3, 0x9e, 0xfa, 0x85, 0x66, 0x8f, 0x4b, 0x1d, 0x8c, 0x64, 0x4f, 0xb8, 0xc6, 0x0f, 0x5b, 0x57, 0xd8, 0x24, 0x19, 0x5a, 0x14, 0x4b, 0x92, 0xd3, 0x96, 0xbc, + /* (2^388)P */ 0xa9, 0x3f, 0xc9, 0x6c, 0xca, 0x64, 0x1e, 0x6f, 0xdf, 0x65, 0x7f, 0x9a, 0x47, 0x6b, 0x8a, 0x60, 0x31, 0xa6, 0x06, 0xac, 0x69, 0x30, 0xe6, 0xea, 0x63, 0x42, 0x26, 0x5f, 0xdb, 0xd0, 0xf2, 0x8e, 0x34, 0x0a, 0x3a, 0xeb, 0xf3, 0x79, 0xc8, 0xb7, 0x60, 0x56, 0x5c, 0x37, 0x95, 0x71, 0xf8, 0x7f, 0x49, 0x3e, 0x9e, 0x01, 0x26, 0x1e, 0x80, 0x9f, + /* (2^389)P */ 0xf8, 0x16, 0x9a, 0xaa, 0xb0, 0x28, 0xb5, 0x8e, 0xd0, 0x60, 0xe5, 0x26, 0xa9, 0x47, 0xc4, 0x5c, 0xa9, 0x39, 0xfe, 0x0a, 0xd8, 0x07, 0x2b, 0xb3, 0xce, 0xf1, 0xea, 0x1a, 0xf4, 0x7b, 0x98, 0x31, 0x3d, 0x13, 0x29, 0x80, 0xe8, 0x0d, 0xcf, 0x56, 0x39, 0x86, 0x50, 0x0c, 0xb3, 0x18, 0xf4, 0xc5, 0xca, 0xf2, 0x6f, 0xcd, 0x8d, 0xd5, 0x02, 0xb0, + /* (2^390)P */ 0xbf, 0x39, 0x3f, 0xac, 0x6d, 0x1a, 0x6a, 0xe4, 0x42, 0x24, 0xd6, 0x41, 0x9d, 0xb9, 0x5b, 0x46, 0x73, 0x93, 0x76, 0xaa, 0xb7, 0x37, 0x36, 0xa6, 0x09, 0xe5, 0x04, 0x3b, 0x66, 0xc4, 0x29, 0x3e, 0x41, 0xc2, 0xcb, 0xe5, 0x17, 0xd7, 0x34, 0x67, 0x1d, 0x2c, 0x12, 0xec, 0x24, 0x7a, 0x40, 0xa2, 0x45, 0x41, 0xf0, 0x75, 0xed, 0x43, 0x30, 0xc9, + /* (2^391)P */ 0x80, 0xf6, 0x47, 0x5b, 0xad, 0x54, 0x02, 0xbc, 0xdd, 0xa4, 0xb2, 0xd7, 0x42, 0x95, 0xf2, 0x0d, 0x1b, 0xef, 0x37, 0xa7, 0xb4, 0x34, 0x04, 0x08, 0x71, 0x1b, 0xd3, 0xdf, 0xa1, 0xf0, 0x2b, 0xfa, 0xc0, 0x1f, 0xf3, 0x44, 0xb5, 0xc6, 0x47, 0x3d, 0x65, 0x67, 0x45, 0x4d, 0x2f, 0xde, 0x52, 0x73, 0xfc, 0x30, 0x01, 0x6b, 0xc1, 0x03, 0xd8, 0xd7, + /* (2^392)P */ 0x1c, 0x67, 0x55, 0x3e, 0x01, 0x17, 0x0f, 0x3e, 0xe5, 0x34, 0x58, 0xfc, 0xcb, 0x71, 0x24, 0x74, 0x5d, 0x36, 0x1e, 0x89, 0x2a, 0x63, 0xf8, 0xf8, 0x9f, 0x50, 0x9f, 0x32, 0x92, 0x29, 0xd8, 0x1a, 0xec, 0x76, 0x57, 0x6c, 0x67, 0x12, 0x6a, 0x6e, 0xef, 0x97, 0x1f, 0xc3, 0x77, 0x60, 0x3c, 0x22, 0xcb, 0xc7, 0x04, 0x1a, 0x89, 0x2d, 0x10, 0xa6, + /* (2^393)P */ 0x12, 0xf5, 0xa9, 0x26, 0x16, 0xd9, 0x3c, 0x65, 0x5d, 0x83, 0xab, 0xd1, 0x70, 0x6b, 0x1c, 0xdb, 0xe7, 0x86, 0x0d, 0xfb, 0xe7, 0xf8, 0x2a, 0x58, 0x6e, 0x7a, 0x66, 0x13, 0x53, 0x3a, 0x6f, 0x8d, 0x43, 0x5f, 0x14, 0x23, 0x14, 0xff, 0x3d, 0x52, 0x7f, 0xee, 0xbd, 0x7a, 0x34, 0x8b, 0x35, 0x24, 0xc3, 0x7a, 0xdb, 0xcf, 0x22, 0x74, 0x9a, 0x8f, + /* (2^394)P */ 0xdb, 0x20, 0xfc, 0xe5, 0x39, 0x4e, 0x7d, 0x78, 0xee, 0x0b, 0xbf, 0x1d, 0x80, 0xd4, 0x05, 0x4f, 0xb9, 0xd7, 0x4e, 0x94, 0x88, 0x9a, 0x50, 0x78, 0x1a, 0x70, 0x8c, 0xcc, 0x25, 0xb6, 0x61, 0x09, 0xdc, 0x7b, 0xea, 0x3f, 0x7f, 0xea, 0x2a, 0x0d, 0x47, 0x1c, 0x8e, 0xa6, 0x5b, 0xd2, 0xa3, 0x61, 0x93, 0x3c, 0x68, 0x9f, 0x8b, 0xea, 0xb0, 0xcb, + /* (2^395)P */ 0xff, 0x54, 0x02, 0x19, 0xae, 0x8b, 0x4c, 0x2c, 0x3a, 0xe0, 0xe4, 0xac, 0x87, 0xf7, 0x51, 0x45, 0x41, 0x43, 0xdc, 0xaa, 0xcd, 0xcb, 0xdc, 0x40, 0xe3, 0x44, 0x3b, 0x1d, 0x9e, 0x3d, 0xb9, 0x82, 0xcc, 0x7a, 0xc5, 0x12, 0xf8, 0x1e, 0xdd, 0xdb, 0x8d, 0xb0, 0x2a, 0xe8, 0xe6, 0x6c, 0x94, 0x3b, 0xb7, 0x2d, 0xba, 0x79, 0x3b, 0xb5, 0x86, 0xfb, + /* (2^396)P */ 0x82, 0x88, 0x13, 0xdd, 0x6c, 0xcd, 0x85, 0x2b, 0x90, 0x86, 0xb7, 0xac, 0x16, 0xa6, 0x6e, 0x6a, 0x94, 0xd8, 0x1e, 0x4e, 0x41, 0x0f, 0xce, 0x81, 0x6a, 0xa8, 0x26, 0x56, 0x43, 0x52, 0x52, 0xe6, 0xff, 0x88, 0xcf, 0x47, 0x05, 0x1d, 0xff, 0xf3, 0xa0, 0x10, 0xb2, 0x97, 0x87, 0xeb, 0x47, 0xbb, 0xfa, 0x1f, 0xe8, 0x4c, 0xce, 0xc4, 0xcd, 0x93, + /* (2^397)P */ 0xf4, 0x11, 0xf5, 0x8d, 0x89, 0x29, 0x79, 0xb3, 0x59, 0x0b, 0x29, 0x7d, 0x9c, 0x12, 0x4a, 0x65, 0x72, 0x3a, 0xf9, 0xec, 0x37, 0x18, 0x86, 0xef, 0x44, 0x07, 0x25, 0x74, 0x76, 0x53, 0xed, 0x51, 0x01, 0xc6, 0x28, 0xc5, 0xc3, 0x4a, 0x0f, 0x99, 0xec, 0xc8, 0x40, 0x5a, 0x83, 0x30, 0x79, 0xa2, 0x3e, 0x63, 0x09, 0x2d, 0x6f, 0x23, 0x54, 0x1c, + /* (2^398)P */ 0x5c, 0x6f, 0x3b, 0x1c, 0x30, 0x77, 0x7e, 0x87, 0x66, 0x83, 0x2e, 0x7e, 0x85, 0x50, 0xfd, 0xa0, 0x7a, 0xc2, 0xf5, 0x0f, 0xc1, 0x64, 0xe7, 0x0b, 0xbd, 0x59, 0xa7, 0xe7, 0x65, 0x53, 0xc3, 0xf5, 0x55, 0x5b, 0xe1, 0x82, 0x30, 0x5a, 0x61, 0xcd, 0xa0, 0x89, 0x32, 0xdb, 0x87, 0xfc, 0x21, 0x8a, 0xab, 0x6d, 0x82, 0xa8, 0x42, 0x81, 0x4f, 0xf2, + /* (2^399)P */ 0xb3, 0xeb, 0x88, 0x18, 0xf6, 0x56, 0x96, 0xbf, 0xba, 0x5d, 0x71, 0xa1, 0x5a, 0xd1, 0x04, 0x7b, 0xd5, 0x46, 0x01, 0x74, 0xfe, 0x15, 0x25, 0xb7, 0xff, 0x0c, 0x24, 0x47, 0xac, 0xfd, 0xab, 0x47, 0x32, 0xe1, 0x6a, 0x4e, 0xca, 0xcf, 0x7f, 0xdd, 0xf8, 0xd2, 0x4b, 0x3b, 0xf5, 0x17, 0xba, 0xba, 0x8b, 0xa1, 0xec, 0x28, 0x3f, 0x97, 0xab, 0x2a, + /* (2^400)P */ 0x51, 0x38, 0xc9, 0x5e, 0xc6, 0xb3, 0x64, 0xf2, 0x24, 0x4d, 0x04, 0x7d, 0xc8, 0x39, 0x0c, 0x4a, 0xc9, 0x73, 0x74, 0x1b, 0x5c, 0xb2, 0xc5, 0x41, 0x62, 0xa0, 0x4c, 0x6d, 0x8d, 0x91, 0x9a, 0x7b, 0x88, 0xab, 0x9c, 0x7e, 0x23, 0xdb, 0x6f, 0xb5, 0x72, 0xd6, 0x47, 0x40, 0xef, 0x22, 0x58, 0x62, 0x19, 0x6c, 0x38, 0xba, 0x5b, 0x00, 0x30, 0x9f, + /* (2^401)P */ 0x65, 0xbb, 0x3b, 0x9b, 0xe9, 0xae, 0xbf, 0xbe, 0xe4, 0x13, 0x95, 0xf3, 0xe3, 0x77, 0xcb, 0xe4, 0x9a, 0x22, 0xb5, 0x4a, 0x08, 0x9d, 0xb3, 0x9e, 0x27, 0xe0, 0x15, 0x6c, 0x9f, 0x7e, 0x9a, 0x5e, 0x15, 0x45, 0x25, 0x8d, 0x01, 0x0a, 0xd2, 0x2b, 0xbd, 0x48, 0x06, 0x0d, 0x18, 0x97, 0x4b, 0xdc, 0xbc, 0xf0, 0xcd, 0xb2, 0x52, 0x3c, 0xac, 0xf5, + /* (2^402)P */ 0x3e, 0xed, 0x47, 0x6b, 0x5c, 0xf6, 0x76, 0xd0, 0xe9, 0x15, 0xa3, 0xcb, 0x36, 0x00, 0x21, 0xa3, 0x79, 0x20, 0xa5, 0x3e, 0x88, 0x03, 0xcb, 0x7e, 0x63, 0xbb, 0xed, 0xa9, 0x13, 0x35, 0x16, 0xaf, 0x2e, 0xb4, 0x70, 0x14, 0x93, 0xfb, 0xc4, 0x9b, 0xd8, 0xb1, 0xbe, 0x43, 0xd1, 0x85, 0xb8, 0x97, 0xef, 0xea, 0x88, 0xa1, 0x25, 0x52, 0x62, 0x75, + /* (2^403)P */ 0x8e, 0x4f, 0xaa, 0x23, 0x62, 0x7e, 0x2b, 0x37, 0x89, 0x00, 0x11, 0x30, 0xc5, 0x33, 0x4a, 0x89, 0x8a, 0xe2, 0xfc, 0x5c, 0x6a, 0x75, 0xe5, 0xf7, 0x02, 0x4a, 0x9b, 0xf7, 0xb5, 0x6a, 0x85, 0x31, 0xd3, 0x5a, 0xcf, 0xc3, 0xf8, 0xde, 0x2f, 0xcf, 0xb5, 0x24, 0xf4, 0xe3, 0xa1, 0xad, 0x42, 0xae, 0x09, 0xb9, 0x2e, 0x04, 0x2d, 0x01, 0x22, 0x3f, + /* (2^404)P */ 0x41, 0x16, 0xfb, 0x7d, 0x50, 0xfd, 0xb5, 0xba, 0x88, 0x24, 0xba, 0xfd, 0x3d, 0xb2, 0x90, 0x15, 0xb7, 0xfa, 0xa2, 0xe1, 0x4c, 0x7d, 0xb9, 0xc6, 0xff, 0x81, 0x57, 0xb6, 0xc2, 0x9e, 0xcb, 0xc4, 0x35, 0xbd, 0x01, 0xb7, 0xaa, 0xce, 0xd0, 0xe9, 0xb5, 0xd6, 0x72, 0xbf, 0xd2, 0xee, 0xc7, 0xac, 0x94, 0xff, 0x29, 0x57, 0x02, 0x49, 0x09, 0xad, + /* (2^405)P */ 0x27, 0xa5, 0x78, 0x1b, 0xbf, 0x6b, 0xaf, 0x0b, 0x8c, 0xd9, 0xa8, 0x37, 0xb0, 0x67, 0x18, 0xb6, 0xc7, 0x05, 0x8a, 0x67, 0x03, 0x30, 0x62, 0x6e, 0x56, 0x82, 0xa9, 0x54, 0x3e, 0x0c, 0x4e, 0x07, 0xe1, 0x5a, 0x38, 0xed, 0xfa, 0xc8, 0x55, 0x6b, 0x08, 0xa3, 0x6b, 0x64, 0x2a, 0x15, 0xd6, 0x39, 0x6f, 0x47, 0x99, 0x42, 0x3f, 0x33, 0x84, 0x8f, + /* (2^406)P */ 0xbc, 0x45, 0x29, 0x81, 0x0e, 0xa4, 0xc5, 0x72, 0x3a, 0x10, 0xe1, 0xc4, 0x1e, 0xda, 0xc3, 0xfe, 0xb0, 0xce, 0xd2, 0x13, 0x34, 0x67, 0x21, 0xc6, 0x7e, 0xf9, 0x8c, 0xff, 0x39, 0x50, 0xae, 0x92, 0x60, 0x35, 0x2f, 0x8b, 0x6e, 0xc9, 0xc1, 0x27, 0x3a, 0x94, 0x66, 0x3e, 0x26, 0x84, 0x93, 0xc8, 0x6c, 0xcf, 0xd2, 0x03, 0xa1, 0x10, 0xcf, 0xb7, + /* (2^407)P */ 0x64, 0xda, 0x19, 0xf6, 0xc5, 0x73, 0x17, 0x44, 0x88, 0x81, 0x07, 0x0d, 0x34, 0xb2, 0x75, 0xf9, 0xd9, 0xe2, 0xe0, 0x8b, 0x71, 0xcf, 0x72, 0x34, 0x83, 0xb4, 0xce, 0xfc, 0xd7, 0x29, 0x09, 0x5a, 0x98, 0xbf, 0x14, 0xac, 0x77, 0x55, 0x38, 0x47, 0x5b, 0x0f, 0x40, 0x24, 0xe5, 0xa5, 0xa6, 0xac, 0x2d, 0xa6, 0xff, 0x9c, 0x73, 0xfe, 0x5c, 0x7e, + /* (2^408)P */ 0x1e, 0x33, 0xcc, 0x68, 0xb2, 0xbc, 0x8c, 0x93, 0xaf, 0xcc, 0x38, 0xf8, 0xd9, 0x16, 0x72, 0x50, 0xac, 0xd9, 0xb5, 0x0b, 0x9a, 0xbe, 0x46, 0x7a, 0xf1, 0xee, 0xf1, 0xad, 0xec, 0x5b, 0x59, 0x27, 0x9c, 0x05, 0xa3, 0x87, 0xe0, 0x37, 0x2c, 0x83, 0xce, 0xb3, 0x65, 0x09, 0x8e, 0xc3, 0x9c, 0xbf, 0x6a, 0xa2, 0x00, 0xcc, 0x12, 0x36, 0xc5, 0x95, + /* (2^409)P */ 0x36, 0x11, 0x02, 0x14, 0x9c, 0x3c, 0xeb, 0x2f, 0x23, 0x5b, 0x6b, 0x2b, 0x08, 0x54, 0x53, 0xac, 0xb2, 0xa3, 0xe0, 0x26, 0x62, 0x3c, 0xe4, 0xe1, 0x81, 0xee, 0x13, 0x3e, 0xa4, 0x97, 0xef, 0xf9, 0x92, 0x27, 0x01, 0xce, 0x54, 0x8b, 0x3e, 0x31, 0xbe, 0xa7, 0x88, 0xcf, 0x47, 0x99, 0x3c, 0x10, 0x6f, 0x60, 0xb3, 0x06, 0x4e, 0xee, 0x1b, 0xf0, + /* (2^410)P */ 0x59, 0x49, 0x66, 0xcf, 0x22, 0xe6, 0xf6, 0x73, 0xfe, 0xa3, 0x1c, 0x09, 0xfa, 0x5f, 0x65, 0xa8, 0xf0, 0x82, 0xc2, 0xef, 0x16, 0x63, 0x6e, 0x79, 0x69, 0x51, 0x39, 0x07, 0x65, 0xc4, 0x81, 0xec, 0x73, 0x0f, 0x15, 0x93, 0xe1, 0x30, 0x33, 0xe9, 0x37, 0x86, 0x42, 0x4c, 0x1f, 0x9b, 0xad, 0xee, 0x3f, 0xf1, 0x2a, 0x8e, 0x6a, 0xa3, 0xc8, 0x35, + /* (2^411)P */ 0x1e, 0x49, 0xf1, 0xdd, 0xd2, 0x9c, 0x8e, 0x78, 0xb2, 0x06, 0xe4, 0x6a, 0xab, 0x3a, 0xdc, 0xcd, 0xf4, 0xeb, 0xe1, 0xe7, 0x2f, 0xaa, 0xeb, 0x40, 0x31, 0x9f, 0xb9, 0xab, 0x13, 0xa9, 0x78, 0xbf, 0x38, 0x89, 0x0e, 0x85, 0x14, 0x8b, 0x46, 0x76, 0x14, 0xda, 0xcf, 0x33, 0xc8, 0x79, 0xd3, 0xd5, 0xa3, 0x6a, 0x69, 0x45, 0x70, 0x34, 0xc3, 0xe9, + /* (2^412)P */ 0x5e, 0xe7, 0x78, 0xe9, 0x24, 0xcc, 0xe9, 0xf4, 0xc8, 0x6b, 0xe0, 0xfb, 0x3a, 0xbe, 0xcc, 0x42, 0x4a, 0x00, 0x22, 0xf8, 0xe6, 0x32, 0xbe, 0x6d, 0x18, 0x55, 0x60, 0xe9, 0x72, 0x69, 0x50, 0x56, 0xca, 0x04, 0x18, 0x38, 0xa1, 0xee, 0xd8, 0x38, 0x3c, 0xa7, 0x70, 0xe2, 0xb9, 0x4c, 0xa0, 0xc8, 0x89, 0x72, 0xcf, 0x49, 0x7f, 0xdf, 0xbc, 0x67, + /* (2^413)P */ 0x1d, 0x17, 0xcb, 0x0b, 0xbd, 0xb2, 0x36, 0xe3, 0xa8, 0x99, 0x31, 0xb6, 0x26, 0x9c, 0x0c, 0x74, 0xaf, 0x4d, 0x24, 0x61, 0xcf, 0x31, 0x7b, 0xed, 0xdd, 0xc3, 0xf6, 0x32, 0x70, 0xfe, 0x17, 0xf6, 0x51, 0x37, 0x65, 0xce, 0x5d, 0xaf, 0xa5, 0x2f, 0x2a, 0xfe, 0x00, 0x71, 0x7c, 0x50, 0xbe, 0x21, 0xc7, 0xed, 0xc6, 0xfc, 0x67, 0xcf, 0x9c, 0xdd, + /* (2^414)P */ 0x26, 0x3e, 0xf8, 0xbb, 0xd0, 0xb1, 0x01, 0xd8, 0xeb, 0x0b, 0x62, 0x87, 0x35, 0x4c, 0xde, 0xca, 0x99, 0x9c, 0x6d, 0xf7, 0xb6, 0xf0, 0x57, 0x0a, 0x52, 0x29, 0x6a, 0x3f, 0x26, 0x31, 0x04, 0x07, 0x2a, 0xc9, 0xfa, 0x9b, 0x0e, 0x62, 0x8e, 0x72, 0xf2, 0xad, 0xce, 0xb6, 0x35, 0x7a, 0xc1, 0xae, 0x35, 0xc7, 0xa3, 0x14, 0xcf, 0x0c, 0x28, 0xb7, + /* (2^415)P */ 0xa6, 0xf1, 0x32, 0x3a, 0x20, 0xd2, 0x24, 0x97, 0xcf, 0x5d, 0x37, 0x99, 0xaf, 0x33, 0x7a, 0x5b, 0x7a, 0xcc, 0x4e, 0x41, 0x38, 0xb1, 0x4e, 0xad, 0xc9, 0xd9, 0x71, 0x7e, 0xb2, 0xf5, 0xd5, 0x01, 0x6c, 0x4d, 0xfd, 0xa1, 0xda, 0x03, 0x38, 0x9b, 0x3d, 0x92, 0x92, 0xf2, 0xca, 0xbf, 0x1f, 0x24, 0xa4, 0xbb, 0x30, 0x6a, 0x74, 0x56, 0xc8, 0xce, + /* (2^416)P */ 0x27, 0xf4, 0xed, 0xc9, 0xc3, 0xb1, 0x79, 0x85, 0xbe, 0xf6, 0xeb, 0xf3, 0x55, 0xc7, 0xaa, 0xa6, 0xe9, 0x07, 0x5d, 0xf4, 0xeb, 0xa6, 0x81, 0xe3, 0x0e, 0xcf, 0xa3, 0xc1, 0xef, 0xe7, 0x34, 0xb2, 0x03, 0x73, 0x8a, 0x91, 0xf1, 0xad, 0x05, 0xc7, 0x0b, 0x43, 0x99, 0x12, 0x31, 0xc8, 0xc7, 0xc5, 0xa4, 0x3d, 0xcd, 0xe5, 0x4e, 0x6d, 0x24, 0xdd, + /* (2^417)P */ 0x61, 0x54, 0xd0, 0x95, 0x2c, 0x45, 0x75, 0xac, 0xb5, 0x1a, 0x9d, 0x11, 0xeb, 0xed, 0x6b, 0x57, 0xa3, 0xe6, 0xcd, 0x77, 0xd4, 0x83, 0x8e, 0x39, 0xf1, 0x0f, 0x98, 0xcb, 0x40, 0x02, 0x6e, 0x10, 0x82, 0x9e, 0xb4, 0x93, 0x76, 0xd7, 0x97, 0xa3, 0x53, 0x12, 0x86, 0xc6, 0x15, 0x78, 0x73, 0x93, 0xe7, 0x7f, 0xcf, 0x1f, 0xbf, 0xcd, 0xd2, 0x7a, + /* (2^418)P */ 0xc2, 0x21, 0xdc, 0xd5, 0x69, 0xff, 0xca, 0x49, 0x3a, 0xe1, 0xc3, 0x69, 0x41, 0x56, 0xc1, 0x76, 0x63, 0x24, 0xbd, 0x64, 0x1b, 0x3d, 0x92, 0xf9, 0x13, 0x04, 0x25, 0xeb, 0x27, 0xa6, 0xef, 0x39, 0x3a, 0x80, 0xe0, 0xf8, 0x27, 0xee, 0xc9, 0x49, 0x77, 0xef, 0x3f, 0x29, 0x3d, 0x5e, 0xe6, 0x66, 0x83, 0xd1, 0xf6, 0xfe, 0x9d, 0xbc, 0xf1, 0x96, + /* (2^419)P */ 0x6b, 0xc6, 0x99, 0x26, 0x3c, 0xf3, 0x63, 0xf9, 0xc7, 0x29, 0x8c, 0x52, 0x62, 0x2d, 0xdc, 0x8a, 0x66, 0xce, 0x2c, 0xa7, 0xe4, 0xf0, 0xd7, 0x37, 0x17, 0x1e, 0xe4, 0xa3, 0x53, 0x7b, 0x29, 0x8e, 0x60, 0x99, 0xf9, 0x0c, 0x7c, 0x6f, 0xa2, 0xcc, 0x9f, 0x80, 0xdd, 0x5e, 0x46, 0xaa, 0x0d, 0x6c, 0xc9, 0x6c, 0xf7, 0x78, 0x5b, 0x38, 0xe3, 0x24, + /* (2^420)P */ 0x4b, 0x75, 0x6a, 0x2f, 0x08, 0xe1, 0x72, 0x76, 0xab, 0x82, 0x96, 0xdf, 0x3b, 0x1f, 0x9b, 0xd8, 0xed, 0xdb, 0xcd, 0x15, 0x09, 0x5a, 0x1e, 0xb7, 0xc5, 0x26, 0x72, 0x07, 0x0c, 0x50, 0xcd, 0x3b, 0x4d, 0x3f, 0xa2, 0x67, 0xc2, 0x02, 0x61, 0x2e, 0x68, 0xe9, 0x6f, 0xf0, 0x21, 0x2a, 0xa7, 0x3b, 0x88, 0x04, 0x11, 0x64, 0x49, 0x0d, 0xb4, 0x46, + /* (2^421)P */ 0x63, 0x85, 0xf3, 0xc5, 0x2b, 0x5a, 0x9f, 0xf0, 0x17, 0xcb, 0x45, 0x0a, 0xf3, 0x6e, 0x7e, 0xb0, 0x7c, 0xbc, 0xf0, 0x4f, 0x3a, 0xb0, 0xbc, 0x36, 0x36, 0x52, 0x51, 0xcb, 0xfe, 0x9a, 0xcb, 0xe8, 0x7e, 0x4b, 0x06, 0x7f, 0xaa, 0x35, 0xc8, 0x0e, 0x7a, 0x30, 0xa3, 0xb1, 0x09, 0xbb, 0x86, 0x4c, 0xbe, 0xb8, 0xbd, 0xe0, 0x32, 0xa5, 0xd4, 0xf7, + /* (2^422)P */ 0x7d, 0x50, 0x37, 0x68, 0x4e, 0x22, 0xb2, 0x2c, 0xd5, 0x0f, 0x2b, 0x6d, 0xb1, 0x51, 0xf2, 0x82, 0xe9, 0x98, 0x7c, 0x50, 0xc7, 0x96, 0x7e, 0x0e, 0xdc, 0xb1, 0x0e, 0xb2, 0x63, 0x8c, 0x30, 0x37, 0x72, 0x21, 0x9c, 0x61, 0xc2, 0xa7, 0x33, 0xd9, 0xb2, 0x63, 0x93, 0xd1, 0x6b, 0x6a, 0x73, 0xa5, 0x58, 0x80, 0xff, 0x04, 0xc7, 0x83, 0x21, 0x29, + /* (2^423)P */ 0x29, 0x04, 0xbc, 0x99, 0x39, 0xc9, 0x58, 0xc9, 0x6b, 0x17, 0xe8, 0x90, 0xb3, 0xe6, 0xa9, 0xb6, 0x28, 0x9b, 0xcb, 0x3b, 0x28, 0x90, 0x68, 0x71, 0xff, 0xcf, 0x08, 0x78, 0xc9, 0x8d, 0xa8, 0x4e, 0x43, 0xd1, 0x1c, 0x9e, 0xa4, 0xe3, 0xdf, 0xbf, 0x92, 0xf4, 0xf9, 0x41, 0xba, 0x4d, 0x1c, 0xf9, 0xdd, 0x74, 0x76, 0x1c, 0x6e, 0x3e, 0x94, 0x87, + /* (2^424)P */ 0xe4, 0xda, 0xc5, 0xd7, 0xfb, 0x87, 0xc5, 0x4d, 0x6b, 0x19, 0xaa, 0xb9, 0xbc, 0x8c, 0xf2, 0x8a, 0xd8, 0x5d, 0xdb, 0x4d, 0xef, 0xa6, 0xf2, 0x65, 0xf1, 0x22, 0x9c, 0xf1, 0x46, 0x30, 0x71, 0x7c, 0xe4, 0x53, 0x8e, 0x55, 0x2e, 0x9c, 0x9a, 0x31, 0x2a, 0xc3, 0xab, 0x0f, 0xde, 0xe4, 0xbe, 0xd8, 0x96, 0x50, 0x6e, 0x0c, 0x54, 0x49, 0xe6, 0xec, + /* (2^425)P */ 0x3c, 0x1d, 0x5a, 0xa5, 0xda, 0xad, 0xdd, 0xc2, 0xae, 0xac, 0x6f, 0x86, 0x75, 0x31, 0x91, 0x64, 0x45, 0x9d, 0xa4, 0xf0, 0x81, 0xf1, 0x0e, 0xba, 0x74, 0xaf, 0x7b, 0xcd, 0x6f, 0xfe, 0xac, 0x4e, 0xdb, 0x4e, 0x45, 0x35, 0x36, 0xc5, 0xc0, 0x6c, 0x3d, 0x64, 0xf4, 0xd8, 0x07, 0x62, 0xd1, 0xec, 0xf3, 0xfc, 0x93, 0xc9, 0x28, 0x0c, 0x2c, 0xf3, + /* (2^426)P */ 0x0c, 0x69, 0x2b, 0x5c, 0xb6, 0x41, 0x69, 0xf1, 0xa4, 0xf1, 0x5b, 0x75, 0x4c, 0x42, 0x8b, 0x47, 0xeb, 0x69, 0xfb, 0xa8, 0xe6, 0xf9, 0x7b, 0x48, 0x50, 0xaf, 0xd3, 0xda, 0xb2, 0x35, 0x10, 0xb5, 0x5b, 0x40, 0x90, 0x39, 0xc9, 0x07, 0x06, 0x73, 0x26, 0x20, 0x95, 0x01, 0xa4, 0x2d, 0xf0, 0xe7, 0x2e, 0x00, 0x7d, 0x41, 0x09, 0x68, 0x13, 0xc4, + /* (2^427)P */ 0xbe, 0x38, 0x78, 0xcf, 0xc9, 0x4f, 0x36, 0xca, 0x09, 0x61, 0x31, 0x3c, 0x57, 0x2e, 0xec, 0x17, 0xa4, 0x7d, 0x19, 0x2b, 0x9b, 0x5b, 0xbe, 0x8f, 0xd6, 0xc5, 0x2f, 0x86, 0xf2, 0x64, 0x76, 0x17, 0x00, 0x6e, 0x1a, 0x8c, 0x67, 0x1b, 0x68, 0xeb, 0x15, 0xa2, 0xd6, 0x09, 0x91, 0xdd, 0x23, 0x0d, 0x98, 0xb2, 0x10, 0x19, 0x55, 0x9b, 0x63, 0xf2, + /* (2^428)P */ 0x51, 0x1f, 0x93, 0xea, 0x2a, 0x3a, 0xfa, 0x41, 0xc0, 0x57, 0xfb, 0x74, 0xa6, 0x65, 0x09, 0x56, 0x14, 0xb6, 0x12, 0xaa, 0xb3, 0x1a, 0x8d, 0x3b, 0x76, 0x91, 0x7a, 0x23, 0x56, 0x9c, 0x6a, 0xc0, 0xe0, 0x3c, 0x3f, 0xb5, 0x1a, 0xf4, 0x57, 0x71, 0x93, 0x2b, 0xb1, 0xa7, 0x70, 0x57, 0x22, 0x80, 0xf5, 0xb8, 0x07, 0x77, 0x87, 0x0c, 0xbe, 0x83, + /* (2^429)P */ 0x07, 0x9b, 0x0e, 0x52, 0x38, 0x63, 0x13, 0x86, 0x6a, 0xa6, 0xb4, 0xd2, 0x60, 0x68, 0x9a, 0x99, 0x82, 0x0a, 0x04, 0x5f, 0x89, 0x7a, 0x1a, 0x2a, 0xae, 0x2d, 0x35, 0x0c, 0x1e, 0xad, 0xef, 0x4f, 0x9a, 0xfc, 0xc8, 0xd9, 0xcf, 0x9d, 0x48, 0x71, 0xa5, 0x55, 0x79, 0x73, 0x39, 0x1b, 0xd8, 0x73, 0xec, 0x9b, 0x03, 0x16, 0xd8, 0x82, 0xf7, 0x67, + /* (2^430)P */ 0x52, 0x67, 0x42, 0x21, 0xc9, 0x40, 0x78, 0x82, 0x2b, 0x95, 0x2d, 0x20, 0x92, 0xd1, 0xe2, 0x61, 0x25, 0xb0, 0xc6, 0x9c, 0x20, 0x59, 0x8e, 0x28, 0x6f, 0xf3, 0xfd, 0xd3, 0xc1, 0x32, 0x43, 0xc9, 0xa6, 0x08, 0x7a, 0x77, 0x9c, 0x4c, 0x8c, 0x33, 0x71, 0x13, 0x69, 0xe3, 0x52, 0x30, 0xa7, 0xf5, 0x07, 0x67, 0xac, 0xad, 0x46, 0x8a, 0x26, 0x25, + /* (2^431)P */ 0xda, 0x86, 0xc4, 0xa2, 0x71, 0x56, 0xdd, 0xd2, 0x48, 0xd3, 0xde, 0x42, 0x63, 0x01, 0xa7, 0x2c, 0x92, 0x83, 0x6f, 0x2e, 0xd8, 0x1e, 0x3f, 0xc1, 0xc5, 0x42, 0x4e, 0x34, 0x19, 0x54, 0x6e, 0x35, 0x2c, 0x51, 0x2e, 0xfd, 0x0f, 0x9a, 0x45, 0x66, 0x5e, 0x4a, 0x83, 0xda, 0x0a, 0x53, 0x68, 0x63, 0xfa, 0xce, 0x47, 0x20, 0xd3, 0x34, 0xba, 0x0d, + /* (2^432)P */ 0xd0, 0xe9, 0x64, 0xa4, 0x61, 0x4b, 0x86, 0xe5, 0x93, 0x6f, 0xda, 0x0e, 0x31, 0x7e, 0x6e, 0xe3, 0xc6, 0x73, 0xd8, 0xa3, 0x08, 0x57, 0x52, 0xcd, 0x51, 0x63, 0x1d, 0x9f, 0x93, 0x00, 0x62, 0x91, 0x26, 0x21, 0xa7, 0xdd, 0x25, 0x0f, 0x09, 0x0d, 0x35, 0xad, 0xcf, 0x11, 0x8e, 0x6e, 0xe8, 0xae, 0x1d, 0x95, 0xcb, 0x88, 0xf8, 0x70, 0x7b, 0x91, + /* (2^433)P */ 0x0c, 0x19, 0x5c, 0xd9, 0x8d, 0xda, 0x9d, 0x2c, 0x90, 0x54, 0x65, 0xe8, 0xb6, 0x35, 0x50, 0xae, 0xea, 0xae, 0x43, 0xb7, 0x1e, 0x99, 0x8b, 0x4c, 0x36, 0x4e, 0xe4, 0x1e, 0xc4, 0x64, 0x43, 0xb6, 0xeb, 0xd4, 0xe9, 0x60, 0x22, 0xee, 0xcf, 0xb8, 0x52, 0x1b, 0xf0, 0x04, 0xce, 0xbc, 0x2b, 0xf0, 0xbe, 0xcd, 0x44, 0x74, 0x1e, 0x1f, 0x63, 0xf9, + /* (2^434)P */ 0xe1, 0x3f, 0x95, 0x94, 0xb2, 0xb6, 0x31, 0xa9, 0x1b, 0xdb, 0xfd, 0x0e, 0xdb, 0xdd, 0x1a, 0x22, 0x78, 0x60, 0x9f, 0x75, 0x5f, 0x93, 0x06, 0x0c, 0xd8, 0xbb, 0xa2, 0x85, 0x2b, 0x5e, 0xc0, 0x9b, 0xa8, 0x5d, 0xaf, 0x93, 0x91, 0x91, 0x47, 0x41, 0x1a, 0xfc, 0xb4, 0x51, 0x85, 0xad, 0x69, 0x4d, 0x73, 0x69, 0xd5, 0x4e, 0x82, 0xfb, 0x66, 0xcb, + /* (2^435)P */ 0x7c, 0xbe, 0xc7, 0x51, 0xc4, 0x74, 0x6e, 0xab, 0xfd, 0x41, 0x4f, 0x76, 0x4f, 0x24, 0x03, 0xd6, 0x2a, 0xb7, 0x42, 0xb4, 0xda, 0x41, 0x2c, 0x82, 0x48, 0x4c, 0x7f, 0x6f, 0x25, 0x5d, 0x36, 0xd4, 0x69, 0xf5, 0xef, 0x02, 0x81, 0xea, 0x6f, 0x19, 0x69, 0xe8, 0x6f, 0x5b, 0x2f, 0x14, 0x0e, 0x6f, 0x89, 0xb4, 0xb5, 0xd8, 0xae, 0xef, 0x7b, 0x87, + /* (2^436)P */ 0xe9, 0x91, 0xa0, 0x8b, 0xc9, 0xe0, 0x01, 0x90, 0x37, 0xc1, 0x6f, 0xdc, 0x5e, 0xf7, 0xbf, 0x43, 0x00, 0xaa, 0x10, 0x76, 0x76, 0x18, 0x6e, 0x19, 0x1e, 0x94, 0x50, 0x11, 0x0a, 0xd1, 0xe2, 0xdb, 0x08, 0x21, 0xa0, 0x1f, 0xdb, 0x54, 0xfe, 0xea, 0x6e, 0xa3, 0x68, 0x56, 0x87, 0x0b, 0x22, 0x4e, 0x66, 0xf3, 0x82, 0x82, 0x00, 0xcd, 0xd4, 0x12, + /* (2^437)P */ 0x25, 0x8e, 0x24, 0x77, 0x64, 0x4c, 0xe0, 0xf8, 0x18, 0xc0, 0xdc, 0xc7, 0x1b, 0x35, 0x65, 0xde, 0x67, 0x41, 0x5e, 0x6f, 0x90, 0x82, 0xa7, 0x2e, 0x6d, 0xf1, 0x47, 0xb4, 0x92, 0x9c, 0xfd, 0x6a, 0x9a, 0x41, 0x36, 0x20, 0x24, 0x58, 0xc3, 0x59, 0x07, 0x9a, 0xfa, 0x9f, 0x03, 0xcb, 0xc7, 0x69, 0x37, 0x60, 0xe1, 0xab, 0x13, 0x72, 0xee, 0xa2, + /* (2^438)P */ 0x74, 0x78, 0xfb, 0x13, 0xcb, 0x8e, 0x37, 0x1a, 0xf6, 0x1d, 0x17, 0x83, 0x06, 0xd4, 0x27, 0x06, 0x21, 0xe8, 0xda, 0xdf, 0x6b, 0xf3, 0x83, 0x6b, 0x34, 0x8a, 0x8c, 0xee, 0x01, 0x05, 0x5b, 0xed, 0xd3, 0x1b, 0xc9, 0x64, 0x83, 0xc9, 0x49, 0xc2, 0x57, 0x1b, 0xdd, 0xcf, 0xf1, 0x9d, 0x63, 0xee, 0x1c, 0x0d, 0xa0, 0x0a, 0x73, 0x1f, 0x5b, 0x32, + /* (2^439)P */ 0x29, 0xce, 0x1e, 0xc0, 0x6a, 0xf5, 0xeb, 0x99, 0x5a, 0x39, 0x23, 0xe9, 0xdd, 0xac, 0x44, 0x88, 0xbc, 0x80, 0x22, 0xde, 0x2c, 0xcb, 0xa8, 0x3b, 0xff, 0xf7, 0x6f, 0xc7, 0x71, 0x72, 0xa8, 0xa3, 0xf6, 0x4d, 0xc6, 0x75, 0xda, 0x80, 0xdc, 0xd9, 0x30, 0xd9, 0x07, 0x50, 0x5a, 0x54, 0x7d, 0xda, 0x39, 0x6f, 0x78, 0x94, 0xbf, 0x25, 0x98, 0xdc, + /* (2^440)P */ 0x01, 0x26, 0x62, 0x44, 0xfb, 0x0f, 0x11, 0x72, 0x73, 0x0a, 0x16, 0xc7, 0x16, 0x9c, 0x9b, 0x37, 0xd8, 0xff, 0x4f, 0xfe, 0x57, 0xdb, 0xae, 0xef, 0x7d, 0x94, 0x30, 0x04, 0x70, 0x83, 0xde, 0x3c, 0xd4, 0xb5, 0x70, 0xda, 0xa7, 0x55, 0xc8, 0x19, 0xe1, 0x36, 0x15, 0x61, 0xe7, 0x3b, 0x7d, 0x85, 0xbb, 0xf3, 0x42, 0x5a, 0x94, 0xf4, 0x53, 0x2a, + /* (2^441)P */ 0x14, 0x60, 0xa6, 0x0b, 0x83, 0xe1, 0x23, 0x77, 0xc0, 0xce, 0x50, 0xed, 0x35, 0x8d, 0x98, 0x99, 0x7d, 0xf5, 0x8d, 0xce, 0x94, 0x25, 0xc8, 0x0f, 0x6d, 0xfa, 0x4a, 0xa4, 0x3a, 0x1f, 0x66, 0xfb, 0x5a, 0x64, 0xaf, 0x8b, 0x54, 0x54, 0x44, 0x3f, 0x5b, 0x88, 0x61, 0xe4, 0x48, 0x45, 0x26, 0x20, 0xbe, 0x0d, 0x06, 0xbb, 0x65, 0x59, 0xe1, 0x36, + /* (2^442)P */ 0xb7, 0x98, 0xce, 0xa3, 0xe3, 0xee, 0x11, 0x1b, 0x9e, 0x24, 0x59, 0x75, 0x31, 0x37, 0x44, 0x6f, 0x6b, 0x9e, 0xec, 0xb7, 0x44, 0x01, 0x7e, 0xab, 0xbb, 0x69, 0x5d, 0x11, 0xb0, 0x30, 0x64, 0xea, 0x91, 0xb4, 0x7a, 0x8c, 0x02, 0x4c, 0xb9, 0x10, 0xa7, 0xc7, 0x79, 0xe6, 0xdc, 0x77, 0xe3, 0xc8, 0xef, 0x3e, 0xf9, 0x38, 0x81, 0xce, 0x9a, 0xb2, + /* (2^443)P */ 0x91, 0x12, 0x76, 0xd0, 0x10, 0xb4, 0xaf, 0xe1, 0x89, 0x3a, 0x93, 0x6b, 0x5c, 0x19, 0x5f, 0x24, 0xed, 0x04, 0x92, 0xc7, 0xf0, 0x00, 0x08, 0xc1, 0x92, 0xff, 0x90, 0xdb, 0xb2, 0xbf, 0xdf, 0x49, 0xcd, 0xbd, 0x5c, 0x6e, 0xbf, 0x16, 0xbb, 0x61, 0xf9, 0x20, 0x33, 0x35, 0x93, 0x11, 0xbc, 0x59, 0x69, 0xce, 0x18, 0x9f, 0xf8, 0x7b, 0xa1, 0x6e, + /* (2^444)P */ 0xa1, 0xf4, 0xaf, 0xad, 0xf8, 0xe6, 0x99, 0xd2, 0xa1, 0x4d, 0xde, 0x56, 0xc9, 0x7b, 0x0b, 0x11, 0x3e, 0xbf, 0x89, 0x1a, 0x9a, 0x90, 0xe5, 0xe2, 0xa6, 0x37, 0x88, 0xa1, 0x68, 0x59, 0xae, 0x8c, 0xec, 0x02, 0x14, 0x8d, 0xb7, 0x2e, 0x25, 0x75, 0x7f, 0x76, 0x1a, 0xd3, 0x4d, 0xad, 0x8a, 0x00, 0x6c, 0x96, 0x49, 0xa4, 0xc3, 0x2e, 0x5c, 0x7b, + /* (2^445)P */ 0x26, 0x53, 0xf7, 0xda, 0xa8, 0x01, 0x14, 0xb1, 0x63, 0xe3, 0xc3, 0x89, 0x88, 0xb0, 0x85, 0x40, 0x2b, 0x26, 0x9a, 0x10, 0x1a, 0x70, 0x33, 0xf4, 0x50, 0x9d, 0x4d, 0xd8, 0x64, 0xc6, 0x0f, 0xe1, 0x17, 0xc8, 0x10, 0x4b, 0xfc, 0xa0, 0xc9, 0xba, 0x2c, 0x98, 0x09, 0xf5, 0x84, 0xb6, 0x7c, 0x4e, 0xa3, 0xe3, 0x81, 0x1b, 0x32, 0x60, 0x02, 0xdd, + /* (2^446)P */ 0xa3, 0xe5, 0x86, 0xd4, 0x43, 0xa8, 0xd1, 0x98, 0x9d, 0x9d, 0xdb, 0x04, 0xcf, 0x6e, 0x35, 0x05, 0x30, 0x53, 0x3b, 0xbc, 0x90, 0x00, 0x4a, 0xc5, 0x40, 0x2a, 0x0f, 0xde, 0x1a, 0xd7, 0x36, 0x27, 0x44, 0x62, 0xa6, 0xac, 0x9d, 0xd2, 0x70, 0x69, 0x14, 0x39, 0x9b, 0xd1, 0xc3, 0x0a, 0x3a, 0x82, 0x0e, 0xf1, 0x94, 0xd7, 0x42, 0x94, 0xd5, 0x7d, + /* (2^447)P */ 0x04, 0xc0, 0x6e, 0x12, 0x90, 0x70, 0xf9, 0xdf, 0xf7, 0xc9, 0x86, 0xc0, 0xe6, 0x92, 0x8b, 0x0a, 0xa1, 0xc1, 0x3b, 0xcc, 0x33, 0xb7, 0xf0, 0xeb, 0x51, 0x50, 0x80, 0x20, 0x69, 0x1c, 0x4f, 0x89, 0x05, 0x1e, 0xe4, 0x7a, 0x0a, 0xc2, 0xf0, 0xf5, 0x78, 0x91, 0x76, 0x34, 0x45, 0xdc, 0x24, 0x53, 0x24, 0x98, 0xe2, 0x73, 0x6f, 0xe6, 0x46, 0x67, +} diff --git a/vendor/github.com/cloudflare/circl/internal/conv/conv.go b/vendor/github.com/cloudflare/circl/internal/conv/conv.go new file mode 100644 index 00000000..649a8e93 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/conv/conv.go @@ -0,0 +1,140 @@ +package conv + +import ( + "encoding/binary" + "fmt" + "math/big" + "strings" +) + +// BytesLe2Hex returns an hexadecimal string of a number stored in a +// little-endian order slice x. +func BytesLe2Hex(x []byte) string { + b := &strings.Builder{} + b.Grow(2*len(x) + 2) + fmt.Fprint(b, "0x") + if len(x) == 0 { + fmt.Fprint(b, "00") + } + for i := len(x) - 1; i >= 0; i-- { + fmt.Fprintf(b, "%02x", x[i]) + } + return b.String() +} + +// BytesLe2BigInt converts a little-endian slice x into a big-endian +// math/big.Int. +func BytesLe2BigInt(x []byte) *big.Int { + n := len(x) + b := new(big.Int) + if len(x) > 0 { + y := make([]byte, n) + for i := 0; i < n; i++ { + y[n-1-i] = x[i] + } + b.SetBytes(y) + } + return b +} + +// BytesBe2Uint64Le converts a big-endian slice x to a little-endian slice of uint64. +func BytesBe2Uint64Le(x []byte) []uint64 { + l := len(x) + z := make([]uint64, (l+7)/8) + blocks := l / 8 + for i := 0; i < blocks; i++ { + z[i] = binary.BigEndian.Uint64(x[l-8*(i+1):]) + } + remBytes := l % 8 + for i := 0; i < remBytes; i++ { + z[blocks] |= uint64(x[l-1-8*blocks-i]) << uint(8*i) + } + return z +} + +// BigInt2BytesLe stores a positive big.Int number x into a little-endian slice z. +// The slice is modified if the bitlength of x <= 8*len(z) (padding with zeros). +// If x does not fit in the slice or is negative, z is not modified. +func BigInt2BytesLe(z []byte, x *big.Int) { + xLen := (x.BitLen() + 7) >> 3 + zLen := len(z) + if zLen >= xLen && x.Sign() >= 0 { + y := x.Bytes() + for i := 0; i < xLen; i++ { + z[i] = y[xLen-1-i] + } + for i := xLen; i < zLen; i++ { + z[i] = 0 + } + } +} + +// Uint64Le2BigInt converts a little-endian slice x into a big number. +func Uint64Le2BigInt(x []uint64) *big.Int { + n := len(x) + b := new(big.Int) + var bi big.Int + for i := n - 1; i >= 0; i-- { + bi.SetUint64(x[i]) + b.Lsh(b, 64) + b.Add(b, &bi) + } + return b +} + +// Uint64Le2BytesLe converts a little-endian slice x to a little-endian slice of bytes. +func Uint64Le2BytesLe(x []uint64) []byte { + b := make([]byte, 8*len(x)) + n := len(x) + for i := 0; i < n; i++ { + binary.LittleEndian.PutUint64(b[i*8:], x[i]) + } + return b +} + +// Uint64Le2BytesBe converts a little-endian slice x to a big-endian slice of bytes. +func Uint64Le2BytesBe(x []uint64) []byte { + b := make([]byte, 8*len(x)) + n := len(x) + for i := 0; i < n; i++ { + binary.BigEndian.PutUint64(b[i*8:], x[n-1-i]) + } + return b +} + +// Uint64Le2Hex returns an hexadecimal string of a number stored in a +// little-endian order slice x. +func Uint64Le2Hex(x []uint64) string { + b := new(strings.Builder) + b.Grow(16*len(x) + 2) + fmt.Fprint(b, "0x") + if len(x) == 0 { + fmt.Fprint(b, "00") + } + for i := len(x) - 1; i >= 0; i-- { + fmt.Fprintf(b, "%016x", x[i]) + } + return b.String() +} + +// BigInt2Uint64Le stores a positive big.Int number x into a little-endian slice z. +// The slice is modified if the bitlength of x <= 8*len(z) (padding with zeros). +// If x does not fit in the slice or is negative, z is not modified. +func BigInt2Uint64Le(z []uint64, x *big.Int) { + xLen := (x.BitLen() + 63) >> 6 // number of 64-bit words + zLen := len(z) + if zLen >= xLen && x.Sign() > 0 { + var y, yi big.Int + y.Set(x) + two64 := big.NewInt(1) + two64.Lsh(two64, 64).Sub(two64, big.NewInt(1)) + for i := 0; i < xLen; i++ { + yi.And(&y, two64) + z[i] = yi.Uint64() + y.Rsh(&y, 64) + } + } + for i := xLen; i < zLen; i++ { + z[i] = 0 + } +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/doc.go b/vendor/github.com/cloudflare/circl/internal/sha3/doc.go new file mode 100644 index 00000000..7e023090 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/doc.go @@ -0,0 +1,62 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sha3 implements the SHA-3 fixed-output-length hash functions and +// the SHAKE variable-output-length hash functions defined by FIPS-202. +// +// Both types of hash function use the "sponge" construction and the Keccak +// permutation. For a detailed specification see http://keccak.noekeon.org/ +// +// # Guidance +// +// If you aren't sure what function you need, use SHAKE256 with at least 64 +// bytes of output. The SHAKE instances are faster than the SHA3 instances; +// the latter have to allocate memory to conform to the hash.Hash interface. +// +// If you need a secret-key MAC (message authentication code), prepend the +// secret key to the input, hash with SHAKE256 and read at least 32 bytes of +// output. +// +// # Security strengths +// +// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security +// strength against preimage attacks of x bits. Since they only produce "x" +// bits of output, their collision-resistance is only "x/2" bits. +// +// The SHAKE-256 and -128 functions have a generic security strength of 256 and +// 128 bits against all attacks, provided that at least 2x bits of their output +// is used. Requesting more than 64 or 32 bytes of output, respectively, does +// not increase the collision-resistance of the SHAKE functions. +// +// # The sponge construction +// +// A sponge builds a pseudo-random function from a public pseudo-random +// permutation, by applying the permutation to a state of "rate + capacity" +// bytes, but hiding "capacity" of the bytes. +// +// A sponge starts out with a zero state. To hash an input using a sponge, up +// to "rate" bytes of the input are XORed into the sponge's state. The sponge +// is then "full" and the permutation is applied to "empty" it. This process is +// repeated until all the input has been "absorbed". The input is then padded. +// The digest is "squeezed" from the sponge in the same way, except that output +// is copied out instead of input being XORed in. +// +// A sponge is parameterized by its generic security strength, which is equal +// to half its capacity; capacity + rate is equal to the permutation's width. +// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means +// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2. +// +// # Recommendations +// +// The SHAKE functions are recommended for most new uses. They can produce +// output of arbitrary length. SHAKE256, with an output length of at least +// 64 bytes, provides 256-bit security against all attacks. The Keccak team +// recommends it for most applications upgrading from SHA2-512. (NIST chose a +// much stronger, but much slower, sponge instance for SHA3-512.) +// +// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions. +// They produce output of the same length, with the same security strengths +// against all attacks. This means, in particular, that SHA3-256 only has +// 128-bit collision resistance, because its output length is 32 bytes. +package sha3 diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/hashes.go b/vendor/github.com/cloudflare/circl/internal/sha3/hashes.go new file mode 100644 index 00000000..7d2365a7 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/hashes.go @@ -0,0 +1,69 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// This file provides functions for creating instances of the SHA-3 +// and SHAKE hash functions, as well as utility functions for hashing +// bytes. + +// New224 creates a new SHA3-224 hash. +// Its generic security strength is 224 bits against preimage attacks, +// and 112 bits against collision attacks. +func New224() State { + return State{rate: 144, outputLen: 28, dsbyte: 0x06} +} + +// New256 creates a new SHA3-256 hash. +// Its generic security strength is 256 bits against preimage attacks, +// and 128 bits against collision attacks. +func New256() State { + return State{rate: 136, outputLen: 32, dsbyte: 0x06} +} + +// New384 creates a new SHA3-384 hash. +// Its generic security strength is 384 bits against preimage attacks, +// and 192 bits against collision attacks. +func New384() State { + return State{rate: 104, outputLen: 48, dsbyte: 0x06} +} + +// New512 creates a new SHA3-512 hash. +// Its generic security strength is 512 bits against preimage attacks, +// and 256 bits against collision attacks. +func New512() State { + return State{rate: 72, outputLen: 64, dsbyte: 0x06} +} + +// Sum224 returns the SHA3-224 digest of the data. +func Sum224(data []byte) (digest [28]byte) { + h := New224() + _, _ = h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum256 returns the SHA3-256 digest of the data. +func Sum256(data []byte) (digest [32]byte) { + h := New256() + _, _ = h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum384 returns the SHA3-384 digest of the data. +func Sum384(data []byte) (digest [48]byte) { + h := New384() + _, _ = h.Write(data) + h.Sum(digest[:0]) + return +} + +// Sum512 returns the SHA3-512 digest of the data. +func Sum512(data []byte) (digest [64]byte) { + h := New512() + _, _ = h.Write(data) + h.Sum(digest[:0]) + return +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go new file mode 100644 index 00000000..e606f0ca --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go @@ -0,0 +1,385 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !amd64 || appengine || gccgo +// +build !amd64 appengine gccgo + +package sha3 + +// KeccakF1600 applies the Keccak permutation to a 1600b-wide +// state represented as a slice of 25 uint64s. +func KeccakF1600(a *[25]uint64) { + // Implementation translated from Keccak-inplace.c + // in the keccak reference code. + var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 + + for i := 0; i < 24; i += 4 { + // Combines the 5 steps in each round into 2 steps. + // Unrolls 4 rounds per loop and spreads some steps across rounds. + + // Round 1 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[6] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[12] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[18] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[24] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ RC[i] + a[6] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[16] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[22] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[3] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[10] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[1] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[7] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[19] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[20] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[11] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[23] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[4] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[5] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[2] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[8] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[14] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[15] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + // Round 2 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[16] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[7] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[23] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[14] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ RC[i+1] + a[16] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[11] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[2] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[18] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[20] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[6] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[22] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[4] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[15] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[1] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[8] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[24] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[10] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[12] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[3] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[19] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[5] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + // Round 3 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[11] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[22] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[8] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[19] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ RC[i+2] + a[11] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[1] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[12] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[23] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[15] = bc0 ^ (bc2 &^ bc1) + a[1] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[16] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[2] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[24] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[5] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[6] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[3] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[14] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[20] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[7] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[18] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[4] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[10] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + // Round 4 + bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] + bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] + bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] + bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] + bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] + d0 = bc4 ^ (bc1<<1 | bc1>>63) + d1 = bc0 ^ (bc2<<1 | bc2>>63) + d2 = bc1 ^ (bc3<<1 | bc3>>63) + d3 = bc2 ^ (bc4<<1 | bc4>>63) + d4 = bc3 ^ (bc0<<1 | bc0>>63) + + bc0 = a[0] ^ d0 + t = a[1] ^ d1 + bc1 = t<<44 | t>>(64-44) + t = a[2] ^ d2 + bc2 = t<<43 | t>>(64-43) + t = a[3] ^ d3 + bc3 = t<<21 | t>>(64-21) + t = a[4] ^ d4 + bc4 = t<<14 | t>>(64-14) + a[0] = bc0 ^ (bc2 &^ bc1) ^ RC[i+3] + a[1] = bc1 ^ (bc3 &^ bc2) + a[2] = bc2 ^ (bc4 &^ bc3) + a[3] = bc3 ^ (bc0 &^ bc4) + a[4] = bc4 ^ (bc1 &^ bc0) + + t = a[5] ^ d0 + bc2 = t<<3 | t>>(64-3) + t = a[6] ^ d1 + bc3 = t<<45 | t>>(64-45) + t = a[7] ^ d2 + bc4 = t<<61 | t>>(64-61) + t = a[8] ^ d3 + bc0 = t<<28 | t>>(64-28) + t = a[9] ^ d4 + bc1 = t<<20 | t>>(64-20) + a[5] = bc0 ^ (bc2 &^ bc1) + a[6] = bc1 ^ (bc3 &^ bc2) + a[7] = bc2 ^ (bc4 &^ bc3) + a[8] = bc3 ^ (bc0 &^ bc4) + a[9] = bc4 ^ (bc1 &^ bc0) + + t = a[10] ^ d0 + bc4 = t<<18 | t>>(64-18) + t = a[11] ^ d1 + bc0 = t<<1 | t>>(64-1) + t = a[12] ^ d2 + bc1 = t<<6 | t>>(64-6) + t = a[13] ^ d3 + bc2 = t<<25 | t>>(64-25) + t = a[14] ^ d4 + bc3 = t<<8 | t>>(64-8) + a[10] = bc0 ^ (bc2 &^ bc1) + a[11] = bc1 ^ (bc3 &^ bc2) + a[12] = bc2 ^ (bc4 &^ bc3) + a[13] = bc3 ^ (bc0 &^ bc4) + a[14] = bc4 ^ (bc1 &^ bc0) + + t = a[15] ^ d0 + bc1 = t<<36 | t>>(64-36) + t = a[16] ^ d1 + bc2 = t<<10 | t>>(64-10) + t = a[17] ^ d2 + bc3 = t<<15 | t>>(64-15) + t = a[18] ^ d3 + bc4 = t<<56 | t>>(64-56) + t = a[19] ^ d4 + bc0 = t<<27 | t>>(64-27) + a[15] = bc0 ^ (bc2 &^ bc1) + a[16] = bc1 ^ (bc3 &^ bc2) + a[17] = bc2 ^ (bc4 &^ bc3) + a[18] = bc3 ^ (bc0 &^ bc4) + a[19] = bc4 ^ (bc1 &^ bc0) + + t = a[20] ^ d0 + bc3 = t<<41 | t>>(64-41) + t = a[21] ^ d1 + bc4 = t<<2 | t>>(64-2) + t = a[22] ^ d2 + bc0 = t<<62 | t>>(64-62) + t = a[23] ^ d3 + bc1 = t<<55 | t>>(64-55) + t = a[24] ^ d4 + bc2 = t<<39 | t>>(64-39) + a[20] = bc0 ^ (bc2 &^ bc1) + a[21] = bc1 ^ (bc3 &^ bc2) + a[22] = bc2 ^ (bc4 &^ bc3) + a[23] = bc3 ^ (bc0 &^ bc4) + a[24] = bc4 ^ (bc1 &^ bc0) + } +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.go b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.go new file mode 100644 index 00000000..e7364eb0 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.go @@ -0,0 +1,14 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build amd64 && !appengine && !gccgo +// +build amd64,!appengine,!gccgo + +package sha3 + +// This function is implemented in keccakf_amd64.s. + +//go:noescape + +func KeccakF1600(state *[25]uint64) diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.s b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.s new file mode 100644 index 00000000..8589363a --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf_amd64.s @@ -0,0 +1,390 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!appengine,!gccgo + +// This code was translated into a form compatible with 6a from the public +// domain sources at https://github.com/gvanas/KeccakCodePackage + +// Offsets in state +#define _ba (0*8) +#define _be (1*8) +#define _bi (2*8) +#define _bo (3*8) +#define _bu (4*8) +#define _ga (5*8) +#define _ge (6*8) +#define _gi (7*8) +#define _go (8*8) +#define _gu (9*8) +#define _ka (10*8) +#define _ke (11*8) +#define _ki (12*8) +#define _ko (13*8) +#define _ku (14*8) +#define _ma (15*8) +#define _me (16*8) +#define _mi (17*8) +#define _mo (18*8) +#define _mu (19*8) +#define _sa (20*8) +#define _se (21*8) +#define _si (22*8) +#define _so (23*8) +#define _su (24*8) + +// Temporary registers +#define rT1 AX + +// Round vars +#define rpState DI +#define rpStack SP + +#define rDa BX +#define rDe CX +#define rDi DX +#define rDo R8 +#define rDu R9 + +#define rBa R10 +#define rBe R11 +#define rBi R12 +#define rBo R13 +#define rBu R14 + +#define rCa SI +#define rCe BP +#define rCi rBi +#define rCo rBo +#define rCu R15 + +#define MOVQ_RBI_RCE MOVQ rBi, rCe +#define XORQ_RT1_RCA XORQ rT1, rCa +#define XORQ_RT1_RCE XORQ rT1, rCe +#define XORQ_RBA_RCU XORQ rBa, rCu +#define XORQ_RBE_RCU XORQ rBe, rCu +#define XORQ_RDU_RCU XORQ rDu, rCu +#define XORQ_RDA_RCA XORQ rDa, rCa +#define XORQ_RDE_RCE XORQ rDe, rCe + +#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \ + /* Prepare round */ \ + MOVQ rCe, rDa; \ + ROLQ $1, rDa; \ + \ + MOVQ _bi(iState), rCi; \ + XORQ _gi(iState), rDi; \ + XORQ rCu, rDa; \ + XORQ _ki(iState), rCi; \ + XORQ _mi(iState), rDi; \ + XORQ rDi, rCi; \ + \ + MOVQ rCi, rDe; \ + ROLQ $1, rDe; \ + \ + MOVQ _bo(iState), rCo; \ + XORQ _go(iState), rDo; \ + XORQ rCa, rDe; \ + XORQ _ko(iState), rCo; \ + XORQ _mo(iState), rDo; \ + XORQ rDo, rCo; \ + \ + MOVQ rCo, rDi; \ + ROLQ $1, rDi; \ + \ + MOVQ rCu, rDo; \ + XORQ rCe, rDi; \ + ROLQ $1, rDo; \ + \ + MOVQ rCa, rDu; \ + XORQ rCi, rDo; \ + ROLQ $1, rDu; \ + \ + /* Result b */ \ + MOVQ _ba(iState), rBa; \ + MOVQ _ge(iState), rBe; \ + XORQ rCo, rDu; \ + MOVQ _ki(iState), rBi; \ + MOVQ _mo(iState), rBo; \ + MOVQ _su(iState), rBu; \ + XORQ rDe, rBe; \ + ROLQ $44, rBe; \ + XORQ rDi, rBi; \ + XORQ rDa, rBa; \ + ROLQ $43, rBi; \ + \ + MOVQ rBe, rCa; \ + MOVQ rc, rT1; \ + ORQ rBi, rCa; \ + XORQ rBa, rT1; \ + XORQ rT1, rCa; \ + MOVQ rCa, _ba(oState); \ + \ + XORQ rDu, rBu; \ + ROLQ $14, rBu; \ + MOVQ rBa, rCu; \ + ANDQ rBe, rCu; \ + XORQ rBu, rCu; \ + MOVQ rCu, _bu(oState); \ + \ + XORQ rDo, rBo; \ + ROLQ $21, rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _bi(oState); \ + \ + NOTQ rBi; \ + ORQ rBa, rBu; \ + ORQ rBo, rBi; \ + XORQ rBo, rBu; \ + XORQ rBe, rBi; \ + MOVQ rBu, _bo(oState); \ + MOVQ rBi, _be(oState); \ + B_RBI_RCE; \ + \ + /* Result g */ \ + MOVQ _gu(iState), rBe; \ + XORQ rDu, rBe; \ + MOVQ _ka(iState), rBi; \ + ROLQ $20, rBe; \ + XORQ rDa, rBi; \ + ROLQ $3, rBi; \ + MOVQ _bo(iState), rBa; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDo, rBa; \ + MOVQ _me(iState), rBo; \ + MOVQ _si(iState), rBu; \ + ROLQ $28, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ga(oState); \ + G_RT1_RCA; \ + \ + XORQ rDe, rBo; \ + ROLQ $45, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ge(oState); \ + G_RT1_RCE; \ + \ + XORQ rDi, rBu; \ + ROLQ $61, rBu; \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _go(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _gu(oState); \ + NOTQ rBu; \ + G_RBA_RCU; \ + \ + ORQ rBu, rBo; \ + XORQ rBi, rBo; \ + MOVQ rBo, _gi(oState); \ + \ + /* Result k */ \ + MOVQ _be(iState), rBa; \ + MOVQ _gi(iState), rBe; \ + MOVQ _ko(iState), rBi; \ + MOVQ _mu(iState), rBo; \ + MOVQ _sa(iState), rBu; \ + XORQ rDi, rBe; \ + ROLQ $6, rBe; \ + XORQ rDo, rBi; \ + ROLQ $25, rBi; \ + MOVQ rBe, rT1; \ + ORQ rBi, rT1; \ + XORQ rDe, rBa; \ + ROLQ $1, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ka(oState); \ + K_RT1_RCA; \ + \ + XORQ rDu, rBo; \ + ROLQ $8, rBo; \ + MOVQ rBi, rT1; \ + ANDQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _ke(oState); \ + K_RT1_RCE; \ + \ + XORQ rDa, rBu; \ + ROLQ $18, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ANDQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _ki(oState); \ + \ + MOVQ rBu, rT1; \ + ORQ rBa, rT1; \ + XORQ rBo, rT1; \ + MOVQ rT1, _ko(oState); \ + \ + ANDQ rBe, rBa; \ + XORQ rBu, rBa; \ + MOVQ rBa, _ku(oState); \ + K_RBA_RCU; \ + \ + /* Result m */ \ + MOVQ _ga(iState), rBe; \ + XORQ rDa, rBe; \ + MOVQ _ke(iState), rBi; \ + ROLQ $36, rBe; \ + XORQ rDe, rBi; \ + MOVQ _bu(iState), rBa; \ + ROLQ $10, rBi; \ + MOVQ rBe, rT1; \ + MOVQ _mi(iState), rBo; \ + ANDQ rBi, rT1; \ + XORQ rDu, rBa; \ + MOVQ _so(iState), rBu; \ + ROLQ $27, rBa; \ + XORQ rBa, rT1; \ + MOVQ rT1, _ma(oState); \ + M_RT1_RCA; \ + \ + XORQ rDi, rBo; \ + ROLQ $15, rBo; \ + MOVQ rBi, rT1; \ + ORQ rBo, rT1; \ + XORQ rBe, rT1; \ + MOVQ rT1, _me(oState); \ + M_RT1_RCE; \ + \ + XORQ rDo, rBu; \ + ROLQ $56, rBu; \ + NOTQ rBo; \ + MOVQ rBo, rT1; \ + ORQ rBu, rT1; \ + XORQ rBi, rT1; \ + MOVQ rT1, _mi(oState); \ + \ + ORQ rBa, rBe; \ + XORQ rBu, rBe; \ + MOVQ rBe, _mu(oState); \ + \ + ANDQ rBa, rBu; \ + XORQ rBo, rBu; \ + MOVQ rBu, _mo(oState); \ + M_RBE_RCU; \ + \ + /* Result s */ \ + MOVQ _bi(iState), rBa; \ + MOVQ _go(iState), rBe; \ + MOVQ _ku(iState), rBi; \ + XORQ rDi, rBa; \ + MOVQ _ma(iState), rBo; \ + ROLQ $62, rBa; \ + XORQ rDo, rBe; \ + MOVQ _se(iState), rBu; \ + ROLQ $55, rBe; \ + \ + XORQ rDu, rBi; \ + MOVQ rBa, rDu; \ + XORQ rDe, rBu; \ + ROLQ $2, rBu; \ + ANDQ rBe, rDu; \ + XORQ rBu, rDu; \ + MOVQ rDu, _su(oState); \ + \ + ROLQ $39, rBi; \ + S_RDU_RCU; \ + NOTQ rBe; \ + XORQ rDa, rBo; \ + MOVQ rBe, rDa; \ + ANDQ rBi, rDa; \ + XORQ rBa, rDa; \ + MOVQ rDa, _sa(oState); \ + S_RDA_RCA; \ + \ + ROLQ $41, rBo; \ + MOVQ rBi, rDe; \ + ORQ rBo, rDe; \ + XORQ rBe, rDe; \ + MOVQ rDe, _se(oState); \ + S_RDE_RCE; \ + \ + MOVQ rBo, rDi; \ + MOVQ rBu, rDo; \ + ANDQ rBu, rDi; \ + ORQ rBa, rDo; \ + XORQ rBi, rDi; \ + XORQ rBo, rDo; \ + MOVQ rDi, _si(oState); \ + MOVQ rDo, _so(oState) \ + +// func KeccakF1600(state *[25]uint64) +TEXT ·KeccakF1600(SB), 0, $200-8 + MOVQ state+0(FP), rpState + + // Convert the user state into an internal state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + // Execute the KeccakF permutation + MOVQ _ba(rpState), rCa + MOVQ _be(rpState), rCe + MOVQ _bu(rpState), rCu + + XORQ _ga(rpState), rCa + XORQ _ge(rpState), rCe + XORQ _gu(rpState), rCu + + XORQ _ka(rpState), rCa + XORQ _ke(rpState), rCe + XORQ _ku(rpState), rCu + + XORQ _ma(rpState), rCa + XORQ _me(rpState), rCe + XORQ _mu(rpState), rCu + + XORQ _sa(rpState), rCa + XORQ _se(rpState), rCe + MOVQ _si(rpState), rDi + MOVQ _so(rpState), rDo + XORQ _su(rpState), rCu + + mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE) + mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP) + + // Revert the internal state to the user state + NOTQ _be(rpState) + NOTQ _bi(rpState) + NOTQ _go(rpState) + NOTQ _ki(rpState) + NOTQ _mi(rpState) + NOTQ _sa(rpState) + + RET diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/rc.go b/vendor/github.com/cloudflare/circl/internal/sha3/rc.go new file mode 100644 index 00000000..6a3df42f --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/rc.go @@ -0,0 +1,29 @@ +package sha3 + +// RC stores the round constants for use in the ι step. +var RC = [24]uint64{ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go b/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go new file mode 100644 index 00000000..b35cd006 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go @@ -0,0 +1,195 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// spongeDirection indicates the direction bytes are flowing through the sponge. +type spongeDirection int + +const ( + // spongeAbsorbing indicates that the sponge is absorbing input. + spongeAbsorbing spongeDirection = iota + // spongeSqueezing indicates that the sponge is being squeezed. + spongeSqueezing +) + +const ( + // maxRate is the maximum size of the internal buffer. SHAKE-256 + // currently needs the largest buffer. + maxRate = 168 +) + +func (d *State) buf() []byte { + return d.storage.asBytes()[d.bufo:d.bufe] +} + +type State struct { + // Generic sponge components. + a [25]uint64 // main state of the hash + rate int // the number of bytes of state to use + + bufo int // offset of buffer in storage + bufe int // end of buffer in storage + + // dsbyte contains the "domain separation" bits and the first bit of + // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the + // SHA-3 and SHAKE functions by appending bitstrings to the message. + // Using a little-endian bit-ordering convention, these are "01" for SHA-3 + // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the + // padding rule from section 5.1 is applied to pad the message to a multiple + // of the rate, which involves adding a "1" bit, zero or more "0" bits, and + // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, + // giving 00000110b (0x06) and 00011111b (0x1f). + // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf + // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and + // Extendable-Output Functions (May 2014)" + dsbyte byte + + storage storageBuf + + // Specific to SHA-3 and SHAKE. + outputLen int // the default output size in bytes + state spongeDirection // whether the sponge is absorbing or squeezing +} + +// BlockSize returns the rate of sponge underlying this hash function. +func (d *State) BlockSize() int { return d.rate } + +// Size returns the output size of the hash function in bytes. +func (d *State) Size() int { return d.outputLen } + +// Reset clears the internal state by zeroing the sponge state and +// the byte buffer, and setting Sponge.state to absorbing. +func (d *State) Reset() { + // Zero the permutation's state. + for i := range d.a { + d.a[i] = 0 + } + d.state = spongeAbsorbing + d.bufo = 0 + d.bufe = 0 +} + +func (d *State) clone() *State { + ret := *d + return &ret +} + +// permute applies the KeccakF-1600 permutation. It handles +// any input-output buffering. +func (d *State) permute() { + switch d.state { + case spongeAbsorbing: + // If we're absorbing, we need to xor the input into the state + // before applying the permutation. + xorIn(d, d.buf()) + d.bufe = 0 + d.bufo = 0 + KeccakF1600(&d.a) + case spongeSqueezing: + // If we're squeezing, we need to apply the permutation before + // copying more output. + KeccakF1600(&d.a) + d.bufe = d.rate + d.bufo = 0 + copyOut(d, d.buf()) + } +} + +// pads appends the domain separation bits in dsbyte, applies +// the multi-bitrate 10..1 padding rule, and permutes the state. +func (d *State) padAndPermute(dsbyte byte) { + // Pad with this instance's domain-separator bits. We know that there's + // at least one byte of space in d.buf() because, if it were full, + // permute would have been called to empty it. dsbyte also contains the + // first one bit for the padding. See the comment in the state struct. + zerosStart := d.bufe + 1 + d.bufe = d.rate + buf := d.buf() + buf[zerosStart-1] = dsbyte + for i := zerosStart; i < d.rate; i++ { + buf[i] = 0 + } + // This adds the final one bit for the padding. Because of the way that + // bits are numbered from the LSB upwards, the final bit is the MSB of + // the last byte. + buf[d.rate-1] ^= 0x80 + // Apply the permutation + d.permute() + d.state = spongeSqueezing + d.bufe = d.rate + copyOut(d, buf) +} + +// Write absorbs more data into the hash's state. It produces an error +// if more data is written to the ShakeHash after writing +func (d *State) Write(p []byte) (written int, err error) { + if d.state != spongeAbsorbing { + panic("sha3: write to sponge after read") + } + written = len(p) + + for len(p) > 0 { + bufl := d.bufe - d.bufo + if bufl == 0 && len(p) >= d.rate { + // The fast path; absorb a full "rate" bytes of input and apply the permutation. + xorIn(d, p[:d.rate]) + p = p[d.rate:] + KeccakF1600(&d.a) + } else { + // The slow path; buffer the input until we can fill the sponge, and then xor it in. + todo := d.rate - bufl + if todo > len(p) { + todo = len(p) + } + d.bufe += todo + buf := d.buf() + copy(buf[bufl:], p[:todo]) + p = p[todo:] + + // If the sponge is full, apply the permutation. + if d.bufe == d.rate { + d.permute() + } + } + } + + return written, nil +} + +// Read squeezes an arbitrary number of bytes from the sponge. +func (d *State) Read(out []byte) (n int, err error) { + // If we're still absorbing, pad and apply the permutation. + if d.state == spongeAbsorbing { + d.padAndPermute(d.dsbyte) + } + + n = len(out) + + // Now, do the squeezing. + for len(out) > 0 { + buf := d.buf() + n := copy(out, buf) + d.bufo += n + out = out[n:] + + // Apply the permutation if we've squeezed the sponge dry. + if d.bufo == d.bufe { + d.permute() + } + } + + return +} + +// Sum applies padding to the hash state and then squeezes out the desired +// number of output bytes. +func (d *State) Sum(in []byte) []byte { + // Make a copy of the original hash so that caller can keep writing + // and summing. + dup := d.clone() + hash := make([]byte, dup.outputLen) + _, _ = dup.Read(hash) + return append(in, hash...) +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/sha3_s390x.s b/vendor/github.com/cloudflare/circl/internal/sha3/sha3_s390x.s new file mode 100644 index 00000000..8a4458f6 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/sha3_s390x.s @@ -0,0 +1,33 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo,!appengine + +#include "textflag.h" + +// func kimd(function code, chain *[200]byte, src []byte) +TEXT ·kimd(SB), NOFRAME|NOSPLIT, $0-40 + MOVD function+0(FP), R0 + MOVD chain+8(FP), R1 + LMG src+16(FP), R2, R3 // R2=base, R3=len + +continue: + WORD $0xB93E0002 // KIMD --, R2 + BVS continue // continue if interrupted + MOVD $0, R0 // reset R0 for pre-go1.8 compilers + RET + +// func klmd(function code, chain *[200]byte, dst, src []byte) +TEXT ·klmd(SB), NOFRAME|NOSPLIT, $0-64 + // TODO: SHAKE support + MOVD function+0(FP), R0 + MOVD chain+8(FP), R1 + LMG dst+16(FP), R2, R3 // R2=base, R3=len + LMG src+40(FP), R4, R5 // R4=base, R5=len + +continue: + WORD $0xB93F0024 // KLMD R2, R4 + BVS continue // continue if interrupted + MOVD $0, R0 // reset R0 for pre-go1.8 compilers + RET diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/shake.go b/vendor/github.com/cloudflare/circl/internal/sha3/shake.go new file mode 100644 index 00000000..b92c5b7d --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/shake.go @@ -0,0 +1,79 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sha3 + +// This file defines the ShakeHash interface, and provides +// functions for creating SHAKE and cSHAKE instances, as well as utility +// functions for hashing bytes to arbitrary-length output. +// +// +// SHAKE implementation is based on FIPS PUB 202 [1] +// cSHAKE implementations is based on NIST SP 800-185 [2] +// +// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf +// [2] https://doi.org/10.6028/NIST.SP.800-185 + +import ( + "io" +) + +// ShakeHash defines the interface to hash functions that +// support arbitrary-length output. +type ShakeHash interface { + // Write absorbs more data into the hash's state. It panics if input is + // written to it after output has been read from it. + io.Writer + + // Read reads more output from the hash; reading affects the hash's + // state. (ShakeHash.Read is thus very different from Hash.Sum) + // It never returns an error. + io.Reader + + // Clone returns a copy of the ShakeHash in its current state. + Clone() ShakeHash + + // Reset resets the ShakeHash to its initial state. + Reset() +} + +// Consts for configuring initial SHA-3 state +const ( + dsbyteShake = 0x1f + rate128 = 168 + rate256 = 136 +) + +// Clone returns copy of SHAKE context within its current state. +func (d *State) Clone() ShakeHash { + return d.clone() +} + +// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 128 bits against all attacks if at +// least 32 bytes of its output are used. +func NewShake128() State { + return State{rate: rate128, dsbyte: dsbyteShake} +} + +// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. +// Its generic security strength is 256 bits against all attacks if +// at least 64 bytes of its output are used. +func NewShake256() State { + return State{rate: rate256, dsbyte: dsbyteShake} +} + +// ShakeSum128 writes an arbitrary-length digest of data into hash. +func ShakeSum128(hash, data []byte) { + h := NewShake128() + _, _ = h.Write(data) + _, _ = h.Read(hash) +} + +// ShakeSum256 writes an arbitrary-length digest of data into hash. +func ShakeSum256(hash, data []byte) { + h := NewShake256() + _, _ = h.Write(data) + _, _ = h.Read(hash) +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/xor.go b/vendor/github.com/cloudflare/circl/internal/sha3/xor.go new file mode 100644 index 00000000..1e213374 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/xor.go @@ -0,0 +1,15 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (!amd64 && !386 && !ppc64le) || appengine +// +build !amd64,!386,!ppc64le appengine + +package sha3 + +// A storageBuf is an aligned array of maxRate bytes. +type storageBuf [maxRate]byte + +func (b *storageBuf) asBytes() *[maxRate]byte { + return (*[maxRate]byte)(b) +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/xor_generic.go b/vendor/github.com/cloudflare/circl/internal/sha3/xor_generic.go new file mode 100644 index 00000000..2b0c6617 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/xor_generic.go @@ -0,0 +1,33 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (!amd64 || appengine) && (!386 || appengine) && (!ppc64le || appengine) +// +build !amd64 appengine +// +build !386 appengine +// +build !ppc64le appengine + +package sha3 + +import "encoding/binary" + +// xorIn xors the bytes in buf into the state; it +// makes no non-portable assumptions about memory layout +// or alignment. +func xorIn(d *State, buf []byte) { + n := len(buf) / 8 + + for i := 0; i < n; i++ { + a := binary.LittleEndian.Uint64(buf) + d.a[i] ^= a + buf = buf[8:] + } +} + +// copyOut copies ulint64s to a byte buffer. +func copyOut(d *State, b []byte) { + for i := 0; len(b) >= 8; i++ { + binary.LittleEndian.PutUint64(b, d.a[i]) + b = b[8:] + } +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go b/vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go new file mode 100644 index 00000000..052fc8d3 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.go @@ -0,0 +1,61 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build (amd64 || 386 || ppc64le) && !appengine +// +build amd64 386 ppc64le +// +build !appengine + +package sha3 + +import "unsafe" + +// A storageBuf is an aligned array of maxRate bytes. +type storageBuf [maxRate / 8]uint64 + +func (b *storageBuf) asBytes() *[maxRate]byte { + return (*[maxRate]byte)(unsafe.Pointer(b)) +} + +// xorInuses unaligned reads and writes to update d.a to contain d.a +// XOR buf. +func xorIn(d *State, buf []byte) { + n := len(buf) + bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8] + if n >= 72 { + d.a[0] ^= bw[0] + d.a[1] ^= bw[1] + d.a[2] ^= bw[2] + d.a[3] ^= bw[3] + d.a[4] ^= bw[4] + d.a[5] ^= bw[5] + d.a[6] ^= bw[6] + d.a[7] ^= bw[7] + d.a[8] ^= bw[8] + } + if n >= 104 { + d.a[9] ^= bw[9] + d.a[10] ^= bw[10] + d.a[11] ^= bw[11] + d.a[12] ^= bw[12] + } + if n >= 136 { + d.a[13] ^= bw[13] + d.a[14] ^= bw[14] + d.a[15] ^= bw[15] + d.a[16] ^= bw[16] + } + if n >= 144 { + d.a[17] ^= bw[17] + } + if n >= 168 { + d.a[18] ^= bw[18] + d.a[19] ^= bw[19] + d.a[20] ^= bw[20] + } +} + +func copyOut(d *State, buf []byte) { + ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) + copy(buf, ab[:]) +} diff --git a/vendor/github.com/cloudflare/circl/kem/hybrid/hybrid.go b/vendor/github.com/cloudflare/circl/kem/hybrid/hybrid.go new file mode 100644 index 00000000..f0384278 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/hybrid/hybrid.go @@ -0,0 +1,335 @@ +// Package hybrid defines several hybrid classical/quantum KEMs. +// +// KEMs are combined by simple concatenation of shared secrets, cipher texts, +// public keys, etc, see +// +// https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/ +// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr2.pdf +// +// Note that this is only fine if the shared secret is used in its entirety +// in a next step, such as being hashed or used as key. +// +// For deriving a KEM keypair deterministically and encapsulating +// deterministically, we expand a single seed to both using SHAKE256, +// so that a non-uniform seed (such as a shared secret generated by a hybrid +// KEM where one of the KEMs is weak) doesn't impact just one of the KEMs. +// +// Of our XOF (SHAKE256), we desire two security properties: +// +// 1. The internal state of the XOF should be big enough so that we +// do not loose entropy. +// 2. From one of the new seeds, we shouldn't be able to derive +// the other or the original seed. +// +// SHAKE256, and all siblings in the SHA3 family, have a 200B internal +// state, so (1) is fine if our seeds are less than 200B. +// If SHAKE256 is computationally indistinguishable from a random +// sponge, then it affords us 256b security against (2) by the +// flat sponge claim [https://keccak.team/files/SpongeFunctions.pdf]. +// None of the implemented schemes claim more than 256b security +// and so SHAKE256 will do fine. +package hybrid + +import ( + "errors" + + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/kyber/kyber1024" + "github.com/cloudflare/circl/kem/kyber/kyber512" + "github.com/cloudflare/circl/kem/kyber/kyber768" +) + +var ErrUninitialized = errors.New("public or private key not initialized") + +// Returns the hybrid KEM of Kyber512 and X25519. +func Kyber512X25519() kem.Scheme { return kyber512X } + +// Returns the hybrid KEM of Kyber768 and X25519. +func Kyber768X25519() kem.Scheme { return kyber768X } + +// Returns the hybrid KEM of Kyber768 and X448. +func Kyber768X448() kem.Scheme { return kyber768X4 } + +// Returns the hybrid KEM of Kyber1024 and X448. +func Kyber1024X448() kem.Scheme { return kyber1024X } + +var kyber512X kem.Scheme = &scheme{ + "Kyber512-X25519", + x25519Kem, + kyber512.Scheme(), +} + +var kyber768X kem.Scheme = &scheme{ + "Kyber768-X25519", + x25519Kem, + kyber768.Scheme(), +} + +var kyber768X4 kem.Scheme = &scheme{ + "Kyber768-X448", + x448Kem, + kyber768.Scheme(), +} + +var kyber1024X kem.Scheme = &scheme{ + "Kyber1024-X448", + x448Kem, + kyber1024.Scheme(), +} + +// Public key of a hybrid KEM. +type publicKey struct { + scheme *scheme + first kem.PublicKey + second kem.PublicKey +} + +// Private key of a hybrid KEM. +type privateKey struct { + scheme *scheme + first kem.PrivateKey + second kem.PrivateKey +} + +// Scheme for a hybrid KEM. +type scheme struct { + name string + first kem.Scheme + second kem.Scheme +} + +func (sch *scheme) Name() string { return sch.name } +func (sch *scheme) PublicKeySize() int { + return sch.first.PublicKeySize() + sch.second.PublicKeySize() +} + +func (sch *scheme) PrivateKeySize() int { + return sch.first.PrivateKeySize() + sch.second.PrivateKeySize() +} + +func (sch *scheme) SeedSize() int { + first := sch.first.SeedSize() + second := sch.second.SeedSize() + ret := second + if first > second { + ret = first + } + return ret +} + +func (sch *scheme) SharedKeySize() int { + return sch.first.SharedKeySize() + sch.second.SharedKeySize() +} + +func (sch *scheme) CiphertextSize() int { + return sch.first.CiphertextSize() + sch.second.CiphertextSize() +} + +func (sch *scheme) EncapsulationSeedSize() int { + first := sch.first.EncapsulationSeedSize() + second := sch.second.EncapsulationSeedSize() + ret := second + if first > second { + ret = first + } + return ret +} + +func (sk *privateKey) Scheme() kem.Scheme { return sk.scheme } +func (pk *publicKey) Scheme() kem.Scheme { return pk.scheme } + +func (sk *privateKey) MarshalBinary() ([]byte, error) { + if sk.first == nil || sk.second == nil { + return nil, ErrUninitialized + } + first, err := sk.first.MarshalBinary() + if err != nil { + return nil, err + } + second, err := sk.second.MarshalBinary() + if err != nil { + return nil, err + } + return append(first, second...), nil +} + +func (sk *privateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*privateKey) + if !ok { + return false + } + if sk.first == nil && sk.second == nil && oth.first == nil && oth.second == nil { + return true + } + if sk.first == nil || sk.second == nil || oth.first == nil || oth.second == nil { + return false + } + return sk.first.Equal(oth.first) && sk.second.Equal(oth.second) +} + +func (sk *privateKey) Public() kem.PublicKey { + return &publicKey{sk.scheme, sk.first.Public(), sk.second.Public()} +} + +func (pk *publicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*publicKey) + if !ok { + return false + } + if pk.first == nil && pk.second == nil && oth.first == nil && oth.second == nil { + return true + } + if pk.first == nil || pk.second == nil || oth.first == nil || oth.second == nil { + return false + } + return pk.first.Equal(oth.first) && pk.second.Equal(oth.second) +} + +func (pk *publicKey) MarshalBinary() ([]byte, error) { + if pk.first == nil || pk.second == nil { + return nil, ErrUninitialized + } + first, err := pk.first.MarshalBinary() + if err != nil { + return nil, err + } + second, err := pk.second.MarshalBinary() + if err != nil { + return nil, err + } + return append(first, second...), nil +} + +func (sch *scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + pk1, sk1, err := sch.first.GenerateKeyPair() + if err != nil { + return nil, nil, err + } + pk2, sk2, err := sch.second.GenerateKeyPair() + if err != nil { + return nil, nil, err + } + + return &publicKey{sch, pk1, pk2}, &privateKey{sch, sk1, sk2}, nil +} + +func (sch *scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + if len(seed) != sch.SeedSize() { + panic(kem.ErrSeedSize) + } + h := sha3.NewShake256() + _, _ = h.Write(seed) + first := make([]byte, sch.first.SeedSize()) + second := make([]byte, sch.second.SeedSize()) + _, _ = h.Read(first) + _, _ = h.Read(second) + + pk1, sk1 := sch.first.DeriveKeyPair(first) + pk2, sk2 := sch.second.DeriveKeyPair(second) + + return &publicKey{sch, pk1, pk2}, &privateKey{sch, sk1, sk2} +} + +func (sch *scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + pub, ok := pk.(*publicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + ct1, ss1, err := sch.first.Encapsulate(pub.first) + if err != nil { + return nil, nil, err + } + + ct2, ss2, err := sch.second.Encapsulate(pub.second) + if err != nil { + return nil, nil, err + } + + return append(ct1, ct2...), append(ss1, ss2...), nil +} + +func (sch *scheme) EncapsulateDeterministically( + pk kem.PublicKey, seed []byte, +) (ct, ss []byte, err error) { + if len(seed) != sch.EncapsulationSeedSize() { + return nil, nil, kem.ErrSeedSize + } + + h := sha3.NewShake256() + _, _ = h.Write(seed) + first := make([]byte, sch.first.EncapsulationSeedSize()) + second := make([]byte, sch.second.EncapsulationSeedSize()) + _, _ = h.Read(first) + _, _ = h.Read(second) + + pub, ok := pk.(*publicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + + ct1, ss1, err := sch.first.EncapsulateDeterministically(pub.first, first) + if err != nil { + return nil, nil, err + } + ct2, ss2, err := sch.second.EncapsulateDeterministically(pub.second, second) + if err != nil { + return nil, nil, err + } + return append(ct1, ct2...), append(ss1, ss2...), nil +} + +func (sch *scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + if len(ct) != sch.CiphertextSize() { + return nil, kem.ErrCiphertextSize + } + + priv, ok := sk.(*privateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + + firstSize := sch.first.CiphertextSize() + ss1, err := sch.first.Decapsulate(priv.first, ct[:firstSize]) + if err != nil { + return nil, err + } + ss2, err := sch.second.Decapsulate(priv.second, ct[firstSize:]) + if err != nil { + return nil, err + } + return append(ss1, ss2...), nil +} + +func (sch *scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != sch.PublicKeySize() { + return nil, kem.ErrPubKeySize + } + firstSize := sch.first.PublicKeySize() + pk1, err := sch.first.UnmarshalBinaryPublicKey(buf[:firstSize]) + if err != nil { + return nil, err + } + pk2, err := sch.second.UnmarshalBinaryPublicKey(buf[firstSize:]) + if err != nil { + return nil, err + } + return &publicKey{sch, pk1, pk2}, nil +} + +func (sch *scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != sch.PrivateKeySize() { + return nil, kem.ErrPrivKeySize + } + firstSize := sch.first.PrivateKeySize() + sk1, err := sch.first.UnmarshalBinaryPrivateKey(buf[:firstSize]) + if err != nil { + return nil, err + } + sk2, err := sch.second.UnmarshalBinaryPrivateKey(buf[firstSize:]) + if err != nil { + return nil, err + } + return &privateKey{sch, sk1, sk2}, nil +} diff --git a/vendor/github.com/cloudflare/circl/kem/hybrid/xkem.go b/vendor/github.com/cloudflare/circl/kem/hybrid/xkem.go new file mode 100644 index 00000000..919fb8a9 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/hybrid/xkem.go @@ -0,0 +1,208 @@ +package hybrid + +import ( + "bytes" + cryptoRand "crypto/rand" + "crypto/subtle" + + "github.com/cloudflare/circl/dh/x25519" + "github.com/cloudflare/circl/dh/x448" + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/kem" +) + +type xPublicKey struct { + scheme *xScheme + key []byte +} +type xPrivateKey struct { + scheme *xScheme + key []byte +} +type xScheme struct { + size int +} + +var ( + x25519Kem = &xScheme{x25519.Size} + x448Kem = &xScheme{x448.Size} +) + +func (sch *xScheme) Name() string { + switch sch.size { + case x25519.Size: + return "X25519" + case x448.Size: + return "X448" + } + panic(kem.ErrTypeMismatch) +} + +func (sch *xScheme) PublicKeySize() int { return sch.size } +func (sch *xScheme) PrivateKeySize() int { return sch.size } +func (sch *xScheme) SeedSize() int { return sch.size } +func (sch *xScheme) SharedKeySize() int { return sch.size } +func (sch *xScheme) CiphertextSize() int { return sch.size } +func (sch *xScheme) EncapsulationSeedSize() int { return sch.size } + +func (sk *xPrivateKey) Scheme() kem.Scheme { return sk.scheme } +func (pk *xPublicKey) Scheme() kem.Scheme { return pk.scheme } + +func (sk *xPrivateKey) MarshalBinary() ([]byte, error) { + ret := make([]byte, len(sk.key)) + copy(ret, sk.key) + return ret, nil +} + +func (sk *xPrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*xPrivateKey) + if !ok { + return false + } + if oth.scheme != sk.scheme { + return false + } + return subtle.ConstantTimeCompare(oth.key, sk.key) == 1 +} + +func (sk *xPrivateKey) Public() kem.PublicKey { + pk := xPublicKey{sk.scheme, make([]byte, sk.scheme.size)} + switch sk.scheme.size { + case x25519.Size: + var sk2, pk2 x25519.Key + copy(sk2[:], sk.key) + x25519.KeyGen(&pk2, &sk2) + copy(pk.key, pk2[:]) + case x448.Size: + var sk2, pk2 x448.Key + copy(sk2[:], sk.key) + x448.KeyGen(&pk2, &sk2) + copy(pk.key, pk2[:]) + } + return &pk +} + +func (pk *xPublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*xPublicKey) + if !ok { + return false + } + if oth.scheme != pk.scheme { + return false + } + return bytes.Equal(oth.key, pk.key) +} + +func (pk *xPublicKey) MarshalBinary() ([]byte, error) { + ret := make([]byte, pk.scheme.size) + copy(ret, pk.key) + return ret, nil +} + +func (sch *xScheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + seed := make([]byte, sch.SeedSize()) + _, err := cryptoRand.Read(seed) + if err != nil { + return nil, nil, err + } + pk, sk := sch.DeriveKeyPair(seed) + return pk, sk, nil +} + +func (sch *xScheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + if len(seed) != sch.SeedSize() { + panic(kem.ErrSeedSize) + } + sk := xPrivateKey{scheme: sch, key: make([]byte, sch.size)} + + h := sha3.NewShake256() + _, _ = h.Write(seed) + _, _ = h.Read(sk.key) + + return sk.Public(), &sk +} + +func (sch *xScheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + seed := make([]byte, sch.EncapsulationSeedSize()) + _, err = cryptoRand.Read(seed) + if err != nil { + return + } + return sch.EncapsulateDeterministically(pk, seed) +} + +func (pk *xPublicKey) X(sk *xPrivateKey) []byte { + if pk.scheme != sk.scheme { + panic(kem.ErrTypeMismatch) + } + + switch pk.scheme.size { + case x25519.Size: + var ss2, pk2, sk2 x25519.Key + copy(pk2[:], pk.key) + copy(sk2[:], sk.key) + x25519.Shared(&ss2, &sk2, &pk2) + return ss2[:] + case x448.Size: + var ss2, pk2, sk2 x448.Key + copy(pk2[:], pk.key) + copy(sk2[:], sk.key) + x448.Shared(&ss2, &sk2, &pk2) + return ss2[:] + } + panic(kem.ErrTypeMismatch) +} + +func (sch *xScheme) EncapsulateDeterministically( + pk kem.PublicKey, seed []byte, +) (ct, ss []byte, err error) { + if len(seed) != sch.EncapsulationSeedSize() { + return nil, nil, kem.ErrSeedSize + } + pub, ok := pk.(*xPublicKey) + if !ok || pub.scheme != sch { + return nil, nil, kem.ErrTypeMismatch + } + + pk2, sk2 := sch.DeriveKeyPair(seed) + ss = pub.X(sk2.(*xPrivateKey)) + ct, _ = pk2.MarshalBinary() + return +} + +func (sch *xScheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + if len(ct) != sch.CiphertextSize() { + return nil, kem.ErrCiphertextSize + } + + priv, ok := sk.(*xPrivateKey) + if !ok || priv.scheme != sch { + return nil, kem.ErrTypeMismatch + } + + pk, err := sch.UnmarshalBinaryPublicKey(ct) + if err != nil { + return nil, err + } + + ss := pk.(*xPublicKey).X(priv) + return ss, nil +} + +func (sch *xScheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != sch.PublicKeySize() { + return nil, kem.ErrPubKeySize + } + ret := xPublicKey{sch, make([]byte, sch.size)} + copy(ret.key, buf) + return &ret, nil +} + +func (sch *xScheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != sch.PrivateKeySize() { + return nil, kem.ErrPrivKeySize + } + ret := xPrivateKey{sch, make([]byte, sch.size)} + copy(ret.key, buf) + return &ret, nil +} diff --git a/vendor/github.com/cloudflare/circl/kem/kem.go b/vendor/github.com/cloudflare/circl/kem/kem.go new file mode 100644 index 00000000..ca550c65 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/kem.go @@ -0,0 +1,118 @@ +// Package kem provides a unified interface for KEM schemes. +// +// A register of schemes is available in the package +// +// github.com/cloudflare/circl/kem/schemes +package kem + +import ( + "encoding" + "errors" +) + +// A KEM public key +type PublicKey interface { + // Returns the scheme for this public key + Scheme() Scheme + + encoding.BinaryMarshaler + Equal(PublicKey) bool +} + +// A KEM private key +type PrivateKey interface { + // Returns the scheme for this private key + Scheme() Scheme + + encoding.BinaryMarshaler + Equal(PrivateKey) bool + Public() PublicKey +} + +// A Scheme represents a specific instance of a KEM. +type Scheme interface { + // Name of the scheme + Name() string + + // GenerateKeyPair creates a new key pair. + GenerateKeyPair() (PublicKey, PrivateKey, error) + + // Encapsulate generates a shared key ss for the public key and + // encapsulates it into a ciphertext ct. + Encapsulate(pk PublicKey) (ct, ss []byte, err error) + + // Returns the shared key encapsulated in ciphertext ct for the + // private key sk. + Decapsulate(sk PrivateKey, ct []byte) ([]byte, error) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPublicKey([]byte) (PublicKey, error) + + // Unmarshals a PrivateKey from the provided buffer. + UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) + + // Size of encapsulated keys. + CiphertextSize() int + + // Size of established shared keys. + SharedKeySize() int + + // Size of packed private keys. + PrivateKeySize() int + + // Size of packed public keys. + PublicKeySize() int + + // DeriveKeyPair deterministicallly derives a pair of keys from a seed. + // Panics if the length of seed is not equal to the value returned by + // SeedSize. + DeriveKeyPair(seed []byte) (PublicKey, PrivateKey) + + // Size of seed used in DeriveKey + SeedSize() int + + // EncapsulateDeterministically generates a shared key ss for the public + // key deterministically from the given seed and encapsulates it into + // a ciphertext ct. If unsure, you're better off using Encapsulate(). + EncapsulateDeterministically(pk PublicKey, seed []byte) ( + ct, ss []byte, err error) + + // Size of seed used in EncapsulateDeterministically(). + EncapsulationSeedSize() int +} + +// AuthScheme represents a KEM that supports authenticated key encapsulation. +type AuthScheme interface { + Scheme + AuthEncapsulate(pkr PublicKey, sks PrivateKey) (ct, ss []byte, err error) + AuthEncapsulateDeterministically(pkr PublicKey, sks PrivateKey, seed []byte) (ct, ss []byte, err error) + AuthDecapsulate(skr PrivateKey, ct []byte, pks PublicKey) ([]byte, error) +} + +var ( + // ErrTypeMismatch is the error used if types of, for instance, private + // and public keys don't match + ErrTypeMismatch = errors.New("types mismatch") + + // ErrSeedSize is the error used if the provided seed is of the wrong + // size. + ErrSeedSize = errors.New("wrong seed size") + + // ErrPubKeySize is the error used if the provided public key is of + // the wrong size. + ErrPubKeySize = errors.New("wrong size for public key") + + // ErrCiphertextSize is the error used if the provided ciphertext + // is of the wrong size. + ErrCiphertextSize = errors.New("wrong size for ciphertext") + + // ErrPrivKeySize is the error used if the provided private key is of + // the wrong size. + ErrPrivKeySize = errors.New("wrong size for private key") + + // ErrPubKey is the error used if the provided public key is invalid. + ErrPubKey = errors.New("invalid public key") + + // ErrCipherText is the error used if the provided ciphertext is invalid. + ErrCipherText = errors.New("invalid ciphertext") +) diff --git a/vendor/github.com/cloudflare/circl/kem/kyber/kyber1024/kyber.go b/vendor/github.com/cloudflare/circl/kem/kyber/kyber1024/kyber.go new file mode 100644 index 00000000..223c842e --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/kyber/kyber1024/kyber.go @@ -0,0 +1,402 @@ +// Code generated from pkg.templ.go. DO NOT EDIT. + +// Package kyber1024 implements the IND-CCA2 secure key encapsulation mechanism +// Kyber1024.CCAKEM as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber1024 + +import ( + "bytes" + "crypto/subtle" + "io" + + cryptoRand "crypto/rand" + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/kem" + cpapke "github.com/cloudflare/circl/pke/kyber/kyber1024" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = cpapke.KeySeedSize + 32 + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 32 + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = cpapke.CiphertextSize + + // Size of a packed public key. + PublicKeySize = cpapke.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = cpapke.PrivateKeySize + cpapke.PublicKeySize + 64 +) + +// Type of a Kyber1024.CCAKEM public key +type PublicKey struct { + pk *cpapke.PublicKey + + hpk [32]byte // H(pk) +} + +// Type of a Kyber1024.CCAKEM private key +type PrivateKey struct { + sk *cpapke.PrivateKey + pk *cpapke.PublicKey + hpk [32]byte // H(pk) + z [32]byte +} + +// NewKeyFromSeed derives a public/private keypair deterministically +// from the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var sk PrivateKey + var pk PublicKey + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + pk.pk, sk.sk = cpapke.NewKeyFromSeed(seed[:cpapke.KeySeedSize]) + sk.pk = pk.pk + copy(sk.z[:], seed[cpapke.KeySeedSize:]) + + // Compute H(pk) + var ppk [cpapke.PublicKeySize]byte + sk.pk.Pack(ppk[:]) + h := sha3.New256() + h.Write(ppk[:]) + h.Read(sk.hpk[:]) + copy(pk.hpk[:], sk.hpk[:]) + + return &pk, &sk +} + +// GenerateKeyPair generates public and private keys using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := NewKeyFromSeed(seed[:]) + return pk, sk, nil +} + +// EncapsulateTo generates a shared key and ciphertext that contains it +// for the public key using randomness from seed and writes the shared key +// to ss and ciphertext to ct. +// +// Panics if ss, ct or seed are not of length SharedKeySize, CiphertextSize +// and EncapsulationSeedSize respectively. +// +// seed may be nil, in which case crypto/rand.Reader is used to generate one. +func (pk *PublicKey) EncapsulateTo(ct, ss []byte, seed []byte) { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed[:]) + } else { + if len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + } + + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m = H(seed) + var m [32]byte + h := sha3.New256() + h.Write(seed[:]) + h.Read(m[:]) + + // (K', r) = G(m ‖ H(pk)) + var kr [64]byte + g := sha3.New512() + g.Write(m[:]) + g.Write(pk.hpk[:]) + g.Read(kr[:]) + + // c = Kyber.CPAPKE.Enc(pk, m, r) + pk.pk.EncryptTo(ct, m[:], kr[32:]) + + // Compute H(c) and put in second slot of kr, which will be (K', H(c)). + h.Reset() + h.Write(ct[:CiphertextSize]) + h.Read(kr[32:]) + + // K = KDF(K' ‖ H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// DecapsulateTo computes the shared key which is encapsulated in ct +// for the private key. +// +// Panics if ct or ss are not of length CiphertextSize and SharedKeySize +// respectively. +func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m' = Kyber.CPAPKE.Dec(sk, ct) + var m2 [32]byte + sk.sk.DecryptTo(m2[:], ct) + + // (K'', r') = G(m' ‖ H(pk)) + var kr2 [64]byte + g := sha3.New512() + g.Write(m2[:]) + g.Write(sk.hpk[:]) + g.Read(kr2[:]) + + // c' = Kyber.CPAPKE.Enc(pk, m', r') + var ct2 [CiphertextSize]byte + sk.pk.EncryptTo(ct2[:], m2[:], kr2[32:]) + + // Compute H(c) and put in second slot of kr2, which will be (K'', H(c)). + h := sha3.New256() + h.Write(ct[:CiphertextSize]) + h.Read(kr2[32:]) + + // Replace K'' by z in the first slot of kr2 if c ≠ c'. + subtle.ConstantTimeCopy( + 1-subtle.ConstantTimeCompare(ct, ct2[:]), + kr2[:32], + sk.z[:], + ) + + // K = KDF(K''/z, H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr2[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// Packs sk to buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk.Pack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk.Pack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(buf, sk.hpk[:]) + buf = buf[32:] + copy(buf, sk.z[:]) +} + +// Unpacks sk from buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk = new(cpapke.PrivateKey) + sk.sk.Unpack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk = new(cpapke.PublicKey) + sk.pk.Unpack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(sk.hpk[:], buf[:32]) + copy(sk.z[:], buf[32:]) +} + +// Packs pk to buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk.Pack(buf) +} + +// Unpacks pk from buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk = new(cpapke.PublicKey) + pk.pk.Unpack(buf) + + // Compute cached H(pk) + h := sha3.New256() + h.Write(buf) + h.Read(pk.hpk[:]) +} + +// Boilerplate down below for the KEM scheme API. + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "Kyber1024" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + sk.Pack(ret[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + if sk.pk == nil && oth.pk == nil { + return true + } + if sk.pk == nil || oth.pk == nil { + return false + } + if !bytes.Equal(sk.hpk[:], oth.hpk[:]) || + !bytes.Equal(sk.z[:], oth.z[:]) { + return false + } + return sk.sk.Equal(oth.sk) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + if pk.pk == nil && oth.pk == nil { + return true + } + if pk.pk == nil || oth.pk == nil { + return false + } + return bytes.Equal(pk.hpk[:], oth.hpk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + pk := new(PublicKey) + pk.pk = sk.pk + copy(pk.hpk[:], sk.hpk[:]) + return pk +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + pk.Pack(ret[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + return GenerateKeyPair(cryptoRand.Reader) +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + if len(seed) != KeySeedSize { + panic(kem.ErrSeedSize) + } + return NewKeyFromSeed(seed[:]) +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, nil) + return +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) ( + ct, ss []byte, err error) { + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, seed) + return +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + + priv, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + ss := make([]byte, SharedKeySize) + priv.DecapsulateTo(ss, ct) + return ss, nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + var ret PublicKey + ret.Unpack(buf) + return &ret, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + var ret PrivateKey + ret.Unpack(buf) + return &ret, nil +} diff --git a/vendor/github.com/cloudflare/circl/kem/kyber/kyber512/kyber.go b/vendor/github.com/cloudflare/circl/kem/kyber/kyber512/kyber.go new file mode 100644 index 00000000..8cc1ec76 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/kyber/kyber512/kyber.go @@ -0,0 +1,402 @@ +// Code generated from pkg.templ.go. DO NOT EDIT. + +// Package kyber512 implements the IND-CCA2 secure key encapsulation mechanism +// Kyber512.CCAKEM as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber512 + +import ( + "bytes" + "crypto/subtle" + "io" + + cryptoRand "crypto/rand" + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/kem" + cpapke "github.com/cloudflare/circl/pke/kyber/kyber512" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = cpapke.KeySeedSize + 32 + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 32 + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = cpapke.CiphertextSize + + // Size of a packed public key. + PublicKeySize = cpapke.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = cpapke.PrivateKeySize + cpapke.PublicKeySize + 64 +) + +// Type of a Kyber512.CCAKEM public key +type PublicKey struct { + pk *cpapke.PublicKey + + hpk [32]byte // H(pk) +} + +// Type of a Kyber512.CCAKEM private key +type PrivateKey struct { + sk *cpapke.PrivateKey + pk *cpapke.PublicKey + hpk [32]byte // H(pk) + z [32]byte +} + +// NewKeyFromSeed derives a public/private keypair deterministically +// from the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var sk PrivateKey + var pk PublicKey + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + pk.pk, sk.sk = cpapke.NewKeyFromSeed(seed[:cpapke.KeySeedSize]) + sk.pk = pk.pk + copy(sk.z[:], seed[cpapke.KeySeedSize:]) + + // Compute H(pk) + var ppk [cpapke.PublicKeySize]byte + sk.pk.Pack(ppk[:]) + h := sha3.New256() + h.Write(ppk[:]) + h.Read(sk.hpk[:]) + copy(pk.hpk[:], sk.hpk[:]) + + return &pk, &sk +} + +// GenerateKeyPair generates public and private keys using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := NewKeyFromSeed(seed[:]) + return pk, sk, nil +} + +// EncapsulateTo generates a shared key and ciphertext that contains it +// for the public key using randomness from seed and writes the shared key +// to ss and ciphertext to ct. +// +// Panics if ss, ct or seed are not of length SharedKeySize, CiphertextSize +// and EncapsulationSeedSize respectively. +// +// seed may be nil, in which case crypto/rand.Reader is used to generate one. +func (pk *PublicKey) EncapsulateTo(ct, ss []byte, seed []byte) { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed[:]) + } else { + if len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + } + + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m = H(seed) + var m [32]byte + h := sha3.New256() + h.Write(seed[:]) + h.Read(m[:]) + + // (K', r) = G(m ‖ H(pk)) + var kr [64]byte + g := sha3.New512() + g.Write(m[:]) + g.Write(pk.hpk[:]) + g.Read(kr[:]) + + // c = Kyber.CPAPKE.Enc(pk, m, r) + pk.pk.EncryptTo(ct, m[:], kr[32:]) + + // Compute H(c) and put in second slot of kr, which will be (K', H(c)). + h.Reset() + h.Write(ct[:CiphertextSize]) + h.Read(kr[32:]) + + // K = KDF(K' ‖ H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// DecapsulateTo computes the shared key which is encapsulated in ct +// for the private key. +// +// Panics if ct or ss are not of length CiphertextSize and SharedKeySize +// respectively. +func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m' = Kyber.CPAPKE.Dec(sk, ct) + var m2 [32]byte + sk.sk.DecryptTo(m2[:], ct) + + // (K'', r') = G(m' ‖ H(pk)) + var kr2 [64]byte + g := sha3.New512() + g.Write(m2[:]) + g.Write(sk.hpk[:]) + g.Read(kr2[:]) + + // c' = Kyber.CPAPKE.Enc(pk, m', r') + var ct2 [CiphertextSize]byte + sk.pk.EncryptTo(ct2[:], m2[:], kr2[32:]) + + // Compute H(c) and put in second slot of kr2, which will be (K'', H(c)). + h := sha3.New256() + h.Write(ct[:CiphertextSize]) + h.Read(kr2[32:]) + + // Replace K'' by z in the first slot of kr2 if c ≠ c'. + subtle.ConstantTimeCopy( + 1-subtle.ConstantTimeCompare(ct, ct2[:]), + kr2[:32], + sk.z[:], + ) + + // K = KDF(K''/z, H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr2[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// Packs sk to buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk.Pack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk.Pack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(buf, sk.hpk[:]) + buf = buf[32:] + copy(buf, sk.z[:]) +} + +// Unpacks sk from buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk = new(cpapke.PrivateKey) + sk.sk.Unpack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk = new(cpapke.PublicKey) + sk.pk.Unpack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(sk.hpk[:], buf[:32]) + copy(sk.z[:], buf[32:]) +} + +// Packs pk to buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk.Pack(buf) +} + +// Unpacks pk from buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk = new(cpapke.PublicKey) + pk.pk.Unpack(buf) + + // Compute cached H(pk) + h := sha3.New256() + h.Write(buf) + h.Read(pk.hpk[:]) +} + +// Boilerplate down below for the KEM scheme API. + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "Kyber512" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + sk.Pack(ret[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + if sk.pk == nil && oth.pk == nil { + return true + } + if sk.pk == nil || oth.pk == nil { + return false + } + if !bytes.Equal(sk.hpk[:], oth.hpk[:]) || + !bytes.Equal(sk.z[:], oth.z[:]) { + return false + } + return sk.sk.Equal(oth.sk) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + if pk.pk == nil && oth.pk == nil { + return true + } + if pk.pk == nil || oth.pk == nil { + return false + } + return bytes.Equal(pk.hpk[:], oth.hpk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + pk := new(PublicKey) + pk.pk = sk.pk + copy(pk.hpk[:], sk.hpk[:]) + return pk +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + pk.Pack(ret[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + return GenerateKeyPair(cryptoRand.Reader) +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + if len(seed) != KeySeedSize { + panic(kem.ErrSeedSize) + } + return NewKeyFromSeed(seed[:]) +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, nil) + return +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) ( + ct, ss []byte, err error) { + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, seed) + return +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + + priv, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + ss := make([]byte, SharedKeySize) + priv.DecapsulateTo(ss, ct) + return ss, nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + var ret PublicKey + ret.Unpack(buf) + return &ret, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + var ret PrivateKey + ret.Unpack(buf) + return &ret, nil +} diff --git a/vendor/github.com/cloudflare/circl/kem/kyber/kyber768/kyber.go b/vendor/github.com/cloudflare/circl/kem/kyber/kyber768/kyber.go new file mode 100644 index 00000000..98c40279 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/kem/kyber/kyber768/kyber.go @@ -0,0 +1,402 @@ +// Code generated from pkg.templ.go. DO NOT EDIT. + +// Package kyber768 implements the IND-CCA2 secure key encapsulation mechanism +// Kyber768.CCAKEM as submitted to round 3 of the NIST PQC competition and +// described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber768 + +import ( + "bytes" + "crypto/subtle" + "io" + + cryptoRand "crypto/rand" + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/kem" + cpapke "github.com/cloudflare/circl/pke/kyber/kyber768" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = cpapke.KeySeedSize + 32 + + // Size of seed for EncapsulateTo. + EncapsulationSeedSize = 32 + + // Size of the established shared key. + SharedKeySize = 32 + + // Size of the encapsulated shared key. + CiphertextSize = cpapke.CiphertextSize + + // Size of a packed public key. + PublicKeySize = cpapke.PublicKeySize + + // Size of a packed private key. + PrivateKeySize = cpapke.PrivateKeySize + cpapke.PublicKeySize + 64 +) + +// Type of a Kyber768.CCAKEM public key +type PublicKey struct { + pk *cpapke.PublicKey + + hpk [32]byte // H(pk) +} + +// Type of a Kyber768.CCAKEM private key +type PrivateKey struct { + sk *cpapke.PrivateKey + pk *cpapke.PublicKey + hpk [32]byte // H(pk) + z [32]byte +} + +// NewKeyFromSeed derives a public/private keypair deterministically +// from the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var sk PrivateKey + var pk PublicKey + + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + + pk.pk, sk.sk = cpapke.NewKeyFromSeed(seed[:cpapke.KeySeedSize]) + sk.pk = pk.pk + copy(sk.z[:], seed[cpapke.KeySeedSize:]) + + // Compute H(pk) + var ppk [cpapke.PublicKeySize]byte + sk.pk.Pack(ppk[:]) + h := sha3.New256() + h.Write(ppk[:]) + h.Read(sk.hpk[:]) + copy(pk.hpk[:], sk.hpk[:]) + + return &pk, &sk +} + +// GenerateKeyPair generates public and private keys using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := NewKeyFromSeed(seed[:]) + return pk, sk, nil +} + +// EncapsulateTo generates a shared key and ciphertext that contains it +// for the public key using randomness from seed and writes the shared key +// to ss and ciphertext to ct. +// +// Panics if ss, ct or seed are not of length SharedKeySize, CiphertextSize +// and EncapsulationSeedSize respectively. +// +// seed may be nil, in which case crypto/rand.Reader is used to generate one. +func (pk *PublicKey) EncapsulateTo(ct, ss []byte, seed []byte) { + if seed == nil { + seed = make([]byte, EncapsulationSeedSize) + cryptoRand.Read(seed[:]) + } else { + if len(seed) != EncapsulationSeedSize { + panic("seed must be of length EncapsulationSeedSize") + } + } + + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m = H(seed) + var m [32]byte + h := sha3.New256() + h.Write(seed[:]) + h.Read(m[:]) + + // (K', r) = G(m ‖ H(pk)) + var kr [64]byte + g := sha3.New512() + g.Write(m[:]) + g.Write(pk.hpk[:]) + g.Read(kr[:]) + + // c = Kyber.CPAPKE.Enc(pk, m, r) + pk.pk.EncryptTo(ct, m[:], kr[32:]) + + // Compute H(c) and put in second slot of kr, which will be (K', H(c)). + h.Reset() + h.Write(ct[:CiphertextSize]) + h.Read(kr[32:]) + + // K = KDF(K' ‖ H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// DecapsulateTo computes the shared key which is encapsulated in ct +// for the private key. +// +// Panics if ct or ss are not of length CiphertextSize and SharedKeySize +// respectively. +func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + + if len(ss) != SharedKeySize { + panic("ss must be of length SharedKeySize") + } + + // m' = Kyber.CPAPKE.Dec(sk, ct) + var m2 [32]byte + sk.sk.DecryptTo(m2[:], ct) + + // (K'', r') = G(m' ‖ H(pk)) + var kr2 [64]byte + g := sha3.New512() + g.Write(m2[:]) + g.Write(sk.hpk[:]) + g.Read(kr2[:]) + + // c' = Kyber.CPAPKE.Enc(pk, m', r') + var ct2 [CiphertextSize]byte + sk.pk.EncryptTo(ct2[:], m2[:], kr2[32:]) + + // Compute H(c) and put in second slot of kr2, which will be (K'', H(c)). + h := sha3.New256() + h.Write(ct[:CiphertextSize]) + h.Read(kr2[32:]) + + // Replace K'' by z in the first slot of kr2 if c ≠ c'. + subtle.ConstantTimeCopy( + 1-subtle.ConstantTimeCompare(ct, ct2[:]), + kr2[:32], + sk.z[:], + ) + + // K = KDF(K''/z, H(c)) + kdf := sha3.NewShake256() + kdf.Write(kr2[:]) + kdf.Read(ss[:SharedKeySize]) +} + +// Packs sk to buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk.Pack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk.Pack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(buf, sk.hpk[:]) + buf = buf[32:] + copy(buf, sk.z[:]) +} + +// Unpacks sk from buf. +// +// Panics if buf is not of size PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of length PrivateKeySize") + } + + sk.sk = new(cpapke.PrivateKey) + sk.sk.Unpack(buf[:cpapke.PrivateKeySize]) + buf = buf[cpapke.PrivateKeySize:] + sk.pk = new(cpapke.PublicKey) + sk.pk.Unpack(buf[:cpapke.PublicKeySize]) + buf = buf[cpapke.PublicKeySize:] + copy(sk.hpk[:], buf[:32]) + copy(sk.z[:], buf[32:]) +} + +// Packs pk to buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk.Pack(buf) +} + +// Unpacks pk from buf. +// +// Panics if buf is not of size PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of length PublicKeySize") + } + + pk.pk = new(cpapke.PublicKey) + pk.pk.Unpack(buf) + + // Compute cached H(pk) + h := sha3.New256() + h.Write(buf) + h.Read(pk.hpk[:]) +} + +// Boilerplate down below for the KEM scheme API. + +type scheme struct{} + +var sch kem.Scheme = &scheme{} + +// Scheme returns a KEM interface. +func Scheme() kem.Scheme { return sch } + +func (*scheme) Name() string { return "Kyber768" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SeedSize() int { return KeySeedSize } +func (*scheme) SharedKeySize() int { return SharedKeySize } +func (*scheme) CiphertextSize() int { return CiphertextSize } +func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize } + +func (sk *PrivateKey) Scheme() kem.Scheme { return sch } +func (pk *PublicKey) Scheme() kem.Scheme { return sch } + +func (sk *PrivateKey) MarshalBinary() ([]byte, error) { + var ret [PrivateKeySize]byte + sk.Pack(ret[:]) + return ret[:], nil +} + +func (sk *PrivateKey) Equal(other kem.PrivateKey) bool { + oth, ok := other.(*PrivateKey) + if !ok { + return false + } + if sk.pk == nil && oth.pk == nil { + return true + } + if sk.pk == nil || oth.pk == nil { + return false + } + if !bytes.Equal(sk.hpk[:], oth.hpk[:]) || + !bytes.Equal(sk.z[:], oth.z[:]) { + return false + } + return sk.sk.Equal(oth.sk) +} + +func (pk *PublicKey) Equal(other kem.PublicKey) bool { + oth, ok := other.(*PublicKey) + if !ok { + return false + } + if pk.pk == nil && oth.pk == nil { + return true + } + if pk.pk == nil || oth.pk == nil { + return false + } + return bytes.Equal(pk.hpk[:], oth.hpk[:]) +} + +func (sk *PrivateKey) Public() kem.PublicKey { + pk := new(PublicKey) + pk.pk = sk.pk + copy(pk.hpk[:], sk.hpk[:]) + return pk +} + +func (pk *PublicKey) MarshalBinary() ([]byte, error) { + var ret [PublicKeySize]byte + pk.Pack(ret[:]) + return ret[:], nil +} + +func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + return GenerateKeyPair(cryptoRand.Reader) +} + +func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + if len(seed) != KeySeedSize { + panic(kem.ErrSeedSize) + } + return NewKeyFromSeed(seed[:]) +} + +func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, nil) + return +} + +func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) ( + ct, ss []byte, err error) { + if len(seed) != EncapsulationSeedSize { + return nil, nil, kem.ErrSeedSize + } + + ct = make([]byte, CiphertextSize) + ss = make([]byte, SharedKeySize) + + pub, ok := pk.(*PublicKey) + if !ok { + return nil, nil, kem.ErrTypeMismatch + } + pub.EncapsulateTo(ct, ss, seed) + return +} + +func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { + if len(ct) != CiphertextSize { + return nil, kem.ErrCiphertextSize + } + + priv, ok := sk.(*PrivateKey) + if !ok { + return nil, kem.ErrTypeMismatch + } + ss := make([]byte, SharedKeySize) + priv.DecapsulateTo(ss, ct) + return ss, nil +} + +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, kem.ErrPubKeySize + } + var ret PublicKey + ret.Unpack(buf) + return &ret, nil +} + +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, kem.ErrPrivKeySize + } + var ret PrivateKey + ret.Unpack(buf) + return &ret, nil +} diff --git a/vendor/github.com/cloudflare/circl/math/fp25519/fp.go b/vendor/github.com/cloudflare/circl/math/fp25519/fp.go new file mode 100644 index 00000000..57a50ff5 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp25519/fp.go @@ -0,0 +1,205 @@ +// Package fp25519 provides prime field arithmetic over GF(2^255-19). +package fp25519 + +import ( + "errors" + + "github.com/cloudflare/circl/internal/conv" +) + +// Size in bytes of an element. +const Size = 32 + +// Elt is a prime field element. +type Elt [Size]byte + +func (e Elt) String() string { return conv.BytesLe2Hex(e[:]) } + +// p is the prime modulus 2^255-19. +var p = Elt{ + 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, +} + +// P returns the prime modulus 2^255-19. +func P() Elt { return p } + +// ToBytes stores in b the little-endian byte representation of x. +func ToBytes(b []byte, x *Elt) error { + if len(b) != Size { + return errors.New("wrong size") + } + Modp(x) + copy(b, x[:]) + return nil +} + +// IsZero returns true if x is equal to 0. +func IsZero(x *Elt) bool { Modp(x); return *x == Elt{} } + +// SetOne assigns x=1. +func SetOne(x *Elt) { *x = Elt{}; x[0] = 1 } + +// Neg calculates z = -x. +func Neg(z, x *Elt) { Sub(z, &p, x) } + +// InvSqrt calculates z = sqrt(x/y) iff x/y is a quadratic-residue, which is +// indicated by returning isQR = true. Otherwise, when x/y is a quadratic +// non-residue, z will have an undetermined value and isQR = false. +func InvSqrt(z, x, y *Elt) (isQR bool) { + sqrtMinusOne := &Elt{ + 0xb0, 0xa0, 0x0e, 0x4a, 0x27, 0x1b, 0xee, 0xc4, + 0x78, 0xe4, 0x2f, 0xad, 0x06, 0x18, 0x43, 0x2f, + 0xa7, 0xd7, 0xfb, 0x3d, 0x99, 0x00, 0x4d, 0x2b, + 0x0b, 0xdf, 0xc1, 0x4f, 0x80, 0x24, 0x83, 0x2b, + } + t0, t1, t2, t3 := &Elt{}, &Elt{}, &Elt{}, &Elt{} + + Mul(t0, x, y) // t0 = u*v + Sqr(t1, y) // t1 = v^2 + Mul(t2, t0, t1) // t2 = u*v^3 + Sqr(t0, t1) // t0 = v^4 + Mul(t1, t0, t2) // t1 = u*v^7 + + var Tab [4]*Elt + Tab[0] = &Elt{} + Tab[1] = &Elt{} + Tab[2] = t3 + Tab[3] = t1 + + *Tab[0] = *t1 + Sqr(Tab[0], Tab[0]) + Sqr(Tab[1], Tab[0]) + Sqr(Tab[1], Tab[1]) + Mul(Tab[1], Tab[1], Tab[3]) + Mul(Tab[0], Tab[0], Tab[1]) + Sqr(Tab[0], Tab[0]) + Mul(Tab[0], Tab[0], Tab[1]) + Sqr(Tab[1], Tab[0]) + for i := 0; i < 4; i++ { + Sqr(Tab[1], Tab[1]) + } + Mul(Tab[1], Tab[1], Tab[0]) + Sqr(Tab[2], Tab[1]) + for i := 0; i < 4; i++ { + Sqr(Tab[2], Tab[2]) + } + Mul(Tab[2], Tab[2], Tab[0]) + Sqr(Tab[1], Tab[2]) + for i := 0; i < 14; i++ { + Sqr(Tab[1], Tab[1]) + } + Mul(Tab[1], Tab[1], Tab[2]) + Sqr(Tab[2], Tab[1]) + for i := 0; i < 29; i++ { + Sqr(Tab[2], Tab[2]) + } + Mul(Tab[2], Tab[2], Tab[1]) + Sqr(Tab[1], Tab[2]) + for i := 0; i < 59; i++ { + Sqr(Tab[1], Tab[1]) + } + Mul(Tab[1], Tab[1], Tab[2]) + for i := 0; i < 5; i++ { + Sqr(Tab[1], Tab[1]) + } + Mul(Tab[1], Tab[1], Tab[0]) + Sqr(Tab[2], Tab[1]) + for i := 0; i < 124; i++ { + Sqr(Tab[2], Tab[2]) + } + Mul(Tab[2], Tab[2], Tab[1]) + Sqr(Tab[2], Tab[2]) + Sqr(Tab[2], Tab[2]) + Mul(Tab[2], Tab[2], Tab[3]) + + Mul(z, t3, t2) // z = xy^(p+3)/8 = xy^3*(xy^7)^(p-5)/8 + // Checking whether y z^2 == x + Sqr(t0, z) // t0 = z^2 + Mul(t0, t0, y) // t0 = yz^2 + Sub(t1, t0, x) // t1 = t0-u + Add(t2, t0, x) // t2 = t0+u + if IsZero(t1) { + return true + } else if IsZero(t2) { + Mul(z, z, sqrtMinusOne) // z = z*sqrt(-1) + return true + } else { + return false + } +} + +// Inv calculates z = 1/x mod p. +func Inv(z, x *Elt) { + x0, x1, x2 := &Elt{}, &Elt{}, &Elt{} + Sqr(x1, x) + Sqr(x0, x1) + Sqr(x0, x0) + Mul(x0, x0, x) + Mul(z, x0, x1) + Sqr(x1, z) + Mul(x0, x0, x1) + Sqr(x1, x0) + for i := 0; i < 4; i++ { + Sqr(x1, x1) + } + Mul(x0, x0, x1) + Sqr(x1, x0) + for i := 0; i < 9; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, x0) + Sqr(x2, x1) + for i := 0; i < 19; i++ { + Sqr(x2, x2) + } + Mul(x2, x2, x1) + for i := 0; i < 10; i++ { + Sqr(x2, x2) + } + Mul(x2, x2, x0) + Sqr(x0, x2) + for i := 0; i < 49; i++ { + Sqr(x0, x0) + } + Mul(x0, x0, x2) + Sqr(x1, x0) + for i := 0; i < 99; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, x0) + for i := 0; i < 50; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, x2) + for i := 0; i < 5; i++ { + Sqr(x1, x1) + } + Mul(z, z, x1) +} + +// Cmov assigns y to x if n is 1. +func Cmov(x, y *Elt, n uint) { cmov(x, y, n) } + +// Cswap interchanges x and y if n is 1. +func Cswap(x, y *Elt, n uint) { cswap(x, y, n) } + +// Add calculates z = x+y mod p. +func Add(z, x, y *Elt) { add(z, x, y) } + +// Sub calculates z = x-y mod p. +func Sub(z, x, y *Elt) { sub(z, x, y) } + +// AddSub calculates (x,y) = (x+y mod p, x-y mod p). +func AddSub(x, y *Elt) { addsub(x, y) } + +// Mul calculates z = x*y mod p. +func Mul(z, x, y *Elt) { mul(z, x, y) } + +// Sqr calculates z = x^2 mod p. +func Sqr(z, x *Elt) { sqr(z, x) } + +// Modp ensures that z is between [0,p-1]. +func Modp(z *Elt) { modp(z) } diff --git a/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.go b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.go new file mode 100644 index 00000000..057f0d28 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.go @@ -0,0 +1,45 @@ +//go:build amd64 && !purego +// +build amd64,!purego + +package fp25519 + +import ( + "golang.org/x/sys/cpu" +) + +var hasBmi2Adx = cpu.X86.HasBMI2 && cpu.X86.HasADX + +var _ = hasBmi2Adx + +func cmov(x, y *Elt, n uint) { cmovAmd64(x, y, n) } +func cswap(x, y *Elt, n uint) { cswapAmd64(x, y, n) } +func add(z, x, y *Elt) { addAmd64(z, x, y) } +func sub(z, x, y *Elt) { subAmd64(z, x, y) } +func addsub(x, y *Elt) { addsubAmd64(x, y) } +func mul(z, x, y *Elt) { mulAmd64(z, x, y) } +func sqr(z, x *Elt) { sqrAmd64(z, x) } +func modp(z *Elt) { modpAmd64(z) } + +//go:noescape +func cmovAmd64(x, y *Elt, n uint) + +//go:noescape +func cswapAmd64(x, y *Elt, n uint) + +//go:noescape +func addAmd64(z, x, y *Elt) + +//go:noescape +func subAmd64(z, x, y *Elt) + +//go:noescape +func addsubAmd64(x, y *Elt) + +//go:noescape +func mulAmd64(z, x, y *Elt) + +//go:noescape +func sqrAmd64(z, x *Elt) + +//go:noescape +func modpAmd64(z *Elt) diff --git a/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.h b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.h new file mode 100644 index 00000000..7b25f16e --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.h @@ -0,0 +1,350 @@ +// This code was imported from https://github.com/armfazh/rfc7748_precomputed + +// CHECK_BMI2ADX triggers bmi2adx if supported, +// otherwise it fallbacks to legacy code. +#define CHECK_BMI2ADX(label, legacy, bmi2adx) \ + CMPB ·hasBmi2Adx(SB), $0 \ + JE label \ + bmi2adx \ + RET \ + label: \ + legacy \ + RET + +// cselect is a conditional move +// if b=1: it copies y into x; +// if b=0: x remains with the same value; +// if b<> 0,1: undefined. +// Uses: AX, DX, FLAGS +// Instr: x86_64, cmov +#define cselect(x,y,b) \ + TESTQ b, b \ + MOVQ 0+x, AX; MOVQ 0+y, DX; CMOVQNE DX, AX; MOVQ AX, 0+x; \ + MOVQ 8+x, AX; MOVQ 8+y, DX; CMOVQNE DX, AX; MOVQ AX, 8+x; \ + MOVQ 16+x, AX; MOVQ 16+y, DX; CMOVQNE DX, AX; MOVQ AX, 16+x; \ + MOVQ 24+x, AX; MOVQ 24+y, DX; CMOVQNE DX, AX; MOVQ AX, 24+x; + +// cswap is a conditional swap +// if b=1: x,y <- y,x; +// if b=0: x,y remain with the same values; +// if b<> 0,1: undefined. +// Uses: AX, DX, R8, FLAGS +// Instr: x86_64, cmov +#define cswap(x,y,b) \ + TESTQ b, b \ + MOVQ 0+x, AX; MOVQ AX, R8; MOVQ 0+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 0+x; MOVQ DX, 0+y; \ + MOVQ 8+x, AX; MOVQ AX, R8; MOVQ 8+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 8+x; MOVQ DX, 8+y; \ + MOVQ 16+x, AX; MOVQ AX, R8; MOVQ 16+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 16+x; MOVQ DX, 16+y; \ + MOVQ 24+x, AX; MOVQ AX, R8; MOVQ 24+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 24+x; MOVQ DX, 24+y; + +// additionLeg adds x and y and stores in z +// Uses: AX, DX, R8-R11, FLAGS +// Instr: x86_64, cmov +#define additionLeg(z,x,y) \ + MOVL $38, AX; \ + MOVL $0, DX; \ + MOVQ 0+x, R8; ADDQ 0+y, R8; \ + MOVQ 8+x, R9; ADCQ 8+y, R9; \ + MOVQ 16+x, R10; ADCQ 16+y, R10; \ + MOVQ 24+x, R11; ADCQ 24+y, R11; \ + CMOVQCS AX, DX; \ + ADDQ DX, R8; \ + ADCQ $0, R9; MOVQ R9, 8+z; \ + ADCQ $0, R10; MOVQ R10, 16+z; \ + ADCQ $0, R11; MOVQ R11, 24+z; \ + MOVL $0, DX; \ + CMOVQCS AX, DX; \ + ADDQ DX, R8; MOVQ R8, 0+z; + +// additionAdx adds x and y and stores in z +// Uses: AX, DX, R8-R11, FLAGS +// Instr: x86_64, cmov, adx +#define additionAdx(z,x,y) \ + MOVL $38, AX; \ + XORL DX, DX; \ + MOVQ 0+x, R8; ADCXQ 0+y, R8; \ + MOVQ 8+x, R9; ADCXQ 8+y, R9; \ + MOVQ 16+x, R10; ADCXQ 16+y, R10; \ + MOVQ 24+x, R11; ADCXQ 24+y, R11; \ + CMOVQCS AX, DX ; \ + XORL AX, AX; \ + ADCXQ DX, R8; \ + ADCXQ AX, R9; MOVQ R9, 8+z; \ + ADCXQ AX, R10; MOVQ R10, 16+z; \ + ADCXQ AX, R11; MOVQ R11, 24+z; \ + MOVL $38, DX; \ + CMOVQCS DX, AX; \ + ADDQ AX, R8; MOVQ R8, 0+z; + +// subtraction subtracts y from x and stores in z +// Uses: AX, DX, R8-R11, FLAGS +// Instr: x86_64, cmov +#define subtraction(z,x,y) \ + MOVL $38, AX; \ + MOVQ 0+x, R8; SUBQ 0+y, R8; \ + MOVQ 8+x, R9; SBBQ 8+y, R9; \ + MOVQ 16+x, R10; SBBQ 16+y, R10; \ + MOVQ 24+x, R11; SBBQ 24+y, R11; \ + MOVL $0, DX; \ + CMOVQCS AX, DX; \ + SUBQ DX, R8; \ + SBBQ $0, R9; MOVQ R9, 8+z; \ + SBBQ $0, R10; MOVQ R10, 16+z; \ + SBBQ $0, R11; MOVQ R11, 24+z; \ + MOVL $0, DX; \ + CMOVQCS AX, DX; \ + SUBQ DX, R8; MOVQ R8, 0+z; + +// integerMulAdx multiplies x and y and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64, bmi2, adx +#define integerMulAdx(z,x,y) \ + MOVQ 0+y, DX; XORL AX, AX; \ + MULXQ 0+x, AX, R8; MOVQ AX, 0+z; \ + MULXQ 8+x, AX, R9; ADCXQ AX, R8; \ + MULXQ 16+x, AX, R10; ADCXQ AX, R9; \ + MULXQ 24+x, AX, R11; ADCXQ AX, R10; \ + MOVL $0, AX;;;;;;;;; ADCXQ AX, R11; \ + MOVQ 8+y, DX; XORL AX, AX; \ + MULXQ 0+x, AX, R12; ADCXQ R8, AX; MOVQ AX, 8+z; \ + MULXQ 8+x, AX, R13; ADCXQ R9, R12; ADOXQ AX, R12; \ + MULXQ 16+x, AX, R14; ADCXQ R10, R13; ADOXQ AX, R13; \ + MULXQ 24+x, AX, R15; ADCXQ R11, R14; ADOXQ AX, R14; \ + MOVL $0, AX;;;;;;;;; ADCXQ AX, R15; ADOXQ AX, R15; \ + MOVQ 16+y, DX; XORL AX, AX; \ + MULXQ 0+x, AX, R8; ADCXQ R12, AX; MOVQ AX, 16+z; \ + MULXQ 8+x, AX, R9; ADCXQ R13, R8; ADOXQ AX, R8; \ + MULXQ 16+x, AX, R10; ADCXQ R14, R9; ADOXQ AX, R9; \ + MULXQ 24+x, AX, R11; ADCXQ R15, R10; ADOXQ AX, R10; \ + MOVL $0, AX;;;;;;;;; ADCXQ AX, R11; ADOXQ AX, R11; \ + MOVQ 24+y, DX; XORL AX, AX; \ + MULXQ 0+x, AX, R12; ADCXQ R8, AX; MOVQ AX, 24+z; \ + MULXQ 8+x, AX, R13; ADCXQ R9, R12; ADOXQ AX, R12; MOVQ R12, 32+z; \ + MULXQ 16+x, AX, R14; ADCXQ R10, R13; ADOXQ AX, R13; MOVQ R13, 40+z; \ + MULXQ 24+x, AX, R15; ADCXQ R11, R14; ADOXQ AX, R14; MOVQ R14, 48+z; \ + MOVL $0, AX;;;;;;;;; ADCXQ AX, R15; ADOXQ AX, R15; MOVQ R15, 56+z; + +// integerMulLeg multiplies x and y and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64 +#define integerMulLeg(z,x,y) \ + MOVQ 0+y, R8; \ + MOVQ 0+x, AX; MULQ R8; MOVQ AX, 0+z; MOVQ DX, R15; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, R13; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R8; MOVQ AX, R14; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R8; \ + ADDQ R13, R15; \ + ADCQ R14, R10; MOVQ R10, 16+z; \ + ADCQ AX, R11; MOVQ R11, 24+z; \ + ADCQ $0, DX; MOVQ DX, 32+z; \ + MOVQ 8+y, R8; \ + MOVQ 0+x, AX; MULQ R8; MOVQ AX, R12; MOVQ DX, R9; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, R13; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R8; MOVQ AX, R14; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R8; \ + ADDQ R12, R15; MOVQ R15, 8+z; \ + ADCQ R13, R9; \ + ADCQ R14, R10; \ + ADCQ AX, R11; \ + ADCQ $0, DX; \ + ADCQ 16+z, R9; MOVQ R9, R15; \ + ADCQ 24+z, R10; MOVQ R10, 24+z; \ + ADCQ 32+z, R11; MOVQ R11, 32+z; \ + ADCQ $0, DX; MOVQ DX, 40+z; \ + MOVQ 16+y, R8; \ + MOVQ 0+x, AX; MULQ R8; MOVQ AX, R12; MOVQ DX, R9; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, R13; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R8; MOVQ AX, R14; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R8; \ + ADDQ R12, R15; MOVQ R15, 16+z; \ + ADCQ R13, R9; \ + ADCQ R14, R10; \ + ADCQ AX, R11; \ + ADCQ $0, DX; \ + ADCQ 24+z, R9; MOVQ R9, R15; \ + ADCQ 32+z, R10; MOVQ R10, 32+z; \ + ADCQ 40+z, R11; MOVQ R11, 40+z; \ + ADCQ $0, DX; MOVQ DX, 48+z; \ + MOVQ 24+y, R8; \ + MOVQ 0+x, AX; MULQ R8; MOVQ AX, R12; MOVQ DX, R9; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, R13; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R8; MOVQ AX, R14; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R8; \ + ADDQ R12, R15; MOVQ R15, 24+z; \ + ADCQ R13, R9; \ + ADCQ R14, R10; \ + ADCQ AX, R11; \ + ADCQ $0, DX; \ + ADCQ 32+z, R9; MOVQ R9, 32+z; \ + ADCQ 40+z, R10; MOVQ R10, 40+z; \ + ADCQ 48+z, R11; MOVQ R11, 48+z; \ + ADCQ $0, DX; MOVQ DX, 56+z; + +// integerSqrLeg squares x and stores in z +// Uses: AX, CX, DX, R8-R15, FLAGS +// Instr: x86_64 +#define integerSqrLeg(z,x) \ + MOVQ 0+x, R8; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, R9; MOVQ DX, R10; /* A[0]*A[1] */ \ + MOVQ 16+x, AX; MULQ R8; MOVQ AX, R14; MOVQ DX, R11; /* A[0]*A[2] */ \ + MOVQ 24+x, AX; MULQ R8; MOVQ AX, R15; MOVQ DX, R12; /* A[0]*A[3] */ \ + MOVQ 24+x, R8; \ + MOVQ 8+x, AX; MULQ R8; MOVQ AX, CX; MOVQ DX, R13; /* A[3]*A[1] */ \ + MOVQ 16+x, AX; MULQ R8; /* A[3]*A[2] */ \ + \ + ADDQ R14, R10;\ + ADCQ R15, R11; MOVL $0, R15;\ + ADCQ CX, R12;\ + ADCQ AX, R13;\ + ADCQ $0, DX; MOVQ DX, R14;\ + MOVQ 8+x, AX; MULQ 16+x;\ + \ + ADDQ AX, R11;\ + ADCQ DX, R12;\ + ADCQ $0, R13;\ + ADCQ $0, R14;\ + ADCQ $0, R15;\ + \ + SHLQ $1, R14, R15; MOVQ R15, 56+z;\ + SHLQ $1, R13, R14; MOVQ R14, 48+z;\ + SHLQ $1, R12, R13; MOVQ R13, 40+z;\ + SHLQ $1, R11, R12; MOVQ R12, 32+z;\ + SHLQ $1, R10, R11; MOVQ R11, 24+z;\ + SHLQ $1, R9, R10; MOVQ R10, 16+z;\ + SHLQ $1, R9; MOVQ R9, 8+z;\ + \ + MOVQ 0+x,AX; MULQ AX; MOVQ AX, 0+z; MOVQ DX, R9;\ + MOVQ 8+x,AX; MULQ AX; MOVQ AX, R10; MOVQ DX, R11;\ + MOVQ 16+x,AX; MULQ AX; MOVQ AX, R12; MOVQ DX, R13;\ + MOVQ 24+x,AX; MULQ AX; MOVQ AX, R14; MOVQ DX, R15;\ + \ + ADDQ 8+z, R9; MOVQ R9, 8+z;\ + ADCQ 16+z, R10; MOVQ R10, 16+z;\ + ADCQ 24+z, R11; MOVQ R11, 24+z;\ + ADCQ 32+z, R12; MOVQ R12, 32+z;\ + ADCQ 40+z, R13; MOVQ R13, 40+z;\ + ADCQ 48+z, R14; MOVQ R14, 48+z;\ + ADCQ 56+z, R15; MOVQ R15, 56+z; + +// integerSqrAdx squares x and stores in z +// Uses: AX, CX, DX, R8-R15, FLAGS +// Instr: x86_64, bmi2, adx +#define integerSqrAdx(z,x) \ + MOVQ 0+x, DX; /* A[0] */ \ + MULXQ 8+x, R8, R14; /* A[1]*A[0] */ XORL R15, R15; \ + MULXQ 16+x, R9, R10; /* A[2]*A[0] */ ADCXQ R14, R9; \ + MULXQ 24+x, AX, CX; /* A[3]*A[0] */ ADCXQ AX, R10; \ + MOVQ 24+x, DX; /* A[3] */ \ + MULXQ 8+x, R11, R12; /* A[1]*A[3] */ ADCXQ CX, R11; \ + MULXQ 16+x, AX, R13; /* A[2]*A[3] */ ADCXQ AX, R12; \ + MOVQ 8+x, DX; /* A[1] */ ADCXQ R15, R13; \ + MULXQ 16+x, AX, CX; /* A[2]*A[1] */ MOVL $0, R14; \ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADCXQ R15, R14; \ + XORL R15, R15; \ + ADOXQ AX, R10; ADCXQ R8, R8; \ + ADOXQ CX, R11; ADCXQ R9, R9; \ + ADOXQ R15, R12; ADCXQ R10, R10; \ + ADOXQ R15, R13; ADCXQ R11, R11; \ + ADOXQ R15, R14; ADCXQ R12, R12; \ + ;;;;;;;;;;;;;;; ADCXQ R13, R13; \ + ;;;;;;;;;;;;;;; ADCXQ R14, R14; \ + MOVQ 0+x, DX; MULXQ DX, AX, CX; /* A[0]^2 */ \ + ;;;;;;;;;;;;;;; MOVQ AX, 0+z; \ + ADDQ CX, R8; MOVQ R8, 8+z; \ + MOVQ 8+x, DX; MULXQ DX, AX, CX; /* A[1]^2 */ \ + ADCQ AX, R9; MOVQ R9, 16+z; \ + ADCQ CX, R10; MOVQ R10, 24+z; \ + MOVQ 16+x, DX; MULXQ DX, AX, CX; /* A[2]^2 */ \ + ADCQ AX, R11; MOVQ R11, 32+z; \ + ADCQ CX, R12; MOVQ R12, 40+z; \ + MOVQ 24+x, DX; MULXQ DX, AX, CX; /* A[3]^2 */ \ + ADCQ AX, R13; MOVQ R13, 48+z; \ + ADCQ CX, R14; MOVQ R14, 56+z; + +// reduceFromDouble finds z congruent to x modulo p such that 0> 63) + // PUT BIT 255 IN CARRY FLAG AND CLEAR + x3 &^= 1 << 63 + + x0, c0 := bits.Add64(x0, cx, 0) + x1, c1 := bits.Add64(x1, 0, c0) + x2, c2 := bits.Add64(x2, 0, c1) + x3, _ = bits.Add64(x3, 0, c2) + + // TEST FOR BIT 255 AGAIN; ONLY TRIGGERED ON OVERFLOW MODULO 2^255-19 + // cx = C[255] ? 0 : 19 + cx = uint64(19) &^ (-(x3 >> 63)) + // CLEAR BIT 255 + x3 &^= 1 << 63 + + x0, c0 = bits.Sub64(x0, cx, 0) + x1, c1 = bits.Sub64(x1, 0, c0) + x2, c2 = bits.Sub64(x2, 0, c1) + x3, _ = bits.Sub64(x3, 0, c2) + + binary.LittleEndian.PutUint64(x[0*8:1*8], x0) + binary.LittleEndian.PutUint64(x[1*8:2*8], x1) + binary.LittleEndian.PutUint64(x[2*8:3*8], x2) + binary.LittleEndian.PutUint64(x[3*8:4*8], x3) +} + +func red64(z *Elt, x0, x1, x2, x3, x4, x5, x6, x7 uint64) { + h0, l0 := bits.Mul64(x4, 38) + h1, l1 := bits.Mul64(x5, 38) + h2, l2 := bits.Mul64(x6, 38) + h3, l3 := bits.Mul64(x7, 38) + + l1, c0 := bits.Add64(h0, l1, 0) + l2, c1 := bits.Add64(h1, l2, c0) + l3, c2 := bits.Add64(h2, l3, c1) + l4, _ := bits.Add64(h3, 0, c2) + + l0, c0 = bits.Add64(l0, x0, 0) + l1, c1 = bits.Add64(l1, x1, c0) + l2, c2 = bits.Add64(l2, x2, c1) + l3, c3 := bits.Add64(l3, x3, c2) + l4, _ = bits.Add64(l4, 0, c3) + + _, l4 = bits.Mul64(l4, 38) + l0, c0 = bits.Add64(l0, l4, 0) + z1, c1 := bits.Add64(l1, 0, c0) + z2, c2 := bits.Add64(l2, 0, c1) + z3, c3 := bits.Add64(l3, 0, c2) + z0, _ := bits.Add64(l0, (-c3)&38, 0) + + binary.LittleEndian.PutUint64(z[0*8:1*8], z0) + binary.LittleEndian.PutUint64(z[1*8:2*8], z1) + binary.LittleEndian.PutUint64(z[2*8:3*8], z2) + binary.LittleEndian.PutUint64(z[3*8:4*8], z3) +} diff --git a/vendor/github.com/cloudflare/circl/math/fp25519/fp_noasm.go b/vendor/github.com/cloudflare/circl/math/fp25519/fp_noasm.go new file mode 100644 index 00000000..26ca4d01 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp25519/fp_noasm.go @@ -0,0 +1,13 @@ +//go:build !amd64 || purego +// +build !amd64 purego + +package fp25519 + +func cmov(x, y *Elt, n uint) { cmovGeneric(x, y, n) } +func cswap(x, y *Elt, n uint) { cswapGeneric(x, y, n) } +func add(z, x, y *Elt) { addGeneric(z, x, y) } +func sub(z, x, y *Elt) { subGeneric(z, x, y) } +func addsub(x, y *Elt) { addsubGeneric(x, y) } +func mul(z, x, y *Elt) { mulGeneric(z, x, y) } +func sqr(z, x *Elt) { sqrGeneric(z, x) } +func modp(z *Elt) { modpGeneric(z) } diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp.go b/vendor/github.com/cloudflare/circl/math/fp448/fp.go new file mode 100644 index 00000000..a5e36600 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp.go @@ -0,0 +1,164 @@ +// Package fp448 provides prime field arithmetic over GF(2^448-2^224-1). +package fp448 + +import ( + "errors" + + "github.com/cloudflare/circl/internal/conv" +) + +// Size in bytes of an element. +const Size = 56 + +// Elt is a prime field element. +type Elt [Size]byte + +func (e Elt) String() string { return conv.BytesLe2Hex(e[:]) } + +// p is the prime modulus 2^448-2^224-1. +var p = Elt{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +} + +// P returns the prime modulus 2^448-2^224-1. +func P() Elt { return p } + +// ToBytes stores in b the little-endian byte representation of x. +func ToBytes(b []byte, x *Elt) error { + if len(b) != Size { + return errors.New("wrong size") + } + Modp(x) + copy(b, x[:]) + return nil +} + +// IsZero returns true if x is equal to 0. +func IsZero(x *Elt) bool { Modp(x); return *x == Elt{} } + +// IsOne returns true if x is equal to 1. +func IsOne(x *Elt) bool { Modp(x); return *x == Elt{1} } + +// SetOne assigns x=1. +func SetOne(x *Elt) { *x = Elt{1} } + +// One returns the 1 element. +func One() (x Elt) { x = Elt{1}; return } + +// Neg calculates z = -x. +func Neg(z, x *Elt) { Sub(z, &p, x) } + +// Modp ensures that z is between [0,p-1]. +func Modp(z *Elt) { Sub(z, z, &p) } + +// InvSqrt calculates z = sqrt(x/y) iff x/y is a quadratic-residue. If so, +// isQR = true; otherwise, isQR = false, since x/y is a quadratic non-residue, +// and z = sqrt(-x/y). +func InvSqrt(z, x, y *Elt) (isQR bool) { + // First note that x^(2(k+1)) = x^(p-1)/2 * x = legendre(x) * x + // so that's x if x is a quadratic residue and -x otherwise. + // Next, y^(6k+3) = y^(4k+2) * y^(2k+1) = y^(p-1) * y^((p-1)/2) = legendre(y). + // So the z we compute satisfies z^2 y = x^(2(k+1)) y^(6k+3) = legendre(x)*legendre(y). + // Thus if x and y are quadratic residues, then z is indeed sqrt(x/y). + t0, t1 := &Elt{}, &Elt{} + Mul(t0, x, y) // x*y + Sqr(t1, y) // y^2 + Mul(t1, t0, t1) // x*y^3 + powPminus3div4(z, t1) // (x*y^3)^k + Mul(z, z, t0) // z = x*y*(x*y^3)^k = x^(k+1) * y^(3k+1) + + // Check if x/y is a quadratic residue + Sqr(t0, z) // z^2 + Mul(t0, t0, y) // y*z^2 + Sub(t0, t0, x) // y*z^2-x + return IsZero(t0) +} + +// Inv calculates z = 1/x mod p. +func Inv(z, x *Elt) { + // Calculates z = x^(4k+1) = x^(p-3+1) = x^(p-2) = x^-1, where k = (p-3)/4. + t := &Elt{} + powPminus3div4(t, x) // t = x^k + Sqr(t, t) // t = x^2k + Sqr(t, t) // t = x^4k + Mul(z, t, x) // z = x^(4k+1) +} + +// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4. +func powPminus3div4(z, x *Elt) { + x0, x1 := &Elt{}, &Elt{} + Sqr(z, x) + Mul(z, z, x) + Sqr(x0, z) + Mul(x0, x0, x) + Sqr(z, x0) + Sqr(z, z) + Sqr(z, z) + Mul(z, z, x0) + Sqr(x1, z) + for i := 0; i < 5; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, z) + Sqr(z, x1) + for i := 0; i < 11; i++ { + Sqr(z, z) + } + Mul(z, z, x1) + Sqr(z, z) + Sqr(z, z) + Sqr(z, z) + Mul(z, z, x0) + Sqr(x1, z) + for i := 0; i < 26; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, z) + Sqr(z, x1) + for i := 0; i < 53; i++ { + Sqr(z, z) + } + Mul(z, z, x1) + Sqr(z, z) + Sqr(z, z) + Sqr(z, z) + Mul(z, z, x0) + Sqr(x1, z) + for i := 0; i < 110; i++ { + Sqr(x1, x1) + } + Mul(x1, x1, z) + Sqr(z, x1) + Mul(z, z, x) + for i := 0; i < 223; i++ { + Sqr(z, z) + } + Mul(z, z, x1) +} + +// Cmov assigns y to x if n is 1. +func Cmov(x, y *Elt, n uint) { cmov(x, y, n) } + +// Cswap interchanges x and y if n is 1. +func Cswap(x, y *Elt, n uint) { cswap(x, y, n) } + +// Add calculates z = x+y mod p. +func Add(z, x, y *Elt) { add(z, x, y) } + +// Sub calculates z = x-y mod p. +func Sub(z, x, y *Elt) { sub(z, x, y) } + +// AddSub calculates (x,y) = (x+y mod p, x-y mod p). +func AddSub(x, y *Elt) { addsub(x, y) } + +// Mul calculates z = x*y mod p. +func Mul(z, x, y *Elt) { mul(z, x, y) } + +// Sqr calculates z = x^2 mod p. +func Sqr(z, x *Elt) { sqr(z, x) } diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.go b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.go new file mode 100644 index 00000000..6a12209a --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.go @@ -0,0 +1,43 @@ +//go:build amd64 && !purego +// +build amd64,!purego + +package fp448 + +import ( + "golang.org/x/sys/cpu" +) + +var hasBmi2Adx = cpu.X86.HasBMI2 && cpu.X86.HasADX + +var _ = hasBmi2Adx + +func cmov(x, y *Elt, n uint) { cmovAmd64(x, y, n) } +func cswap(x, y *Elt, n uint) { cswapAmd64(x, y, n) } +func add(z, x, y *Elt) { addAmd64(z, x, y) } +func sub(z, x, y *Elt) { subAmd64(z, x, y) } +func addsub(x, y *Elt) { addsubAmd64(x, y) } +func mul(z, x, y *Elt) { mulAmd64(z, x, y) } +func sqr(z, x *Elt) { sqrAmd64(z, x) } + +/* Functions defined in fp_amd64.s */ + +//go:noescape +func cmovAmd64(x, y *Elt, n uint) + +//go:noescape +func cswapAmd64(x, y *Elt, n uint) + +//go:noescape +func addAmd64(z, x, y *Elt) + +//go:noescape +func subAmd64(z, x, y *Elt) + +//go:noescape +func addsubAmd64(x, y *Elt) + +//go:noescape +func mulAmd64(z, x, y *Elt) + +//go:noescape +func sqrAmd64(z, x *Elt) diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.h b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.h new file mode 100644 index 00000000..0b7dea17 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.h @@ -0,0 +1,590 @@ +// This code was imported from https://github.com/armfazh/rfc7748_precomputed + +// CHECK_BMI2ADX triggers bmi2adx if supported, +// otherwise it fallbacks to legacy code. +#define CHECK_BMI2ADX(label, legacy, bmi2adx) \ + CMPB ·hasBmi2Adx(SB), $0 \ + JE label \ + bmi2adx \ + RET \ + label: \ + legacy \ + RET + +// cselect is a conditional move +// if b=1: it copies y into x; +// if b=0: x remains with the same value; +// if b<> 0,1: undefined. +// Uses: AX, DX, FLAGS +// Instr: x86_64, cmov +#define cselect(x,y,b) \ + TESTQ b, b \ + MOVQ 0+x, AX; MOVQ 0+y, DX; CMOVQNE DX, AX; MOVQ AX, 0+x; \ + MOVQ 8+x, AX; MOVQ 8+y, DX; CMOVQNE DX, AX; MOVQ AX, 8+x; \ + MOVQ 16+x, AX; MOVQ 16+y, DX; CMOVQNE DX, AX; MOVQ AX, 16+x; \ + MOVQ 24+x, AX; MOVQ 24+y, DX; CMOVQNE DX, AX; MOVQ AX, 24+x; \ + MOVQ 32+x, AX; MOVQ 32+y, DX; CMOVQNE DX, AX; MOVQ AX, 32+x; \ + MOVQ 40+x, AX; MOVQ 40+y, DX; CMOVQNE DX, AX; MOVQ AX, 40+x; \ + MOVQ 48+x, AX; MOVQ 48+y, DX; CMOVQNE DX, AX; MOVQ AX, 48+x; + +// cswap is a conditional swap +// if b=1: x,y <- y,x; +// if b=0: x,y remain with the same values; +// if b<> 0,1: undefined. +// Uses: AX, DX, R8, FLAGS +// Instr: x86_64, cmov +#define cswap(x,y,b) \ + TESTQ b, b \ + MOVQ 0+x, AX; MOVQ AX, R8; MOVQ 0+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 0+x; MOVQ DX, 0+y; \ + MOVQ 8+x, AX; MOVQ AX, R8; MOVQ 8+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 8+x; MOVQ DX, 8+y; \ + MOVQ 16+x, AX; MOVQ AX, R8; MOVQ 16+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 16+x; MOVQ DX, 16+y; \ + MOVQ 24+x, AX; MOVQ AX, R8; MOVQ 24+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 24+x; MOVQ DX, 24+y; \ + MOVQ 32+x, AX; MOVQ AX, R8; MOVQ 32+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 32+x; MOVQ DX, 32+y; \ + MOVQ 40+x, AX; MOVQ AX, R8; MOVQ 40+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 40+x; MOVQ DX, 40+y; \ + MOVQ 48+x, AX; MOVQ AX, R8; MOVQ 48+y, DX; CMOVQNE DX, AX; CMOVQNE R8, DX; MOVQ AX, 48+x; MOVQ DX, 48+y; + +// additionLeg adds x and y and stores in z +// Uses: AX, DX, R8-R14, FLAGS +// Instr: x86_64 +#define additionLeg(z,x,y) \ + MOVQ 0+x, R8; ADDQ 0+y, R8; \ + MOVQ 8+x, R9; ADCQ 8+y, R9; \ + MOVQ 16+x, R10; ADCQ 16+y, R10; \ + MOVQ 24+x, R11; ADCQ 24+y, R11; \ + MOVQ 32+x, R12; ADCQ 32+y, R12; \ + MOVQ 40+x, R13; ADCQ 40+y, R13; \ + MOVQ 48+x, R14; ADCQ 48+y, R14; \ + MOVQ $0, AX; ADCQ $0, AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + ADDQ AX, R8; MOVQ $0, AX; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ DX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + ADCQ $0, AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + ADDQ AX, R8; MOVQ R8, 0+z; \ + ADCQ $0, R9; MOVQ R9, 8+z; \ + ADCQ $0, R10; MOVQ R10, 16+z; \ + ADCQ DX, R11; MOVQ R11, 24+z; \ + ADCQ $0, R12; MOVQ R12, 32+z; \ + ADCQ $0, R13; MOVQ R13, 40+z; \ + ADCQ $0, R14; MOVQ R14, 48+z; + + +// additionAdx adds x and y and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64, adx +#define additionAdx(z,x,y) \ + MOVL $32, R15; \ + XORL DX, DX; \ + MOVQ 0+x, R8; ADCXQ 0+y, R8; \ + MOVQ 8+x, R9; ADCXQ 8+y, R9; \ + MOVQ 16+x, R10; ADCXQ 16+y, R10; \ + MOVQ 24+x, R11; ADCXQ 24+y, R11; \ + MOVQ 32+x, R12; ADCXQ 32+y, R12; \ + MOVQ 40+x, R13; ADCXQ 40+y, R13; \ + MOVQ 48+x, R14; ADCXQ 48+y, R14; \ + ;;;;;;;;;;;;;;; ADCXQ DX, DX; \ + XORL AX, AX; \ + ADCXQ DX, R8; SHLXQ R15, DX, DX; \ + ADCXQ AX, R9; \ + ADCXQ AX, R10; \ + ADCXQ DX, R11; \ + ADCXQ AX, R12; \ + ADCXQ AX, R13; \ + ADCXQ AX, R14; \ + ADCXQ AX, AX; \ + XORL DX, DX; \ + ADCXQ AX, R8; MOVQ R8, 0+z; SHLXQ R15, AX, AX; \ + ADCXQ DX, R9; MOVQ R9, 8+z; \ + ADCXQ DX, R10; MOVQ R10, 16+z; \ + ADCXQ AX, R11; MOVQ R11, 24+z; \ + ADCXQ DX, R12; MOVQ R12, 32+z; \ + ADCXQ DX, R13; MOVQ R13, 40+z; \ + ADCXQ DX, R14; MOVQ R14, 48+z; + +// subtraction subtracts y from x and stores in z +// Uses: AX, DX, R8-R14, FLAGS +// Instr: x86_64 +#define subtraction(z,x,y) \ + MOVQ 0+x, R8; SUBQ 0+y, R8; \ + MOVQ 8+x, R9; SBBQ 8+y, R9; \ + MOVQ 16+x, R10; SBBQ 16+y, R10; \ + MOVQ 24+x, R11; SBBQ 24+y, R11; \ + MOVQ 32+x, R12; SBBQ 32+y, R12; \ + MOVQ 40+x, R13; SBBQ 40+y, R13; \ + MOVQ 48+x, R14; SBBQ 48+y, R14; \ + MOVQ $0, AX; SETCS AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + SUBQ AX, R8; MOVQ $0, AX; \ + SBBQ $0, R9; \ + SBBQ $0, R10; \ + SBBQ DX, R11; \ + SBBQ $0, R12; \ + SBBQ $0, R13; \ + SBBQ $0, R14; \ + SETCS AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + SUBQ AX, R8; MOVQ R8, 0+z; \ + SBBQ $0, R9; MOVQ R9, 8+z; \ + SBBQ $0, R10; MOVQ R10, 16+z; \ + SBBQ DX, R11; MOVQ R11, 24+z; \ + SBBQ $0, R12; MOVQ R12, 32+z; \ + SBBQ $0, R13; MOVQ R13, 40+z; \ + SBBQ $0, R14; MOVQ R14, 48+z; + +// maddBmi2Adx multiplies x and y and accumulates in z +// Uses: AX, DX, R15, FLAGS +// Instr: x86_64, bmi2, adx +#define maddBmi2Adx(z,x,y,i,r0,r1,r2,r3,r4,r5,r6) \ + MOVQ i+y, DX; XORL AX, AX; \ + MULXQ 0+x, AX, R8; ADOXQ AX, r0; ADCXQ R8, r1; MOVQ r0,i+z; \ + MULXQ 8+x, AX, r0; ADOXQ AX, r1; ADCXQ r0, r2; MOVQ $0, R8; \ + MULXQ 16+x, AX, r0; ADOXQ AX, r2; ADCXQ r0, r3; \ + MULXQ 24+x, AX, r0; ADOXQ AX, r3; ADCXQ r0, r4; \ + MULXQ 32+x, AX, r0; ADOXQ AX, r4; ADCXQ r0, r5; \ + MULXQ 40+x, AX, r0; ADOXQ AX, r5; ADCXQ r0, r6; \ + MULXQ 48+x, AX, r0; ADOXQ AX, r6; ADCXQ R8, r0; \ + ;;;;;;;;;;;;;;;;;;; ADOXQ R8, r0; + +// integerMulAdx multiplies x and y and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64, bmi2, adx +#define integerMulAdx(z,x,y) \ + MOVQ 0+y, DX; XORL AX, AX; MOVQ $0, R8; \ + MULXQ 0+x, AX, R9; MOVQ AX, 0+z; \ + MULXQ 8+x, AX, R10; ADCXQ AX, R9; \ + MULXQ 16+x, AX, R11; ADCXQ AX, R10; \ + MULXQ 24+x, AX, R12; ADCXQ AX, R11; \ + MULXQ 32+x, AX, R13; ADCXQ AX, R12; \ + MULXQ 40+x, AX, R14; ADCXQ AX, R13; \ + MULXQ 48+x, AX, R15; ADCXQ AX, R14; \ + ;;;;;;;;;;;;;;;;;;;; ADCXQ R8, R15; \ + maddBmi2Adx(z,x,y, 8, R9,R10,R11,R12,R13,R14,R15) \ + maddBmi2Adx(z,x,y,16,R10,R11,R12,R13,R14,R15, R9) \ + maddBmi2Adx(z,x,y,24,R11,R12,R13,R14,R15, R9,R10) \ + maddBmi2Adx(z,x,y,32,R12,R13,R14,R15, R9,R10,R11) \ + maddBmi2Adx(z,x,y,40,R13,R14,R15, R9,R10,R11,R12) \ + maddBmi2Adx(z,x,y,48,R14,R15, R9,R10,R11,R12,R13) \ + MOVQ R15, 56+z; \ + MOVQ R9, 64+z; \ + MOVQ R10, 72+z; \ + MOVQ R11, 80+z; \ + MOVQ R12, 88+z; \ + MOVQ R13, 96+z; \ + MOVQ R14, 104+z; + +// maddLegacy multiplies x and y and accumulates in z +// Uses: AX, DX, R15, FLAGS +// Instr: x86_64 +#define maddLegacy(z,x,y,i) \ + MOVQ i+y, R15; \ + MOVQ 0+x, AX; MULQ R15; MOVQ AX, R8; ;;;;;;;;;;;; MOVQ DX, R9; \ + MOVQ 8+x, AX; MULQ R15; ADDQ AX, R9; ADCQ $0, DX; MOVQ DX, R10; \ + MOVQ 16+x, AX; MULQ R15; ADDQ AX, R10; ADCQ $0, DX; MOVQ DX, R11; \ + MOVQ 24+x, AX; MULQ R15; ADDQ AX, R11; ADCQ $0, DX; MOVQ DX, R12; \ + MOVQ 32+x, AX; MULQ R15; ADDQ AX, R12; ADCQ $0, DX; MOVQ DX, R13; \ + MOVQ 40+x, AX; MULQ R15; ADDQ AX, R13; ADCQ $0, DX; MOVQ DX, R14; \ + MOVQ 48+x, AX; MULQ R15; ADDQ AX, R14; ADCQ $0, DX; \ + ADDQ 0+i+z, R8; MOVQ R8, 0+i+z; \ + ADCQ 8+i+z, R9; MOVQ R9, 8+i+z; \ + ADCQ 16+i+z, R10; MOVQ R10, 16+i+z; \ + ADCQ 24+i+z, R11; MOVQ R11, 24+i+z; \ + ADCQ 32+i+z, R12; MOVQ R12, 32+i+z; \ + ADCQ 40+i+z, R13; MOVQ R13, 40+i+z; \ + ADCQ 48+i+z, R14; MOVQ R14, 48+i+z; \ + ADCQ $0, DX; MOVQ DX, 56+i+z; + +// integerMulLeg multiplies x and y and stores in z +// Uses: AX, DX, R8-R15, FLAGS +// Instr: x86_64 +#define integerMulLeg(z,x,y) \ + MOVQ 0+y, R15; \ + MOVQ 0+x, AX; MULQ R15; MOVQ AX, 0+z; ;;;;;;;;;;;; MOVQ DX, R8; \ + MOVQ 8+x, AX; MULQ R15; ADDQ AX, R8; ADCQ $0, DX; MOVQ DX, R9; MOVQ R8, 8+z; \ + MOVQ 16+x, AX; MULQ R15; ADDQ AX, R9; ADCQ $0, DX; MOVQ DX, R10; MOVQ R9, 16+z; \ + MOVQ 24+x, AX; MULQ R15; ADDQ AX, R10; ADCQ $0, DX; MOVQ DX, R11; MOVQ R10, 24+z; \ + MOVQ 32+x, AX; MULQ R15; ADDQ AX, R11; ADCQ $0, DX; MOVQ DX, R12; MOVQ R11, 32+z; \ + MOVQ 40+x, AX; MULQ R15; ADDQ AX, R12; ADCQ $0, DX; MOVQ DX, R13; MOVQ R12, 40+z; \ + MOVQ 48+x, AX; MULQ R15; ADDQ AX, R13; ADCQ $0, DX; MOVQ DX,56+z; MOVQ R13, 48+z; \ + maddLegacy(z,x,y, 8) \ + maddLegacy(z,x,y,16) \ + maddLegacy(z,x,y,24) \ + maddLegacy(z,x,y,32) \ + maddLegacy(z,x,y,40) \ + maddLegacy(z,x,y,48) + +// integerSqrLeg squares x and stores in z +// Uses: AX, CX, DX, R8-R15, FLAGS +// Instr: x86_64 +#define integerSqrLeg(z,x) \ + XORL R15, R15; \ + MOVQ 0+x, CX; \ + MOVQ CX, AX; MULQ CX; MOVQ AX, 0+z; MOVQ DX, R8; \ + ADDQ CX, CX; ADCQ $0, R15; \ + MOVQ 8+x, AX; MULQ CX; ADDQ AX, R8; ADCQ $0, DX; MOVQ DX, R9; MOVQ R8, 8+z; \ + MOVQ 16+x, AX; MULQ CX; ADDQ AX, R9; ADCQ $0, DX; MOVQ DX, R10; \ + MOVQ 24+x, AX; MULQ CX; ADDQ AX, R10; ADCQ $0, DX; MOVQ DX, R11; \ + MOVQ 32+x, AX; MULQ CX; ADDQ AX, R11; ADCQ $0, DX; MOVQ DX, R12; \ + MOVQ 40+x, AX; MULQ CX; ADDQ AX, R12; ADCQ $0, DX; MOVQ DX, R13; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R13; ADCQ $0, DX; MOVQ DX, R14; \ + \ + MOVQ 8+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R9; ADCQ $0, DX; MOVQ R9,16+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 8+x, AX; ADDQ AX, DX; ADCQ $0, R11; MOVQ DX, R8; \ + ADDQ 8+x, CX; ADCQ $0, R15; \ + MOVQ 16+x, AX; MULQ CX; ADDQ AX, R10; ADCQ $0, DX; ADDQ R8, R10; ADCQ $0, DX; MOVQ DX, R8; MOVQ R10, 24+z; \ + MOVQ 24+x, AX; MULQ CX; ADDQ AX, R11; ADCQ $0, DX; ADDQ R8, R11; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 32+x, AX; MULQ CX; ADDQ AX, R12; ADCQ $0, DX; ADDQ R8, R12; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 40+x, AX; MULQ CX; ADDQ AX, R13; ADCQ $0, DX; ADDQ R8, R13; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R14; ADCQ $0, DX; ADDQ R8, R14; ADCQ $0, DX; MOVQ DX, R9; \ + \ + MOVQ 16+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R11; ADCQ $0, DX; MOVQ R11, 32+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 16+x,AX; ADDQ AX, DX; ADCQ $0, R13; MOVQ DX, R8; \ + ADDQ 16+x, CX; ADCQ $0, R15; \ + MOVQ 24+x, AX; MULQ CX; ADDQ AX, R12; ADCQ $0, DX; ADDQ R8, R12; ADCQ $0, DX; MOVQ DX, R8; MOVQ R12, 40+z; \ + MOVQ 32+x, AX; MULQ CX; ADDQ AX, R13; ADCQ $0, DX; ADDQ R8, R13; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 40+x, AX; MULQ CX; ADDQ AX, R14; ADCQ $0, DX; ADDQ R8, R14; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R9; ADCQ $0, DX; ADDQ R8, R9; ADCQ $0, DX; MOVQ DX,R10; \ + \ + MOVQ 24+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R13; ADCQ $0, DX; MOVQ R13, 48+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 24+x,AX; ADDQ AX, DX; ADCQ $0, R9; MOVQ DX, R8; \ + ADDQ 24+x, CX; ADCQ $0, R15; \ + MOVQ 32+x, AX; MULQ CX; ADDQ AX, R14; ADCQ $0, DX; ADDQ R8, R14; ADCQ $0, DX; MOVQ DX, R8; MOVQ R14, 56+z; \ + MOVQ 40+x, AX; MULQ CX; ADDQ AX, R9; ADCQ $0, DX; ADDQ R8, R9; ADCQ $0, DX; MOVQ DX, R8; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R10; ADCQ $0, DX; ADDQ R8, R10; ADCQ $0, DX; MOVQ DX,R11; \ + \ + MOVQ 32+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R9; ADCQ $0, DX; MOVQ R9, 64+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 32+x,AX; ADDQ AX, DX; ADCQ $0, R11; MOVQ DX, R8; \ + ADDQ 32+x, CX; ADCQ $0, R15; \ + MOVQ 40+x, AX; MULQ CX; ADDQ AX, R10; ADCQ $0, DX; ADDQ R8, R10; ADCQ $0, DX; MOVQ DX, R8; MOVQ R10, 72+z; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R11; ADCQ $0, DX; ADDQ R8, R11; ADCQ $0, DX; MOVQ DX,R12; \ + \ + XORL R13, R13; \ + XORL R14, R14; \ + MOVQ 40+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R11; ADCQ $0, DX; MOVQ R11, 80+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 40+x,AX; ADDQ AX, DX; ADCQ $0, R13; MOVQ DX, R8; \ + ADDQ 40+x, CX; ADCQ $0, R15; \ + MOVQ 48+x, AX; MULQ CX; ADDQ AX, R12; ADCQ $0, DX; ADDQ R8, R12; ADCQ $0, DX; MOVQ DX, R8; MOVQ R12, 88+z; \ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADDQ R8, R13; ADCQ $0,R14; \ + \ + XORL R9, R9; \ + MOVQ 48+x, CX; \ + MOVQ CX, AX; ADDQ R15, CX; MOVQ $0, R15; ADCQ $0, R15; \ + ;;;;;;;;;;;;;; MULQ CX; ADDQ AX, R13; ADCQ $0, DX; MOVQ R13, 96+z; \ + MOVQ R15, AX; NEGQ AX; ANDQ 48+x,AX; ADDQ AX, DX; ADCQ $0, R9; MOVQ DX, R8; \ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ADDQ R8,R14; ADCQ $0, R9; MOVQ R14, 104+z; + + +// integerSqrAdx squares x and stores in z +// Uses: AX, CX, DX, R8-R15, FLAGS +// Instr: x86_64, bmi2, adx +#define integerSqrAdx(z,x) \ + XORL R15, R15; \ + MOVQ 0+x, DX; \ + ;;;;;;;;;;;;;; MULXQ DX, AX, R8; MOVQ AX, 0+z; \ + ADDQ DX, DX; ADCQ $0, R15; CLC; \ + MULXQ 8+x, AX, R9; ADCXQ AX, R8; MOVQ R8, 8+z; \ + MULXQ 16+x, AX, R10; ADCXQ AX, R9; MOVQ $0, R8;\ + MULXQ 24+x, AX, R11; ADCXQ AX, R10; \ + MULXQ 32+x, AX, R12; ADCXQ AX, R11; \ + MULXQ 40+x, AX, R13; ADCXQ AX, R12; \ + MULXQ 48+x, AX, R14; ADCXQ AX, R13; \ + ;;;;;;;;;;;;;;;;;;;; ADCXQ R8, R14; \ + \ + MOVQ 8+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 8+x, R8; \ + ADDQ AX, R9; MOVQ R9, 16+z; \ + ADCQ CX, R8; \ + ADCQ $0, R11; \ + ADDQ 8+x, DX; \ + ADCQ $0, R15; \ + XORL R9, R9; ;;;;;;;;;;;;;;;;;;;;; ADOXQ R8, R10; \ + MULXQ 16+x, AX, CX; ADCXQ AX, R10; ADOXQ CX, R11; MOVQ R10, 24+z; \ + MULXQ 24+x, AX, CX; ADCXQ AX, R11; ADOXQ CX, R12; MOVQ $0, R10; \ + MULXQ 32+x, AX, CX; ADCXQ AX, R12; ADOXQ CX, R13; \ + MULXQ 40+x, AX, CX; ADCXQ AX, R13; ADOXQ CX, R14; \ + MULXQ 48+x, AX, CX; ADCXQ AX, R14; ADOXQ CX, R9; \ + ;;;;;;;;;;;;;;;;;;; ADCXQ R10, R9; \ + \ + MOVQ 16+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 16+x, R8; \ + ADDQ AX, R11; MOVQ R11, 32+z; \ + ADCQ CX, R8; \ + ADCQ $0, R13; \ + ADDQ 16+x, DX; \ + ADCQ $0, R15; \ + XORL R11, R11; ;;;;;;;;;;;;;;;;;;; ADOXQ R8, R12; \ + MULXQ 24+x, AX, CX; ADCXQ AX, R12; ADOXQ CX, R13; MOVQ R12, 40+z; \ + MULXQ 32+x, AX, CX; ADCXQ AX, R13; ADOXQ CX, R14; MOVQ $0, R12; \ + MULXQ 40+x, AX, CX; ADCXQ AX, R14; ADOXQ CX, R9; \ + MULXQ 48+x, AX, CX; ADCXQ AX, R9; ADOXQ CX, R10; \ + ;;;;;;;;;;;;;;;;;;; ADCXQ R11,R10; \ + \ + MOVQ 24+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 24+x, R8; \ + ADDQ AX, R13; MOVQ R13, 48+z; \ + ADCQ CX, R8; \ + ADCQ $0, R9; \ + ADDQ 24+x, DX; \ + ADCQ $0, R15; \ + XORL R13, R13; ;;;;;;;;;;;;;;;;;;; ADOXQ R8, R14; \ + MULXQ 32+x, AX, CX; ADCXQ AX, R14; ADOXQ CX, R9; MOVQ R14, 56+z; \ + MULXQ 40+x, AX, CX; ADCXQ AX, R9; ADOXQ CX, R10; MOVQ $0, R14; \ + MULXQ 48+x, AX, CX; ADCXQ AX, R10; ADOXQ CX, R11; \ + ;;;;;;;;;;;;;;;;;;; ADCXQ R12,R11; \ + \ + MOVQ 32+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 32+x, R8; \ + ADDQ AX, R9; MOVQ R9, 64+z; \ + ADCQ CX, R8; \ + ADCQ $0, R11; \ + ADDQ 32+x, DX; \ + ADCQ $0, R15; \ + XORL R9, R9; ;;;;;;;;;;;;;;;;;;;;; ADOXQ R8, R10; \ + MULXQ 40+x, AX, CX; ADCXQ AX, R10; ADOXQ CX, R11; MOVQ R10, 72+z; \ + MULXQ 48+x, AX, CX; ADCXQ AX, R11; ADOXQ CX, R12; \ + ;;;;;;;;;;;;;;;;;;; ADCXQ R13,R12; \ + \ + MOVQ 40+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 40+x, R8; \ + ADDQ AX, R11; MOVQ R11, 80+z; \ + ADCQ CX, R8; \ + ADCQ $0, R13; \ + ADDQ 40+x, DX; \ + ADCQ $0, R15; \ + XORL R11, R11; ;;;;;;;;;;;;;;;;;;; ADOXQ R8, R12; \ + MULXQ 48+x, AX, CX; ADCXQ AX, R12; ADOXQ CX, R13; MOVQ R12, 88+z; \ + ;;;;;;;;;;;;;;;;;;; ADCXQ R14,R13; \ + \ + MOVQ 48+x, DX; \ + MOVQ DX, AX; ADDQ R15, DX; MOVQ $0, R15; ADCQ $0, R15; \ + MULXQ AX, AX, CX; \ + MOVQ R15, R8; NEGQ R8; ANDQ 48+x, R8; \ + XORL R10, R10; ;;;;;;;;;;;;;; ADOXQ CX, R14; \ + ;;;;;;;;;;;;;; ADCXQ AX, R13; ;;;;;;;;;;;;;; MOVQ R13, 96+z; \ + ;;;;;;;;;;;;;; ADCXQ R8, R14; MOVQ R14, 104+z; + +// reduceFromDoubleLeg finds a z=x modulo p such that z<2^448 and stores in z +// Uses: AX, R8-R15, FLAGS +// Instr: x86_64 +#define reduceFromDoubleLeg(z,x) \ + /* ( ,2C13,2C12,2C11,2C10|C10,C9,C8, C7) + (C6,...,C0) */ \ + /* (r14, r13, r12, r11, r10,r9,r8,r15) */ \ + MOVQ 80+x,AX; MOVQ AX,R10; \ + MOVQ $0xFFFFFFFF00000000, R8; \ + ANDQ R8,R10; \ + \ + MOVQ $0,R14; \ + MOVQ 104+x,R13; SHLQ $1,R13,R14; \ + MOVQ 96+x,R12; SHLQ $1,R12,R13; \ + MOVQ 88+x,R11; SHLQ $1,R11,R12; \ + MOVQ 72+x, R9; SHLQ $1,R10,R11; \ + MOVQ 64+x, R8; SHLQ $1,R10; \ + MOVQ $0xFFFFFFFF,R15; ANDQ R15,AX; ORQ AX,R10; \ + MOVQ 56+x,R15; \ + \ + ADDQ 0+x,R15; MOVQ R15, 0+z; MOVQ 56+x,R15; \ + ADCQ 8+x, R8; MOVQ R8, 8+z; MOVQ 64+x, R8; \ + ADCQ 16+x, R9; MOVQ R9,16+z; MOVQ 72+x, R9; \ + ADCQ 24+x,R10; MOVQ R10,24+z; MOVQ 80+x,R10; \ + ADCQ 32+x,R11; MOVQ R11,32+z; MOVQ 88+x,R11; \ + ADCQ 40+x,R12; MOVQ R12,40+z; MOVQ 96+x,R12; \ + ADCQ 48+x,R13; MOVQ R13,48+z; MOVQ 104+x,R13; \ + ADCQ $0,R14; \ + /* (c10c9,c9c8,c8c7,c7c13,c13c12,c12c11,c11c10) + (c6,...,c0) */ \ + /* ( r9, r8, r15, r13, r12, r11, r10) */ \ + MOVQ R10, AX; \ + SHRQ $32,R11,R10; \ + SHRQ $32,R12,R11; \ + SHRQ $32,R13,R12; \ + SHRQ $32,R15,R13; \ + SHRQ $32, R8,R15; \ + SHRQ $32, R9, R8; \ + SHRQ $32, AX, R9; \ + \ + ADDQ 0+z,R10; \ + ADCQ 8+z,R11; \ + ADCQ 16+z,R12; \ + ADCQ 24+z,R13; \ + ADCQ 32+z,R15; \ + ADCQ 40+z, R8; \ + ADCQ 48+z, R9; \ + ADCQ $0,R14; \ + /* ( c7) + (c6,...,c0) */ \ + /* (r14) */ \ + MOVQ R14, AX; SHLQ $32, AX; \ + ADDQ R14,R10; MOVQ $0,R14; \ + ADCQ $0,R11; \ + ADCQ $0,R12; \ + ADCQ AX,R13; \ + ADCQ $0,R15; \ + ADCQ $0, R8; \ + ADCQ $0, R9; \ + ADCQ $0,R14; \ + /* ( c7) + (c6,...,c0) */ \ + /* (r14) */ \ + MOVQ R14, AX; SHLQ $32,AX; \ + ADDQ R14,R10; MOVQ R10, 0+z; \ + ADCQ $0,R11; MOVQ R11, 8+z; \ + ADCQ $0,R12; MOVQ R12,16+z; \ + ADCQ AX,R13; MOVQ R13,24+z; \ + ADCQ $0,R15; MOVQ R15,32+z; \ + ADCQ $0, R8; MOVQ R8,40+z; \ + ADCQ $0, R9; MOVQ R9,48+z; + +// reduceFromDoubleAdx finds a z=x modulo p such that z<2^448 and stores in z +// Uses: AX, R8-R15, FLAGS +// Instr: x86_64, adx +#define reduceFromDoubleAdx(z,x) \ + /* ( ,2C13,2C12,2C11,2C10|C10,C9,C8, C7) + (C6,...,C0) */ \ + /* (r14, r13, r12, r11, r10,r9,r8,r15) */ \ + MOVQ 80+x,AX; MOVQ AX,R10; \ + MOVQ $0xFFFFFFFF00000000, R8; \ + ANDQ R8,R10; \ + \ + MOVQ $0,R14; \ + MOVQ 104+x,R13; SHLQ $1,R13,R14; \ + MOVQ 96+x,R12; SHLQ $1,R12,R13; \ + MOVQ 88+x,R11; SHLQ $1,R11,R12; \ + MOVQ 72+x, R9; SHLQ $1,R10,R11; \ + MOVQ 64+x, R8; SHLQ $1,R10; \ + MOVQ $0xFFFFFFFF,R15; ANDQ R15,AX; ORQ AX,R10; \ + MOVQ 56+x,R15; \ + \ + XORL AX,AX; \ + ADCXQ 0+x,R15; MOVQ R15, 0+z; MOVQ 56+x,R15; \ + ADCXQ 8+x, R8; MOVQ R8, 8+z; MOVQ 64+x, R8; \ + ADCXQ 16+x, R9; MOVQ R9,16+z; MOVQ 72+x, R9; \ + ADCXQ 24+x,R10; MOVQ R10,24+z; MOVQ 80+x,R10; \ + ADCXQ 32+x,R11; MOVQ R11,32+z; MOVQ 88+x,R11; \ + ADCXQ 40+x,R12; MOVQ R12,40+z; MOVQ 96+x,R12; \ + ADCXQ 48+x,R13; MOVQ R13,48+z; MOVQ 104+x,R13; \ + ADCXQ AX,R14; \ + /* (c10c9,c9c8,c8c7,c7c13,c13c12,c12c11,c11c10) + (c6,...,c0) */ \ + /* ( r9, r8, r15, r13, r12, r11, r10) */ \ + MOVQ R10, AX; \ + SHRQ $32,R11,R10; \ + SHRQ $32,R12,R11; \ + SHRQ $32,R13,R12; \ + SHRQ $32,R15,R13; \ + SHRQ $32, R8,R15; \ + SHRQ $32, R9, R8; \ + SHRQ $32, AX, R9; \ + \ + XORL AX,AX; \ + ADCXQ 0+z,R10; \ + ADCXQ 8+z,R11; \ + ADCXQ 16+z,R12; \ + ADCXQ 24+z,R13; \ + ADCXQ 32+z,R15; \ + ADCXQ 40+z, R8; \ + ADCXQ 48+z, R9; \ + ADCXQ AX,R14; \ + /* ( c7) + (c6,...,c0) */ \ + /* (r14) */ \ + MOVQ R14, AX; SHLQ $32, AX; \ + CLC; \ + ADCXQ R14,R10; MOVQ $0,R14; \ + ADCXQ R14,R11; \ + ADCXQ R14,R12; \ + ADCXQ AX,R13; \ + ADCXQ R14,R15; \ + ADCXQ R14, R8; \ + ADCXQ R14, R9; \ + ADCXQ R14,R14; \ + /* ( c7) + (c6,...,c0) */ \ + /* (r14) */ \ + MOVQ R14, AX; SHLQ $32, AX; \ + CLC; \ + ADCXQ R14,R10; MOVQ R10, 0+z; MOVQ $0,R14; \ + ADCXQ R14,R11; MOVQ R11, 8+z; \ + ADCXQ R14,R12; MOVQ R12,16+z; \ + ADCXQ AX,R13; MOVQ R13,24+z; \ + ADCXQ R14,R15; MOVQ R15,32+z; \ + ADCXQ R14, R8; MOVQ R8,40+z; \ + ADCXQ R14, R9; MOVQ R9,48+z; + +// addSub calculates two operations: x,y = x+y,x-y +// Uses: AX, DX, R8-R15, FLAGS +#define addSub(x,y) \ + MOVQ 0+x, R8; ADDQ 0+y, R8; \ + MOVQ 8+x, R9; ADCQ 8+y, R9; \ + MOVQ 16+x, R10; ADCQ 16+y, R10; \ + MOVQ 24+x, R11; ADCQ 24+y, R11; \ + MOVQ 32+x, R12; ADCQ 32+y, R12; \ + MOVQ 40+x, R13; ADCQ 40+y, R13; \ + MOVQ 48+x, R14; ADCQ 48+y, R14; \ + MOVQ $0, AX; ADCQ $0, AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + ADDQ AX, R8; MOVQ $0, AX; \ + ADCQ $0, R9; \ + ADCQ $0, R10; \ + ADCQ DX, R11; \ + ADCQ $0, R12; \ + ADCQ $0, R13; \ + ADCQ $0, R14; \ + ADCQ $0, AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + ADDQ AX, R8; MOVQ 0+x,AX; MOVQ R8, 0+x; MOVQ AX, R8; \ + ADCQ $0, R9; MOVQ 8+x,AX; MOVQ R9, 8+x; MOVQ AX, R9; \ + ADCQ $0, R10; MOVQ 16+x,AX; MOVQ R10, 16+x; MOVQ AX, R10; \ + ADCQ DX, R11; MOVQ 24+x,AX; MOVQ R11, 24+x; MOVQ AX, R11; \ + ADCQ $0, R12; MOVQ 32+x,AX; MOVQ R12, 32+x; MOVQ AX, R12; \ + ADCQ $0, R13; MOVQ 40+x,AX; MOVQ R13, 40+x; MOVQ AX, R13; \ + ADCQ $0, R14; MOVQ 48+x,AX; MOVQ R14, 48+x; MOVQ AX, R14; \ + SUBQ 0+y, R8; \ + SBBQ 8+y, R9; \ + SBBQ 16+y, R10; \ + SBBQ 24+y, R11; \ + SBBQ 32+y, R12; \ + SBBQ 40+y, R13; \ + SBBQ 48+y, R14; \ + MOVQ $0, AX; SETCS AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + SUBQ AX, R8; MOVQ $0, AX; \ + SBBQ $0, R9; \ + SBBQ $0, R10; \ + SBBQ DX, R11; \ + SBBQ $0, R12; \ + SBBQ $0, R13; \ + SBBQ $0, R14; \ + SETCS AX; \ + MOVQ AX, DX; \ + SHLQ $32, DX; \ + SUBQ AX, R8; MOVQ R8, 0+y; \ + SBBQ $0, R9; MOVQ R9, 8+y; \ + SBBQ $0, R10; MOVQ R10, 16+y; \ + SBBQ DX, R11; MOVQ R11, 24+y; \ + SBBQ $0, R12; MOVQ R12, 32+y; \ + SBBQ $0, R13; MOVQ R13, 40+y; \ + SBBQ $0, R14; MOVQ R14, 48+y; diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s new file mode 100644 index 00000000..435addf5 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s @@ -0,0 +1,74 @@ +// +build amd64 + +#include "textflag.h" +#include "fp_amd64.h" + +// func cmovAmd64(x, y *Elt, n uint) +TEXT ·cmovAmd64(SB),NOSPLIT,$0-24 + MOVQ x+0(FP), DI + MOVQ y+8(FP), SI + MOVQ n+16(FP), BX + cselect(0(DI),0(SI),BX) + RET + +// func cswapAmd64(x, y *Elt, n uint) +TEXT ·cswapAmd64(SB),NOSPLIT,$0-24 + MOVQ x+0(FP), DI + MOVQ y+8(FP), SI + MOVQ n+16(FP), BX + cswap(0(DI),0(SI),BX) + RET + +// func subAmd64(z, x, y *Elt) +TEXT ·subAmd64(SB),NOSPLIT,$0-24 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + MOVQ y+16(FP), BX + subtraction(0(DI),0(SI),0(BX)) + RET + +// func addsubAmd64(x, y *Elt) +TEXT ·addsubAmd64(SB),NOSPLIT,$0-16 + MOVQ x+0(FP), DI + MOVQ y+8(FP), SI + addSub(0(DI),0(SI)) + RET + +#define addLegacy \ + additionLeg(0(DI),0(SI),0(BX)) +#define addBmi2Adx \ + additionAdx(0(DI),0(SI),0(BX)) + +#define mulLegacy \ + integerMulLeg(0(SP),0(SI),0(BX)) \ + reduceFromDoubleLeg(0(DI),0(SP)) +#define mulBmi2Adx \ + integerMulAdx(0(SP),0(SI),0(BX)) \ + reduceFromDoubleAdx(0(DI),0(SP)) + +#define sqrLegacy \ + integerSqrLeg(0(SP),0(SI)) \ + reduceFromDoubleLeg(0(DI),0(SP)) +#define sqrBmi2Adx \ + integerSqrAdx(0(SP),0(SI)) \ + reduceFromDoubleAdx(0(DI),0(SP)) + +// func addAmd64(z, x, y *Elt) +TEXT ·addAmd64(SB),NOSPLIT,$0-24 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + MOVQ y+16(FP), BX + CHECK_BMI2ADX(LADD, addLegacy, addBmi2Adx) + +// func mulAmd64(z, x, y *Elt) +TEXT ·mulAmd64(SB),NOSPLIT,$112-24 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + MOVQ y+16(FP), BX + CHECK_BMI2ADX(LMUL, mulLegacy, mulBmi2Adx) + +// func sqrAmd64(z, x *Elt) +TEXT ·sqrAmd64(SB),NOSPLIT,$112-16 + MOVQ z+0(FP), DI + MOVQ x+8(FP), SI + CHECK_BMI2ADX(LSQR, sqrLegacy, sqrBmi2Adx) diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_generic.go b/vendor/github.com/cloudflare/circl/math/fp448/fp_generic.go new file mode 100644 index 00000000..47a0b632 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_generic.go @@ -0,0 +1,339 @@ +package fp448 + +import ( + "encoding/binary" + "math/bits" +) + +func cmovGeneric(x, y *Elt, n uint) { + m := -uint64(n & 0x1) + x0 := binary.LittleEndian.Uint64(x[0*8 : 1*8]) + x1 := binary.LittleEndian.Uint64(x[1*8 : 2*8]) + x2 := binary.LittleEndian.Uint64(x[2*8 : 3*8]) + x3 := binary.LittleEndian.Uint64(x[3*8 : 4*8]) + x4 := binary.LittleEndian.Uint64(x[4*8 : 5*8]) + x5 := binary.LittleEndian.Uint64(x[5*8 : 6*8]) + x6 := binary.LittleEndian.Uint64(x[6*8 : 7*8]) + + y0 := binary.LittleEndian.Uint64(y[0*8 : 1*8]) + y1 := binary.LittleEndian.Uint64(y[1*8 : 2*8]) + y2 := binary.LittleEndian.Uint64(y[2*8 : 3*8]) + y3 := binary.LittleEndian.Uint64(y[3*8 : 4*8]) + y4 := binary.LittleEndian.Uint64(y[4*8 : 5*8]) + y5 := binary.LittleEndian.Uint64(y[5*8 : 6*8]) + y6 := binary.LittleEndian.Uint64(y[6*8 : 7*8]) + + x0 = (x0 &^ m) | (y0 & m) + x1 = (x1 &^ m) | (y1 & m) + x2 = (x2 &^ m) | (y2 & m) + x3 = (x3 &^ m) | (y3 & m) + x4 = (x4 &^ m) | (y4 & m) + x5 = (x5 &^ m) | (y5 & m) + x6 = (x6 &^ m) | (y6 & m) + + binary.LittleEndian.PutUint64(x[0*8:1*8], x0) + binary.LittleEndian.PutUint64(x[1*8:2*8], x1) + binary.LittleEndian.PutUint64(x[2*8:3*8], x2) + binary.LittleEndian.PutUint64(x[3*8:4*8], x3) + binary.LittleEndian.PutUint64(x[4*8:5*8], x4) + binary.LittleEndian.PutUint64(x[5*8:6*8], x5) + binary.LittleEndian.PutUint64(x[6*8:7*8], x6) +} + +func cswapGeneric(x, y *Elt, n uint) { + m := -uint64(n & 0x1) + x0 := binary.LittleEndian.Uint64(x[0*8 : 1*8]) + x1 := binary.LittleEndian.Uint64(x[1*8 : 2*8]) + x2 := binary.LittleEndian.Uint64(x[2*8 : 3*8]) + x3 := binary.LittleEndian.Uint64(x[3*8 : 4*8]) + x4 := binary.LittleEndian.Uint64(x[4*8 : 5*8]) + x5 := binary.LittleEndian.Uint64(x[5*8 : 6*8]) + x6 := binary.LittleEndian.Uint64(x[6*8 : 7*8]) + + y0 := binary.LittleEndian.Uint64(y[0*8 : 1*8]) + y1 := binary.LittleEndian.Uint64(y[1*8 : 2*8]) + y2 := binary.LittleEndian.Uint64(y[2*8 : 3*8]) + y3 := binary.LittleEndian.Uint64(y[3*8 : 4*8]) + y4 := binary.LittleEndian.Uint64(y[4*8 : 5*8]) + y5 := binary.LittleEndian.Uint64(y[5*8 : 6*8]) + y6 := binary.LittleEndian.Uint64(y[6*8 : 7*8]) + + t0 := m & (x0 ^ y0) + t1 := m & (x1 ^ y1) + t2 := m & (x2 ^ y2) + t3 := m & (x3 ^ y3) + t4 := m & (x4 ^ y4) + t5 := m & (x5 ^ y5) + t6 := m & (x6 ^ y6) + x0 ^= t0 + x1 ^= t1 + x2 ^= t2 + x3 ^= t3 + x4 ^= t4 + x5 ^= t5 + x6 ^= t6 + y0 ^= t0 + y1 ^= t1 + y2 ^= t2 + y3 ^= t3 + y4 ^= t4 + y5 ^= t5 + y6 ^= t6 + + binary.LittleEndian.PutUint64(x[0*8:1*8], x0) + binary.LittleEndian.PutUint64(x[1*8:2*8], x1) + binary.LittleEndian.PutUint64(x[2*8:3*8], x2) + binary.LittleEndian.PutUint64(x[3*8:4*8], x3) + binary.LittleEndian.PutUint64(x[4*8:5*8], x4) + binary.LittleEndian.PutUint64(x[5*8:6*8], x5) + binary.LittleEndian.PutUint64(x[6*8:7*8], x6) + + binary.LittleEndian.PutUint64(y[0*8:1*8], y0) + binary.LittleEndian.PutUint64(y[1*8:2*8], y1) + binary.LittleEndian.PutUint64(y[2*8:3*8], y2) + binary.LittleEndian.PutUint64(y[3*8:4*8], y3) + binary.LittleEndian.PutUint64(y[4*8:5*8], y4) + binary.LittleEndian.PutUint64(y[5*8:6*8], y5) + binary.LittleEndian.PutUint64(y[6*8:7*8], y6) +} + +func addGeneric(z, x, y *Elt) { + x0 := binary.LittleEndian.Uint64(x[0*8 : 1*8]) + x1 := binary.LittleEndian.Uint64(x[1*8 : 2*8]) + x2 := binary.LittleEndian.Uint64(x[2*8 : 3*8]) + x3 := binary.LittleEndian.Uint64(x[3*8 : 4*8]) + x4 := binary.LittleEndian.Uint64(x[4*8 : 5*8]) + x5 := binary.LittleEndian.Uint64(x[5*8 : 6*8]) + x6 := binary.LittleEndian.Uint64(x[6*8 : 7*8]) + + y0 := binary.LittleEndian.Uint64(y[0*8 : 1*8]) + y1 := binary.LittleEndian.Uint64(y[1*8 : 2*8]) + y2 := binary.LittleEndian.Uint64(y[2*8 : 3*8]) + y3 := binary.LittleEndian.Uint64(y[3*8 : 4*8]) + y4 := binary.LittleEndian.Uint64(y[4*8 : 5*8]) + y5 := binary.LittleEndian.Uint64(y[5*8 : 6*8]) + y6 := binary.LittleEndian.Uint64(y[6*8 : 7*8]) + + z0, c0 := bits.Add64(x0, y0, 0) + z1, c1 := bits.Add64(x1, y1, c0) + z2, c2 := bits.Add64(x2, y2, c1) + z3, c3 := bits.Add64(x3, y3, c2) + z4, c4 := bits.Add64(x4, y4, c3) + z5, c5 := bits.Add64(x5, y5, c4) + z6, z7 := bits.Add64(x6, y6, c5) + + z0, c0 = bits.Add64(z0, z7, 0) + z1, c1 = bits.Add64(z1, 0, c0) + z2, c2 = bits.Add64(z2, 0, c1) + z3, c3 = bits.Add64(z3, z7<<32, c2) + z4, c4 = bits.Add64(z4, 0, c3) + z5, c5 = bits.Add64(z5, 0, c4) + z6, z7 = bits.Add64(z6, 0, c5) + + z0, c0 = bits.Add64(z0, z7, 0) + z1, c1 = bits.Add64(z1, 0, c0) + z2, c2 = bits.Add64(z2, 0, c1) + z3, c3 = bits.Add64(z3, z7<<32, c2) + z4, c4 = bits.Add64(z4, 0, c3) + z5, c5 = bits.Add64(z5, 0, c4) + z6, _ = bits.Add64(z6, 0, c5) + + binary.LittleEndian.PutUint64(z[0*8:1*8], z0) + binary.LittleEndian.PutUint64(z[1*8:2*8], z1) + binary.LittleEndian.PutUint64(z[2*8:3*8], z2) + binary.LittleEndian.PutUint64(z[3*8:4*8], z3) + binary.LittleEndian.PutUint64(z[4*8:5*8], z4) + binary.LittleEndian.PutUint64(z[5*8:6*8], z5) + binary.LittleEndian.PutUint64(z[6*8:7*8], z6) +} + +func subGeneric(z, x, y *Elt) { + x0 := binary.LittleEndian.Uint64(x[0*8 : 1*8]) + x1 := binary.LittleEndian.Uint64(x[1*8 : 2*8]) + x2 := binary.LittleEndian.Uint64(x[2*8 : 3*8]) + x3 := binary.LittleEndian.Uint64(x[3*8 : 4*8]) + x4 := binary.LittleEndian.Uint64(x[4*8 : 5*8]) + x5 := binary.LittleEndian.Uint64(x[5*8 : 6*8]) + x6 := binary.LittleEndian.Uint64(x[6*8 : 7*8]) + + y0 := binary.LittleEndian.Uint64(y[0*8 : 1*8]) + y1 := binary.LittleEndian.Uint64(y[1*8 : 2*8]) + y2 := binary.LittleEndian.Uint64(y[2*8 : 3*8]) + y3 := binary.LittleEndian.Uint64(y[3*8 : 4*8]) + y4 := binary.LittleEndian.Uint64(y[4*8 : 5*8]) + y5 := binary.LittleEndian.Uint64(y[5*8 : 6*8]) + y6 := binary.LittleEndian.Uint64(y[6*8 : 7*8]) + + z0, c0 := bits.Sub64(x0, y0, 0) + z1, c1 := bits.Sub64(x1, y1, c0) + z2, c2 := bits.Sub64(x2, y2, c1) + z3, c3 := bits.Sub64(x3, y3, c2) + z4, c4 := bits.Sub64(x4, y4, c3) + z5, c5 := bits.Sub64(x5, y5, c4) + z6, z7 := bits.Sub64(x6, y6, c5) + + z0, c0 = bits.Sub64(z0, z7, 0) + z1, c1 = bits.Sub64(z1, 0, c0) + z2, c2 = bits.Sub64(z2, 0, c1) + z3, c3 = bits.Sub64(z3, z7<<32, c2) + z4, c4 = bits.Sub64(z4, 0, c3) + z5, c5 = bits.Sub64(z5, 0, c4) + z6, z7 = bits.Sub64(z6, 0, c5) + + z0, c0 = bits.Sub64(z0, z7, 0) + z1, c1 = bits.Sub64(z1, 0, c0) + z2, c2 = bits.Sub64(z2, 0, c1) + z3, c3 = bits.Sub64(z3, z7<<32, c2) + z4, c4 = bits.Sub64(z4, 0, c3) + z5, c5 = bits.Sub64(z5, 0, c4) + z6, _ = bits.Sub64(z6, 0, c5) + + binary.LittleEndian.PutUint64(z[0*8:1*8], z0) + binary.LittleEndian.PutUint64(z[1*8:2*8], z1) + binary.LittleEndian.PutUint64(z[2*8:3*8], z2) + binary.LittleEndian.PutUint64(z[3*8:4*8], z3) + binary.LittleEndian.PutUint64(z[4*8:5*8], z4) + binary.LittleEndian.PutUint64(z[5*8:6*8], z5) + binary.LittleEndian.PutUint64(z[6*8:7*8], z6) +} + +func addsubGeneric(x, y *Elt) { + z := &Elt{} + addGeneric(z, x, y) + subGeneric(y, x, y) + *x = *z +} + +func mulGeneric(z, x, y *Elt) { + x0 := binary.LittleEndian.Uint64(x[0*8 : 1*8]) + x1 := binary.LittleEndian.Uint64(x[1*8 : 2*8]) + x2 := binary.LittleEndian.Uint64(x[2*8 : 3*8]) + x3 := binary.LittleEndian.Uint64(x[3*8 : 4*8]) + x4 := binary.LittleEndian.Uint64(x[4*8 : 5*8]) + x5 := binary.LittleEndian.Uint64(x[5*8 : 6*8]) + x6 := binary.LittleEndian.Uint64(x[6*8 : 7*8]) + + y0 := binary.LittleEndian.Uint64(y[0*8 : 1*8]) + y1 := binary.LittleEndian.Uint64(y[1*8 : 2*8]) + y2 := binary.LittleEndian.Uint64(y[2*8 : 3*8]) + y3 := binary.LittleEndian.Uint64(y[3*8 : 4*8]) + y4 := binary.LittleEndian.Uint64(y[4*8 : 5*8]) + y5 := binary.LittleEndian.Uint64(y[5*8 : 6*8]) + y6 := binary.LittleEndian.Uint64(y[6*8 : 7*8]) + + yy := [7]uint64{y0, y1, y2, y3, y4, y5, y6} + zz := [7]uint64{} + + yi := yy[0] + h0, l0 := bits.Mul64(x0, yi) + h1, l1 := bits.Mul64(x1, yi) + h2, l2 := bits.Mul64(x2, yi) + h3, l3 := bits.Mul64(x3, yi) + h4, l4 := bits.Mul64(x4, yi) + h5, l5 := bits.Mul64(x5, yi) + h6, l6 := bits.Mul64(x6, yi) + + zz[0] = l0 + a0, c0 := bits.Add64(h0, l1, 0) + a1, c1 := bits.Add64(h1, l2, c0) + a2, c2 := bits.Add64(h2, l3, c1) + a3, c3 := bits.Add64(h3, l4, c2) + a4, c4 := bits.Add64(h4, l5, c3) + a5, c5 := bits.Add64(h5, l6, c4) + a6, _ := bits.Add64(h6, 0, c5) + + for i := 1; i < 7; i++ { + yi = yy[i] + h0, l0 = bits.Mul64(x0, yi) + h1, l1 = bits.Mul64(x1, yi) + h2, l2 = bits.Mul64(x2, yi) + h3, l3 = bits.Mul64(x3, yi) + h4, l4 = bits.Mul64(x4, yi) + h5, l5 = bits.Mul64(x5, yi) + h6, l6 = bits.Mul64(x6, yi) + + zz[i], c0 = bits.Add64(a0, l0, 0) + a0, c1 = bits.Add64(a1, l1, c0) + a1, c2 = bits.Add64(a2, l2, c1) + a2, c3 = bits.Add64(a3, l3, c2) + a3, c4 = bits.Add64(a4, l4, c3) + a4, c5 = bits.Add64(a5, l5, c4) + a5, a6 = bits.Add64(a6, l6, c5) + + a0, c0 = bits.Add64(a0, h0, 0) + a1, c1 = bits.Add64(a1, h1, c0) + a2, c2 = bits.Add64(a2, h2, c1) + a3, c3 = bits.Add64(a3, h3, c2) + a4, c4 = bits.Add64(a4, h4, c3) + a5, c5 = bits.Add64(a5, h5, c4) + a6, _ = bits.Add64(a6, h6, c5) + } + red64(z, &zz, &[7]uint64{a0, a1, a2, a3, a4, a5, a6}) +} + +func sqrGeneric(z, x *Elt) { mulGeneric(z, x, x) } + +func red64(z *Elt, l, h *[7]uint64) { + /* (2C13, 2C12, 2C11, 2C10|C10, C9, C8, C7) + (C6,...,C0) */ + h0 := h[0] + h1 := h[1] + h2 := h[2] + h3 := ((h[3] & (0xFFFFFFFF << 32)) << 1) | (h[3] & 0xFFFFFFFF) + h4 := (h[3] >> 63) | (h[4] << 1) + h5 := (h[4] >> 63) | (h[5] << 1) + h6 := (h[5] >> 63) | (h[6] << 1) + h7 := (h[6] >> 63) + + l0, c0 := bits.Add64(h0, l[0], 0) + l1, c1 := bits.Add64(h1, l[1], c0) + l2, c2 := bits.Add64(h2, l[2], c1) + l3, c3 := bits.Add64(h3, l[3], c2) + l4, c4 := bits.Add64(h4, l[4], c3) + l5, c5 := bits.Add64(h5, l[5], c4) + l6, c6 := bits.Add64(h6, l[6], c5) + l7, _ := bits.Add64(h7, 0, c6) + + /* (C10C9, C9C8,C8C7,C7C13,C13C12,C12C11,C11C10) + (C6,...,C0) */ + h0 = (h[3] >> 32) | (h[4] << 32) + h1 = (h[4] >> 32) | (h[5] << 32) + h2 = (h[5] >> 32) | (h[6] << 32) + h3 = (h[6] >> 32) | (h[0] << 32) + h4 = (h[0] >> 32) | (h[1] << 32) + h5 = (h[1] >> 32) | (h[2] << 32) + h6 = (h[2] >> 32) | (h[3] << 32) + + l0, c0 = bits.Add64(l0, h0, 0) + l1, c1 = bits.Add64(l1, h1, c0) + l2, c2 = bits.Add64(l2, h2, c1) + l3, c3 = bits.Add64(l3, h3, c2) + l4, c4 = bits.Add64(l4, h4, c3) + l5, c5 = bits.Add64(l5, h5, c4) + l6, c6 = bits.Add64(l6, h6, c5) + l7, _ = bits.Add64(l7, 0, c6) + + /* (C7) + (C6,...,C0) */ + l0, c0 = bits.Add64(l0, l7, 0) + l1, c1 = bits.Add64(l1, 0, c0) + l2, c2 = bits.Add64(l2, 0, c1) + l3, c3 = bits.Add64(l3, l7<<32, c2) + l4, c4 = bits.Add64(l4, 0, c3) + l5, c5 = bits.Add64(l5, 0, c4) + l6, l7 = bits.Add64(l6, 0, c5) + + /* (C7) + (C6,...,C0) */ + l0, c0 = bits.Add64(l0, l7, 0) + l1, c1 = bits.Add64(l1, 0, c0) + l2, c2 = bits.Add64(l2, 0, c1) + l3, c3 = bits.Add64(l3, l7<<32, c2) + l4, c4 = bits.Add64(l4, 0, c3) + l5, c5 = bits.Add64(l5, 0, c4) + l6, _ = bits.Add64(l6, 0, c5) + + binary.LittleEndian.PutUint64(z[0*8:1*8], l0) + binary.LittleEndian.PutUint64(z[1*8:2*8], l1) + binary.LittleEndian.PutUint64(z[2*8:3*8], l2) + binary.LittleEndian.PutUint64(z[3*8:4*8], l3) + binary.LittleEndian.PutUint64(z[4*8:5*8], l4) + binary.LittleEndian.PutUint64(z[5*8:6*8], l5) + binary.LittleEndian.PutUint64(z[6*8:7*8], l6) +} diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_noasm.go b/vendor/github.com/cloudflare/circl/math/fp448/fp_noasm.go new file mode 100644 index 00000000..a62225d2 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_noasm.go @@ -0,0 +1,12 @@ +//go:build !amd64 || purego +// +build !amd64 purego + +package fp448 + +func cmov(x, y *Elt, n uint) { cmovGeneric(x, y, n) } +func cswap(x, y *Elt, n uint) { cswapGeneric(x, y, n) } +func add(z, x, y *Elt) { addGeneric(z, x, y) } +func sub(z, x, y *Elt) { subGeneric(z, x, y) } +func addsub(x, y *Elt) { addsubGeneric(x, y) } +func mul(z, x, y *Elt) { mulGeneric(z, x, y) } +func sqr(z, x *Elt) { sqrGeneric(z, x) } diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fuzzer.go b/vendor/github.com/cloudflare/circl/math/fp448/fuzzer.go new file mode 100644 index 00000000..2d7afc80 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/fp448/fuzzer.go @@ -0,0 +1,75 @@ +//go:build gofuzz +// +build gofuzz + +// How to run the fuzzer: +// +// $ go get -u github.com/dvyukov/go-fuzz/go-fuzz +// $ go get -u github.com/dvyukov/go-fuzz/go-fuzz-build +// $ go-fuzz-build -libfuzzer -func FuzzReduction -o lib.a +// $ clang -fsanitize=fuzzer lib.a -o fu.exe +// $ ./fu.exe +package fp448 + +import ( + "encoding/binary" + "fmt" + "math/big" + + "github.com/cloudflare/circl/internal/conv" +) + +// FuzzReduction is a fuzzer target for red64 function, which reduces t +// (112 bits) to a number t' (56 bits) congruent modulo p448. +func FuzzReduction(data []byte) int { + if len(data) != 2*Size { + return -1 + } + var got, want Elt + var lo, hi [7]uint64 + a := data[:Size] + b := data[Size:] + lo[0] = binary.LittleEndian.Uint64(a[0*8 : 1*8]) + lo[1] = binary.LittleEndian.Uint64(a[1*8 : 2*8]) + lo[2] = binary.LittleEndian.Uint64(a[2*8 : 3*8]) + lo[3] = binary.LittleEndian.Uint64(a[3*8 : 4*8]) + lo[4] = binary.LittleEndian.Uint64(a[4*8 : 5*8]) + lo[5] = binary.LittleEndian.Uint64(a[5*8 : 6*8]) + lo[6] = binary.LittleEndian.Uint64(a[6*8 : 7*8]) + + hi[0] = binary.LittleEndian.Uint64(b[0*8 : 1*8]) + hi[1] = binary.LittleEndian.Uint64(b[1*8 : 2*8]) + hi[2] = binary.LittleEndian.Uint64(b[2*8 : 3*8]) + hi[3] = binary.LittleEndian.Uint64(b[3*8 : 4*8]) + hi[4] = binary.LittleEndian.Uint64(b[4*8 : 5*8]) + hi[5] = binary.LittleEndian.Uint64(b[5*8 : 6*8]) + hi[6] = binary.LittleEndian.Uint64(b[6*8 : 7*8]) + + red64(&got, &lo, &hi) + + t := conv.BytesLe2BigInt(data[:2*Size]) + + two448 := big.NewInt(1) + two448.Lsh(two448, 448) // 2^448 + mask448 := big.NewInt(1) + mask448.Sub(two448, mask448) // 2^448-1 + two224plus1 := big.NewInt(1) + two224plus1.Lsh(two224plus1, 224) + two224plus1.Add(two224plus1, big.NewInt(1)) // 2^224+1 + + var loBig, hiBig big.Int + for t.Cmp(two448) >= 0 { + loBig.And(t, mask448) + hiBig.Rsh(t, 448) + t.Mul(&hiBig, two224plus1) + t.Add(t, &loBig) + } + conv.BigInt2BytesLe(want[:], t) + + if got != want { + fmt.Printf("in: %v\n", conv.BytesLe2BigInt(data[:2*Size])) + fmt.Printf("got: %v\n", got) + fmt.Printf("want: %v\n", want) + panic("error found") + } + return 1 +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.go new file mode 100644 index 00000000..79629160 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.go @@ -0,0 +1,302 @@ +//go:build amd64 +// +build amd64 + +package common + +import ( + "golang.org/x/sys/cpu" +) + +// ZetasAVX2 contains all ζ used in NTT (like the Zetas array), but also +// the values int16(zeta * 62209) for each zeta, which is used in +// Montgomery reduction. There is some duplication and reordering as +// compared to Zetas to make it more covenient for use with AVX2. +var ZetasAVX2 = [...]int16{ + // level 1: int16(Zetas[1]*62209) and Zetas[1] + 31499, 2571, + + // level 2 + // + // int16(Zetas[2]*62209), Zetas[2], int16(Zetas[3]*62209), Zetas[3] + 14746, 2970, 788, 1812, + + // level 3, like level 2. + 13525, 1493, -12402, 1422, 28191, 287, -16694, 202, + + 0, 0, // padding + + // layer 4. offset: 1*16 + // + // The precomputed multiplication and zetas are grouped by 16 at a + // time as used in the set of butterflies, etc. + -20906, -20906, -20906, -20906, -20906, -20906, -20906, -20906, + 27758, 27758, 27758, 27758, 27758, 27758, 27758, 27758, + 3158, 3158, 3158, 3158, 3158, 3158, 3158, 3158, + 622, 622, 622, 622, 622, 622, 622, 622, + -3799, -3799, -3799, -3799, -3799, -3799, -3799, -3799, + -15690, -15690, -15690, -15690, -15690, -15690, -15690, -15690, + 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, + 182, 182, 182, 182, 182, 182, 182, 182, + 10690, 10690, 10690, 10690, 10690, 10690, 10690, 10690, + 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, + 962, 962, 962, 962, 962, 962, 962, 962, + 2127, 2127, 2127, 2127, 2127, 2127, 2127, 2127, + -11201, -11201, -11201, -11201, -11201, -11201, -11201, -11201, + 31164, 31164, 31164, 31164, 31164, 31164, 31164, 31164, + 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, + 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, + + // layer 5. offset: 9*16 + -5827, -5827, -5827, -5827, 17364, 17364, 17364, 17364, + -26360, -26360, -26360, -26360, -29057, -29057, -29057, -29057, + 573, 573, 573, 573, 2004, 2004, 2004, 2004, + 264, 264, 264, 264, 383, 383, 383, 383, + 5572, 5572, 5572, 5572, -1102, -1102, -1102, -1102, + 21439, 21439, 21439, 21439, -26241, -26241, -26241, -26241, + 2500, 2500, 2500, 2500, 1458, 1458, 1458, 1458, + 1727, 1727, 1727, 1727, 3199, 3199, 3199, 3199, + -28072, -28072, -28072, -28072, 24313, 24313, 24313, 24313, + -10532, -10532, -10532, -10532, 8800, 8800, 8800, 8800, + 2648, 2648, 2648, 2648, 1017, 1017, 1017, 1017, + 732, 732, 732, 732, 608, 608, 608, 608, + 18427, 18427, 18427, 18427, 8859, 8859, 8859, 8859, + 26676, 26676, 26676, 26676, -16162, -16162, -16162, -16162, + 1787, 1787, 1787, 1787, 411, 411, 411, 411, + 3124, 3124, 3124, 3124, 1758, 1758, 1758, 1758, + + // layer 6. offset: 17*16 + -5689, -5689, -6516, -6516, 1497, 1497, 30967, 30967, + -23564, -23564, 20179, 20179, 20711, 20711, 25081, 25081, + 1223, 1223, 652, 652, 2777, 2777, 1015, 1015, + 2036, 2036, 1491, 1491, 3047, 3047, 1785, 1785, + -12796, -12796, 26617, 26617, 16065, 16065, -12441, -12441, + 9135, 9135, -649, -649, -25986, -25986, 27837, 27837, + 516, 516, 3321, 3321, 3009, 3009, 2663, 2663, + 1711, 1711, 2167, 2167, 126, 126, 1469, 1469, + 19884, 19884, -28249, -28249, -15886, -15886, -8898, -8898, + -28309, -28309, 9076, 9076, -30198, -30198, 18250, 18250, + 2476, 2476, 3239, 3239, 3058, 3058, 830, 830, + 107, 107, 1908, 1908, 3082, 3082, 2378, 2378, + 13427, 13427, 14017, 14017, -29155, -29155, -12756, -12756, + 16832, 16832, 4312, 4312, -24155, -24155, -17914, -17914, + 2931, 2931, 961, 961, 1821, 1821, 2604, 2604, + 448, 448, 2264, 2264, 677, 677, 2054, 2054, + + // layer 7. offset: 25*16 + -334, 11182, -11477, 13387, -32226, -14233, 20494, -21655, + -27738, 13131, 945, -4586, -14882, 23093, 6182, 5493, + 2226, 430, 555, 843, 2078, 871, 1550, 105, + 422, 587, 177, 3094, 3038, 2869, 1574, 1653, + 32011, -32502, 10631, 30318, 29176, -18741, -28761, 12639, + -18485, 20100, 17561, 18525, -14430, 19529, -5275, -12618, + 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, + 1739, 644, 2457, 349, 418, 329, 3173, 3254, + -31183, 20297, 25435, 2146, -7382, 15356, 24392, -32384, + -20926, -6279, 10946, -14902, 24215, -11044, 16990, 14470, + 817, 1097, 603, 610, 1322, 2044, 1864, 384, + 2114, 3193, 1218, 1994, 2455, 220, 2142, 1670, + 10336, -21497, -7933, -20198, -22501, 23211, 10907, -17442, + 31637, -23859, 28644, -20257, 23998, 7757, -17422, 23132, + 2144, 1799, 2051, 794, 1819, 2475, 2459, 478, + 3221, 3021, 996, 991, 958, 1869, 1522, 1628, + + // layer 1 inverse + 23132, -17422, 7757, 23998, -20257, 28644, -23859, 31637, + -17442, 10907, 23211, -22501, -20198, -7933, -21497, 10336, + 1628, 1522, 1869, 958, 991, 996, 3021, 3221, + 478, 2459, 2475, 1819, 794, 2051, 1799, 2144, + 14470, 16990, -11044, 24215, -14902, 10946, -6279, -20926, + -32384, 24392, 15356, -7382, 2146, 25435, 20297, -31183, + 1670, 2142, 220, 2455, 1994, 1218, 3193, 2114, + 384, 1864, 2044, 1322, 610, 603, 1097, 817, + -12618, -5275, 19529, -14430, 18525, 17561, 20100, -18485, + 12639, -28761, -18741, 29176, 30318, 10631, -32502, 32011, + 3254, 3173, 329, 418, 349, 2457, 644, 1739, + 1119, 2727, 1483, 2552, 3182, 1159, 778, 3083, + 5493, 6182, 23093, -14882, -4586, 945, 13131, -27738, + -21655, 20494, -14233, -32226, 13387, -11477, 11182, -334, + 1653, 1574, 2869, 3038, 3094, 177, 587, 422, + 105, 1550, 871, 2078, 843, 555, 430, 2226, + + // layer 2 inverse + -17914, -17914, -24155, -24155, 4312, 4312, 16832, 16832, + -12756, -12756, -29155, -29155, 14017, 14017, 13427, 13427, + 2054, 2054, 677, 677, 2264, 2264, 448, 448, + 2604, 2604, 1821, 1821, 961, 961, 2931, 2931, + 18250, 18250, -30198, -30198, 9076, 9076, -28309, -28309, + -8898, -8898, -15886, -15886, -28249, -28249, 19884, 19884, + 2378, 2378, 3082, 3082, 1908, 1908, 107, 107, + 830, 830, 3058, 3058, 3239, 3239, 2476, 2476, + 27837, 27837, -25986, -25986, -649, -649, 9135, 9135, + -12441, -12441, 16065, 16065, 26617, 26617, -12796, -12796, + 1469, 1469, 126, 126, 2167, 2167, 1711, 1711, + 2663, 2663, 3009, 3009, 3321, 3321, 516, 516, + 25081, 25081, 20711, 20711, 20179, 20179, -23564, -23564, + 30967, 30967, 1497, 1497, -6516, -6516, -5689, -5689, + 1785, 1785, 3047, 3047, 1491, 1491, 2036, 2036, + 1015, 1015, 2777, 2777, 652, 652, 1223, 1223, + + // layer 3 inverse + -16162, -16162, -16162, -16162, 26676, 26676, 26676, 26676, + 8859, 8859, 8859, 8859, 18427, 18427, 18427, 18427, + 1758, 1758, 1758, 1758, 3124, 3124, 3124, 3124, + 411, 411, 411, 411, 1787, 1787, 1787, 1787, + 8800, 8800, 8800, 8800, -10532, -10532, -10532, -10532, + 24313, 24313, 24313, 24313, -28072, -28072, -28072, -28072, + 608, 608, 608, 608, 732, 732, 732, 732, + 1017, 1017, 1017, 1017, 2648, 2648, 2648, 2648, + -26241, -26241, -26241, -26241, 21439, 21439, 21439, 21439, + -1102, -1102, -1102, -1102, 5572, 5572, 5572, 5572, + 3199, 3199, 3199, 3199, 1727, 1727, 1727, 1727, + 1458, 1458, 1458, 1458, 2500, 2500, 2500, 2500, + -29057, -29057, -29057, -29057, -26360, -26360, -26360, -26360, + 17364, 17364, 17364, 17364, -5827, -5827, -5827, -5827, + 383, 383, 383, 383, 264, 264, 264, 264, + 2004, 2004, 2004, 2004, 573, 573, 573, 573, + + // layer 4 inverse + 31164, 31164, 31164, 31164, 31164, 31164, 31164, 31164, + -11201, -11201, -11201, -11201, -11201, -11201, -11201, -11201, + 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, + 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, + 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, + 10690, 10690, 10690, 10690, 10690, 10690, 10690, 10690, + 2127, 2127, 2127, 2127, 2127, 2127, 2127, 2127, + 962, 962, 962, 962, 962, 962, 962, 962, + -15690, -15690, -15690, -15690, -15690, -15690, -15690, -15690, + -3799, -3799, -3799, -3799, -3799, -3799, -3799, -3799, + 182, 182, 182, 182, 182, 182, 182, 182, + 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, + 27758, 27758, 27758, 27758, 27758, 27758, 27758, 27758, + -20906, -20906, -20906, -20906, -20906, -20906, -20906, -20906, + 622, 622, 622, 622, 622, 622, 622, 622, + 3158, 3158, 3158, 3158, 3158, 3158, 3158, 3158, + + // layer 5 inverse + -16694, 202, 28191, 287, -12402, 1422, 13525, 1493, + + // layer 6 inverse + 788, 1812, 14746, 2970, + + // layer 7 inverse + 31499, 2571, +} + +// Sets p to a + b. Does not normalize coefficients. +func (p *Poly) Add(a, b *Poly) { + if cpu.X86.HasAVX2 { + addAVX2( + (*[N]int16)(p), + (*[N]int16)(a), + (*[N]int16)(b), + ) + } else { + p.addGeneric(a, b) + } +} + +// Sets p to a - b. Does not normalize coefficients. +func (p *Poly) Sub(a, b *Poly) { + if cpu.X86.HasAVX2 { + subAVX2( + (*[N]int16)(p), + (*[N]int16)(a), + (*[N]int16)(b), + ) + } else { + p.subGeneric(a, b) + } +} + +// Executes an in-place forward "NTT" on p. +// +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤7q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity of the NTT) +// if the input is in regular form, then the result is also in regular form. +// The order of coefficients will be "tangled". These can be put back into +// their proper order by calling Detangle(). +func (p *Poly) NTT() { + if cpu.X86.HasAVX2 { + nttAVX2((*[N]int16)(p)) + } else { + p.nttGeneric() + } +} + +// Executes an in-place inverse "NTT" on p and multiply by the Montgomery +// factor R. +// +// Requires coefficients to be in "tangled" order, see Tangle(). +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity) +// if the input is in regular form, then the result is also in regular form. +func (p *Poly) InvNTT() { + if cpu.X86.HasAVX2 { + invNttAVX2((*[N]int16)(p)) + } else { + p.invNTTGeneric() + } +} + +// Sets p to the "pointwise" multiplication of a and b. +// +// That is: InvNTT(p) = InvNTT(a) * InvNTT(b). Assumes a and b are in +// Montgomery form. Products between coefficients of a and b must be strictly +// bounded in absolute value by 2¹⁵q. p will be in Montgomery form and +// bounded in absolute value by 2q. +// +// Requires a and b to be in "tangled" order, see Tangle(). p will be in +// tangled order as well. +func (p *Poly) MulHat(a, b *Poly) { + if cpu.X86.HasAVX2 { + mulHatAVX2( + (*[N]int16)(p), + (*[N]int16)(a), + (*[N]int16)(b), + ) + } else { + p.mulHatGeneric(a, b) + } +} + +// Puts p into the right form to be used with (among others) InvNTT(). +func (p *Poly) Tangle() { + if cpu.X86.HasAVX2 { + tangleAVX2((*[N]int16)(p)) + } + + // When AVX2 is not available, we use the standard order. +} + +// Puts p back into standard form. +func (p *Poly) Detangle() { + if cpu.X86.HasAVX2 { + detangleAVX2((*[N]int16)(p)) + } + + // When AVX2 is not available, we use the standard order. +} + +// Almost normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q}. +func (p *Poly) BarrettReduce() { + if cpu.X86.HasAVX2 { + barrettReduceAVX2((*[N]int16)(p)) + } else { + p.barrettReduceGeneric() + } +} + +// Normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q-1}. +func (p *Poly) Normalize() { + if cpu.X86.HasAVX2 { + normalizeAVX2((*[N]int16)(p)) + } else { + p.normalizeGeneric() + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.s b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.s new file mode 100644 index 00000000..d8205465 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/amd64.s @@ -0,0 +1,2354 @@ +// Code generated by command: go run src.go -out ../amd64.s -stubs ../stubs_amd64.go -pkg common. DO NOT EDIT. + +// +build amd64 + +#include "textflag.h" + +// func addAVX2(p *[256]int16, a *[256]int16, b *[256]int16) +// Requires: AVX, AVX2 +TEXT ·addAVX2(SB), NOSPLIT, $0-24 + MOVQ p+0(FP), AX + MOVQ a+8(FP), CX + MOVQ b+16(FP), DX + VMOVDQU (CX), Y0 + VMOVDQU 32(CX), Y2 + VMOVDQU 64(CX), Y4 + VMOVDQU 96(CX), Y6 + VMOVDQU 128(CX), Y8 + VMOVDQU 160(CX), Y10 + VMOVDQU 192(CX), Y12 + VMOVDQU 224(CX), Y14 + VMOVDQU (DX), Y1 + VMOVDQU 32(DX), Y3 + VMOVDQU 64(DX), Y5 + VMOVDQU 96(DX), Y7 + VMOVDQU 128(DX), Y9 + VMOVDQU 160(DX), Y11 + VMOVDQU 192(DX), Y13 + VMOVDQU 224(DX), Y15 + VPADDW Y0, Y1, Y1 + VPADDW Y2, Y3, Y3 + VPADDW Y4, Y5, Y5 + VPADDW Y6, Y7, Y7 + VPADDW Y8, Y9, Y9 + VPADDW Y10, Y11, Y11 + VPADDW Y12, Y13, Y13 + VPADDW Y14, Y15, Y15 + VMOVDQU Y1, (AX) + VMOVDQU Y3, 32(AX) + VMOVDQU Y5, 64(AX) + VMOVDQU Y7, 96(AX) + VMOVDQU Y9, 128(AX) + VMOVDQU Y11, 160(AX) + VMOVDQU Y13, 192(AX) + VMOVDQU Y15, 224(AX) + VMOVDQU 256(CX), Y0 + VMOVDQU 288(CX), Y2 + VMOVDQU 320(CX), Y4 + VMOVDQU 352(CX), Y6 + VMOVDQU 384(CX), Y8 + VMOVDQU 416(CX), Y10 + VMOVDQU 448(CX), Y12 + VMOVDQU 480(CX), Y14 + VMOVDQU 256(DX), Y1 + VMOVDQU 288(DX), Y3 + VMOVDQU 320(DX), Y5 + VMOVDQU 352(DX), Y7 + VMOVDQU 384(DX), Y9 + VMOVDQU 416(DX), Y11 + VMOVDQU 448(DX), Y13 + VMOVDQU 480(DX), Y15 + VPADDW Y0, Y1, Y1 + VPADDW Y2, Y3, Y3 + VPADDW Y4, Y5, Y5 + VPADDW Y6, Y7, Y7 + VPADDW Y8, Y9, Y9 + VPADDW Y10, Y11, Y11 + VPADDW Y12, Y13, Y13 + VPADDW Y14, Y15, Y15 + VMOVDQU Y1, 256(AX) + VMOVDQU Y3, 288(AX) + VMOVDQU Y5, 320(AX) + VMOVDQU Y7, 352(AX) + VMOVDQU Y9, 384(AX) + VMOVDQU Y11, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y15, 480(AX) + RET + +// func subAVX2(p *[256]int16, a *[256]int16, b *[256]int16) +// Requires: AVX, AVX2 +TEXT ·subAVX2(SB), NOSPLIT, $0-24 + MOVQ p+0(FP), AX + MOVQ a+8(FP), CX + MOVQ b+16(FP), DX + VMOVDQU (CX), Y0 + VMOVDQU 32(CX), Y2 + VMOVDQU 64(CX), Y4 + VMOVDQU 96(CX), Y6 + VMOVDQU 128(CX), Y8 + VMOVDQU 160(CX), Y10 + VMOVDQU 192(CX), Y12 + VMOVDQU 224(CX), Y14 + VMOVDQU (DX), Y1 + VMOVDQU 32(DX), Y3 + VMOVDQU 64(DX), Y5 + VMOVDQU 96(DX), Y7 + VMOVDQU 128(DX), Y9 + VMOVDQU 160(DX), Y11 + VMOVDQU 192(DX), Y13 + VMOVDQU 224(DX), Y15 + VPSUBW Y1, Y0, Y1 + VPSUBW Y3, Y2, Y3 + VPSUBW Y5, Y4, Y5 + VPSUBW Y7, Y6, Y7 + VPSUBW Y9, Y8, Y9 + VPSUBW Y11, Y10, Y11 + VPSUBW Y13, Y12, Y13 + VPSUBW Y15, Y14, Y15 + VMOVDQU Y1, (AX) + VMOVDQU Y3, 32(AX) + VMOVDQU Y5, 64(AX) + VMOVDQU Y7, 96(AX) + VMOVDQU Y9, 128(AX) + VMOVDQU Y11, 160(AX) + VMOVDQU Y13, 192(AX) + VMOVDQU Y15, 224(AX) + VMOVDQU 256(CX), Y0 + VMOVDQU 288(CX), Y2 + VMOVDQU 320(CX), Y4 + VMOVDQU 352(CX), Y6 + VMOVDQU 384(CX), Y8 + VMOVDQU 416(CX), Y10 + VMOVDQU 448(CX), Y12 + VMOVDQU 480(CX), Y14 + VMOVDQU 256(DX), Y1 + VMOVDQU 288(DX), Y3 + VMOVDQU 320(DX), Y5 + VMOVDQU 352(DX), Y7 + VMOVDQU 384(DX), Y9 + VMOVDQU 416(DX), Y11 + VMOVDQU 448(DX), Y13 + VMOVDQU 480(DX), Y15 + VPSUBW Y1, Y0, Y1 + VPSUBW Y3, Y2, Y3 + VPSUBW Y5, Y4, Y5 + VPSUBW Y7, Y6, Y7 + VPSUBW Y9, Y8, Y9 + VPSUBW Y11, Y10, Y11 + VPSUBW Y13, Y12, Y13 + VPSUBW Y15, Y14, Y15 + VMOVDQU Y1, 256(AX) + VMOVDQU Y3, 288(AX) + VMOVDQU Y5, 320(AX) + VMOVDQU Y7, 352(AX) + VMOVDQU Y9, 384(AX) + VMOVDQU Y11, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y15, 480(AX) + RET + +// func nttAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·nttAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + LEAQ ·ZetasAVX2+0(SB), CX + MOVL $0x00000d01, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y15 + VPBROADCASTW (CX), Y0 + VPBROADCASTW 2(CX), Y1 + VMOVDQU (AX), Y7 + VMOVDQU 32(AX), Y8 + VMOVDQU 64(AX), Y9 + VMOVDQU 96(AX), Y10 + VMOVDQU 256(AX), Y11 + VMOVDQU 288(AX), Y12 + VMOVDQU 320(AX), Y13 + VMOVDQU 352(AX), Y14 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y2 + VPSUBW Y3, Y12, Y3 + VPSUBW Y4, Y13, Y4 + VPSUBW Y5, Y14, Y5 + VPSUBW Y2, Y7, Y11 + VPSUBW Y3, Y8, Y12 + VPSUBW Y4, Y9, Y13 + VPSUBW Y5, Y10, Y14 + VPADDW Y2, Y7, Y7 + VPADDW Y3, Y8, Y8 + VPADDW Y4, Y9, Y9 + VPADDW Y5, Y10, Y10 + VMOVDQU Y7, (AX) + VMOVDQU Y8, 32(AX) + VMOVDQU Y9, 64(AX) + VMOVDQU Y10, 96(AX) + VMOVDQU Y11, 256(AX) + VMOVDQU Y12, 288(AX) + VMOVDQU Y13, 320(AX) + VMOVDQU Y14, 352(AX) + VMOVDQU 128(AX), Y7 + VMOVDQU 160(AX), Y8 + VMOVDQU 192(AX), Y9 + VMOVDQU 224(AX), Y10 + VMOVDQU 384(AX), Y11 + VMOVDQU 416(AX), Y12 + VMOVDQU 448(AX), Y13 + VMOVDQU 480(AX), Y14 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y2 + VPSUBW Y3, Y12, Y3 + VPSUBW Y4, Y13, Y4 + VPSUBW Y5, Y14, Y5 + VPSUBW Y2, Y7, Y11 + VPSUBW Y3, Y8, Y12 + VPSUBW Y4, Y9, Y13 + VPSUBW Y5, Y10, Y14 + VPADDW Y2, Y7, Y7 + VPADDW Y3, Y8, Y8 + VPADDW Y4, Y9, Y9 + VPADDW Y5, Y10, Y10 + VMOVDQU Y7, 128(AX) + VMOVDQU Y8, 160(AX) + VMOVDQU Y9, 192(AX) + VMOVDQU Y10, 224(AX) + VMOVDQU Y11, 384(AX) + VMOVDQU Y12, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y14, 480(AX) + VPBROADCASTW 4(CX), Y0 + VPBROADCASTW 6(CX), Y1 + VMOVDQU (AX), Y7 + VMOVDQU 32(AX), Y8 + VMOVDQU 64(AX), Y9 + VMOVDQU 96(AX), Y10 + VMOVDQU 128(AX), Y11 + VMOVDQU 160(AX), Y12 + VMOVDQU 192(AX), Y13 + VMOVDQU 224(AX), Y14 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y2 + VPSUBW Y3, Y12, Y3 + VPSUBW Y4, Y13, Y4 + VPSUBW Y5, Y14, Y5 + VPSUBW Y2, Y7, Y11 + VPSUBW Y3, Y8, Y12 + VPSUBW Y4, Y9, Y13 + VPSUBW Y5, Y10, Y14 + VPADDW Y2, Y7, Y7 + VPADDW Y3, Y8, Y8 + VPADDW Y4, Y9, Y9 + VPADDW Y5, Y10, Y10 + VPBROADCASTW 12(CX), Y0 + VPBROADCASTW 14(CX), Y1 + VPBROADCASTW 16(CX), Y2 + VPBROADCASTW 18(CX), Y3 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU 32(CX), Y0 + VMOVDQU 64(CX), Y1 + VMOVDQU 96(CX), Y2 + VMOVDQU 128(CX), Y3 + VPERM2I128 $0x20, Y9, Y7, Y4 + VPERM2I128 $0x31, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VPERM2I128 $0x20, Y10, Y8, Y4 + VPERM2I128 $0x31, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VPERM2I128 $0x20, Y13, Y11, Y4 + VPERM2I128 $0x31, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VPERM2I128 $0x20, Y14, Y12, Y4 + VPERM2I128 $0x31, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPMULLW Y8, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y12, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y8, Y1, Y8 + VPMULHW Y10, Y1, Y10 + VPMULHW Y12, Y3, Y12 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y8, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y12, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y8 + VPSUBW Y5, Y9, Y10 + VPSUBW Y6, Y11, Y12 + VPSUBW Y0, Y13, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y9, Y9 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y13, Y13 + VMOVDQU 288(CX), Y0 + VMOVDQU 320(CX), Y1 + VMOVDQU 352(CX), Y2 + VMOVDQU 384(CX), Y3 + VPUNPCKLQDQ Y8, Y7, Y4 + VPUNPCKHQDQ Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPUNPCKLQDQ Y10, Y9, Y4 + VPUNPCKHQDQ Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPUNPCKLQDQ Y12, Y11, Y4 + VPUNPCKHQDQ Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPUNPCKLQDQ Y14, Y13, Y4 + VPUNPCKHQDQ Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU 544(CX), Y0 + VMOVDQU 576(CX), Y1 + VMOVDQU 608(CX), Y2 + VMOVDQU 640(CX), Y3 + VMOVSLDUP Y9, Y4 + VPBLENDD $0xaa, Y4, Y7, Y4 + VPSRLQ $0x20, Y7, Y7 + VPBLENDD $0xaa, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VMOVSLDUP Y10, Y4 + VPBLENDD $0xaa, Y4, Y8, Y4 + VPSRLQ $0x20, Y8, Y8 + VPBLENDD $0xaa, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VMOVSLDUP Y13, Y4 + VPBLENDD $0xaa, Y4, Y11, Y4 + VPSRLQ $0x20, Y11, Y11 + VPBLENDD $0xaa, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VMOVSLDUP Y14, Y4 + VPBLENDD $0xaa, Y4, Y12, Y4 + VPSRLQ $0x20, Y12, Y12 + VPBLENDD $0xaa, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPMULLW Y8, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y12, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y8, Y1, Y8 + VPMULHW Y10, Y1, Y10 + VPMULHW Y12, Y3, Y12 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y8, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y12, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y8 + VPSUBW Y5, Y9, Y10 + VPSUBW Y6, Y11, Y12 + VPSUBW Y0, Y13, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y9, Y9 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y13, Y13 + VMOVDQU 800(CX), Y0 + VMOVDQU 832(CX), Y1 + VMOVDQU 864(CX), Y2 + VMOVDQU 896(CX), Y3 + VPSLLD $0x10, Y8, Y4 + VPBLENDW $0xaa, Y4, Y7, Y4 + VPSRLD $0x10, Y7, Y7 + VPBLENDW $0xaa, Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPSLLD $0x10, Y10, Y4 + VPBLENDW $0xaa, Y4, Y9, Y4 + VPSRLD $0x10, Y9, Y9 + VPBLENDW $0xaa, Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPSLLD $0x10, Y12, Y4 + VPBLENDW $0xaa, Y4, Y11, Y4 + VPSRLD $0x10, Y11, Y11 + VPBLENDW $0xaa, Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPSLLD $0x10, Y14, Y4 + VPBLENDW $0xaa, Y4, Y13, Y4 + VPSRLD $0x10, Y13, Y13 + VPBLENDW $0xaa, Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU Y7, (AX) + VMOVDQU Y8, 32(AX) + VMOVDQU Y9, 64(AX) + VMOVDQU Y10, 96(AX) + VMOVDQU Y11, 128(AX) + VMOVDQU Y12, 160(AX) + VMOVDQU Y13, 192(AX) + VMOVDQU Y14, 224(AX) + VPBROADCASTW 8(CX), Y0 + VPBROADCASTW 10(CX), Y1 + VMOVDQU 256(AX), Y7 + VMOVDQU 288(AX), Y8 + VMOVDQU 320(AX), Y9 + VMOVDQU 352(AX), Y10 + VMOVDQU 384(AX), Y11 + VMOVDQU 416(AX), Y12 + VMOVDQU 448(AX), Y13 + VMOVDQU 480(AX), Y14 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y2 + VPSUBW Y3, Y12, Y3 + VPSUBW Y4, Y13, Y4 + VPSUBW Y5, Y14, Y5 + VPSUBW Y2, Y7, Y11 + VPSUBW Y3, Y8, Y12 + VPSUBW Y4, Y9, Y13 + VPSUBW Y5, Y10, Y14 + VPADDW Y2, Y7, Y7 + VPADDW Y3, Y8, Y8 + VPADDW Y4, Y9, Y9 + VPADDW Y5, Y10, Y10 + VPBROADCASTW 20(CX), Y0 + VPBROADCASTW 22(CX), Y1 + VPBROADCASTW 24(CX), Y2 + VPBROADCASTW 26(CX), Y3 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU 160(CX), Y0 + VMOVDQU 192(CX), Y1 + VMOVDQU 224(CX), Y2 + VMOVDQU 256(CX), Y3 + VPERM2I128 $0x20, Y9, Y7, Y4 + VPERM2I128 $0x31, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VPERM2I128 $0x20, Y10, Y8, Y4 + VPERM2I128 $0x31, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VPERM2I128 $0x20, Y13, Y11, Y4 + VPERM2I128 $0x31, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VPERM2I128 $0x20, Y14, Y12, Y4 + VPERM2I128 $0x31, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPMULLW Y8, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y12, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y8, Y1, Y8 + VPMULHW Y10, Y1, Y10 + VPMULHW Y12, Y3, Y12 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y8, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y12, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y8 + VPSUBW Y5, Y9, Y10 + VPSUBW Y6, Y11, Y12 + VPSUBW Y0, Y13, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y9, Y9 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y13, Y13 + VMOVDQU 416(CX), Y0 + VMOVDQU 448(CX), Y1 + VMOVDQU 480(CX), Y2 + VMOVDQU 512(CX), Y3 + VPUNPCKLQDQ Y8, Y7, Y4 + VPUNPCKHQDQ Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPUNPCKLQDQ Y10, Y9, Y4 + VPUNPCKHQDQ Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPUNPCKLQDQ Y12, Y11, Y4 + VPUNPCKHQDQ Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPUNPCKLQDQ Y14, Y13, Y4 + VPUNPCKHQDQ Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU 672(CX), Y0 + VMOVDQU 704(CX), Y1 + VMOVDQU 736(CX), Y2 + VMOVDQU 768(CX), Y3 + VMOVSLDUP Y9, Y4 + VPBLENDD $0xaa, Y4, Y7, Y4 + VPSRLQ $0x20, Y7, Y7 + VPBLENDD $0xaa, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VMOVSLDUP Y10, Y4 + VPBLENDD $0xaa, Y4, Y8, Y4 + VPSRLQ $0x20, Y8, Y8 + VPBLENDD $0xaa, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VMOVSLDUP Y13, Y4 + VPBLENDD $0xaa, Y4, Y11, Y4 + VPSRLQ $0x20, Y11, Y11 + VPBLENDD $0xaa, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VMOVSLDUP Y14, Y4 + VPBLENDD $0xaa, Y4, Y12, Y4 + VPSRLQ $0x20, Y12, Y12 + VPBLENDD $0xaa, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPMULLW Y8, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y12, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y8, Y1, Y8 + VPMULHW Y10, Y1, Y10 + VPMULHW Y12, Y3, Y12 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y8, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y12, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y8 + VPSUBW Y5, Y9, Y10 + VPSUBW Y6, Y11, Y12 + VPSUBW Y0, Y13, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y9, Y9 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y13, Y13 + VMOVDQU 928(CX), Y0 + VMOVDQU 960(CX), Y1 + VMOVDQU 992(CX), Y2 + VMOVDQU 1024(CX), Y3 + VPSLLD $0x10, Y8, Y4 + VPBLENDW $0xaa, Y4, Y7, Y4 + VPSRLD $0x10, Y7, Y7 + VPBLENDW $0xaa, Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPSLLD $0x10, Y10, Y4 + VPBLENDW $0xaa, Y4, Y9, Y4 + VPSRLD $0x10, Y9, Y9 + VPBLENDW $0xaa, Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPSLLD $0x10, Y12, Y4 + VPBLENDW $0xaa, Y4, Y11, Y4 + VPSRLD $0x10, Y11, Y11 + VPBLENDW $0xaa, Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPSLLD $0x10, Y14, Y4 + VPBLENDW $0xaa, Y4, Y13, Y4 + VPSRLD $0x10, Y13, Y13 + VPBLENDW $0xaa, Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULLW Y13, Y2, Y6 + VPMULLW Y14, Y2, Y0 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y13, Y3, Y13 + VPMULHW Y14, Y3, Y14 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPMULHW Y6, Y15, Y6 + VPMULHW Y0, Y15, Y0 + VPSUBW Y4, Y9, Y4 + VPSUBW Y5, Y10, Y5 + VPSUBW Y6, Y13, Y6 + VPSUBW Y0, Y14, Y0 + VPSUBW Y4, Y7, Y9 + VPSUBW Y5, Y8, Y10 + VPSUBW Y6, Y11, Y13 + VPSUBW Y0, Y12, Y14 + VPADDW Y4, Y7, Y7 + VPADDW Y5, Y8, Y8 + VPADDW Y6, Y11, Y11 + VPADDW Y0, Y12, Y12 + VMOVDQU Y7, 256(AX) + VMOVDQU Y8, 288(AX) + VMOVDQU Y9, 320(AX) + VMOVDQU Y10, 352(AX) + VMOVDQU Y11, 384(AX) + VMOVDQU Y12, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y14, 480(AX) + RET + +// func invNttAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·invNttAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + LEAQ ·ZetasAVX2+0(SB), CX + MOVL $0x00000d01, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y15 + VMOVDQU (AX), Y7 + VMOVDQU 32(AX), Y8 + VMOVDQU 64(AX), Y9 + VMOVDQU 96(AX), Y10 + VMOVDQU 128(AX), Y11 + VMOVDQU 160(AX), Y12 + VMOVDQU 192(AX), Y13 + VMOVDQU 224(AX), Y14 + VMOVDQU 1056(CX), Y0 + VMOVDQU 1088(CX), Y1 + VMOVDQU 1120(CX), Y2 + VMOVDQU 1152(CX), Y3 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + VMOVDQU 1312(CX), Y0 + VMOVDQU 1344(CX), Y1 + VMOVDQU 1376(CX), Y2 + VMOVDQU 1408(CX), Y3 + VPSLLD $0x10, Y8, Y4 + VPBLENDW $0xaa, Y4, Y7, Y4 + VPSRLD $0x10, Y7, Y7 + VPBLENDW $0xaa, Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPSLLD $0x10, Y10, Y4 + VPBLENDW $0xaa, Y4, Y9, Y4 + VPSRLD $0x10, Y9, Y9 + VPBLENDW $0xaa, Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPSLLD $0x10, Y12, Y4 + VPBLENDW $0xaa, Y4, Y11, Y4 + VPSRLD $0x10, Y11, Y11 + VPBLENDW $0xaa, Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPSLLD $0x10, Y14, Y4 + VPBLENDW $0xaa, Y4, Y13, Y4 + VPSRLD $0x10, Y13, Y13 + VPBLENDW $0xaa, Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPSUBW Y7, Y8, Y4 + VPSUBW Y9, Y10, Y5 + VPSUBW Y11, Y12, Y6 + VPADDW Y7, Y8, Y7 + VPADDW Y9, Y10, Y9 + VPADDW Y11, Y12, Y11 + VPMULLW Y4, Y0, Y8 + VPMULLW Y5, Y0, Y10 + VPSUBW Y13, Y14, Y0 + VPMULLW Y6, Y2, Y12 + VPADDW Y13, Y14, Y13 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y8, Y15, Y8 + VPMULHW Y10, Y15, Y10 + VPMULHW Y12, Y15, Y12 + VPMULHW Y14, Y15, Y14 + VPSUBW Y8, Y4, Y8 + VPSUBW Y10, Y5, Y10 + VPSUBW Y12, Y6, Y12 + VPSUBW Y14, Y0, Y14 + VMOVDQU 1568(CX), Y0 + VMOVDQU 1600(CX), Y1 + VMOVDQU 1632(CX), Y2 + VMOVDQU 1664(CX), Y3 + VMOVSLDUP Y9, Y4 + VPBLENDD $0xaa, Y4, Y7, Y4 + VPSRLQ $0x20, Y7, Y7 + VPBLENDD $0xaa, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VMOVSLDUP Y10, Y4 + VPBLENDD $0xaa, Y4, Y8, Y4 + VPSRLQ $0x20, Y8, Y8 + VPBLENDD $0xaa, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VMOVSLDUP Y13, Y4 + VPBLENDD $0xaa, Y4, Y11, Y4 + VPSRLQ $0x20, Y11, Y11 + VPBLENDD $0xaa, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VMOVSLDUP Y14, Y4 + VPBLENDD $0xaa, Y4, Y12, Y4 + VPSRLQ $0x20, Y12, Y12 + VPBLENDD $0xaa, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + MOVL $0x00004ebf, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y4 + VPMULHW Y4, Y7, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y7, Y7 + VPMULHW Y4, Y11, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y11, Y11 + VMOVDQU 1824(CX), Y0 + VMOVDQU 1856(CX), Y1 + VMOVDQU 1888(CX), Y2 + VMOVDQU 1920(CX), Y3 + VPUNPCKLQDQ Y8, Y7, Y4 + VPUNPCKHQDQ Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPUNPCKLQDQ Y10, Y9, Y4 + VPUNPCKHQDQ Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPUNPCKLQDQ Y12, Y11, Y4 + VPUNPCKHQDQ Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPUNPCKLQDQ Y14, Y13, Y4 + VPUNPCKHQDQ Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPSUBW Y7, Y8, Y4 + VPSUBW Y9, Y10, Y5 + VPSUBW Y11, Y12, Y6 + VPADDW Y7, Y8, Y7 + VPADDW Y9, Y10, Y9 + VPADDW Y11, Y12, Y11 + VPMULLW Y4, Y0, Y8 + VPMULLW Y5, Y0, Y10 + VPSUBW Y13, Y14, Y0 + VPMULLW Y6, Y2, Y12 + VPADDW Y13, Y14, Y13 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y8, Y15, Y8 + VPMULHW Y10, Y15, Y10 + VPMULHW Y12, Y15, Y12 + VPMULHW Y14, Y15, Y14 + VPSUBW Y8, Y4, Y8 + VPSUBW Y10, Y5, Y10 + VPSUBW Y12, Y6, Y12 + VPSUBW Y14, Y0, Y14 + VPBROADCASTW 2080(CX), Y0 + VPBROADCASTW 2082(CX), Y1 + VPBROADCASTW 2084(CX), Y2 + VPBROADCASTW 2086(CX), Y3 + VPERM2I128 $0x20, Y9, Y7, Y4 + VPERM2I128 $0x31, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VPERM2I128 $0x20, Y10, Y8, Y4 + VPERM2I128 $0x31, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VPERM2I128 $0x20, Y13, Y11, Y4 + VPERM2I128 $0x31, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VPERM2I128 $0x20, Y14, Y12, Y4 + VPERM2I128 $0x31, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + MOVL $0x00004ebf, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y4 + VPMULHW Y4, Y7, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y7, Y7 + VPMULHW Y4, Y11, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y11, Y11 + VPBROADCASTW 2096(CX), Y0 + VPBROADCASTW 2098(CX), Y1 + VPSUBW Y7, Y11, Y4 + VPSUBW Y8, Y12, Y5 + VPSUBW Y9, Y13, Y6 + VPADDW Y7, Y11, Y7 + VPADDW Y8, Y12, Y8 + VPADDW Y9, Y13, Y9 + VPMULLW Y4, Y0, Y11 + VPMULLW Y5, Y0, Y12 + VPSUBW Y10, Y14, Y2 + VPMULLW Y6, Y0, Y13 + VPADDW Y10, Y14, Y10 + VPMULLW Y2, Y0, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y1, Y6 + VPMULHW Y2, Y1, Y2 + VPMULHW Y11, Y15, Y11 + VPMULHW Y12, Y15, Y12 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y11, Y4, Y11 + VPSUBW Y12, Y5, Y12 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y2, Y14 + VMOVDQU Y7, (AX) + VMOVDQU Y8, 32(AX) + VMOVDQU Y9, 64(AX) + VMOVDQU Y10, 96(AX) + VMOVDQU Y11, 128(AX) + VMOVDQU Y12, 160(AX) + VMOVDQU Y13, 192(AX) + VMOVDQU Y14, 224(AX) + VMOVDQU 256(AX), Y7 + VMOVDQU 288(AX), Y8 + VMOVDQU 320(AX), Y9 + VMOVDQU 352(AX), Y10 + VMOVDQU 384(AX), Y11 + VMOVDQU 416(AX), Y12 + VMOVDQU 448(AX), Y13 + VMOVDQU 480(AX), Y14 + VMOVDQU 1184(CX), Y0 + VMOVDQU 1216(CX), Y1 + VMOVDQU 1248(CX), Y2 + VMOVDQU 1280(CX), Y3 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + VMOVDQU 1440(CX), Y0 + VMOVDQU 1472(CX), Y1 + VMOVDQU 1504(CX), Y2 + VMOVDQU 1536(CX), Y3 + VPSLLD $0x10, Y8, Y4 + VPBLENDW $0xaa, Y4, Y7, Y4 + VPSRLD $0x10, Y7, Y7 + VPBLENDW $0xaa, Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPSLLD $0x10, Y10, Y4 + VPBLENDW $0xaa, Y4, Y9, Y4 + VPSRLD $0x10, Y9, Y9 + VPBLENDW $0xaa, Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPSLLD $0x10, Y12, Y4 + VPBLENDW $0xaa, Y4, Y11, Y4 + VPSRLD $0x10, Y11, Y11 + VPBLENDW $0xaa, Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPSLLD $0x10, Y14, Y4 + VPBLENDW $0xaa, Y4, Y13, Y4 + VPSRLD $0x10, Y13, Y13 + VPBLENDW $0xaa, Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPSUBW Y7, Y8, Y4 + VPSUBW Y9, Y10, Y5 + VPSUBW Y11, Y12, Y6 + VPADDW Y7, Y8, Y7 + VPADDW Y9, Y10, Y9 + VPADDW Y11, Y12, Y11 + VPMULLW Y4, Y0, Y8 + VPMULLW Y5, Y0, Y10 + VPSUBW Y13, Y14, Y0 + VPMULLW Y6, Y2, Y12 + VPADDW Y13, Y14, Y13 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y8, Y15, Y8 + VPMULHW Y10, Y15, Y10 + VPMULHW Y12, Y15, Y12 + VPMULHW Y14, Y15, Y14 + VPSUBW Y8, Y4, Y8 + VPSUBW Y10, Y5, Y10 + VPSUBW Y12, Y6, Y12 + VPSUBW Y14, Y0, Y14 + VMOVDQU 1696(CX), Y0 + VMOVDQU 1728(CX), Y1 + VMOVDQU 1760(CX), Y2 + VMOVDQU 1792(CX), Y3 + VMOVSLDUP Y9, Y4 + VPBLENDD $0xaa, Y4, Y7, Y4 + VPSRLQ $0x20, Y7, Y7 + VPBLENDD $0xaa, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VMOVSLDUP Y10, Y4 + VPBLENDD $0xaa, Y4, Y8, Y4 + VPSRLQ $0x20, Y8, Y8 + VPBLENDD $0xaa, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VMOVSLDUP Y13, Y4 + VPBLENDD $0xaa, Y4, Y11, Y4 + VPSRLQ $0x20, Y11, Y11 + VPBLENDD $0xaa, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VMOVSLDUP Y14, Y4 + VPBLENDD $0xaa, Y4, Y12, Y4 + VPSRLQ $0x20, Y12, Y12 + VPBLENDD $0xaa, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + MOVL $0x00004ebf, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y4 + VPMULHW Y4, Y7, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y7, Y7 + VPMULHW Y4, Y11, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y11, Y11 + VMOVDQU 1952(CX), Y0 + VMOVDQU 1984(CX), Y1 + VMOVDQU 2016(CX), Y2 + VMOVDQU 2048(CX), Y3 + VPUNPCKLQDQ Y8, Y7, Y4 + VPUNPCKHQDQ Y8, Y7, Y8 + VMOVDQA Y4, Y7 + VPUNPCKLQDQ Y10, Y9, Y4 + VPUNPCKHQDQ Y10, Y9, Y10 + VMOVDQA Y4, Y9 + VPUNPCKLQDQ Y12, Y11, Y4 + VPUNPCKHQDQ Y12, Y11, Y12 + VMOVDQA Y4, Y11 + VPUNPCKLQDQ Y14, Y13, Y4 + VPUNPCKHQDQ Y14, Y13, Y14 + VMOVDQA Y4, Y13 + VPSUBW Y7, Y8, Y4 + VPSUBW Y9, Y10, Y5 + VPSUBW Y11, Y12, Y6 + VPADDW Y7, Y8, Y7 + VPADDW Y9, Y10, Y9 + VPADDW Y11, Y12, Y11 + VPMULLW Y4, Y0, Y8 + VPMULLW Y5, Y0, Y10 + VPSUBW Y13, Y14, Y0 + VPMULLW Y6, Y2, Y12 + VPADDW Y13, Y14, Y13 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y8, Y15, Y8 + VPMULHW Y10, Y15, Y10 + VPMULHW Y12, Y15, Y12 + VPMULHW Y14, Y15, Y14 + VPSUBW Y8, Y4, Y8 + VPSUBW Y10, Y5, Y10 + VPSUBW Y12, Y6, Y12 + VPSUBW Y14, Y0, Y14 + VPBROADCASTW 2088(CX), Y0 + VPBROADCASTW 2090(CX), Y1 + VPBROADCASTW 2092(CX), Y2 + VPBROADCASTW 2094(CX), Y3 + VPERM2I128 $0x20, Y9, Y7, Y4 + VPERM2I128 $0x31, Y9, Y7, Y9 + VMOVDQA Y4, Y7 + VPERM2I128 $0x20, Y10, Y8, Y4 + VPERM2I128 $0x31, Y10, Y8, Y10 + VMOVDQA Y4, Y8 + VPERM2I128 $0x20, Y13, Y11, Y4 + VPERM2I128 $0x31, Y13, Y11, Y13 + VMOVDQA Y4, Y11 + VPERM2I128 $0x20, Y14, Y12, Y4 + VPERM2I128 $0x31, Y14, Y12, Y14 + VMOVDQA Y4, Y12 + VPSUBW Y7, Y9, Y4 + VPSUBW Y8, Y10, Y5 + VPSUBW Y11, Y13, Y6 + VPADDW Y7, Y9, Y7 + VPADDW Y8, Y10, Y8 + VPADDW Y11, Y13, Y11 + VPMULLW Y4, Y0, Y9 + VPMULLW Y5, Y0, Y10 + VPSUBW Y12, Y14, Y0 + VPMULLW Y6, Y2, Y13 + VPADDW Y12, Y14, Y12 + VPMULLW Y0, Y2, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y3, Y6 + VPMULHW Y0, Y3, Y0 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y9, Y4, Y9 + VPSUBW Y10, Y5, Y10 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y0, Y14 + MOVL $0x00004ebf, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y4 + VPMULHW Y4, Y7, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y7, Y7 + VPMULHW Y4, Y11, Y5 + VPSRAW $0x0a, Y5, Y5 + VPMULLW Y15, Y5, Y5 + VPSUBW Y5, Y11, Y11 + VPBROADCASTW 2100(CX), Y0 + VPBROADCASTW 2102(CX), Y1 + VPSUBW Y7, Y11, Y4 + VPSUBW Y8, Y12, Y5 + VPSUBW Y9, Y13, Y6 + VPADDW Y7, Y11, Y7 + VPADDW Y8, Y12, Y8 + VPADDW Y9, Y13, Y9 + VPMULLW Y4, Y0, Y11 + VPMULLW Y5, Y0, Y12 + VPSUBW Y10, Y14, Y2 + VPMULLW Y6, Y0, Y13 + VPADDW Y10, Y14, Y10 + VPMULLW Y2, Y0, Y14 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y6, Y1, Y6 + VPMULHW Y2, Y1, Y2 + VPMULHW Y11, Y15, Y11 + VPMULHW Y12, Y15, Y12 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y11, Y4, Y11 + VPSUBW Y12, Y5, Y12 + VPSUBW Y13, Y6, Y13 + VPSUBW Y14, Y2, Y14 + VMOVDQU Y7, 256(AX) + VMOVDQU Y8, 288(AX) + VMOVDQU Y9, 320(AX) + VMOVDQU Y10, 352(AX) + VMOVDQU Y11, 384(AX) + VMOVDQU Y12, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y14, 480(AX) + VPBROADCASTW 2104(CX), Y0 + VPBROADCASTW 2106(CX), Y1 + VMOVDQU (AX), Y7 + VMOVDQU 32(AX), Y8 + VMOVDQU 64(AX), Y9 + VMOVDQU 96(AX), Y10 + VMOVDQU 256(AX), Y11 + VMOVDQU 288(AX), Y12 + VMOVDQU 320(AX), Y13 + VMOVDQU 352(AX), Y14 + VPSUBW Y7, Y11, Y2 + VPSUBW Y8, Y12, Y3 + VPSUBW Y9, Y13, Y4 + VPADDW Y7, Y11, Y7 + VPADDW Y8, Y12, Y8 + VPADDW Y9, Y13, Y9 + VPMULLW Y2, Y0, Y11 + VPMULLW Y3, Y0, Y12 + VPSUBW Y10, Y14, Y5 + VPMULLW Y4, Y0, Y13 + VPADDW Y10, Y14, Y10 + VPMULLW Y5, Y0, Y14 + VPMULHW Y2, Y1, Y2 + VPMULHW Y3, Y1, Y3 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y11, Y15, Y11 + VPMULHW Y12, Y15, Y12 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y11, Y2, Y11 + VPSUBW Y12, Y3, Y12 + VPSUBW Y13, Y4, Y13 + VPSUBW Y14, Y5, Y14 + MOVL $0xffffd8a1, DX + VMOVD DX, X0 + VPBROADCASTW X0, Y0 + MOVL $0x000005a1, DX + VMOVD DX, X1 + VPBROADCASTW X1, Y1 + VPMULLW Y7, Y0, Y2 + VPMULLW Y8, Y0, Y3 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULHW Y7, Y1, Y7 + VPMULHW Y8, Y1, Y8 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y7, Y7 + VPSUBW Y3, Y8, Y8 + VPSUBW Y4, Y9, Y9 + VPSUBW Y5, Y10, Y10 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y11 + VPSUBW Y3, Y12, Y12 + VPSUBW Y4, Y13, Y13 + VPSUBW Y5, Y14, Y14 + VMOVDQU Y7, (AX) + VMOVDQU Y8, 32(AX) + VMOVDQU Y9, 64(AX) + VMOVDQU Y10, 96(AX) + VMOVDQU Y11, 256(AX) + VMOVDQU Y12, 288(AX) + VMOVDQU Y13, 320(AX) + VMOVDQU Y14, 352(AX) + VPBROADCASTW 2104(CX), Y0 + VPBROADCASTW 2106(CX), Y1 + VMOVDQU 128(AX), Y7 + VMOVDQU 160(AX), Y8 + VMOVDQU 192(AX), Y9 + VMOVDQU 224(AX), Y10 + VMOVDQU 384(AX), Y11 + VMOVDQU 416(AX), Y12 + VMOVDQU 448(AX), Y13 + VMOVDQU 480(AX), Y14 + VPSUBW Y7, Y11, Y2 + VPSUBW Y8, Y12, Y3 + VPSUBW Y9, Y13, Y4 + VPADDW Y7, Y11, Y7 + VPADDW Y8, Y12, Y8 + VPADDW Y9, Y13, Y9 + VPMULLW Y2, Y0, Y11 + VPMULLW Y3, Y0, Y12 + VPSUBW Y10, Y14, Y5 + VPMULLW Y4, Y0, Y13 + VPADDW Y10, Y14, Y10 + VPMULLW Y5, Y0, Y14 + VPMULHW Y2, Y1, Y2 + VPMULHW Y3, Y1, Y3 + VPMULHW Y4, Y1, Y4 + VPMULHW Y5, Y1, Y5 + VPMULHW Y11, Y15, Y11 + VPMULHW Y12, Y15, Y12 + VPMULHW Y13, Y15, Y13 + VPMULHW Y14, Y15, Y14 + VPSUBW Y11, Y2, Y11 + VPSUBW Y12, Y3, Y12 + VPSUBW Y13, Y4, Y13 + VPSUBW Y14, Y5, Y14 + MOVL $0xffffd8a1, CX + VMOVD CX, X0 + VPBROADCASTW X0, Y0 + MOVL $0x000005a1, CX + VMOVD CX, X1 + VPBROADCASTW X1, Y1 + VPMULLW Y7, Y0, Y2 + VPMULLW Y8, Y0, Y3 + VPMULLW Y9, Y0, Y4 + VPMULLW Y10, Y0, Y5 + VPMULHW Y7, Y1, Y7 + VPMULHW Y8, Y1, Y8 + VPMULHW Y9, Y1, Y9 + VPMULHW Y10, Y1, Y10 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y7, Y7 + VPSUBW Y3, Y8, Y8 + VPSUBW Y4, Y9, Y9 + VPSUBW Y5, Y10, Y10 + VPMULLW Y11, Y0, Y2 + VPMULLW Y12, Y0, Y3 + VPMULLW Y13, Y0, Y4 + VPMULLW Y14, Y0, Y5 + VPMULHW Y11, Y1, Y11 + VPMULHW Y12, Y1, Y12 + VPMULHW Y13, Y1, Y13 + VPMULHW Y14, Y1, Y14 + VPMULHW Y2, Y15, Y2 + VPMULHW Y3, Y15, Y3 + VPMULHW Y4, Y15, Y4 + VPMULHW Y5, Y15, Y5 + VPSUBW Y2, Y11, Y11 + VPSUBW Y3, Y12, Y12 + VPSUBW Y4, Y13, Y13 + VPSUBW Y5, Y14, Y14 + VMOVDQU Y7, 128(AX) + VMOVDQU Y8, 160(AX) + VMOVDQU Y9, 192(AX) + VMOVDQU Y10, 224(AX) + VMOVDQU Y11, 384(AX) + VMOVDQU Y12, 416(AX) + VMOVDQU Y13, 448(AX) + VMOVDQU Y14, 480(AX) + RET + +// func mulHatAVX2(p *[256]int16, a *[256]int16, b *[256]int16) +// Requires: AVX, AVX2 +TEXT ·mulHatAVX2(SB), NOSPLIT, $8-24 + MOVQ p+0(FP), AX + MOVQ a+8(FP), CX + MOVQ b+16(FP), DX + LEAQ ·ZetasAVX2+0(SB), BX + MOVL $0xfffff301, BP + VMOVD BP, X0 + VPBROADCASTW X0, Y14 + MOVL $0x00000d01, BP + VMOVD BP, X0 + VPBROADCASTW X0, Y15 + VMOVDQU (CX), Y0 + VMOVDQU 32(CX), Y1 + VMOVDQU 64(CX), Y2 + VMOVDQU 96(CX), Y3 + VMOVDQU (DX), Y4 + VMOVDQU 32(DX), Y5 + VMOVDQU 64(DX), Y6 + VMOVDQU 96(DX), Y7 + VPMULLW Y1, Y5, Y8 + VPMULLW Y0, Y4, Y9 + VPMULLW Y0, Y5, Y10 + VPMULLW Y1, Y4, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y1, Y5, Y12 + VPMULHW Y0, Y4, Y13 + VPMULHW Y0, Y5, Y0 + VPMULHW Y1, Y4, Y1 + VMOVDQA Y12, Y4 + VMOVDQA Y13, Y5 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y4, Y4 + VPSUBW Y9, Y5, Y5 + VPSUBW Y10, Y0, Y0 + VPSUBW Y11, Y1, Y1 + VMOVDQU 800(BX), Y12 + VMOVDQU 832(BX), Y13 + VPMULLW Y4, Y12, Y8 + VPMULHW Y4, Y13, Y4 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y4, Y4 + VPADDW Y4, Y5, Y4 + VPADDW Y0, Y1, Y5 + VPMULLW Y3, Y7, Y8 + VPMULLW Y2, Y6, Y9 + VPMULLW Y2, Y7, Y10 + VPMULLW Y3, Y6, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y3, Y7, Y12 + VPMULHW Y2, Y6, Y13 + VPMULHW Y2, Y7, Y2 + VPMULHW Y3, Y6, Y3 + VMOVDQA Y12, Y6 + VMOVDQA Y13, Y7 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y6, Y6 + VPSUBW Y9, Y7, Y7 + VPSUBW Y10, Y2, Y2 + VPSUBW Y11, Y3, Y3 + VMOVDQU 800(BX), Y12 + VMOVDQU 832(BX), Y13 + VPMULLW Y6, Y12, Y8 + VPMULHW Y6, Y13, Y6 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y6, Y6 + VPSUBW Y6, Y7, Y6 + VPADDW Y2, Y3, Y7 + VMOVDQU Y4, (AX) + VMOVDQU Y5, 32(AX) + VMOVDQU Y6, 64(AX) + VMOVDQU Y7, 96(AX) + VMOVDQU 128(CX), Y0 + VMOVDQU 160(CX), Y1 + VMOVDQU 192(CX), Y2 + VMOVDQU 224(CX), Y3 + VMOVDQU 128(DX), Y4 + VMOVDQU 160(DX), Y5 + VMOVDQU 192(DX), Y6 + VMOVDQU 224(DX), Y7 + VPMULLW Y1, Y5, Y8 + VPMULLW Y0, Y4, Y9 + VPMULLW Y0, Y5, Y10 + VPMULLW Y1, Y4, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y1, Y5, Y12 + VPMULHW Y0, Y4, Y13 + VPMULHW Y0, Y5, Y0 + VPMULHW Y1, Y4, Y1 + VMOVDQA Y12, Y4 + VMOVDQA Y13, Y5 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y4, Y4 + VPSUBW Y9, Y5, Y5 + VPSUBW Y10, Y0, Y0 + VPSUBW Y11, Y1, Y1 + VMOVDQU 864(BX), Y12 + VMOVDQU 896(BX), Y13 + VPMULLW Y4, Y12, Y8 + VPMULHW Y4, Y13, Y4 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y4, Y4 + VPADDW Y4, Y5, Y4 + VPADDW Y0, Y1, Y5 + VPMULLW Y3, Y7, Y8 + VPMULLW Y2, Y6, Y9 + VPMULLW Y2, Y7, Y10 + VPMULLW Y3, Y6, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y3, Y7, Y12 + VPMULHW Y2, Y6, Y13 + VPMULHW Y2, Y7, Y2 + VPMULHW Y3, Y6, Y3 + VMOVDQA Y12, Y6 + VMOVDQA Y13, Y7 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y6, Y6 + VPSUBW Y9, Y7, Y7 + VPSUBW Y10, Y2, Y2 + VPSUBW Y11, Y3, Y3 + VMOVDQU 864(BX), Y12 + VMOVDQU 896(BX), Y13 + VPMULLW Y6, Y12, Y8 + VPMULHW Y6, Y13, Y6 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y6, Y6 + VPSUBW Y6, Y7, Y6 + VPADDW Y2, Y3, Y7 + VMOVDQU Y4, 128(AX) + VMOVDQU Y5, 160(AX) + VMOVDQU Y6, 192(AX) + VMOVDQU Y7, 224(AX) + VMOVDQU 256(CX), Y0 + VMOVDQU 288(CX), Y1 + VMOVDQU 320(CX), Y2 + VMOVDQU 352(CX), Y3 + VMOVDQU 256(DX), Y4 + VMOVDQU 288(DX), Y5 + VMOVDQU 320(DX), Y6 + VMOVDQU 352(DX), Y7 + VPMULLW Y1, Y5, Y8 + VPMULLW Y0, Y4, Y9 + VPMULLW Y0, Y5, Y10 + VPMULLW Y1, Y4, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y1, Y5, Y12 + VPMULHW Y0, Y4, Y13 + VPMULHW Y0, Y5, Y0 + VPMULHW Y1, Y4, Y1 + VMOVDQA Y12, Y4 + VMOVDQA Y13, Y5 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y4, Y4 + VPSUBW Y9, Y5, Y5 + VPSUBW Y10, Y0, Y0 + VPSUBW Y11, Y1, Y1 + VMOVDQU 928(BX), Y12 + VMOVDQU 960(BX), Y13 + VPMULLW Y4, Y12, Y8 + VPMULHW Y4, Y13, Y4 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y4, Y4 + VPADDW Y4, Y5, Y4 + VPADDW Y0, Y1, Y5 + VPMULLW Y3, Y7, Y8 + VPMULLW Y2, Y6, Y9 + VPMULLW Y2, Y7, Y10 + VPMULLW Y3, Y6, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y3, Y7, Y12 + VPMULHW Y2, Y6, Y13 + VPMULHW Y2, Y7, Y2 + VPMULHW Y3, Y6, Y3 + VMOVDQA Y12, Y6 + VMOVDQA Y13, Y7 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y6, Y6 + VPSUBW Y9, Y7, Y7 + VPSUBW Y10, Y2, Y2 + VPSUBW Y11, Y3, Y3 + VMOVDQU 928(BX), Y12 + VMOVDQU 960(BX), Y13 + VPMULLW Y6, Y12, Y8 + VPMULHW Y6, Y13, Y6 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y6, Y6 + VPSUBW Y6, Y7, Y6 + VPADDW Y2, Y3, Y7 + VMOVDQU Y4, 256(AX) + VMOVDQU Y5, 288(AX) + VMOVDQU Y6, 320(AX) + VMOVDQU Y7, 352(AX) + VMOVDQU 384(CX), Y0 + VMOVDQU 416(CX), Y1 + VMOVDQU 448(CX), Y2 + VMOVDQU 480(CX), Y3 + VMOVDQU 384(DX), Y4 + VMOVDQU 416(DX), Y5 + VMOVDQU 448(DX), Y6 + VMOVDQU 480(DX), Y7 + VPMULLW Y1, Y5, Y8 + VPMULLW Y0, Y4, Y9 + VPMULLW Y0, Y5, Y10 + VPMULLW Y1, Y4, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y1, Y5, Y12 + VPMULHW Y0, Y4, Y13 + VPMULHW Y0, Y5, Y0 + VPMULHW Y1, Y4, Y1 + VMOVDQA Y12, Y4 + VMOVDQA Y13, Y5 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y4, Y4 + VPSUBW Y9, Y5, Y5 + VPSUBW Y10, Y0, Y0 + VPSUBW Y11, Y1, Y1 + VMOVDQU 992(BX), Y12 + VMOVDQU 1024(BX), Y13 + VPMULLW Y4, Y12, Y8 + VPMULHW Y4, Y13, Y4 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y4, Y4 + VPADDW Y4, Y5, Y4 + VPADDW Y0, Y1, Y5 + VPMULLW Y3, Y7, Y8 + VPMULLW Y2, Y6, Y9 + VPMULLW Y2, Y7, Y10 + VPMULLW Y3, Y6, Y11 + VPMULLW Y8, Y14, Y8 + VPMULLW Y9, Y14, Y9 + VPMULLW Y10, Y14, Y10 + VPMULLW Y11, Y14, Y11 + VPMULHW Y3, Y7, Y12 + VPMULHW Y2, Y6, Y13 + VPMULHW Y2, Y7, Y2 + VPMULHW Y3, Y6, Y3 + VMOVDQA Y12, Y6 + VMOVDQA Y13, Y7 + VPMULHW Y8, Y15, Y8 + VPMULHW Y9, Y15, Y9 + VPMULHW Y10, Y15, Y10 + VPMULHW Y11, Y15, Y11 + VPSUBW Y8, Y6, Y6 + VPSUBW Y9, Y7, Y7 + VPSUBW Y10, Y2, Y2 + VPSUBW Y11, Y3, Y3 + VMOVDQU 992(BX), Y12 + VMOVDQU 1024(BX), Y13 + VPMULLW Y6, Y12, Y8 + VPMULHW Y6, Y13, Y6 + VPMULHW Y8, Y15, Y8 + VPSUBW Y8, Y6, Y6 + VPSUBW Y6, Y7, Y6 + VPADDW Y2, Y3, Y7 + VMOVDQU Y4, 384(AX) + VMOVDQU Y5, 416(AX) + VMOVDQU Y6, 448(AX) + VMOVDQU Y7, 480(AX) + RET + +// func detangleAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·detangleAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + VMOVDQU (AX), Y0 + VMOVDQU 32(AX), Y1 + VMOVDQU 64(AX), Y2 + VMOVDQU 96(AX), Y3 + VMOVDQU 128(AX), Y4 + VMOVDQU 160(AX), Y5 + VMOVDQU 192(AX), Y6 + VMOVDQU 224(AX), Y7 + VPSLLD $0x10, Y1, Y8 + VPBLENDW $0xaa, Y8, Y0, Y8 + VPSRLD $0x10, Y0, Y0 + VPBLENDW $0xaa, Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPSLLD $0x10, Y3, Y8 + VPBLENDW $0xaa, Y8, Y2, Y8 + VPSRLD $0x10, Y2, Y2 + VPBLENDW $0xaa, Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPSLLD $0x10, Y5, Y8 + VPBLENDW $0xaa, Y8, Y4, Y8 + VPSRLD $0x10, Y4, Y4 + VPBLENDW $0xaa, Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPSLLD $0x10, Y7, Y8 + VPBLENDW $0xaa, Y8, Y6, Y8 + VPSRLD $0x10, Y6, Y6 + VPBLENDW $0xaa, Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVSLDUP Y2, Y8 + VPBLENDD $0xaa, Y8, Y0, Y8 + VPSRLQ $0x20, Y0, Y0 + VPBLENDD $0xaa, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VMOVSLDUP Y3, Y8 + VPBLENDD $0xaa, Y8, Y1, Y8 + VPSRLQ $0x20, Y1, Y1 + VPBLENDD $0xaa, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VMOVSLDUP Y6, Y8 + VPBLENDD $0xaa, Y8, Y4, Y8 + VPSRLQ $0x20, Y4, Y4 + VPBLENDD $0xaa, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VMOVSLDUP Y7, Y8 + VPBLENDD $0xaa, Y8, Y5, Y8 + VPSRLQ $0x20, Y5, Y5 + VPBLENDD $0xaa, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPUNPCKLQDQ Y1, Y0, Y8 + VPUNPCKHQDQ Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPUNPCKLQDQ Y3, Y2, Y8 + VPUNPCKHQDQ Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPUNPCKLQDQ Y5, Y4, Y8 + VPUNPCKHQDQ Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPUNPCKLQDQ Y7, Y6, Y8 + VPUNPCKHQDQ Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VPERM2I128 $0x20, Y2, Y0, Y8 + VPERM2I128 $0x31, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VPERM2I128 $0x20, Y3, Y1, Y8 + VPERM2I128 $0x31, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VPERM2I128 $0x20, Y6, Y4, Y8 + VPERM2I128 $0x31, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VPERM2I128 $0x20, Y7, Y5, Y8 + VPERM2I128 $0x31, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VMOVDQU Y0, (AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VMOVDQU Y4, 128(AX) + VMOVDQU Y5, 160(AX) + VMOVDQU Y6, 192(AX) + VMOVDQU Y7, 224(AX) + VMOVDQU 256(AX), Y0 + VMOVDQU 288(AX), Y1 + VMOVDQU 320(AX), Y2 + VMOVDQU 352(AX), Y3 + VMOVDQU 384(AX), Y4 + VMOVDQU 416(AX), Y5 + VMOVDQU 448(AX), Y6 + VMOVDQU 480(AX), Y7 + VPSLLD $0x10, Y1, Y8 + VPBLENDW $0xaa, Y8, Y0, Y8 + VPSRLD $0x10, Y0, Y0 + VPBLENDW $0xaa, Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPSLLD $0x10, Y3, Y8 + VPBLENDW $0xaa, Y8, Y2, Y8 + VPSRLD $0x10, Y2, Y2 + VPBLENDW $0xaa, Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPSLLD $0x10, Y5, Y8 + VPBLENDW $0xaa, Y8, Y4, Y8 + VPSRLD $0x10, Y4, Y4 + VPBLENDW $0xaa, Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPSLLD $0x10, Y7, Y8 + VPBLENDW $0xaa, Y8, Y6, Y8 + VPSRLD $0x10, Y6, Y6 + VPBLENDW $0xaa, Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVSLDUP Y2, Y8 + VPBLENDD $0xaa, Y8, Y0, Y8 + VPSRLQ $0x20, Y0, Y0 + VPBLENDD $0xaa, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VMOVSLDUP Y3, Y8 + VPBLENDD $0xaa, Y8, Y1, Y8 + VPSRLQ $0x20, Y1, Y1 + VPBLENDD $0xaa, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VMOVSLDUP Y6, Y8 + VPBLENDD $0xaa, Y8, Y4, Y8 + VPSRLQ $0x20, Y4, Y4 + VPBLENDD $0xaa, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VMOVSLDUP Y7, Y8 + VPBLENDD $0xaa, Y8, Y5, Y8 + VPSRLQ $0x20, Y5, Y5 + VPBLENDD $0xaa, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPUNPCKLQDQ Y1, Y0, Y8 + VPUNPCKHQDQ Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPUNPCKLQDQ Y3, Y2, Y8 + VPUNPCKHQDQ Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPUNPCKLQDQ Y5, Y4, Y8 + VPUNPCKHQDQ Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPUNPCKLQDQ Y7, Y6, Y8 + VPUNPCKHQDQ Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VPERM2I128 $0x20, Y2, Y0, Y8 + VPERM2I128 $0x31, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VPERM2I128 $0x20, Y3, Y1, Y8 + VPERM2I128 $0x31, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VPERM2I128 $0x20, Y6, Y4, Y8 + VPERM2I128 $0x31, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VPERM2I128 $0x20, Y7, Y5, Y8 + VPERM2I128 $0x31, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VMOVDQU Y0, 256(AX) + VMOVDQU Y1, 288(AX) + VMOVDQU Y2, 320(AX) + VMOVDQU Y3, 352(AX) + VMOVDQU Y4, 384(AX) + VMOVDQU Y5, 416(AX) + VMOVDQU Y6, 448(AX) + VMOVDQU Y7, 480(AX) + RET + +// func tangleAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·tangleAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + VMOVDQU (AX), Y0 + VMOVDQU 32(AX), Y1 + VMOVDQU 64(AX), Y2 + VMOVDQU 96(AX), Y3 + VMOVDQU 128(AX), Y4 + VMOVDQU 160(AX), Y5 + VMOVDQU 192(AX), Y6 + VMOVDQU 224(AX), Y7 + VPERM2I128 $0x20, Y2, Y0, Y8 + VPERM2I128 $0x31, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VPERM2I128 $0x20, Y3, Y1, Y8 + VPERM2I128 $0x31, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VPERM2I128 $0x20, Y6, Y4, Y8 + VPERM2I128 $0x31, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VPERM2I128 $0x20, Y7, Y5, Y8 + VPERM2I128 $0x31, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPUNPCKLQDQ Y1, Y0, Y8 + VPUNPCKHQDQ Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPUNPCKLQDQ Y3, Y2, Y8 + VPUNPCKHQDQ Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPUNPCKLQDQ Y5, Y4, Y8 + VPUNPCKHQDQ Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPUNPCKLQDQ Y7, Y6, Y8 + VPUNPCKHQDQ Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVSLDUP Y2, Y8 + VPBLENDD $0xaa, Y8, Y0, Y8 + VPSRLQ $0x20, Y0, Y0 + VPBLENDD $0xaa, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VMOVSLDUP Y3, Y8 + VPBLENDD $0xaa, Y8, Y1, Y8 + VPSRLQ $0x20, Y1, Y1 + VPBLENDD $0xaa, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VMOVSLDUP Y6, Y8 + VPBLENDD $0xaa, Y8, Y4, Y8 + VPSRLQ $0x20, Y4, Y4 + VPBLENDD $0xaa, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VMOVSLDUP Y7, Y8 + VPBLENDD $0xaa, Y8, Y5, Y8 + VPSRLQ $0x20, Y5, Y5 + VPBLENDD $0xaa, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPSLLD $0x10, Y1, Y8 + VPBLENDW $0xaa, Y8, Y0, Y8 + VPSRLD $0x10, Y0, Y0 + VPBLENDW $0xaa, Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPSLLD $0x10, Y3, Y8 + VPBLENDW $0xaa, Y8, Y2, Y8 + VPSRLD $0x10, Y2, Y2 + VPBLENDW $0xaa, Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPSLLD $0x10, Y5, Y8 + VPBLENDW $0xaa, Y8, Y4, Y8 + VPSRLD $0x10, Y4, Y4 + VPBLENDW $0xaa, Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPSLLD $0x10, Y7, Y8 + VPBLENDW $0xaa, Y8, Y6, Y8 + VPSRLD $0x10, Y6, Y6 + VPBLENDW $0xaa, Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVDQU Y0, (AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VMOVDQU Y4, 128(AX) + VMOVDQU Y5, 160(AX) + VMOVDQU Y6, 192(AX) + VMOVDQU Y7, 224(AX) + VMOVDQU 256(AX), Y0 + VMOVDQU 288(AX), Y1 + VMOVDQU 320(AX), Y2 + VMOVDQU 352(AX), Y3 + VMOVDQU 384(AX), Y4 + VMOVDQU 416(AX), Y5 + VMOVDQU 448(AX), Y6 + VMOVDQU 480(AX), Y7 + VPERM2I128 $0x20, Y2, Y0, Y8 + VPERM2I128 $0x31, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VPERM2I128 $0x20, Y3, Y1, Y8 + VPERM2I128 $0x31, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VPERM2I128 $0x20, Y6, Y4, Y8 + VPERM2I128 $0x31, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VPERM2I128 $0x20, Y7, Y5, Y8 + VPERM2I128 $0x31, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPUNPCKLQDQ Y1, Y0, Y8 + VPUNPCKHQDQ Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPUNPCKLQDQ Y3, Y2, Y8 + VPUNPCKHQDQ Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPUNPCKLQDQ Y5, Y4, Y8 + VPUNPCKHQDQ Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPUNPCKLQDQ Y7, Y6, Y8 + VPUNPCKHQDQ Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVSLDUP Y2, Y8 + VPBLENDD $0xaa, Y8, Y0, Y8 + VPSRLQ $0x20, Y0, Y0 + VPBLENDD $0xaa, Y2, Y0, Y2 + VMOVDQA Y8, Y0 + VMOVSLDUP Y3, Y8 + VPBLENDD $0xaa, Y8, Y1, Y8 + VPSRLQ $0x20, Y1, Y1 + VPBLENDD $0xaa, Y3, Y1, Y3 + VMOVDQA Y8, Y1 + VMOVSLDUP Y6, Y8 + VPBLENDD $0xaa, Y8, Y4, Y8 + VPSRLQ $0x20, Y4, Y4 + VPBLENDD $0xaa, Y6, Y4, Y6 + VMOVDQA Y8, Y4 + VMOVSLDUP Y7, Y8 + VPBLENDD $0xaa, Y8, Y5, Y8 + VPSRLQ $0x20, Y5, Y5 + VPBLENDD $0xaa, Y7, Y5, Y7 + VMOVDQA Y8, Y5 + VPSLLD $0x10, Y1, Y8 + VPBLENDW $0xaa, Y8, Y0, Y8 + VPSRLD $0x10, Y0, Y0 + VPBLENDW $0xaa, Y1, Y0, Y1 + VMOVDQA Y8, Y0 + VPSLLD $0x10, Y3, Y8 + VPBLENDW $0xaa, Y8, Y2, Y8 + VPSRLD $0x10, Y2, Y2 + VPBLENDW $0xaa, Y3, Y2, Y3 + VMOVDQA Y8, Y2 + VPSLLD $0x10, Y5, Y8 + VPBLENDW $0xaa, Y8, Y4, Y8 + VPSRLD $0x10, Y4, Y4 + VPBLENDW $0xaa, Y5, Y4, Y5 + VMOVDQA Y8, Y4 + VPSLLD $0x10, Y7, Y8 + VPBLENDW $0xaa, Y8, Y6, Y8 + VPSRLD $0x10, Y6, Y6 + VPBLENDW $0xaa, Y7, Y6, Y7 + VMOVDQA Y8, Y6 + VMOVDQU Y0, 256(AX) + VMOVDQU Y1, 288(AX) + VMOVDQU Y2, 320(AX) + VMOVDQU Y3, 352(AX) + VMOVDQU Y4, 384(AX) + VMOVDQU Y5, 416(AX) + VMOVDQU Y6, 448(AX) + VMOVDQU Y7, 480(AX) + RET + +// func barrettReduceAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·barrettReduceAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + MOVL $0x00000d01, CX + VMOVD CX, X0 + VPBROADCASTW X0, Y9 + MOVL $0x00004ebf, CX + VMOVD CX, X0 + VPBROADCASTW X0, Y8 + VMOVDQU (AX), Y0 + VMOVDQU 32(AX), Y1 + VMOVDQU 64(AX), Y2 + VMOVDQU 96(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VMOVDQU Y0, (AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VMOVDQU 128(AX), Y0 + VMOVDQU 160(AX), Y1 + VMOVDQU 192(AX), Y2 + VMOVDQU 224(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VMOVDQU Y0, 128(AX) + VMOVDQU Y1, 160(AX) + VMOVDQU Y2, 192(AX) + VMOVDQU Y3, 224(AX) + VMOVDQU 256(AX), Y0 + VMOVDQU 288(AX), Y1 + VMOVDQU 320(AX), Y2 + VMOVDQU 352(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VMOVDQU Y0, 256(AX) + VMOVDQU Y1, 288(AX) + VMOVDQU Y2, 320(AX) + VMOVDQU Y3, 352(AX) + VMOVDQU 384(AX), Y0 + VMOVDQU 416(AX), Y1 + VMOVDQU 448(AX), Y2 + VMOVDQU 480(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VMOVDQU Y0, 384(AX) + VMOVDQU Y1, 416(AX) + VMOVDQU Y2, 448(AX) + VMOVDQU Y3, 480(AX) + RET + +// func normalizeAVX2(p *[256]int16) +// Requires: AVX, AVX2 +TEXT ·normalizeAVX2(SB), NOSPLIT, $0-8 + MOVQ p+0(FP), AX + MOVL $0x00000d01, CX + VMOVD CX, X0 + VPBROADCASTW X0, Y9 + MOVL $0x00004ebf, CX + VMOVD CX, X0 + VPBROADCASTW X0, Y8 + VMOVDQU (AX), Y0 + VMOVDQU 32(AX), Y1 + VMOVDQU 64(AX), Y2 + VMOVDQU 96(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VPSUBW Y9, Y0, Y0 + VPSUBW Y9, Y1, Y1 + VPSUBW Y9, Y2, Y2 + VPSUBW Y9, Y3, Y3 + VPSRAW $0x0f, Y0, Y4 + VPSRAW $0x0f, Y1, Y5 + VPSRAW $0x0f, Y2, Y6 + VPSRAW $0x0f, Y3, Y7 + VPAND Y4, Y9, Y4 + VPAND Y5, Y9, Y5 + VPAND Y6, Y9, Y6 + VPAND Y7, Y9, Y7 + VPADDW Y0, Y4, Y0 + VPADDW Y1, Y5, Y1 + VPADDW Y2, Y6, Y2 + VPADDW Y3, Y7, Y3 + VMOVDQU Y0, (AX) + VMOVDQU Y1, 32(AX) + VMOVDQU Y2, 64(AX) + VMOVDQU Y3, 96(AX) + VMOVDQU 128(AX), Y0 + VMOVDQU 160(AX), Y1 + VMOVDQU 192(AX), Y2 + VMOVDQU 224(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VPSUBW Y9, Y0, Y0 + VPSUBW Y9, Y1, Y1 + VPSUBW Y9, Y2, Y2 + VPSUBW Y9, Y3, Y3 + VPSRAW $0x0f, Y0, Y4 + VPSRAW $0x0f, Y1, Y5 + VPSRAW $0x0f, Y2, Y6 + VPSRAW $0x0f, Y3, Y7 + VPAND Y4, Y9, Y4 + VPAND Y5, Y9, Y5 + VPAND Y6, Y9, Y6 + VPAND Y7, Y9, Y7 + VPADDW Y0, Y4, Y0 + VPADDW Y1, Y5, Y1 + VPADDW Y2, Y6, Y2 + VPADDW Y3, Y7, Y3 + VMOVDQU Y0, 128(AX) + VMOVDQU Y1, 160(AX) + VMOVDQU Y2, 192(AX) + VMOVDQU Y3, 224(AX) + VMOVDQU 256(AX), Y0 + VMOVDQU 288(AX), Y1 + VMOVDQU 320(AX), Y2 + VMOVDQU 352(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VPSUBW Y9, Y0, Y0 + VPSUBW Y9, Y1, Y1 + VPSUBW Y9, Y2, Y2 + VPSUBW Y9, Y3, Y3 + VPSRAW $0x0f, Y0, Y4 + VPSRAW $0x0f, Y1, Y5 + VPSRAW $0x0f, Y2, Y6 + VPSRAW $0x0f, Y3, Y7 + VPAND Y4, Y9, Y4 + VPAND Y5, Y9, Y5 + VPAND Y6, Y9, Y6 + VPAND Y7, Y9, Y7 + VPADDW Y0, Y4, Y0 + VPADDW Y1, Y5, Y1 + VPADDW Y2, Y6, Y2 + VPADDW Y3, Y7, Y3 + VMOVDQU Y0, 256(AX) + VMOVDQU Y1, 288(AX) + VMOVDQU Y2, 320(AX) + VMOVDQU Y3, 352(AX) + VMOVDQU 384(AX), Y0 + VMOVDQU 416(AX), Y1 + VMOVDQU 448(AX), Y2 + VMOVDQU 480(AX), Y3 + VPMULHW Y8, Y0, Y4 + VPMULHW Y8, Y1, Y5 + VPMULHW Y8, Y2, Y6 + VPMULHW Y8, Y3, Y7 + VPSRAW $0x0a, Y4, Y4 + VPSRAW $0x0a, Y5, Y5 + VPSRAW $0x0a, Y6, Y6 + VPSRAW $0x0a, Y7, Y7 + VPMULLW Y9, Y4, Y4 + VPMULLW Y9, Y5, Y5 + VPMULLW Y9, Y6, Y6 + VPMULLW Y9, Y7, Y7 + VPSUBW Y4, Y0, Y0 + VPSUBW Y5, Y1, Y1 + VPSUBW Y6, Y2, Y2 + VPSUBW Y7, Y3, Y3 + VPSUBW Y9, Y0, Y0 + VPSUBW Y9, Y1, Y1 + VPSUBW Y9, Y2, Y2 + VPSUBW Y9, Y3, Y3 + VPSRAW $0x0f, Y0, Y4 + VPSRAW $0x0f, Y1, Y5 + VPSRAW $0x0f, Y2, Y6 + VPSRAW $0x0f, Y3, Y7 + VPAND Y4, Y9, Y4 + VPAND Y5, Y9, Y5 + VPAND Y6, Y9, Y6 + VPAND Y7, Y9, Y7 + VPADDW Y0, Y4, Y0 + VPADDW Y1, Y5, Y1 + VPADDW Y2, Y6, Y2 + VPADDW Y3, Y7, Y3 + VMOVDQU Y0, 384(AX) + VMOVDQU Y1, 416(AX) + VMOVDQU Y2, 448(AX) + VMOVDQU Y3, 480(AX) + RET diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/field.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/field.go new file mode 100644 index 00000000..33744dff --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/field.go @@ -0,0 +1,74 @@ +package common + +// Given -2¹⁵ q ≤ x < 2¹⁵ q, returns -q < y < q with x 2⁻¹⁶ = y (mod q). +func montReduce(x int32) int16 { + // This is Montgomery reduction with R=2¹⁶. + // + // Note gcd(2¹⁶, q) = 1 as q is prime. Write q' := 62209 = q⁻¹ mod R. + // First we compute + // + // m := ((x mod R) q') mod R + // = x q' mod R + // = int16(x q') + // = int16(int32(x) * int32(q')) + // + // Note that x q' might be as big as 2³² and could overflow the int32 + // multiplication in the last line. However for any int32s a and b, + // we have int32(int64(a)*int64(b)) = int32(a*b) and so the result is ok. + m := int16(x * 62209) + + // Note that x - m q is divisable by R; indeed modulo R we have + // + // x - m q ≡ x - x q' q ≡ x - x q⁻¹ q ≡ x - x = 0. + // + // We return y := (x - m q) / R. Note that y is indeed correct as + // modulo q we have + // + // y ≡ x R⁻¹ - m q R⁻¹ = x R⁻¹ + // + // and as both 2¹⁵ q ≤ m q, x < 2¹⁵ q, we have + // 2¹⁶ q ≤ x - m q < 2¹⁶ and so q ≤ (x - m q) / R < q as desired. + return int16(uint32(x-int32(m)*int32(Q)) >> 16) +} + +// Given any x, returns x R mod q where R=2¹⁶. +func toMont(x int16) int16 { + // Note |1353 x| ≤ 1353 2¹⁵ ≤ 13318 q ≤ 2¹⁵ q and so we're within + // the bounds of montReduce. + return montReduce(int32(x) * 1353) // 1353 = R² mod q. +} + +// Given any x, compute 0 ≤ y ≤ q with x = y (mod q). +// +// Beware: we might have barrettReduce(x) = q ≠ 0 for some x. In fact, +// this happens if and only if x = -nq for some positive integer n. +func barrettReduce(x int16) int16 { + // This is standard Barrett reduction. + // + // For any x we have x mod q = x - ⌊x/q⌋ q. We will use 20159/2²⁶ as + // an approximation of 1/q. Note that 0 ≤ 20159/2²⁶ - 1/q ≤ 0.135/2²⁶ + // and so | x 20156/2²⁶ - x/q | ≤ 2⁻¹⁰ for |x| ≤ 2¹⁶. For all x + // not a multiple of q, the number x/q is further than 1/q from any integer + // and so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋. If x is a multiple of q and x is positive, + // then x 20156/2²⁶ is larger than x/q so ⌊x 20156/2²⁶⌋ = ⌊x/q⌋ as well. + // Finally, if x is negative multiple of q, then ⌊x 20156/2²⁶⌋ = ⌊x/q⌋-1. + // Thus + // [ q if x=-nq for pos. integer n + // x - ⌊x 20156/2²⁶⌋ q = [ + // [ x mod q otherwise + // + // To compute actually compute this, note that + // + // ⌊x 20156/2²⁶⌋ = (20159 x) >> 26. + return x - int16((int32(x)*20159)>>26)*Q +} + +// Returns x if x < q and x - q otherwise. Assumes x ≥ -29439. +func csubq(x int16) int16 { + x -= Q // no overflow due to assumption x ≥ -29439. + // If x is positive, then x >> 15 = 0. If x is negative, + // then uint16(x >> 15) = 2¹⁶-1. So this will add back in q + // if x was smaller than q. + x += (x >> 15) & Q + return x +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/generic.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/generic.go new file mode 100644 index 00000000..2b742b95 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/generic.go @@ -0,0 +1,77 @@ +//go:build !amd64 +// +build !amd64 + +package common + +// Sets p to a + b. Does not normalize coefficients. +func (p *Poly) Add(a, b *Poly) { + p.addGeneric(a, b) +} + +// Sets p to a - b. Does not normalize coefficients. +func (p *Poly) Sub(a, b *Poly) { + p.subGeneric(a, b) +} + +// Executes an in-place forward "NTT" on p. +// +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤7q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity of the NTT) +// if the input is in regular form, then the result is also in regular form. +// The order of coefficients will be "tangled". These can be put back into +// their proper order by calling Detangle(). +func (p *Poly) NTT() { + p.nttGeneric() +} + +// Executes an in-place inverse "NTT" on p and multiply by the Montgomery +// factor R. +// +// Requires coefficients to be in "tangled" order, see Tangle(). +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity) +// if the input is in regular form, then the result is also in regular form. +func (p *Poly) InvNTT() { + p.invNTTGeneric() +} + +// Sets p to the "pointwise" multiplication of a and b. +// +// That is: InvNTT(p) = InvNTT(a) * InvNTT(b). Assumes a and b are in +// Montgomery form. Products between coefficients of a and b must be strictly +// bounded in absolute value by 2¹⁵q. p will be in Montgomery form and +// bounded in absolute value by 2q. +// +// Requires a and b to be in "tangled" order, see Tangle(). p will be in +// tangled order as well. +func (p *Poly) MulHat(a, b *Poly) { + p.mulHatGeneric(a, b) +} + +// Puts p into the right form to be used with (among others) InvNTT(). +func (p *Poly) Tangle() { + // In the generic implementation there is no advantage to using a + // different order, so we use the standard order everywhere. +} + +// Puts p back into standard form. +func (p *Poly) Detangle() { + // In the generic implementation there is no advantage to using a + // different order, so we use the standard order everywhere. +} + +// Almost normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q}. +func (p *Poly) BarrettReduce() { + p.barrettReduceGeneric() +} + +// Normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q-1}. +func (p *Poly) Normalize() { + p.normalizeGeneric() +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/ntt.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/ntt.go new file mode 100644 index 00000000..c1abaf23 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/ntt.go @@ -0,0 +1,193 @@ +package common + +// Zetas lists precomputed powers of the primitive root of unity in +// Montgomery representation used for the NTT: +// +// Zetas[i] = ζᵇʳᵛ⁽ⁱ⁾ R mod q +// +// where ζ = 17, brv(i) is the bitreversal of a 7-bit number and R=2¹⁶ mod q. +// +// The following Python code generates the Zetas arrays: +// +// q = 13*2**8 + 1; zeta = 17 +// R = 2**16 % q # Montgomery const. +// def brv(x): return int(''.join(reversed(bin(x)[2:].zfill(7))),2) +// print([(pow(zeta, brv(i), q)*R)%q for i in range(128)]) +var Zetas = [128]int16{ + 2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, + 962, 2127, 1855, 1468, 573, 2004, 264, 383, 2500, 1458, 1727, 3199, + 2648, 1017, 732, 608, 1787, 411, 3124, 1758, 1223, 652, 2777, 1015, + 2036, 1491, 3047, 1785, 516, 3321, 3009, 2663, 1711, 2167, 126, + 1469, 2476, 3239, 3058, 830, 107, 1908, 3082, 2378, 2931, 961, 1821, + 2604, 448, 2264, 677, 2054, 2226, 430, 555, 843, 2078, 871, 1550, + 105, 422, 587, 177, 3094, 3038, 2869, 1574, 1653, 3083, 778, 1159, + 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349, 418, 329, 3173, + 3254, 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193, 1218, + 1994, 2455, 220, 2142, 1670, 2144, 1799, 2051, 794, 1819, 2475, + 2459, 478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628, +} + +// InvNTTReductions keeps track of which coefficients to apply Barrett +// reduction to in Poly.InvNTT(). +// +// Generated in a lazily: once a butterfly is computed which is about to +// overflow the int16, the largest coefficient is reduced. If that is +// not enough, the other coefficient is reduced as well. +// +// This is actually optimal, as proven in https://eprint.iacr.org/2020/1377.pdf +var InvNTTReductions = [...]int{ + -1, // after layer 1 + -1, // after layer 2 + 16, 17, 48, 49, 80, 81, 112, 113, 144, 145, 176, 177, 208, 209, 240, + 241, -1, // after layer 3 + 0, 1, 32, 33, 34, 35, 64, 65, 96, 97, 98, 99, 128, 129, 160, 161, 162, 163, + 192, 193, 224, 225, 226, 227, -1, // after layer 4 + 2, 3, 66, 67, 68, 69, 70, 71, 130, 131, 194, 195, 196, 197, 198, + 199, -1, // after layer 5 + 4, 5, 6, 7, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, -1, // after layer 6 + -1, // after layer 7 +} + +// Executes an in-place forward "NTT" on p. +// +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤7q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity of the NTT) +// if the input is in regular form, then the result is also in regular form. +// The order of coefficients will be "tangled". These can be put back into +// their proper order by calling Detangle(). +func (p *Poly) nttGeneric() { + // Note that ℤ_q does not have a primitive 512ᵗʰ root of unity (as 512 + // does not divide into q-1) and so we cannot do a regular NTT. ℤ_q + // does have a primitive 256ᵗʰ root of unity, the smallest of which + // is ζ := 17. + // + // Recall that our base ring R := ℤ_q[x] / (x²⁵⁶ + 1). The polynomial + // x²⁵⁶+1 will not split completely (as its roots would be 512ᵗʰ roots + // of unity.) However, it does split almost (using ζ¹²⁸ = -1): + // + // x²⁵⁶ + 1 = (x²)¹²⁸ - ζ¹²⁸ + // = ((x²)⁶⁴ - ζ⁶⁴)((x²)⁶⁴ + ζ⁶⁴) + // = ((x²)³² - ζ³²)((x²)³² + ζ³²)((x²)³² - ζ⁹⁶)((x²)³² + ζ⁹⁶) + // ⋮ + // = (x² - ζ)(x² + ζ)(x² - ζ⁶⁵)(x² + ζ⁶⁵) … (x² + ζ¹²⁷) + // + // Note that the powers of ζ that appear (from the second line down) are + // in binary + // + // 0100000 1100000 + // 0010000 1010000 0110000 1110000 + // 0001000 1001000 0101000 1101000 0011000 1011000 0111000 1111000 + // … + // + // That is: brv(2), brv(3), brv(4), …, where brv(x) denotes the 7-bit + // bitreversal of x. These powers of ζ are given by the Zetas array. + // + // The polynomials x² ± ζⁱ are irreducible and coprime, hence by + // the Chinese Remainder Theorem we know + // + // ℤ_q[x]/(x²⁵⁶+1) → ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷) + // + // given by a ↦ ( a mod x²-ζ, …, a mod x²+ζ¹²⁷ ) + // is an isomorphism, which is the "NTT". It can be efficiently computed by + // + // + // a ↦ ( a mod (x²)⁶⁴ - ζ⁶⁴, a mod (x²)⁶⁴ + ζ⁶⁴ ) + // ↦ ( a mod (x²)³² - ζ³², a mod (x²)³² + ζ³², + // a mod (x²)⁹⁶ - ζ⁹⁶, a mod (x²)⁹⁶ + ζ⁹⁶ ) + // + // et cetera + // + // If N was 8 then this can be pictured in the following diagram: + // + // https://cnx.org/resources/17ee4dfe517a6adda05377b25a00bf6e6c93c334/File0026.png + // + // Each cross is a Cooley-Tukey butterfly: it's the map + // + // (a, b) ↦ (a + ζb, a - ζb) + // + // for the appropriate power ζ for that column and row group. + + k := 0 // Index into Zetas + + // l runs effectively over the columns in the diagram above; it is half the + // height of a row group, i.e. the number of butterflies in each row group. + // In the diagram above it would be 4, 2, 1. + for l := N / 2; l > 1; l >>= 1 { + // On the nᵗʰ iteration of the l-loop, the absolute value of the + // coefficients are bounded by nq. + + // offset effectively loops over the row groups in this column; it is + // the first row in the row group. + for offset := 0; offset < N-l; offset += 2 * l { + k++ + zeta := int32(Zetas[k]) + + // j loops over each butterfly in the row group. + for j := offset; j < offset+l; j++ { + t := montReduce(zeta * int32(p[j+l])) + p[j+l] = p[j] - t + p[j] += t + } + } + } +} + +// Executes an in-place inverse "NTT" on p and multiply by the Montgomery +// factor R. +// +// Requires coefficients to be in "tangled" order, see Tangle(). +// Assumes the coefficients are in absolute value ≤q. The resulting +// coefficients are in absolute value ≤q. If the input is in Montgomery +// form, then the result is in Montgomery form and so (by linearity) +// if the input is in regular form, then the result is also in regular form. +func (p *Poly) invNTTGeneric() { + k := 127 // Index into Zetas + r := -1 // Index into InvNTTReductions. + + // We basically do the oppposite of NTT, but postpone dividing by 2 in the + // inverse of the Cooley-Tukey butterfly and accumulate that into a big + // division by 2⁷ at the end. See the comments in the NTT() function. + + for l := 2; l < N; l <<= 1 { + for offset := 0; offset < N-l; offset += 2 * l { + // As we're inverting, we need powers of ζ⁻¹ (instead of ζ). + // To be precise, we need ζᵇʳᵛ⁽ᵏ⁾⁻¹²⁸. However, as ζ⁻¹²⁸ = -1, + // we can use the existing Zetas table instead of + // keeping a separate InvZetas table as in Dilithium. + + minZeta := int32(Zetas[k]) + k-- + + for j := offset; j < offset+l; j++ { + // Gentleman-Sande butterfly: (a, b) ↦ (a + b, ζ(a-b)) + t := p[j+l] - p[j] + p[j] += p[j+l] + p[j+l] = montReduce(minZeta * int32(t)) + + // Note that if we had |a| < αq and |b| < βq before the + // butterfly, then now we have |a| < (α+β)q and |b| < q. + } + } + + // We let the InvNTTReductions instruct us which coefficients to + // Barrett reduce. See TestInvNTTReductions, which tests whether + // there is an overflow. + for { + r++ + i := InvNTTReductions[r] + if i < 0 { + break + } + p[i] = barrettReduce(p[i]) + } + } + + for j := 0; j < N; j++ { + // Note 1441 = (128)⁻¹ R². The coefficients are bounded by 9q, so + // as 1441 * 9 ≈ 2¹⁴ < 2¹⁵, we're within the required bounds + // for montReduce(). + p[j] = montReduce(1441 * int32(p[j])) + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params.go new file mode 100644 index 00000000..f04d1aaa --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params.go @@ -0,0 +1,22 @@ +package common + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common/params" +) + +const ( + // Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1. + Q = params.Q + + // N is the parameter N: the length of the polynomials + N = params.N + + // PolySize is the size of a packed polynomial. + PolySize = params.PolySize + + // PlaintextSize is the size of the plaintext + PlaintextSize = params.PlaintextSize + + // Eta2 is the parameter η₂ + Eta2 = params.Eta2 +) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params/params.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params/params.go new file mode 100644 index 00000000..f7fe31ab --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/params/params.go @@ -0,0 +1,21 @@ +package params + +// We put these parameters in a separate package so that the Go code, +// such as asm/src.go, that generates assembler can import it. + +const ( + // Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1. + Q int16 = 3329 + + // N is the parameter N: the length of the polynomials + N int = 256 + + // PolySize is the size of a packed polynomial. + PolySize int = 384 + + // PlaintextSize is the size of the plaintext + PlaintextSize = 32 + + // Eta2 is the parameter η₂ + Eta2 = 2 +) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/poly.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/poly.go new file mode 100644 index 00000000..f6842152 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/poly.go @@ -0,0 +1,324 @@ +package common + +// An element of our base ring R which are polynomials over ℤ_q +// modulo the equation Xᴺ = -1, where q=3329 and N=256. +// +// This type is also used to store NTT-transformed polynomials, +// see Poly.NTT(). +// +// Coefficients aren't always reduced. See Normalize(). +type Poly [N]int16 + +// Sets p to a + b. Does not normalize coefficients. +func (p *Poly) addGeneric(a, b *Poly) { + for i := 0; i < N; i++ { + p[i] = a[i] + b[i] + } +} + +// Sets p to a - b. Does not normalize coefficients. +func (p *Poly) subGeneric(a, b *Poly) { + for i := 0; i < N; i++ { + p[i] = a[i] - b[i] + } +} + +// Almost normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q}. +func (p *Poly) barrettReduceGeneric() { + for i := 0; i < N; i++ { + p[i] = barrettReduce(p[i]) + } +} + +// Normalizes coefficients. +// +// Ensures each coefficient is in {0, …, q-1}. +func (p *Poly) normalizeGeneric() { + for i := 0; i < N; i++ { + p[i] = csubq(barrettReduce(p[i])) + } +} + +// Multiplies p in-place by the Montgomery factor 2¹⁶. +// +// Coefficients of p can be artbitray. Resulting coefficients are bounded +// in absolute value by q. +func (p *Poly) ToMont() { + for i := 0; i < N; i++ { + p[i] = toMont(p[i]) + } +} + +// Sets p to the "pointwise" multiplication of a and b. +// +// That is: InvNTT(p) = InvNTT(a) * InvNTT(b). Assumes a and b are in +// Montgomery form. Products between coefficients of a and b must be strictly +// bounded in absolute value by 2¹⁵q. p will be in Montgomery form and +// bounded in absolute value by 2q. +// +// Requires a and b to be in "tangled" order, see Tangle(). p will be in +// tangled order as well. +func (p *Poly) mulHatGeneric(a, b *Poly) { + // Recall from the discussion in NTT(), that a transformed polynomial is + // an element of ℤ_q[x]/(x²-ζ) x … x ℤ_q[x]/(x²+ζ¹²⁷); + // that is: 128 degree-one polynomials instead of simply 256 elements + // from ℤ_q as in the regular NTT. So instead of pointwise multiplication, + // we multiply the 128 pairs of degree-one polynomials modulo the + // right equation: + // + // (a₁ + a₂x)(b₁ + b₂x) = a₁b₁ + a₂b₂ζ' + (a₁b₂ + a₂b₁)x, + // + // where ζ' is the appropriate power of ζ. + + k := 64 + for i := 0; i < N; i += 4 { + zeta := int32(Zetas[k]) + k++ + + p0 := montReduce(int32(a[i+1]) * int32(b[i+1])) + p0 = montReduce(int32(p0) * zeta) + p0 += montReduce(int32(a[i]) * int32(b[i])) + + p1 := montReduce(int32(a[i]) * int32(b[i+1])) + p1 += montReduce(int32(a[i+1]) * int32(b[i])) + + p[i] = p0 + p[i+1] = p1 + + p2 := montReduce(int32(a[i+3]) * int32(b[i+3])) + p2 = -montReduce(int32(p2) * zeta) + p2 += montReduce(int32(a[i+2]) * int32(b[i+2])) + + p3 := montReduce(int32(a[i+2]) * int32(b[i+3])) + p3 += montReduce(int32(a[i+3]) * int32(b[i+2])) + + p[i+2] = p2 + p[i+3] = p3 + } +} + +// Packs p into buf. buf should be of length PolySize. +// +// Assumes p is normalized (and not just Barrett reduced) and "tangled", +// see Tangle(). +func (p *Poly) Pack(buf []byte) { + q := *p + q.Detangle() + for i := 0; i < 128; i++ { + t0 := q[2*i] + t1 := q[2*i+1] + buf[3*i] = byte(t0) + buf[3*i+1] = byte(t0>>8) | byte(t1<<4) + buf[3*i+2] = byte(t1 >> 4) + } +} + +// Unpacks p from buf. +// +// buf should be of length PolySize. p will be "tangled", see Detangle(). +// +// p will not be normalized; instead 0 ≤ p[i] < 4096. +func (p *Poly) Unpack(buf []byte) { + for i := 0; i < 128; i++ { + p[2*i] = int16(buf[3*i]) | ((int16(buf[3*i+1]) << 8) & 0xfff) + p[2*i+1] = int16(buf[3*i+1]>>4) | (int16(buf[3*i+2]) << 4) + } + p.Tangle() +} + +// Set p to Decompress_q(m, 1). +// +// p will be normalized. m has to be of PlaintextSize. +func (p *Poly) DecompressMessage(m []byte) { + // Decompress_q(x, 1) = ⌈xq/2⌋ = ⌊xq/2+½⌋ = (xq+1) >> 1 and so + // Decompress_q(0, 1) = 0 and Decompress_q(1, 1) = (q+1)/2. + for i := 0; i < 32; i++ { + for j := 0; j < 8; j++ { + bit := (m[i] >> uint(j)) & 1 + + // Set coefficient to either 0 or (q+1)/2 depending on the bit. + p[8*i+j] = -int16(bit) & ((Q + 1) / 2) + } + } +} + +// Writes Compress_q(p, 1) to m. +// +// Assumes p is normalized. m has to be of length at least PlaintextSize. +func (p *Poly) CompressMessageTo(m []byte) { + // Compress_q(x, 1) is 1 on {833, …, 2496} and zero elsewhere. + for i := 0; i < 32; i++ { + m[i] = 0 + for j := 0; j < 8; j++ { + x := 1664 - p[8*i+j] + // With the previous substitution, we want to return 1 if + // and only if x is in {831, …, -832}. + x = (x >> 15) ^ x + // Note (x >> 15)ˣ if x≥0 and -x-1 otherwise. Thus now we want + // to return 1 iff x ≤ 831, ie. x - 832 < 0. + x -= 832 + m[i] |= ((byte(x >> 15)) & 1) << uint(j) + } + } +} + +// Set p to Decompress_q(m, 1). +// +// Assumes d is in {3, 4, 5, 10, 11}. p will be normalized. +func (p *Poly) Decompress(m []byte, d int) { + // Decompress_q(x, d) = ⌈(q/2ᵈ)x⌋ + // = ⌊(q/2ᵈ)x+½⌋ + // = ⌊(qx + 2ᵈ⁻¹)/2ᵈ⌋ + // = (qx + (1<<(d-1))) >> d + switch d { + case 4: + for i := 0; i < N/2; i++ { + p[2*i] = int16(((1 << 3) + + uint32(m[i]&15)*uint32(Q)) >> 4) + p[2*i+1] = int16(((1 << 3) + + uint32(m[i]>>4)*uint32(Q)) >> 4) + } + case 5: + var t [8]uint16 + idx := 0 + for i := 0; i < N/8; i++ { + t[0] = uint16(m[idx]) + t[1] = (uint16(m[idx]) >> 5) | (uint16(m[idx+1] << 3)) + t[2] = uint16(m[idx+1]) >> 2 + t[3] = (uint16(m[idx+1]) >> 7) | (uint16(m[idx+2] << 1)) + t[4] = (uint16(m[idx+2]) >> 4) | (uint16(m[idx+3] << 4)) + t[5] = uint16(m[idx+3]) >> 1 + t[6] = (uint16(m[idx+3]) >> 6) | (uint16(m[idx+4] << 2)) + t[7] = uint16(m[idx+4]) >> 3 + + for j := 0; j < 8; j++ { + p[8*i+j] = int16(((1 << 4) + + uint32(t[j]&((1<<5)-1))*uint32(Q)) >> 5) + } + + idx += 5 + } + + case 10: + var t [4]uint16 + idx := 0 + for i := 0; i < N/4; i++ { + t[0] = uint16(m[idx]) | (uint16(m[idx+1]) << 8) + t[1] = (uint16(m[idx+1]) >> 2) | (uint16(m[idx+2]) << 6) + t[2] = (uint16(m[idx+2]) >> 4) | (uint16(m[idx+3]) << 4) + t[3] = (uint16(m[idx+3]) >> 6) | (uint16(m[idx+4]) << 2) + + for j := 0; j < 4; j++ { + p[4*i+j] = int16(((1 << 9) + + uint32(t[j]&((1<<10)-1))*uint32(Q)) >> 10) + } + + idx += 5 + } + case 11: + var t [8]uint16 + idx := 0 + for i := 0; i < N/8; i++ { + t[0] = uint16(m[idx]) | (uint16(m[idx+1]) << 8) + t[1] = (uint16(m[idx+1]) >> 3) | (uint16(m[idx+2]) << 5) + t[2] = (uint16(m[idx+2]) >> 6) | (uint16(m[idx+3]) << 2) | (uint16(m[idx+4]) << 10) + t[3] = (uint16(m[idx+4]) >> 1) | (uint16(m[idx+5]) << 7) + t[4] = (uint16(m[idx+5]) >> 4) | (uint16(m[idx+6]) << 4) + t[5] = (uint16(m[idx+6]) >> 7) | (uint16(m[idx+7]) << 1) | (uint16(m[idx+8]) << 9) + t[6] = (uint16(m[idx+8]) >> 2) | (uint16(m[idx+9]) << 6) + t[7] = (uint16(m[idx+9]) >> 5) | (uint16(m[idx+10]) << 3) + + for j := 0; j < 8; j++ { + p[8*i+j] = int16(((1 << 10) + + uint32(t[j]&((1<<11)-1))*uint32(Q)) >> 11) + } + + idx += 11 + } + default: + panic("unsupported d") + } +} + +// Writes Compress_q(p, d) to m. +// +// Assumes p is normalized and d is in {3, 4, 5, 10, 11}. +func (p *Poly) CompressTo(m []byte, d int) { + // Compress_q(x, d) = ⌈(2ᵈ/q)x⌋ mod⁺ 2ᵈ + // = ⌊(2ᵈ/q)x+½⌋ mod⁺ 2ᵈ + // = ⌊((x << d) + q/2) / q⌋ mod⁺ 2ᵈ + // = DIV((x << d) + q/2, q) & ((1<>3) | byte(t[2]<<2) | byte(t[3]<<7) + m[idx+2] = byte(t[3]>>1) | byte(t[4]<<4) + m[idx+3] = byte(t[4]>>4) | byte(t[5]<<1) | byte(t[6]<<6) + m[idx+4] = byte(t[6]>>2) | byte(t[7]<<3) + idx += 5 + } + + case 10: + var t [4]uint16 + idx := 0 + for i := 0; i < N/4; i++ { + for j := 0; j < 4; j++ { + t[j] = uint16(((uint32(p[4*i+j])<<10)+uint32(Q)/2)/ + uint32(Q)) & ((1 << 10) - 1) + } + m[idx] = byte(t[0]) + m[idx+1] = byte(t[0]>>8) | byte(t[1]<<2) + m[idx+2] = byte(t[1]>>6) | byte(t[2]<<4) + m[idx+3] = byte(t[2]>>4) | byte(t[3]<<6) + m[idx+4] = byte(t[3] >> 2) + idx += 5 + } + case 11: + var t [8]uint16 + idx := 0 + for i := 0; i < N/8; i++ { + for j := 0; j < 8; j++ { + t[j] = uint16(((uint32(p[8*i+j])<<11)+uint32(Q)/2)/ + uint32(Q)) & ((1 << 11) - 1) + } + m[idx] = byte(t[0]) + m[idx+1] = byte(t[0]>>8) | byte(t[1]<<3) + m[idx+2] = byte(t[1]>>5) | byte(t[2]<<6) + m[idx+3] = byte(t[2] >> 2) + m[idx+4] = byte(t[2]>>10) | byte(t[3]<<1) + m[idx+5] = byte(t[3]>>7) | byte(t[4]<<4) + m[idx+6] = byte(t[4]>>4) | byte(t[5]<<7) + m[idx+7] = byte(t[5] >> 1) + m[idx+8] = byte(t[5]>>9) | byte(t[6]<<2) + m[idx+9] = byte(t[6]>>6) | byte(t[7]<<5) + m[idx+10] = byte(t[7] >> 3) + idx += 11 + } + default: + panic("unsupported d") + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/sample.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/sample.go new file mode 100644 index 00000000..1f15f32c --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/sample.go @@ -0,0 +1,236 @@ +package common + +import ( + "encoding/binary" + + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/simd/keccakf1600" +) + +// DeriveX4Available indicates whether the system supports the quick fourway +// sampling variants like PolyDeriveUniformX4. +var DeriveX4Available = keccakf1600.IsEnabledX4() + +// Samples p from a centered binomial distribution with given η. +// +// Essentially CBD_η(PRF(seed, nonce)) from the specification. +func (p *Poly) DeriveNoise(seed []byte, nonce uint8, eta int) { + switch eta { + case 2: + p.DeriveNoise2(seed, nonce) + case 3: + p.DeriveNoise3(seed, nonce) + default: + panic("unsupported eta") + } +} + +// Sample p from a centered binomial distribution with n=6 and p=½ - that is: +// coefficients are in {-3, -2, -1, 0, 1, 2, 3} with probabilities {1/64, 3/32, +// 15/64, 5/16, 16/64, 3/32, 1/64}. +func (p *Poly) DeriveNoise3(seed []byte, nonce uint8) { + keySuffix := [1]byte{nonce} + h := sha3.NewShake256() + _, _ = h.Write(seed[:]) + _, _ = h.Write(keySuffix[:]) + + // The distribution at hand is exactly the same as that + // of (a₁ + a₂ + a₃) - (b₁ + b₂+b₃) where a_i,b_i~U(1). Thus we need + // 6 bits per coefficients, thus 192 bytes of input entropy. + + // We add two extra zero bytes in the buffer to be able to read 8 bytes + // at the same time (while using only 6.) + var buf [192 + 2]byte + _, _ = h.Read(buf[:192]) + + for i := 0; i < 32; i++ { + // t is interpreted as a₁ + 2a₂ + 4a₃ + 8b₁ + 16b₂ + …. + t := binary.LittleEndian.Uint64(buf[6*i:]) + + d := t & 0x249249249249 // a₁ + 8b₁ + … + d += (t >> 1) & 0x249249249249 // a₁ + a₂ + 8(b₁ + b₂) + … + d += (t >> 2) & 0x249249249249 // a₁ + a₂ + a₃ + 4(b₁ + b₂ + b₃) + … + + for j := 0; j < 8; j++ { + a := int16(d) & 0x7 // a₁ + a₂ + a₃ + d >>= 3 + b := int16(d) & 0x7 // b₁ + b₂ + b₃ + d >>= 3 + p[8*i+j] = a - b + } + } +} + +// Sample p from a centered binomial distribution with n=4 and p=½ - that is: +// coefficients are in {-2, -1, 0, 1, 2} with probabilities {1/16, 1/4, +// 3/8, 1/4, 1/16}. +func (p *Poly) DeriveNoise2(seed []byte, nonce uint8) { + keySuffix := [1]byte{nonce} + h := sha3.NewShake256() + _, _ = h.Write(seed[:]) + _, _ = h.Write(keySuffix[:]) + + // The distribution at hand is exactly the same as that + // of (a + a') - (b + b') where a,a',b,b'~U(1). Thus we need 4 bits per + // coefficients, thus 128 bytes of input entropy. + + var buf [128]byte + _, _ = h.Read(buf[:]) + + for i := 0; i < 16; i++ { + // t is interpreted as a + 2a' + 4b + 8b' + …. + t := binary.LittleEndian.Uint64(buf[8*i:]) + + d := t & 0x5555555555555555 // a + 4b + … + d += (t >> 1) & 0x5555555555555555 // a+a' + 4(b + b') + … + + for j := 0; j < 16; j++ { + a := int16(d) & 0x3 + d >>= 2 + b := int16(d) & 0x3 + d >>= 2 + p[16*i+j] = a - b + } + } +} + +// For each i, sample ps[i] uniformly from the given seed for coordinates +// xs[i] and ys[i]. ps[i] may be nil and is ignored in that case. +// +// Can only be called when DeriveX4Available is true. +func PolyDeriveUniformX4(ps [4]*Poly, seed *[32]byte, xs, ys [4]uint8) { + var perm keccakf1600.StateX4 + state := perm.Initialize() + + // Absorb the seed in the four states + for i := 0; i < 4; i++ { + v := binary.LittleEndian.Uint64(seed[8*i : 8*(i+1)]) + for j := 0; j < 4; j++ { + state[i*4+j] = v + } + } + + // Absorb the coordinates, the SHAKE128 domain separator (0b1111), the + // start of the padding (0b…001) and the end of the padding 0b100…. + // Recall that the rate of SHAKE128 is 168; ie. 21 uint64s. + for j := 0; j < 4; j++ { + state[4*4+j] = uint64(xs[j]) | (uint64(ys[j]) << 8) | (0x1f << 16) + state[20*4+j] = 0x80 << 56 + } + + var idx [4]int // indices into ps + for j := 0; j < 4; j++ { + if ps[j] == nil { + idx[j] = N // mark nil polynomials as completed + } + } + + done := false + for !done { + // Applies KeccaK-f[1600] to state to get the next 21 uint64s of each of + // the four SHAKE128 streams. + perm.Permute() + + done = true + + PolyLoop: + for j := 0; j < 4; j++ { + if idx[j] == N { + continue + } + for i := 0; i < 7; i++ { + var t [16]uint16 + + v1 := state[i*3*4+j] + v2 := state[(i*3+1)*4+j] + v3 := state[(i*3+2)*4+j] + + t[0] = uint16(v1) & 0xfff + t[1] = uint16(v1>>12) & 0xfff + t[2] = uint16(v1>>24) & 0xfff + t[3] = uint16(v1>>36) & 0xfff + t[4] = uint16(v1>>48) & 0xfff + t[5] = uint16((v1>>60)|(v2<<4)) & 0xfff + + t[6] = uint16(v2>>8) & 0xfff + t[7] = uint16(v2>>20) & 0xfff + t[8] = uint16(v2>>32) & 0xfff + t[9] = uint16(v2>>44) & 0xfff + t[10] = uint16((v2>>56)|(v3<<8)) & 0xfff + + t[11] = uint16(v3>>4) & 0xfff + t[12] = uint16(v3>>16) & 0xfff + t[13] = uint16(v3>>28) & 0xfff + t[14] = uint16(v3>>40) & 0xfff + t[15] = uint16(v3>>52) & 0xfff + + for k := 0; k < 16; k++ { + if t[k] < uint16(Q) { + ps[j][idx[j]] = int16(t[k]) + idx[j]++ + if idx[j] == N { + continue PolyLoop + } + } + } + } + + done = false + } + } + + for i := 0; i < 4; i++ { + if ps[i] != nil { + ps[i].Tangle() + } + } +} + +// Sample p uniformly from the given seed and x and y coordinates. +// +// Coefficients are reduced and will be in "tangled" order. See Tangle(). +func (p *Poly) DeriveUniform(seed *[32]byte, x, y uint8) { + var seedSuffix [2]byte + var buf [168]byte // rate of SHAKE-128 + + seedSuffix[0] = x + seedSuffix[1] = y + + h := sha3.NewShake128() + _, _ = h.Write(seed[:]) + _, _ = h.Write(seedSuffix[:]) + + i := 0 + for { + _, _ = h.Read(buf[:]) + + for j := 0; j < 168; j += 3 { + t1 := (uint16(buf[j]) | (uint16(buf[j+1]) << 8)) & 0xfff + t2 := (uint16(buf[j+1]>>4) | (uint16(buf[j+2]) << 4)) & 0xfff + + if t1 < uint16(Q) { + p[i] = int16(t1) + i++ + + if i == N { + break + } + } + + if t2 < uint16(Q) { + p[i] = int16(t2) + i++ + + if i == N { + break + } + } + } + + if i == N { + break + } + } + + p.Tangle() +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/stubs_amd64.go b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/stubs_amd64.go new file mode 100644 index 00000000..dd869993 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/internal/common/stubs_amd64.go @@ -0,0 +1,33 @@ +// Code generated by command: go run src.go -out ../amd64.s -stubs ../stubs_amd64.go -pkg common. DO NOT EDIT. + +//go:build amd64 +// +build amd64 + +package common + +//go:noescape +func addAVX2(p *[256]int16, a *[256]int16, b *[256]int16) + +//go:noescape +func subAVX2(p *[256]int16, a *[256]int16, b *[256]int16) + +//go:noescape +func nttAVX2(p *[256]int16) + +//go:noescape +func invNttAVX2(p *[256]int16) + +//go:noescape +func mulHatAVX2(p *[256]int16, a *[256]int16, b *[256]int16) + +//go:noescape +func detangleAVX2(p *[256]int16) + +//go:noescape +func tangleAVX2(p *[256]int16) + +//go:noescape +func barrettReduceAVX2(p *[256]int16) + +//go:noescape +func normalizeAVX2(p *[256]int16) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/cpapke.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/cpapke.go new file mode 100644 index 00000000..01ef88b2 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/cpapke.go @@ -0,0 +1,176 @@ +// Code generated from kyber512/internal/cpapke.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A Kyber.CPAPKE private key. +type PrivateKey struct { + sh Vec // NTT(s), normalized +} + +// A Kyber.CPAPKE public key. +type PublicKey struct { + rho [32]byte // ρ, the seed for the matrix A + th Vec // NTT(t), normalized + + // cached values + aT Mat // the matrix Aᵀ +} + +// Packs the private key to buf. +func (sk *PrivateKey) Pack(buf []byte) { + sk.sh.Pack(buf) +} + +// Unpacks the private key from buf. +func (sk *PrivateKey) Unpack(buf []byte) { + sk.sh.Unpack(buf) + sk.sh.Normalize() +} + +// Packs the public key to buf. +func (pk *PublicKey) Pack(buf []byte) { + pk.th.Pack(buf) + copy(buf[K*common.PolySize:], pk.rho[:]) +} + +// Unpacks the public key from buf. +func (pk *PublicKey) Unpack(buf []byte) { + pk.th.Unpack(buf) + pk.th.Normalize() + copy(pk.rho[:], buf[K*common.PolySize:]) + pk.aT.Derive(&pk.rho, true) +} + +// Derives a new Kyber.CPAPKE keypair from the given seed. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var pk PublicKey + var sk PrivateKey + + var expandedSeed [64]byte + + h := sha3.New512() + _, _ = h.Write(seed) + + // This writes hash into expandedSeed. Yes, this is idiomatic Go. + _, _ = h.Read(expandedSeed[:]) + + copy(pk.rho[:], expandedSeed[:32]) + sigma := expandedSeed[32:] // σ, the noise seed + + pk.aT.Derive(&pk.rho, false) // Expand ρ to matrix A; we'll transpose later + + var eh Vec + sk.sh.DeriveNoise(sigma, 0, Eta1) // Sample secret vector s + sk.sh.NTT() + sk.sh.Normalize() + + eh.DeriveNoise(sigma, K, Eta1) // Sample blind e + eh.NTT() + + // Next, we compute t = A s + e. + for i := 0; i < K; i++ { + // Note that coefficients of s are bounded by q and those of A + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&pk.th[i], &pk.aT[i], &sk.sh) + + // A and s were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // we'll cancel out now. This will also ensure the coefficients of + // t are bounded in absolute value by q. + pk.th[i].ToMont() + } + + pk.th.Add(&pk.th, &eh) // bounded by 8q. + pk.th.Normalize() + pk.aT.Transpose() + + return &pk, &sk +} + +// Decrypts ciphertext ct meant for private key sk to plaintext pt. +func (sk *PrivateKey) DecryptTo(pt, ct []byte) { + var u Vec + var v, m common.Poly + + u.Decompress(ct, DU) + v.Decompress(ct[K*compressedPolySize(DU):], DV) + + // Compute m = v - + u.NTT() + PolyDotHat(&m, &sk.sh, &u) + m.BarrettReduce() + m.InvNTT() + m.Sub(&v, &m) + m.Normalize() + + // Compress polynomial m to original message + m.CompressMessageTo(pt) +} + +// Encrypts message pt for the public key to ciphertext ct using randomness +// from seed. +// +// seed has to be of length SeedSize, pt of PlaintextSize and ct of +// CiphertextSize. +func (pk *PublicKey) EncryptTo(ct, pt, seed []byte) { + var rh, e1, u Vec + var e2, v, m common.Poly + + // Sample r, e₁ and e₂ from B_η + rh.DeriveNoise(seed, 0, Eta1) + rh.NTT() + rh.BarrettReduce() + + e1.DeriveNoise(seed, K, common.Eta2) + e2.DeriveNoise(seed, 2*K, common.Eta2) + + // Next we compute u = Aᵀ r + e₁. First Aᵀ. + for i := 0; i < K; i++ { + // Note that coefficients of r are bounded by q and those of Aᵀ + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&u[i], &pk.aT[i], &rh) + } + + u.BarrettReduce() + + // Aᵀ and r were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // the InvNTT cancels out. + u.InvNTT() + + u.Add(&u, &e1) // u = Aᵀ r + e₁ + + // Next compute v = + e₂ + Decompress_q(m, 1). + PolyDotHat(&v, &pk.th, &rh) + v.BarrettReduce() + v.InvNTT() + + m.DecompressMessage(pt) + v.Add(&v, &m) + v.Add(&v, &e2) // v = + e₂ + Decompress_q(m, 1) + + // Pack ciphertext + u.Normalize() + v.Normalize() + + u.CompressTo(ct, DU) + v.CompressTo(ct[K*compressedPolySize(DU):], DV) +} + +// Returns whether sk equals other. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + ret := int16(0) + for i := 0; i < K; i++ { + for j := 0; j < common.N; j++ { + ret |= sk.sh[i][j] ^ other.sh[i][j] + } + } + return ret == 0 +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/mat.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/mat.go new file mode 100644 index 00000000..e8a35aff --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/mat.go @@ -0,0 +1,85 @@ +// Code generated from kyber512/internal/mat.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A k by k matrix of polynomials. +type Mat [K]Vec + +// Expands the given seed to the corresponding matrix A or its transpose Aᵀ. +func (m *Mat) Derive(seed *[32]byte, transpose bool) { + if !common.DeriveX4Available { + if transpose { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(i), uint8(j)) + } + } + } else { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(j), uint8(i)) + } + } + } + return + } + + var ps [4]*common.Poly + var xs [4]uint8 + var ys [4]uint8 + x := uint8(0) + y := uint8(0) + + for x != K { + idx := 0 + for ; idx < 4; idx++ { + ps[idx] = &m[x][y] + + if transpose { + xs[idx] = x + ys[idx] = y + } else { + xs[idx] = y + ys[idx] = x + } + + y++ + if y == K { + x++ + y = 0 + + if x == K { + if idx == 0 { + // If there is just one left, then a plain DeriveUniform + // is quicker than the X4 variant. + ps[0].DeriveUniform(seed, xs[0], ys[0]) + return + } + + for idx++; idx < 4; idx++ { + ps[idx] = nil + } + + break + } + } + } + + common.PolyDeriveUniformX4(ps, seed, xs, ys) + } +} + +// Tranposes A in place. +func (m *Mat) Transpose() { + for i := 0; i < K-1; i++ { + for j := i + 1; j < K; j++ { + t := m[i][j] + m[i][j] = m[j][i] + m[j][i] = t + } + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/params.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/params.go new file mode 100644 index 00000000..669b0eda --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/params.go @@ -0,0 +1,21 @@ +// Code generated from params.templ.go. DO NOT EDIT. + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +const ( + K = 4 + Eta1 = 2 + DU = 11 + DV = 5 + PublicKeySize = 32 + K*common.PolySize + + PrivateKeySize = K * common.PolySize + + PlaintextSize = common.PlaintextSize + SeedSize = 32 + CiphertextSize = 1568 +) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/vec.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/vec.go new file mode 100644 index 00000000..6681895a --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/internal/vec.go @@ -0,0 +1,125 @@ +// Code generated from kyber512/internal/vec.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A vector of K polynomials +type Vec [K]common.Poly + +// Samples v[i] from a centered binomial distribution with given η, +// seed and nonce+i. +// +// Essentially CBD_η(PRF(seed, nonce+i)) from the specification. +func (v *Vec) DeriveNoise(seed []byte, nonce uint8, eta int) { + for i := 0; i < K; i++ { + v[i].DeriveNoise(seed, nonce+uint8(i), eta) + } +} + +// Sets p to the inner product of a and b using "pointwise" multiplication. +// +// See MulHat() and NTT() for a description of the multiplication. +// Assumes a and b are in Montgomery form. p will be in Montgomery form, +// and its coefficients will be bounded in absolute value by 2kq. +// If a and b are not in Montgomery form, then the action is the same +// as "pointwise" multiplication followed by multiplying by R⁻¹, the inverse +// of the Montgomery factor. +func PolyDotHat(p *common.Poly, a, b *Vec) { + var t common.Poly + *p = common.Poly{} // set p to zero + for i := 0; i < K; i++ { + t.MulHat(&a[i], &b[i]) + p.Add(&t, p) + } +} + +// Almost normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q}. +func (v *Vec) BarrettReduce() { + for i := 0; i < K; i++ { + v[i].BarrettReduce() + } +} + +// Normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q-1}. +func (v *Vec) Normalize() { + for i := 0; i < K; i++ { + v[i].Normalize() + } +} + +// Applies in-place inverse NTT(). See Poly.InvNTT() for assumptions. +func (v *Vec) InvNTT() { + for i := 0; i < K; i++ { + v[i].InvNTT() + } +} + +// Applies in-place forward NTT(). See Poly.NTT() for assumptions. +func (v *Vec) NTT() { + for i := 0; i < K; i++ { + v[i].NTT() + } +} + +// Sets v to a + b. +func (v *Vec) Add(a, b *Vec) { + for i := 0; i < K; i++ { + v[i].Add(&a[i], &b[i]) + } +} + +// Packs v into buf, which must be of length K*PolySize. +func (v *Vec) Pack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Pack(buf[common.PolySize*i:]) + } +} + +// Unpacks v from buf which must be of length K*PolySize. +func (v *Vec) Unpack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Unpack(buf[common.PolySize*i:]) + } +} + +// Writes Compress_q(v, d) to m. +// +// Assumes v is normalized and d is in {3, 4, 5, 10, 11}. +func (v *Vec) CompressTo(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].CompressTo(m[size*i:], d) + } +} + +// Set v to Decompress_q(m, 1). +// +// Assumes d is in {3, 4, 5, 10, 11}. v will be normalized. +func (v *Vec) Decompress(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].Decompress(m[size*i:], d) + } +} + +// ⌈(256 d)/8⌉ +func compressedPolySize(d int) int { + switch d { + case 4: + return 128 + case 5: + return 160 + case 10: + return 320 + case 11: + return 352 + } + panic("unsupported d") +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/kyber.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/kyber.go new file mode 100644 index 00000000..fb5911fa --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber1024/kyber.go @@ -0,0 +1,145 @@ +// Code generated from modePkg.templ.go. DO NOT EDIT. + +// kyber1024 implements the IND-CPA-secure Public Key Encryption +// scheme Kyber1024.CPAPKE as submitted to round 3 of the NIST PQC competition +// and described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber1024 + +import ( + cryptoRand "crypto/rand" + "io" + + "github.com/cloudflare/circl/pke/kyber/kyber1024/internal" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = internal.SeedSize + + // Size of seed for EncryptTo + EncryptionSeedSize = internal.SeedSize + + // Size of a packed PublicKey + PublicKeySize = internal.PublicKeySize + + // Size of a packed PrivateKey + PrivateKeySize = internal.PrivateKeySize + + // Size of a ciphertext + CiphertextSize = internal.CiphertextSize + + // Size of a plaintext + PlaintextSize = internal.PlaintextSize +) + +// PublicKey is the type of Kyber1024.CPAPKE public key +type PublicKey internal.PublicKey + +// PrivateKey is the type of Kyber1024.CPAPKE private key +type PrivateKey internal.PrivateKey + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := internal.NewKeyFromSeed(seed[:]) + return (*PublicKey)(pk), (*PrivateKey)(sk), nil +} + +// NewKeyFromSeed derives a public/private key pair using the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + pk, sk := internal.NewKeyFromSeed(seed) + return (*PublicKey)(pk), (*PrivateKey)(sk) +} + +// EncryptTo encrypts message pt for the public key and writes the ciphertext +// to ct using randomness from seed. +// +// This function panics if the lengths of pt, seed, and ct are not +// PlaintextSize, EncryptionSeedSize, and CiphertextSize respectively. +func (pk *PublicKey) EncryptTo(ct []byte, pt []byte, seed []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(seed) != EncryptionSeedSize { + panic("seed must be of length EncryptionSeedSize") + } + (*internal.PublicKey)(pk).EncryptTo(ct, pt, seed) +} + +// DecryptTo decrypts message ct for the private key and writes the +// plaintext to pt. +// +// This function panics if the lengths of ct and pt are not +// CiphertextSize and PlaintextSize respectively. +func (sk *PrivateKey) DecryptTo(pt []byte, ct []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + (*internal.PrivateKey)(sk).DecryptTo(pt, ct) +} + +// Packs pk into the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Pack(buf) +} + +// Packs sk into the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Pack(buf) +} + +// Unpacks pk from the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Unpack(buf) +} + +// Unpacks sk from the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Unpack(buf) +} + +// Returns whether the two private keys are equal. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(other)) +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/cpapke.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/cpapke.go new file mode 100644 index 00000000..80ab2501 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/cpapke.go @@ -0,0 +1,174 @@ +package internal + +import ( + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A Kyber.CPAPKE private key. +type PrivateKey struct { + sh Vec // NTT(s), normalized +} + +// A Kyber.CPAPKE public key. +type PublicKey struct { + rho [32]byte // ρ, the seed for the matrix A + th Vec // NTT(t), normalized + + // cached values + aT Mat // the matrix Aᵀ +} + +// Packs the private key to buf. +func (sk *PrivateKey) Pack(buf []byte) { + sk.sh.Pack(buf) +} + +// Unpacks the private key from buf. +func (sk *PrivateKey) Unpack(buf []byte) { + sk.sh.Unpack(buf) + sk.sh.Normalize() +} + +// Packs the public key to buf. +func (pk *PublicKey) Pack(buf []byte) { + pk.th.Pack(buf) + copy(buf[K*common.PolySize:], pk.rho[:]) +} + +// Unpacks the public key from buf. +func (pk *PublicKey) Unpack(buf []byte) { + pk.th.Unpack(buf) + pk.th.Normalize() + copy(pk.rho[:], buf[K*common.PolySize:]) + pk.aT.Derive(&pk.rho, true) +} + +// Derives a new Kyber.CPAPKE keypair from the given seed. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var pk PublicKey + var sk PrivateKey + + var expandedSeed [64]byte + + h := sha3.New512() + _, _ = h.Write(seed) + + // This writes hash into expandedSeed. Yes, this is idiomatic Go. + _, _ = h.Read(expandedSeed[:]) + + copy(pk.rho[:], expandedSeed[:32]) + sigma := expandedSeed[32:] // σ, the noise seed + + pk.aT.Derive(&pk.rho, false) // Expand ρ to matrix A; we'll transpose later + + var eh Vec + sk.sh.DeriveNoise(sigma, 0, Eta1) // Sample secret vector s + sk.sh.NTT() + sk.sh.Normalize() + + eh.DeriveNoise(sigma, K, Eta1) // Sample blind e + eh.NTT() + + // Next, we compute t = A s + e. + for i := 0; i < K; i++ { + // Note that coefficients of s are bounded by q and those of A + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&pk.th[i], &pk.aT[i], &sk.sh) + + // A and s were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // we'll cancel out now. This will also ensure the coefficients of + // t are bounded in absolute value by q. + pk.th[i].ToMont() + } + + pk.th.Add(&pk.th, &eh) // bounded by 8q. + pk.th.Normalize() + pk.aT.Transpose() + + return &pk, &sk +} + +// Decrypts ciphertext ct meant for private key sk to plaintext pt. +func (sk *PrivateKey) DecryptTo(pt, ct []byte) { + var u Vec + var v, m common.Poly + + u.Decompress(ct, DU) + v.Decompress(ct[K*compressedPolySize(DU):], DV) + + // Compute m = v - + u.NTT() + PolyDotHat(&m, &sk.sh, &u) + m.BarrettReduce() + m.InvNTT() + m.Sub(&v, &m) + m.Normalize() + + // Compress polynomial m to original message + m.CompressMessageTo(pt) +} + +// Encrypts message pt for the public key to ciphertext ct using randomness +// from seed. +// +// seed has to be of length SeedSize, pt of PlaintextSize and ct of +// CiphertextSize. +func (pk *PublicKey) EncryptTo(ct, pt, seed []byte) { + var rh, e1, u Vec + var e2, v, m common.Poly + + // Sample r, e₁ and e₂ from B_η + rh.DeriveNoise(seed, 0, Eta1) + rh.NTT() + rh.BarrettReduce() + + e1.DeriveNoise(seed, K, common.Eta2) + e2.DeriveNoise(seed, 2*K, common.Eta2) + + // Next we compute u = Aᵀ r + e₁. First Aᵀ. + for i := 0; i < K; i++ { + // Note that coefficients of r are bounded by q and those of Aᵀ + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&u[i], &pk.aT[i], &rh) + } + + u.BarrettReduce() + + // Aᵀ and r were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // the InvNTT cancels out. + u.InvNTT() + + u.Add(&u, &e1) // u = Aᵀ r + e₁ + + // Next compute v = + e₂ + Decompress_q(m, 1). + PolyDotHat(&v, &pk.th, &rh) + v.BarrettReduce() + v.InvNTT() + + m.DecompressMessage(pt) + v.Add(&v, &m) + v.Add(&v, &e2) // v = + e₂ + Decompress_q(m, 1) + + // Pack ciphertext + u.Normalize() + v.Normalize() + + u.CompressTo(ct, DU) + v.CompressTo(ct[K*compressedPolySize(DU):], DV) +} + +// Returns whether sk equals other. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + ret := int16(0) + for i := 0; i < K; i++ { + for j := 0; j < common.N; j++ { + ret |= sk.sh[i][j] ^ other.sh[i][j] + } + } + return ret == 0 +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/mat.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/mat.go new file mode 100644 index 00000000..9871a774 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/mat.go @@ -0,0 +1,83 @@ +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A k by k matrix of polynomials. +type Mat [K]Vec + +// Expands the given seed to the corresponding matrix A or its transpose Aᵀ. +func (m *Mat) Derive(seed *[32]byte, transpose bool) { + if !common.DeriveX4Available { + if transpose { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(i), uint8(j)) + } + } + } else { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(j), uint8(i)) + } + } + } + return + } + + var ps [4]*common.Poly + var xs [4]uint8 + var ys [4]uint8 + x := uint8(0) + y := uint8(0) + + for x != K { + idx := 0 + for ; idx < 4; idx++ { + ps[idx] = &m[x][y] + + if transpose { + xs[idx] = x + ys[idx] = y + } else { + xs[idx] = y + ys[idx] = x + } + + y++ + if y == K { + x++ + y = 0 + + if x == K { + if idx == 0 { + // If there is just one left, then a plain DeriveUniform + // is quicker than the X4 variant. + ps[0].DeriveUniform(seed, xs[0], ys[0]) + return + } + + for idx++; idx < 4; idx++ { + ps[idx] = nil + } + + break + } + } + } + + common.PolyDeriveUniformX4(ps, seed, xs, ys) + } +} + +// Tranposes A in place. +func (m *Mat) Transpose() { + for i := 0; i < K-1; i++ { + for j := i + 1; j < K; j++ { + t := m[i][j] + m[i][j] = m[j][i] + m[j][i] = t + } + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/params.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/params.go new file mode 100644 index 00000000..0e6df77b --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/params.go @@ -0,0 +1,21 @@ +// Code generated from params.templ.go. DO NOT EDIT. + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +const ( + K = 2 + Eta1 = 3 + DU = 10 + DV = 4 + PublicKeySize = 32 + K*common.PolySize + + PrivateKeySize = K * common.PolySize + + PlaintextSize = common.PlaintextSize + SeedSize = 32 + CiphertextSize = 768 +) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/vec.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/vec.go new file mode 100644 index 00000000..222f1ca9 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/internal/vec.go @@ -0,0 +1,123 @@ +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A vector of K polynomials +type Vec [K]common.Poly + +// Samples v[i] from a centered binomial distribution with given η, +// seed and nonce+i. +// +// Essentially CBD_η(PRF(seed, nonce+i)) from the specification. +func (v *Vec) DeriveNoise(seed []byte, nonce uint8, eta int) { + for i := 0; i < K; i++ { + v[i].DeriveNoise(seed, nonce+uint8(i), eta) + } +} + +// Sets p to the inner product of a and b using "pointwise" multiplication. +// +// See MulHat() and NTT() for a description of the multiplication. +// Assumes a and b are in Montgomery form. p will be in Montgomery form, +// and its coefficients will be bounded in absolute value by 2kq. +// If a and b are not in Montgomery form, then the action is the same +// as "pointwise" multiplication followed by multiplying by R⁻¹, the inverse +// of the Montgomery factor. +func PolyDotHat(p *common.Poly, a, b *Vec) { + var t common.Poly + *p = common.Poly{} // set p to zero + for i := 0; i < K; i++ { + t.MulHat(&a[i], &b[i]) + p.Add(&t, p) + } +} + +// Almost normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q}. +func (v *Vec) BarrettReduce() { + for i := 0; i < K; i++ { + v[i].BarrettReduce() + } +} + +// Normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q-1}. +func (v *Vec) Normalize() { + for i := 0; i < K; i++ { + v[i].Normalize() + } +} + +// Applies in-place inverse NTT(). See Poly.InvNTT() for assumptions. +func (v *Vec) InvNTT() { + for i := 0; i < K; i++ { + v[i].InvNTT() + } +} + +// Applies in-place forward NTT(). See Poly.NTT() for assumptions. +func (v *Vec) NTT() { + for i := 0; i < K; i++ { + v[i].NTT() + } +} + +// Sets v to a + b. +func (v *Vec) Add(a, b *Vec) { + for i := 0; i < K; i++ { + v[i].Add(&a[i], &b[i]) + } +} + +// Packs v into buf, which must be of length K*PolySize. +func (v *Vec) Pack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Pack(buf[common.PolySize*i:]) + } +} + +// Unpacks v from buf which must be of length K*PolySize. +func (v *Vec) Unpack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Unpack(buf[common.PolySize*i:]) + } +} + +// Writes Compress_q(v, d) to m. +// +// Assumes v is normalized and d is in {3, 4, 5, 10, 11}. +func (v *Vec) CompressTo(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].CompressTo(m[size*i:], d) + } +} + +// Set v to Decompress_q(m, 1). +// +// Assumes d is in {3, 4, 5, 10, 11}. v will be normalized. +func (v *Vec) Decompress(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].Decompress(m[size*i:], d) + } +} + +// ⌈(256 d)/8⌉ +func compressedPolySize(d int) int { + switch d { + case 4: + return 128 + case 5: + return 160 + case 10: + return 320 + case 11: + return 352 + } + panic("unsupported d") +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/kyber.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/kyber.go new file mode 100644 index 00000000..ea924848 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber512/kyber.go @@ -0,0 +1,145 @@ +// Code generated from modePkg.templ.go. DO NOT EDIT. + +// kyber512 implements the IND-CPA-secure Public Key Encryption +// scheme Kyber512.CPAPKE as submitted to round 3 of the NIST PQC competition +// and described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber512 + +import ( + cryptoRand "crypto/rand" + "io" + + "github.com/cloudflare/circl/pke/kyber/kyber512/internal" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = internal.SeedSize + + // Size of seed for EncryptTo + EncryptionSeedSize = internal.SeedSize + + // Size of a packed PublicKey + PublicKeySize = internal.PublicKeySize + + // Size of a packed PrivateKey + PrivateKeySize = internal.PrivateKeySize + + // Size of a ciphertext + CiphertextSize = internal.CiphertextSize + + // Size of a plaintext + PlaintextSize = internal.PlaintextSize +) + +// PublicKey is the type of Kyber512.CPAPKE public key +type PublicKey internal.PublicKey + +// PrivateKey is the type of Kyber512.CPAPKE private key +type PrivateKey internal.PrivateKey + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := internal.NewKeyFromSeed(seed[:]) + return (*PublicKey)(pk), (*PrivateKey)(sk), nil +} + +// NewKeyFromSeed derives a public/private key pair using the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + pk, sk := internal.NewKeyFromSeed(seed) + return (*PublicKey)(pk), (*PrivateKey)(sk) +} + +// EncryptTo encrypts message pt for the public key and writes the ciphertext +// to ct using randomness from seed. +// +// This function panics if the lengths of pt, seed, and ct are not +// PlaintextSize, EncryptionSeedSize, and CiphertextSize respectively. +func (pk *PublicKey) EncryptTo(ct []byte, pt []byte, seed []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(seed) != EncryptionSeedSize { + panic("seed must be of length EncryptionSeedSize") + } + (*internal.PublicKey)(pk).EncryptTo(ct, pt, seed) +} + +// DecryptTo decrypts message ct for the private key and writes the +// plaintext to pt. +// +// This function panics if the lengths of ct and pt are not +// CiphertextSize and PlaintextSize respectively. +func (sk *PrivateKey) DecryptTo(pt []byte, ct []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + (*internal.PrivateKey)(sk).DecryptTo(pt, ct) +} + +// Packs pk into the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Pack(buf) +} + +// Packs sk into the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Pack(buf) +} + +// Unpacks pk from the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Unpack(buf) +} + +// Unpacks sk from the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Unpack(buf) +} + +// Returns whether the two private keys are equal. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(other)) +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/cpapke.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/cpapke.go new file mode 100644 index 00000000..01ef88b2 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/cpapke.go @@ -0,0 +1,176 @@ +// Code generated from kyber512/internal/cpapke.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/internal/sha3" + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A Kyber.CPAPKE private key. +type PrivateKey struct { + sh Vec // NTT(s), normalized +} + +// A Kyber.CPAPKE public key. +type PublicKey struct { + rho [32]byte // ρ, the seed for the matrix A + th Vec // NTT(t), normalized + + // cached values + aT Mat // the matrix Aᵀ +} + +// Packs the private key to buf. +func (sk *PrivateKey) Pack(buf []byte) { + sk.sh.Pack(buf) +} + +// Unpacks the private key from buf. +func (sk *PrivateKey) Unpack(buf []byte) { + sk.sh.Unpack(buf) + sk.sh.Normalize() +} + +// Packs the public key to buf. +func (pk *PublicKey) Pack(buf []byte) { + pk.th.Pack(buf) + copy(buf[K*common.PolySize:], pk.rho[:]) +} + +// Unpacks the public key from buf. +func (pk *PublicKey) Unpack(buf []byte) { + pk.th.Unpack(buf) + pk.th.Normalize() + copy(pk.rho[:], buf[K*common.PolySize:]) + pk.aT.Derive(&pk.rho, true) +} + +// Derives a new Kyber.CPAPKE keypair from the given seed. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + var pk PublicKey + var sk PrivateKey + + var expandedSeed [64]byte + + h := sha3.New512() + _, _ = h.Write(seed) + + // This writes hash into expandedSeed. Yes, this is idiomatic Go. + _, _ = h.Read(expandedSeed[:]) + + copy(pk.rho[:], expandedSeed[:32]) + sigma := expandedSeed[32:] // σ, the noise seed + + pk.aT.Derive(&pk.rho, false) // Expand ρ to matrix A; we'll transpose later + + var eh Vec + sk.sh.DeriveNoise(sigma, 0, Eta1) // Sample secret vector s + sk.sh.NTT() + sk.sh.Normalize() + + eh.DeriveNoise(sigma, K, Eta1) // Sample blind e + eh.NTT() + + // Next, we compute t = A s + e. + for i := 0; i < K; i++ { + // Note that coefficients of s are bounded by q and those of A + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&pk.th[i], &pk.aT[i], &sk.sh) + + // A and s were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // we'll cancel out now. This will also ensure the coefficients of + // t are bounded in absolute value by q. + pk.th[i].ToMont() + } + + pk.th.Add(&pk.th, &eh) // bounded by 8q. + pk.th.Normalize() + pk.aT.Transpose() + + return &pk, &sk +} + +// Decrypts ciphertext ct meant for private key sk to plaintext pt. +func (sk *PrivateKey) DecryptTo(pt, ct []byte) { + var u Vec + var v, m common.Poly + + u.Decompress(ct, DU) + v.Decompress(ct[K*compressedPolySize(DU):], DV) + + // Compute m = v - + u.NTT() + PolyDotHat(&m, &sk.sh, &u) + m.BarrettReduce() + m.InvNTT() + m.Sub(&v, &m) + m.Normalize() + + // Compress polynomial m to original message + m.CompressMessageTo(pt) +} + +// Encrypts message pt for the public key to ciphertext ct using randomness +// from seed. +// +// seed has to be of length SeedSize, pt of PlaintextSize and ct of +// CiphertextSize. +func (pk *PublicKey) EncryptTo(ct, pt, seed []byte) { + var rh, e1, u Vec + var e2, v, m common.Poly + + // Sample r, e₁ and e₂ from B_η + rh.DeriveNoise(seed, 0, Eta1) + rh.NTT() + rh.BarrettReduce() + + e1.DeriveNoise(seed, K, common.Eta2) + e2.DeriveNoise(seed, 2*K, common.Eta2) + + // Next we compute u = Aᵀ r + e₁. First Aᵀ. + for i := 0; i < K; i++ { + // Note that coefficients of r are bounded by q and those of Aᵀ + // are bounded by 4.5q and so their product is bounded by 2¹⁵q + // as required for multiplication. + PolyDotHat(&u[i], &pk.aT[i], &rh) + } + + u.BarrettReduce() + + // Aᵀ and r were not in Montgomery form, so the Montgomery + // multiplications in the inner product added a factor R⁻¹ which + // the InvNTT cancels out. + u.InvNTT() + + u.Add(&u, &e1) // u = Aᵀ r + e₁ + + // Next compute v = + e₂ + Decompress_q(m, 1). + PolyDotHat(&v, &pk.th, &rh) + v.BarrettReduce() + v.InvNTT() + + m.DecompressMessage(pt) + v.Add(&v, &m) + v.Add(&v, &e2) // v = + e₂ + Decompress_q(m, 1) + + // Pack ciphertext + u.Normalize() + v.Normalize() + + u.CompressTo(ct, DU) + v.CompressTo(ct[K*compressedPolySize(DU):], DV) +} + +// Returns whether sk equals other. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + ret := int16(0) + for i := 0; i < K; i++ { + for j := 0; j < common.N; j++ { + ret |= sk.sh[i][j] ^ other.sh[i][j] + } + } + return ret == 0 +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/mat.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/mat.go new file mode 100644 index 00000000..e8a35aff --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/mat.go @@ -0,0 +1,85 @@ +// Code generated from kyber512/internal/mat.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A k by k matrix of polynomials. +type Mat [K]Vec + +// Expands the given seed to the corresponding matrix A or its transpose Aᵀ. +func (m *Mat) Derive(seed *[32]byte, transpose bool) { + if !common.DeriveX4Available { + if transpose { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(i), uint8(j)) + } + } + } else { + for i := 0; i < K; i++ { + for j := 0; j < K; j++ { + m[i][j].DeriveUniform(seed, uint8(j), uint8(i)) + } + } + } + return + } + + var ps [4]*common.Poly + var xs [4]uint8 + var ys [4]uint8 + x := uint8(0) + y := uint8(0) + + for x != K { + idx := 0 + for ; idx < 4; idx++ { + ps[idx] = &m[x][y] + + if transpose { + xs[idx] = x + ys[idx] = y + } else { + xs[idx] = y + ys[idx] = x + } + + y++ + if y == K { + x++ + y = 0 + + if x == K { + if idx == 0 { + // If there is just one left, then a plain DeriveUniform + // is quicker than the X4 variant. + ps[0].DeriveUniform(seed, xs[0], ys[0]) + return + } + + for idx++; idx < 4; idx++ { + ps[idx] = nil + } + + break + } + } + } + + common.PolyDeriveUniformX4(ps, seed, xs, ys) + } +} + +// Tranposes A in place. +func (m *Mat) Transpose() { + for i := 0; i < K-1; i++ { + for j := i + 1; j < K; j++ { + t := m[i][j] + m[i][j] = m[j][i] + m[j][i] = t + } + } +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/params.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/params.go new file mode 100644 index 00000000..27cdb1ab --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/params.go @@ -0,0 +1,21 @@ +// Code generated from params.templ.go. DO NOT EDIT. + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +const ( + K = 3 + Eta1 = 2 + DU = 10 + DV = 4 + PublicKeySize = 32 + K*common.PolySize + + PrivateKeySize = K * common.PolySize + + PlaintextSize = common.PlaintextSize + SeedSize = 32 + CiphertextSize = 1088 +) diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/vec.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/vec.go new file mode 100644 index 00000000..6681895a --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/internal/vec.go @@ -0,0 +1,125 @@ +// Code generated from kyber512/internal/vec.go by gen.go + +package internal + +import ( + "github.com/cloudflare/circl/pke/kyber/internal/common" +) + +// A vector of K polynomials +type Vec [K]common.Poly + +// Samples v[i] from a centered binomial distribution with given η, +// seed and nonce+i. +// +// Essentially CBD_η(PRF(seed, nonce+i)) from the specification. +func (v *Vec) DeriveNoise(seed []byte, nonce uint8, eta int) { + for i := 0; i < K; i++ { + v[i].DeriveNoise(seed, nonce+uint8(i), eta) + } +} + +// Sets p to the inner product of a and b using "pointwise" multiplication. +// +// See MulHat() and NTT() for a description of the multiplication. +// Assumes a and b are in Montgomery form. p will be in Montgomery form, +// and its coefficients will be bounded in absolute value by 2kq. +// If a and b are not in Montgomery form, then the action is the same +// as "pointwise" multiplication followed by multiplying by R⁻¹, the inverse +// of the Montgomery factor. +func PolyDotHat(p *common.Poly, a, b *Vec) { + var t common.Poly + *p = common.Poly{} // set p to zero + for i := 0; i < K; i++ { + t.MulHat(&a[i], &b[i]) + p.Add(&t, p) + } +} + +// Almost normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q}. +func (v *Vec) BarrettReduce() { + for i := 0; i < K; i++ { + v[i].BarrettReduce() + } +} + +// Normalizes coefficients in-place. +// +// Ensures each coefficient is in {0, …, q-1}. +func (v *Vec) Normalize() { + for i := 0; i < K; i++ { + v[i].Normalize() + } +} + +// Applies in-place inverse NTT(). See Poly.InvNTT() for assumptions. +func (v *Vec) InvNTT() { + for i := 0; i < K; i++ { + v[i].InvNTT() + } +} + +// Applies in-place forward NTT(). See Poly.NTT() for assumptions. +func (v *Vec) NTT() { + for i := 0; i < K; i++ { + v[i].NTT() + } +} + +// Sets v to a + b. +func (v *Vec) Add(a, b *Vec) { + for i := 0; i < K; i++ { + v[i].Add(&a[i], &b[i]) + } +} + +// Packs v into buf, which must be of length K*PolySize. +func (v *Vec) Pack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Pack(buf[common.PolySize*i:]) + } +} + +// Unpacks v from buf which must be of length K*PolySize. +func (v *Vec) Unpack(buf []byte) { + for i := 0; i < K; i++ { + v[i].Unpack(buf[common.PolySize*i:]) + } +} + +// Writes Compress_q(v, d) to m. +// +// Assumes v is normalized and d is in {3, 4, 5, 10, 11}. +func (v *Vec) CompressTo(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].CompressTo(m[size*i:], d) + } +} + +// Set v to Decompress_q(m, 1). +// +// Assumes d is in {3, 4, 5, 10, 11}. v will be normalized. +func (v *Vec) Decompress(m []byte, d int) { + size := compressedPolySize(d) + for i := 0; i < K; i++ { + v[i].Decompress(m[size*i:], d) + } +} + +// ⌈(256 d)/8⌉ +func compressedPolySize(d int) int { + switch d { + case 4: + return 128 + case 5: + return 160 + case 10: + return 320 + case 11: + return 352 + } + panic("unsupported d") +} diff --git a/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/kyber.go b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/kyber.go new file mode 100644 index 00000000..4cecbb1b --- /dev/null +++ b/vendor/github.com/cloudflare/circl/pke/kyber/kyber768/kyber.go @@ -0,0 +1,145 @@ +// Code generated from modePkg.templ.go. DO NOT EDIT. + +// kyber768 implements the IND-CPA-secure Public Key Encryption +// scheme Kyber768.CPAPKE as submitted to round 3 of the NIST PQC competition +// and described in +// +// https://pq-crystals.org/kyber/data/kyber-specification-round3.pdf +package kyber768 + +import ( + cryptoRand "crypto/rand" + "io" + + "github.com/cloudflare/circl/pke/kyber/kyber768/internal" +) + +const ( + // Size of seed for NewKeyFromSeed + KeySeedSize = internal.SeedSize + + // Size of seed for EncryptTo + EncryptionSeedSize = internal.SeedSize + + // Size of a packed PublicKey + PublicKeySize = internal.PublicKeySize + + // Size of a packed PrivateKey + PrivateKeySize = internal.PrivateKeySize + + // Size of a ciphertext + CiphertextSize = internal.CiphertextSize + + // Size of a plaintext + PlaintextSize = internal.PlaintextSize +) + +// PublicKey is the type of Kyber768.CPAPKE public key +type PublicKey internal.PublicKey + +// PrivateKey is the type of Kyber768.CPAPKE private key +type PrivateKey internal.PrivateKey + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) { + var seed [KeySeedSize]byte + if rand == nil { + rand = cryptoRand.Reader + } + _, err := io.ReadFull(rand, seed[:]) + if err != nil { + return nil, nil, err + } + pk, sk := internal.NewKeyFromSeed(seed[:]) + return (*PublicKey)(pk), (*PrivateKey)(sk), nil +} + +// NewKeyFromSeed derives a public/private key pair using the given seed. +// +// Panics if seed is not of length KeySeedSize. +func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { + if len(seed) != KeySeedSize { + panic("seed must be of length KeySeedSize") + } + pk, sk := internal.NewKeyFromSeed(seed) + return (*PublicKey)(pk), (*PrivateKey)(sk) +} + +// EncryptTo encrypts message pt for the public key and writes the ciphertext +// to ct using randomness from seed. +// +// This function panics if the lengths of pt, seed, and ct are not +// PlaintextSize, EncryptionSeedSize, and CiphertextSize respectively. +func (pk *PublicKey) EncryptTo(ct []byte, pt []byte, seed []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + if len(seed) != EncryptionSeedSize { + panic("seed must be of length EncryptionSeedSize") + } + (*internal.PublicKey)(pk).EncryptTo(ct, pt, seed) +} + +// DecryptTo decrypts message ct for the private key and writes the +// plaintext to pt. +// +// This function panics if the lengths of ct and pt are not +// CiphertextSize and PlaintextSize respectively. +func (sk *PrivateKey) DecryptTo(pt []byte, ct []byte) { + if len(pt) != PlaintextSize { + panic("pt must be of length PlaintextSize") + } + if len(ct) != CiphertextSize { + panic("ct must be of length CiphertextSize") + } + (*internal.PrivateKey)(sk).DecryptTo(pt, ct) +} + +// Packs pk into the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Pack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Pack(buf) +} + +// Packs sk into the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Pack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Pack(buf) +} + +// Unpacks pk from the given buffer. +// +// Panics if buf is not of length PublicKeySize. +func (pk *PublicKey) Unpack(buf []byte) { + if len(buf) != PublicKeySize { + panic("buf must be of size PublicKeySize") + } + (*internal.PublicKey)(pk).Unpack(buf) +} + +// Unpacks sk from the given buffer. +// +// Panics if buf is not of length PrivateKeySize. +func (sk *PrivateKey) Unpack(buf []byte) { + if len(buf) != PrivateKeySize { + panic("buf must be of size PrivateKeySize") + } + (*internal.PrivateKey)(sk).Unpack(buf) +} + +// Returns whether the two private keys are equal. +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(other)) +} diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x.go b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x.go new file mode 100644 index 00000000..7ce0c2ef --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x.go @@ -0,0 +1,149 @@ +// Package keccakf1600 provides a two and four-way Keccak-f[1600] permutation in parallel. +// +// Keccak-f[1600] is the permutation underlying several algorithms such as +// Keccak, SHA3 and SHAKE. Running two or four permutations in parallel is +// useful in some scenarios like in hash-based signatures. +// +// # Limitations +// +// Note that not all the architectures support SIMD instructions. This package +// uses AVX2 instructions that are available in some AMD64 architectures +// and NEON instructions that are available in some ARM64 architectures. +// +// For those systems not supporting these, the package still provides the +// expected functionality by means of a generic and slow implementation. +// The recommendation is to beforehand verify IsEnabledX4() and IsEnabledX2() +// to determine if the current system supports the SIMD implementation. +package keccakf1600 + +import ( + "unsafe" + + "github.com/cloudflare/circl/internal/sha3" + "golang.org/x/sys/cpu" +) + +// StateX4 contains state for the four-way permutation including the four +// interleaved [25]uint64 buffers. Call Initialize() before use to initialize +// and get a pointer to the interleaved buffer. +type StateX4 struct { + // Go guarantees a to be aligned on 8 bytes, whereas we need it to be + // aligned on 32 bytes for bet performance. Thus we leave some headroom + // to be able to move the start of the state. + + // 4 x 25 uint64s for the interleaved states and three uint64s headroom + // to fix alignment. + a [103]uint64 + + // Offset into a that is 32 byte aligned. + offset int +} + +// StateX2 contains state for the two-way permutation including the two +// interleaved [25]uint64 buffers. Call Initialize() before use to initialize +// and get a pointer to the interleaved buffer. +type StateX2 struct { + // Go guarantees a to be aligned on 8 bytes, whereas we need it to be + // aligned on 32 bytes for bet performance. Thus we leave some headroom + // to be able to move the start of the state. + + // 2 x 25 uint64s for the interleaved states and three uint64s headroom + // to fix alignment. + a [53]uint64 + + // Offset into a that is 32 byte aligned. + offset int +} + +// IsEnabledX4 returns true if the architecture supports a four-way SIMD +// implementation provided in this package. +func IsEnabledX4() bool { return cpu.X86.HasAVX2 } + +// IsEnabledX2 returns true if the architecture supports a two-way SIMD +// implementation provided in this package. +func IsEnabledX2() bool { + // After Go 1.16 the flag cpu.ARM64.HasSHA3 is no longer exposed. + return false +} + +// Initialize the state and returns the buffer on which the four permutations +// will act: a uint64 slice of length 100. The first permutation will act +// on {a[0], a[4], ..., a[96]}, the second on {a[1], a[5], ..., a[97]}, etc. +func (s *StateX4) Initialize() []uint64 { + rp := unsafe.Pointer(&s.a[0]) + + // uint64s are always aligned by a multiple of 8. Compute the remainder + // of the address modulo 32 divided by 8. + rem := (int(uintptr(rp)&31) >> 3) + + if rem != 0 { + s.offset = 4 - rem + } + + // The slice we return will be aligned on 32 byte boundary. + return s.a[s.offset : s.offset+100] +} + +// Initialize the state and returns the buffer on which the two permutations +// will act: a uint64 slice of length 50. The first permutation will act +// on {a[0], a[2], ..., a[48]} and the second on {a[1], a[3], ..., a[49]}. +func (s *StateX2) Initialize() []uint64 { + rp := unsafe.Pointer(&s.a[0]) + + // uint64s are always aligned by a multiple of 8. Compute the remainder + // of the address modulo 32 divided by 8. + rem := (int(uintptr(rp)&31) >> 3) + + if rem != 0 { + s.offset = 4 - rem + } + + // The slice we return will be aligned on 32 byte boundary. + return s.a[s.offset : s.offset+50] +} + +// Permute performs the four parallel Keccak-f[1600]s interleaved on the slice +// returned from Initialize(). +func (s *StateX4) Permute() { + if IsEnabledX4() { + permuteSIMDx4(s.a[s.offset:]) + } else { + permuteScalarX4(s.a[s.offset:]) // A slower generic implementation. + } +} + +// Permute performs the two parallel Keccak-f[1600]s interleaved on the slice +// returned from Initialize(). +func (s *StateX2) Permute() { + if IsEnabledX2() { + permuteSIMDx2(s.a[s.offset:]) + } else { + permuteScalarX2(s.a[s.offset:]) // A slower generic implementation. + } +} + +func permuteScalarX4(a []uint64) { + var buf [25]uint64 + for i := 0; i < 4; i++ { + for j := 0; j < 25; j++ { + buf[j] = a[4*j+i] + } + sha3.KeccakF1600(&buf) + for j := 0; j < 25; j++ { + a[4*j+i] = buf[j] + } + } +} + +func permuteScalarX2(a []uint64) { + var buf [25]uint64 + for i := 0; i < 2; i++ { + for j := 0; j < 25; j++ { + buf[j] = a[2*j+i] + } + sha3.KeccakF1600(&buf) + for j := 0; j < 25; j++ { + a[2*j+i] = buf[j] + } + } +} diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.go b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.go new file mode 100644 index 00000000..75461dd7 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.go @@ -0,0 +1,13 @@ +//go:build arm64 && go1.16 +// +build arm64,go1.16 + +package keccakf1600 + +import "github.com/cloudflare/circl/internal/sha3" + +func permuteSIMDx2(state []uint64) { f1600x2ARM(&state[0], &sha3.RC) } + +func permuteSIMDx4(state []uint64) { permuteScalarX4(state) } + +//go:noescape +func f1600x2ARM(state *uint64, rc *[24]uint64) diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.s b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.s new file mode 100644 index 00000000..1e8547f9 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x2_arm64.s @@ -0,0 +1,130 @@ +// +build arm64,go1.16 + +// Taken from https://github.com/bwesterb/armed-keccak + +#include "textflag.h" + +// func f1600x2ARM(state *uint64, rc *[24]uint64) +TEXT ·f1600x2ARM(SB), NOSPLIT, $0-16 + MOVD state+0(FP), R0 + MOVD rc+8(FP), R1 + MOVD R0, R2 + MOVD $24, R3 + + VLD1.P 64(R0), [ V0.B16, V1.B16, V2.B16, V3.B16] + VLD1.P 64(R0), [ V4.B16, V5.B16, V6.B16, V7.B16] + VLD1.P 64(R0), [ V8.B16, V9.B16, V10.B16, V11.B16] + VLD1.P 64(R0), [V12.B16, V13.B16, V14.B16, V15.B16] + VLD1.P 64(R0), [V16.B16, V17.B16, V18.B16, V19.B16] + VLD1.P 64(R0), [V20.B16, V21.B16, V22.B16, V23.B16] + VLD1.P (R0), [V24.B16] + +loop: + // Execute theta but without xorring into the state yet. + VEOR3 V10.B16, V5.B16, V0.B16, V25.B16 + VEOR3 V11.B16, V6.B16, V1.B16, V26.B16 + VEOR3 V12.B16, V7.B16, V2.B16, V27.B16 + VEOR3 V13.B16, V8.B16, V3.B16, V28.B16 + VEOR3 V14.B16, V9.B16, V4.B16, V29.B16 + + VEOR3 V20.B16, V15.B16, V25.B16, V25.B16 + VEOR3 V21.B16, V16.B16, V26.B16, V26.B16 + VEOR3 V22.B16, V17.B16, V27.B16, V27.B16 + VEOR3 V23.B16, V18.B16, V28.B16, V28.B16 + VEOR3 V24.B16, V19.B16, V29.B16, V29.B16 + + // Xor parities from step theta into the state at the same time as + // exeuting rho and pi. + VRAX1 V26.D2, V29.D2, V30.D2 + VRAX1 V29.D2, V27.D2, V29.D2 + VRAX1 V27.D2, V25.D2, V27.D2 + VRAX1 V25.D2, V28.D2, V25.D2 + VRAX1 V28.D2, V26.D2, V28.D2 + + VEOR V30.B16, V0.B16, V0.B16 + VMOV V1.B16, V31.B16 + + VXAR $20, V27.D2, V6.D2, V1.D2 + VXAR $44, V25.D2, V9.D2, V6.D2 + VXAR $3 , V28.D2, V22.D2, V9.D2 + VXAR $25, V25.D2, V14.D2, V22.D2 + VXAR $46, V30.D2, V20.D2, V14.D2 + VXAR $2 , V28.D2, V2.D2, V20.D2 + VXAR $21, V28.D2, V12.D2, V2.D2 + VXAR $39, V29.D2, V13.D2, V12.D2 + VXAR $56, V25.D2, V19.D2, V13.D2 + VXAR $8 , V29.D2, V23.D2, V19.D2 + VXAR $23, V30.D2, V15.D2, V23.D2 + VXAR $37, V25.D2, V4.D2, V15.D2 + VXAR $50, V25.D2, V24.D2, V4.D2 + VXAR $62, V27.D2, V21.D2, V24.D2 + VXAR $9 , V29.D2, V8.D2, V21.D2 + VXAR $19, V27.D2, V16.D2, V8.D2 + VXAR $28, V30.D2, V5.D2, V16.D2 + VXAR $36, V29.D2, V3.D2, V5.D2 + VXAR $43, V29.D2, V18.D2, V3.D2 + VXAR $49, V28.D2, V17.D2, V18.D2 + VXAR $54, V27.D2, V11.D2, V17.D2 + VXAR $58, V28.D2, V7.D2, V11.D2 + VXAR $61, V30.D2, V10.D2, V7.D2 + VXAR $63, V27.D2, V31.D2, V10.D2 + + // Chi + VBCAX V1.B16, V2.B16, V0.B16, V25.B16 + VBCAX V2.B16, V3.B16, V1.B16, V26.B16 + VBCAX V3.B16, V4.B16, V2.B16, V2.B16 + VBCAX V4.B16, V0.B16, V3.B16, V3.B16 + VBCAX V0.B16, V1.B16, V4.B16, V4.B16 + VMOV V25.B16, V0.B16 + VMOV V26.B16, V1.B16 + + VBCAX V6.B16, V7.B16, V5.B16, V25.B16 + VBCAX V7.B16, V8.B16, V6.B16, V26.B16 + VBCAX V8.B16, V9.B16, V7.B16, V7.B16 + VBCAX V9.B16, V5.B16, V8.B16, V8.B16 + VBCAX V5.B16, V6.B16, V9.B16, V9.B16 + VMOV V25.B16, V5.B16 + VMOV V26.B16, V6.B16 + + VBCAX V11.B16, V12.B16, V10.B16, V25.B16 + VBCAX V12.B16, V13.B16, V11.B16, V26.B16 + VBCAX V13.B16, V14.B16, V12.B16, V12.B16 + VBCAX V14.B16, V10.B16, V13.B16, V13.B16 + VBCAX V10.B16, V11.B16, V14.B16, V14.B16 + VMOV V25.B16, V10.B16 + VMOV V26.B16, V11.B16 + + VBCAX V16.B16, V17.B16, V15.B16, V25.B16 + VBCAX V17.B16, V18.B16, V16.B16, V26.B16 + VBCAX V18.B16, V19.B16, V17.B16, V17.B16 + VBCAX V19.B16, V15.B16, V18.B16, V18.B16 + VBCAX V15.B16, V16.B16, V19.B16, V19.B16 + VMOV V25.B16, V15.B16 + VMOV V26.B16, V16.B16 + + VBCAX V21.B16, V22.B16, V20.B16, V25.B16 + VBCAX V22.B16, V23.B16, V21.B16, V26.B16 + VBCAX V23.B16, V24.B16, V22.B16, V22.B16 + VBCAX V24.B16, V20.B16, V23.B16, V23.B16 + VBCAX V20.B16, V21.B16, V24.B16, V24.B16 + VMOV V25.B16, V20.B16 + VMOV V26.B16, V21.B16 + + // Iota + VLD1R.P 8(R1), [V25.D2] + VEOR V25.B16, V0.B16, V0.B16 + + SUBS $1, R3, R3 + CBNZ R3, loop + + MOVD R2, R0 + + VST1.P [ V0.B16, V1.B16, V2.B16, V3.B16], 64(R0) + VST1.P [ V4.B16, V5.B16, V6.B16, V7.B16], 64(R0) + VST1.P [ V8.B16, V9.B16, V10.B16, V11.B16], 64(R0) + VST1.P [V12.B16, V13.B16, V14.B16, V15.B16], 64(R0) + VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R0) + VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R0) + VST1.P [V24.B16], (R0) + + RET diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.go b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.go new file mode 100644 index 00000000..ac5c658d --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.go @@ -0,0 +1,7 @@ +package keccakf1600 + +import "github.com/cloudflare/circl/internal/sha3" + +func permuteSIMDx4(state []uint64) { f1600x4AVX2(&state[0], &sha3.RC) } + +func permuteSIMDx2(state []uint64) { permuteScalarX2(state) } diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.s b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.s new file mode 100644 index 00000000..194981f1 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4_amd64.s @@ -0,0 +1,894 @@ +// Code generated by command: go run src.go -out ../../f1600x4_amd64.s -stubs ../../f1600x4stubs_amd64.go -pkg keccakf1600. DO NOT EDIT. + +// +build amd64 + +#include "textflag.h" + +// func f1600x4AVX2(state *uint64, rc *[24]uint64) +// Requires: AVX, AVX2 +TEXT ·f1600x4AVX2(SB), NOSPLIT, $0-16 + MOVQ state+0(FP), AX + MOVQ rc+8(FP), CX + MOVQ $0x0000000000000006, DX + +loop: + VMOVDQA (AX), Y0 + VMOVDQA 32(AX), Y1 + VMOVDQA 64(AX), Y2 + VMOVDQA 96(AX), Y3 + VMOVDQA 128(AX), Y4 + VPXOR 160(AX), Y0, Y0 + VPXOR 192(AX), Y1, Y1 + VPXOR 224(AX), Y2, Y2 + VPXOR 256(AX), Y3, Y3 + VPXOR 288(AX), Y4, Y4 + VPXOR 320(AX), Y0, Y0 + VPXOR 352(AX), Y1, Y1 + VPXOR 384(AX), Y2, Y2 + VPXOR 416(AX), Y3, Y3 + VPXOR 448(AX), Y4, Y4 + VPXOR 480(AX), Y0, Y0 + VPXOR 512(AX), Y1, Y1 + VPXOR 544(AX), Y2, Y2 + VPXOR 576(AX), Y3, Y3 + VPXOR 608(AX), Y4, Y4 + VPXOR 640(AX), Y0, Y0 + VPXOR 672(AX), Y1, Y1 + VPXOR 704(AX), Y2, Y2 + VPXOR 736(AX), Y3, Y3 + VPXOR 768(AX), Y4, Y4 + VPSLLQ $0x01, Y1, Y5 + VPSLLQ $0x01, Y2, Y6 + VPSLLQ $0x01, Y3, Y7 + VPSLLQ $0x01, Y4, Y8 + VPSLLQ $0x01, Y0, Y9 + VPSRLQ $0x3f, Y1, Y10 + VPSRLQ $0x3f, Y2, Y11 + VPSRLQ $0x3f, Y3, Y12 + VPSRLQ $0x3f, Y4, Y13 + VPSRLQ $0x3f, Y0, Y14 + VPOR Y5, Y10, Y10 + VPOR Y6, Y11, Y11 + VPOR Y7, Y12, Y12 + VPOR Y8, Y13, Y13 + VPOR Y9, Y14, Y14 + VPXOR Y10, Y4, Y10 + VPXOR Y11, Y0, Y11 + VPXOR Y12, Y1, Y12 + VPXOR Y13, Y2, Y13 + VPXOR Y14, Y3, Y14 + VPXOR (AX), Y10, Y0 + VPXOR 192(AX), Y11, Y1 + VPXOR 384(AX), Y12, Y2 + VPXOR 576(AX), Y13, Y3 + VPXOR 768(AX), Y14, Y4 + VPSLLQ $0x2c, Y1, Y6 + VPSLLQ $0x2b, Y2, Y7 + VPSLLQ $0x15, Y3, Y8 + VPSLLQ $0x0e, Y4, Y9 + VPSRLQ $0x14, Y1, Y1 + VPSRLQ $0x15, Y2, Y2 + VPSRLQ $0x2b, Y3, Y3 + VPSRLQ $0x32, Y4, Y4 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VPBROADCASTQ (CX), Y0 + VPXOR Y0, Y5, Y5 + VMOVDQA Y5, (AX) + VMOVDQA Y6, 192(AX) + VMOVDQA Y7, 384(AX) + VMOVDQA Y8, 576(AX) + VMOVDQA Y9, 768(AX) + VPXOR 96(AX), Y13, Y0 + VPXOR 288(AX), Y14, Y1 + VPXOR 320(AX), Y10, Y2 + VPXOR 512(AX), Y11, Y3 + VPXOR 704(AX), Y12, Y4 + VPSLLQ $0x1c, Y0, Y5 + VPSLLQ $0x14, Y1, Y6 + VPSLLQ $0x03, Y2, Y7 + VPSLLQ $0x2d, Y3, Y8 + VPSLLQ $0x3d, Y4, Y9 + VPSRLQ $0x24, Y0, Y0 + VPSRLQ $0x2c, Y1, Y1 + VPSRLQ $0x3d, Y2, Y2 + VPSRLQ $0x13, Y3, Y3 + VPSRLQ $0x03, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 320(AX) + VMOVDQA Y6, 512(AX) + VMOVDQA Y7, 704(AX) + VMOVDQA Y8, 96(AX) + VMOVDQA Y9, 288(AX) + VPXOR 32(AX), Y11, Y0 + VPXOR 224(AX), Y12, Y1 + VPXOR 416(AX), Y13, Y2 + VPXOR 608(AX), Y14, Y3 + VPXOR 640(AX), Y10, Y4 + VPSLLQ $0x01, Y0, Y5 + VPSLLQ $0x06, Y1, Y6 + VPSLLQ $0x19, Y2, Y7 + VPSLLQ $0x08, Y3, Y8 + VPSLLQ $0x12, Y4, Y9 + VPSRLQ $0x3f, Y0, Y0 + VPSRLQ $0x3a, Y1, Y1 + VPSRLQ $0x27, Y2, Y2 + VPSRLQ $0x38, Y3, Y3 + VPSRLQ $0x2e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 640(AX) + VMOVDQA Y6, 32(AX) + VMOVDQA Y7, 224(AX) + VMOVDQA Y8, 416(AX) + VMOVDQA Y9, 608(AX) + VPXOR 128(AX), Y14, Y0 + VPXOR 160(AX), Y10, Y1 + VPXOR 352(AX), Y11, Y2 + VPXOR 544(AX), Y12, Y3 + VPXOR 736(AX), Y13, Y4 + VPSLLQ $0x1b, Y0, Y5 + VPSLLQ $0x24, Y1, Y6 + VPSLLQ $0x0a, Y2, Y7 + VPSLLQ $0x0f, Y3, Y8 + VPSLLQ $0x38, Y4, Y9 + VPSRLQ $0x25, Y0, Y0 + VPSRLQ $0x1c, Y1, Y1 + VPSRLQ $0x36, Y2, Y2 + VPSRLQ $0x31, Y3, Y3 + VPSRLQ $0x08, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 160(AX) + VMOVDQA Y6, 352(AX) + VMOVDQA Y7, 544(AX) + VMOVDQA Y8, 736(AX) + VMOVDQA Y9, 128(AX) + VPXOR 64(AX), Y12, Y0 + VPXOR 256(AX), Y13, Y1 + VPXOR 448(AX), Y14, Y2 + VPXOR 480(AX), Y10, Y3 + VPXOR 672(AX), Y11, Y4 + VPSLLQ $0x3e, Y0, Y5 + VPSLLQ $0x37, Y1, Y6 + VPSLLQ $0x27, Y2, Y7 + VPSLLQ $0x29, Y3, Y8 + VPSLLQ $0x02, Y4, Y9 + VPSRLQ $0x02, Y0, Y0 + VPSRLQ $0x09, Y1, Y1 + VPSRLQ $0x19, Y2, Y2 + VPSRLQ $0x17, Y3, Y3 + VPSRLQ $0x3e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 480(AX) + VMOVDQA Y6, 672(AX) + VMOVDQA Y7, 64(AX) + VMOVDQA Y8, 256(AX) + VMOVDQA Y9, 448(AX) + VMOVDQA (AX), Y0 + VMOVDQA 32(AX), Y1 + VMOVDQA 64(AX), Y2 + VMOVDQA 96(AX), Y3 + VMOVDQA 128(AX), Y4 + VPXOR 160(AX), Y0, Y0 + VPXOR 192(AX), Y1, Y1 + VPXOR 224(AX), Y2, Y2 + VPXOR 256(AX), Y3, Y3 + VPXOR 288(AX), Y4, Y4 + VPXOR 320(AX), Y0, Y0 + VPXOR 352(AX), Y1, Y1 + VPXOR 384(AX), Y2, Y2 + VPXOR 416(AX), Y3, Y3 + VPXOR 448(AX), Y4, Y4 + VPXOR 480(AX), Y0, Y0 + VPXOR 512(AX), Y1, Y1 + VPXOR 544(AX), Y2, Y2 + VPXOR 576(AX), Y3, Y3 + VPXOR 608(AX), Y4, Y4 + VPXOR 640(AX), Y0, Y0 + VPXOR 672(AX), Y1, Y1 + VPXOR 704(AX), Y2, Y2 + VPXOR 736(AX), Y3, Y3 + VPXOR 768(AX), Y4, Y4 + VPSLLQ $0x01, Y1, Y5 + VPSLLQ $0x01, Y2, Y6 + VPSLLQ $0x01, Y3, Y7 + VPSLLQ $0x01, Y4, Y8 + VPSLLQ $0x01, Y0, Y9 + VPSRLQ $0x3f, Y1, Y10 + VPSRLQ $0x3f, Y2, Y11 + VPSRLQ $0x3f, Y3, Y12 + VPSRLQ $0x3f, Y4, Y13 + VPSRLQ $0x3f, Y0, Y14 + VPOR Y5, Y10, Y10 + VPOR Y6, Y11, Y11 + VPOR Y7, Y12, Y12 + VPOR Y8, Y13, Y13 + VPOR Y9, Y14, Y14 + VPXOR Y10, Y4, Y10 + VPXOR Y11, Y0, Y11 + VPXOR Y12, Y1, Y12 + VPXOR Y13, Y2, Y13 + VPXOR Y14, Y3, Y14 + VPXOR (AX), Y10, Y0 + VPXOR 512(AX), Y11, Y1 + VPXOR 224(AX), Y12, Y2 + VPXOR 736(AX), Y13, Y3 + VPXOR 448(AX), Y14, Y4 + VPSLLQ $0x2c, Y1, Y6 + VPSLLQ $0x2b, Y2, Y7 + VPSLLQ $0x15, Y3, Y8 + VPSLLQ $0x0e, Y4, Y9 + VPSRLQ $0x14, Y1, Y1 + VPSRLQ $0x15, Y2, Y2 + VPSRLQ $0x2b, Y3, Y3 + VPSRLQ $0x32, Y4, Y4 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VPBROADCASTQ 8(CX), Y0 + VPXOR Y0, Y5, Y5 + VMOVDQA Y5, (AX) + VMOVDQA Y6, 512(AX) + VMOVDQA Y7, 224(AX) + VMOVDQA Y8, 736(AX) + VMOVDQA Y9, 448(AX) + VPXOR 576(AX), Y13, Y0 + VPXOR 288(AX), Y14, Y1 + VPXOR 640(AX), Y10, Y2 + VPXOR 352(AX), Y11, Y3 + VPXOR 64(AX), Y12, Y4 + VPSLLQ $0x1c, Y0, Y5 + VPSLLQ $0x14, Y1, Y6 + VPSLLQ $0x03, Y2, Y7 + VPSLLQ $0x2d, Y3, Y8 + VPSLLQ $0x3d, Y4, Y9 + VPSRLQ $0x24, Y0, Y0 + VPSRLQ $0x2c, Y1, Y1 + VPSRLQ $0x3d, Y2, Y2 + VPSRLQ $0x13, Y3, Y3 + VPSRLQ $0x03, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 640(AX) + VMOVDQA Y6, 352(AX) + VMOVDQA Y7, 64(AX) + VMOVDQA Y8, 576(AX) + VMOVDQA Y9, 288(AX) + VPXOR 192(AX), Y11, Y0 + VPXOR 704(AX), Y12, Y1 + VPXOR 416(AX), Y13, Y2 + VPXOR 128(AX), Y14, Y3 + VPXOR 480(AX), Y10, Y4 + VPSLLQ $0x01, Y0, Y5 + VPSLLQ $0x06, Y1, Y6 + VPSLLQ $0x19, Y2, Y7 + VPSLLQ $0x08, Y3, Y8 + VPSLLQ $0x12, Y4, Y9 + VPSRLQ $0x3f, Y0, Y0 + VPSRLQ $0x3a, Y1, Y1 + VPSRLQ $0x27, Y2, Y2 + VPSRLQ $0x38, Y3, Y3 + VPSRLQ $0x2e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 480(AX) + VMOVDQA Y6, 192(AX) + VMOVDQA Y7, 704(AX) + VMOVDQA Y8, 416(AX) + VMOVDQA Y9, 128(AX) + VPXOR 768(AX), Y14, Y0 + VPXOR 320(AX), Y10, Y1 + VPXOR 32(AX), Y11, Y2 + VPXOR 544(AX), Y12, Y3 + VPXOR 256(AX), Y13, Y4 + VPSLLQ $0x1b, Y0, Y5 + VPSLLQ $0x24, Y1, Y6 + VPSLLQ $0x0a, Y2, Y7 + VPSLLQ $0x0f, Y3, Y8 + VPSLLQ $0x38, Y4, Y9 + VPSRLQ $0x25, Y0, Y0 + VPSRLQ $0x1c, Y1, Y1 + VPSRLQ $0x36, Y2, Y2 + VPSRLQ $0x31, Y3, Y3 + VPSRLQ $0x08, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 320(AX) + VMOVDQA Y6, 32(AX) + VMOVDQA Y7, 544(AX) + VMOVDQA Y8, 256(AX) + VMOVDQA Y9, 768(AX) + VPXOR 384(AX), Y12, Y0 + VPXOR 96(AX), Y13, Y1 + VPXOR 608(AX), Y14, Y2 + VPXOR 160(AX), Y10, Y3 + VPXOR 672(AX), Y11, Y4 + VPSLLQ $0x3e, Y0, Y5 + VPSLLQ $0x37, Y1, Y6 + VPSLLQ $0x27, Y2, Y7 + VPSLLQ $0x29, Y3, Y8 + VPSLLQ $0x02, Y4, Y9 + VPSRLQ $0x02, Y0, Y0 + VPSRLQ $0x09, Y1, Y1 + VPSRLQ $0x19, Y2, Y2 + VPSRLQ $0x17, Y3, Y3 + VPSRLQ $0x3e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 160(AX) + VMOVDQA Y6, 672(AX) + VMOVDQA Y7, 384(AX) + VMOVDQA Y8, 96(AX) + VMOVDQA Y9, 608(AX) + VMOVDQA (AX), Y0 + VMOVDQA 32(AX), Y1 + VMOVDQA 64(AX), Y2 + VMOVDQA 96(AX), Y3 + VMOVDQA 128(AX), Y4 + VPXOR 160(AX), Y0, Y0 + VPXOR 192(AX), Y1, Y1 + VPXOR 224(AX), Y2, Y2 + VPXOR 256(AX), Y3, Y3 + VPXOR 288(AX), Y4, Y4 + VPXOR 320(AX), Y0, Y0 + VPXOR 352(AX), Y1, Y1 + VPXOR 384(AX), Y2, Y2 + VPXOR 416(AX), Y3, Y3 + VPXOR 448(AX), Y4, Y4 + VPXOR 480(AX), Y0, Y0 + VPXOR 512(AX), Y1, Y1 + VPXOR 544(AX), Y2, Y2 + VPXOR 576(AX), Y3, Y3 + VPXOR 608(AX), Y4, Y4 + VPXOR 640(AX), Y0, Y0 + VPXOR 672(AX), Y1, Y1 + VPXOR 704(AX), Y2, Y2 + VPXOR 736(AX), Y3, Y3 + VPXOR 768(AX), Y4, Y4 + VPSLLQ $0x01, Y1, Y5 + VPSLLQ $0x01, Y2, Y6 + VPSLLQ $0x01, Y3, Y7 + VPSLLQ $0x01, Y4, Y8 + VPSLLQ $0x01, Y0, Y9 + VPSRLQ $0x3f, Y1, Y10 + VPSRLQ $0x3f, Y2, Y11 + VPSRLQ $0x3f, Y3, Y12 + VPSRLQ $0x3f, Y4, Y13 + VPSRLQ $0x3f, Y0, Y14 + VPOR Y5, Y10, Y10 + VPOR Y6, Y11, Y11 + VPOR Y7, Y12, Y12 + VPOR Y8, Y13, Y13 + VPOR Y9, Y14, Y14 + VPXOR Y10, Y4, Y10 + VPXOR Y11, Y0, Y11 + VPXOR Y12, Y1, Y12 + VPXOR Y13, Y2, Y13 + VPXOR Y14, Y3, Y14 + VPXOR (AX), Y10, Y0 + VPXOR 352(AX), Y11, Y1 + VPXOR 704(AX), Y12, Y2 + VPXOR 256(AX), Y13, Y3 + VPXOR 608(AX), Y14, Y4 + VPSLLQ $0x2c, Y1, Y6 + VPSLLQ $0x2b, Y2, Y7 + VPSLLQ $0x15, Y3, Y8 + VPSLLQ $0x0e, Y4, Y9 + VPSRLQ $0x14, Y1, Y1 + VPSRLQ $0x15, Y2, Y2 + VPSRLQ $0x2b, Y3, Y3 + VPSRLQ $0x32, Y4, Y4 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VPBROADCASTQ 16(CX), Y0 + VPXOR Y0, Y5, Y5 + VMOVDQA Y5, (AX) + VMOVDQA Y6, 352(AX) + VMOVDQA Y7, 704(AX) + VMOVDQA Y8, 256(AX) + VMOVDQA Y9, 608(AX) + VPXOR 736(AX), Y13, Y0 + VPXOR 288(AX), Y14, Y1 + VPXOR 480(AX), Y10, Y2 + VPXOR 32(AX), Y11, Y3 + VPXOR 384(AX), Y12, Y4 + VPSLLQ $0x1c, Y0, Y5 + VPSLLQ $0x14, Y1, Y6 + VPSLLQ $0x03, Y2, Y7 + VPSLLQ $0x2d, Y3, Y8 + VPSLLQ $0x3d, Y4, Y9 + VPSRLQ $0x24, Y0, Y0 + VPSRLQ $0x2c, Y1, Y1 + VPSRLQ $0x3d, Y2, Y2 + VPSRLQ $0x13, Y3, Y3 + VPSRLQ $0x03, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 480(AX) + VMOVDQA Y6, 32(AX) + VMOVDQA Y7, 384(AX) + VMOVDQA Y8, 736(AX) + VMOVDQA Y9, 288(AX) + VPXOR 512(AX), Y11, Y0 + VPXOR 64(AX), Y12, Y1 + VPXOR 416(AX), Y13, Y2 + VPXOR 768(AX), Y14, Y3 + VPXOR 160(AX), Y10, Y4 + VPSLLQ $0x01, Y0, Y5 + VPSLLQ $0x06, Y1, Y6 + VPSLLQ $0x19, Y2, Y7 + VPSLLQ $0x08, Y3, Y8 + VPSLLQ $0x12, Y4, Y9 + VPSRLQ $0x3f, Y0, Y0 + VPSRLQ $0x3a, Y1, Y1 + VPSRLQ $0x27, Y2, Y2 + VPSRLQ $0x38, Y3, Y3 + VPSRLQ $0x2e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 160(AX) + VMOVDQA Y6, 512(AX) + VMOVDQA Y7, 64(AX) + VMOVDQA Y8, 416(AX) + VMOVDQA Y9, 768(AX) + VPXOR 448(AX), Y14, Y0 + VPXOR 640(AX), Y10, Y1 + VPXOR 192(AX), Y11, Y2 + VPXOR 544(AX), Y12, Y3 + VPXOR 96(AX), Y13, Y4 + VPSLLQ $0x1b, Y0, Y5 + VPSLLQ $0x24, Y1, Y6 + VPSLLQ $0x0a, Y2, Y7 + VPSLLQ $0x0f, Y3, Y8 + VPSLLQ $0x38, Y4, Y9 + VPSRLQ $0x25, Y0, Y0 + VPSRLQ $0x1c, Y1, Y1 + VPSRLQ $0x36, Y2, Y2 + VPSRLQ $0x31, Y3, Y3 + VPSRLQ $0x08, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 640(AX) + VMOVDQA Y6, 192(AX) + VMOVDQA Y7, 544(AX) + VMOVDQA Y8, 96(AX) + VMOVDQA Y9, 448(AX) + VPXOR 224(AX), Y12, Y0 + VPXOR 576(AX), Y13, Y1 + VPXOR 128(AX), Y14, Y2 + VPXOR 320(AX), Y10, Y3 + VPXOR 672(AX), Y11, Y4 + VPSLLQ $0x3e, Y0, Y5 + VPSLLQ $0x37, Y1, Y6 + VPSLLQ $0x27, Y2, Y7 + VPSLLQ $0x29, Y3, Y8 + VPSLLQ $0x02, Y4, Y9 + VPSRLQ $0x02, Y0, Y0 + VPSRLQ $0x09, Y1, Y1 + VPSRLQ $0x19, Y2, Y2 + VPSRLQ $0x17, Y3, Y3 + VPSRLQ $0x3e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 320(AX) + VMOVDQA Y6, 672(AX) + VMOVDQA Y7, 224(AX) + VMOVDQA Y8, 576(AX) + VMOVDQA Y9, 128(AX) + VMOVDQA (AX), Y0 + VMOVDQA 32(AX), Y1 + VMOVDQA 64(AX), Y2 + VMOVDQA 96(AX), Y3 + VMOVDQA 128(AX), Y4 + VPXOR 160(AX), Y0, Y0 + VPXOR 192(AX), Y1, Y1 + VPXOR 224(AX), Y2, Y2 + VPXOR 256(AX), Y3, Y3 + VPXOR 288(AX), Y4, Y4 + VPXOR 320(AX), Y0, Y0 + VPXOR 352(AX), Y1, Y1 + VPXOR 384(AX), Y2, Y2 + VPXOR 416(AX), Y3, Y3 + VPXOR 448(AX), Y4, Y4 + VPXOR 480(AX), Y0, Y0 + VPXOR 512(AX), Y1, Y1 + VPXOR 544(AX), Y2, Y2 + VPXOR 576(AX), Y3, Y3 + VPXOR 608(AX), Y4, Y4 + VPXOR 640(AX), Y0, Y0 + VPXOR 672(AX), Y1, Y1 + VPXOR 704(AX), Y2, Y2 + VPXOR 736(AX), Y3, Y3 + VPXOR 768(AX), Y4, Y4 + VPSLLQ $0x01, Y1, Y5 + VPSLLQ $0x01, Y2, Y6 + VPSLLQ $0x01, Y3, Y7 + VPSLLQ $0x01, Y4, Y8 + VPSLLQ $0x01, Y0, Y9 + VPSRLQ $0x3f, Y1, Y10 + VPSRLQ $0x3f, Y2, Y11 + VPSRLQ $0x3f, Y3, Y12 + VPSRLQ $0x3f, Y4, Y13 + VPSRLQ $0x3f, Y0, Y14 + VPOR Y5, Y10, Y10 + VPOR Y6, Y11, Y11 + VPOR Y7, Y12, Y12 + VPOR Y8, Y13, Y13 + VPOR Y9, Y14, Y14 + VPXOR Y10, Y4, Y10 + VPXOR Y11, Y0, Y11 + VPXOR Y12, Y1, Y12 + VPXOR Y13, Y2, Y13 + VPXOR Y14, Y3, Y14 + VPXOR (AX), Y10, Y0 + VPXOR 32(AX), Y11, Y1 + VPXOR 64(AX), Y12, Y2 + VPXOR 96(AX), Y13, Y3 + VPXOR 128(AX), Y14, Y4 + VPSLLQ $0x2c, Y1, Y6 + VPSLLQ $0x2b, Y2, Y7 + VPSLLQ $0x15, Y3, Y8 + VPSLLQ $0x0e, Y4, Y9 + VPSRLQ $0x14, Y1, Y1 + VPSRLQ $0x15, Y2, Y2 + VPSRLQ $0x2b, Y3, Y3 + VPSRLQ $0x32, Y4, Y4 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VPBROADCASTQ 24(CX), Y0 + VPXOR Y0, Y5, Y5 + VMOVDQA Y5, (AX) + VMOVDQA Y6, 32(AX) + VMOVDQA Y7, 64(AX) + VMOVDQA Y8, 96(AX) + VMOVDQA Y9, 128(AX) + VPXOR 256(AX), Y13, Y0 + VPXOR 288(AX), Y14, Y1 + VPXOR 160(AX), Y10, Y2 + VPXOR 192(AX), Y11, Y3 + VPXOR 224(AX), Y12, Y4 + VPSLLQ $0x1c, Y0, Y5 + VPSLLQ $0x14, Y1, Y6 + VPSLLQ $0x03, Y2, Y7 + VPSLLQ $0x2d, Y3, Y8 + VPSLLQ $0x3d, Y4, Y9 + VPSRLQ $0x24, Y0, Y0 + VPSRLQ $0x2c, Y1, Y1 + VPSRLQ $0x3d, Y2, Y2 + VPSRLQ $0x13, Y3, Y3 + VPSRLQ $0x03, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 160(AX) + VMOVDQA Y6, 192(AX) + VMOVDQA Y7, 224(AX) + VMOVDQA Y8, 256(AX) + VMOVDQA Y9, 288(AX) + VPXOR 352(AX), Y11, Y0 + VPXOR 384(AX), Y12, Y1 + VPXOR 416(AX), Y13, Y2 + VPXOR 448(AX), Y14, Y3 + VPXOR 320(AX), Y10, Y4 + VPSLLQ $0x01, Y0, Y5 + VPSLLQ $0x06, Y1, Y6 + VPSLLQ $0x19, Y2, Y7 + VPSLLQ $0x08, Y3, Y8 + VPSLLQ $0x12, Y4, Y9 + VPSRLQ $0x3f, Y0, Y0 + VPSRLQ $0x3a, Y1, Y1 + VPSRLQ $0x27, Y2, Y2 + VPSRLQ $0x38, Y3, Y3 + VPSRLQ $0x2e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 320(AX) + VMOVDQA Y6, 352(AX) + VMOVDQA Y7, 384(AX) + VMOVDQA Y8, 416(AX) + VMOVDQA Y9, 448(AX) + VPXOR 608(AX), Y14, Y0 + VPXOR 480(AX), Y10, Y1 + VPXOR 512(AX), Y11, Y2 + VPXOR 544(AX), Y12, Y3 + VPXOR 576(AX), Y13, Y4 + VPSLLQ $0x1b, Y0, Y5 + VPSLLQ $0x24, Y1, Y6 + VPSLLQ $0x0a, Y2, Y7 + VPSLLQ $0x0f, Y3, Y8 + VPSLLQ $0x38, Y4, Y9 + VPSRLQ $0x25, Y0, Y0 + VPSRLQ $0x1c, Y1, Y1 + VPSRLQ $0x36, Y2, Y2 + VPSRLQ $0x31, Y3, Y3 + VPSRLQ $0x08, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 480(AX) + VMOVDQA Y6, 512(AX) + VMOVDQA Y7, 544(AX) + VMOVDQA Y8, 576(AX) + VMOVDQA Y9, 608(AX) + VPXOR 704(AX), Y12, Y0 + VPXOR 736(AX), Y13, Y1 + VPXOR 768(AX), Y14, Y2 + VPXOR 640(AX), Y10, Y3 + VPXOR 672(AX), Y11, Y4 + VPSLLQ $0x3e, Y0, Y5 + VPSLLQ $0x37, Y1, Y6 + VPSLLQ $0x27, Y2, Y7 + VPSLLQ $0x29, Y3, Y8 + VPSLLQ $0x02, Y4, Y9 + VPSRLQ $0x02, Y0, Y0 + VPSRLQ $0x09, Y1, Y1 + VPSRLQ $0x19, Y2, Y2 + VPSRLQ $0x17, Y3, Y3 + VPSRLQ $0x3e, Y4, Y4 + VPOR Y5, Y0, Y0 + VPOR Y6, Y1, Y1 + VPOR Y7, Y2, Y2 + VPOR Y8, Y3, Y3 + VPOR Y9, Y4, Y4 + VPANDN Y2, Y1, Y5 + VPANDN Y3, Y2, Y6 + VPANDN Y4, Y3, Y7 + VPANDN Y0, Y4, Y8 + VPANDN Y1, Y0, Y9 + VPXOR Y0, Y5, Y5 + VPXOR Y1, Y6, Y6 + VPXOR Y2, Y7, Y7 + VPXOR Y3, Y8, Y8 + VPXOR Y4, Y9, Y9 + VMOVDQA Y5, 640(AX) + VMOVDQA Y6, 672(AX) + VMOVDQA Y7, 704(AX) + VMOVDQA Y8, 736(AX) + VMOVDQA Y9, 768(AX) + ADDQ $0x20, CX + SUBQ $0x00000001, DX + JNZ loop + RET diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4stubs_amd64.go b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4stubs_amd64.go new file mode 100644 index 00000000..76c6cf99 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/f1600x4stubs_amd64.go @@ -0,0 +1,9 @@ +// Code generated by command: go run src.go -out ../../f1600x4_amd64.s -stubs ../../f1600x4stubs_amd64.go -pkg keccakf1600. DO NOT EDIT. + +//go:build amd64 +// +build amd64 + +package keccakf1600 + +//go:noescape +func f1600x4AVX2(state *uint64, rc *[24]uint64) diff --git a/vendor/github.com/cloudflare/circl/simd/keccakf1600/fallback.go b/vendor/github.com/cloudflare/circl/simd/keccakf1600/fallback.go new file mode 100644 index 00000000..5d56c09b --- /dev/null +++ b/vendor/github.com/cloudflare/circl/simd/keccakf1600/fallback.go @@ -0,0 +1,8 @@ +//go:build (!amd64 && !arm64) || (arm64 && !go1.16) +// +build !amd64,!arm64 arm64,!go1.16 + +package keccakf1600 + +func permuteSIMDx2(state []uint64) { permuteScalarX2(state) } + +func permuteSIMDx4(state []uint64) { permuteScalarX4(state) } diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/cfkem.go b/vendor/github.com/marten-seemann/qtls-go1-18/cfkem.go new file mode 100644 index 00000000..dc2f2302 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-18/cfkem.go @@ -0,0 +1,170 @@ +// Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. +// +// Glue to add Circl's (post-quantum) hybrid KEMs. +// +// To enable set CurvePreferences with the desired scheme as the first element: +// +// import ( +// "github.com/cloudflare/circl/kem/tls" +// "github.com/cloudflare/circl/kem/hybrid" +// +// [...] +// +// config.CurvePreferences = []tls.CurveID{ +// qtls.X25519Kyber512Draft00, +// qtls.X25519, +// qtls.P256, +// } + +package qtls + +import ( + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/hybrid" + + "crypto/tls" + "fmt" + "io" + "sync" + "time" +) + +// Either ecdheParameters or kem.PrivateKey +type clientKeySharePrivate interface{} + +var ( + X25519Kyber512Draft00 = CurveID(0xfe30) + X25519Kyber768Draft00 = CurveID(0xfe31) + invalidCurveID = CurveID(0) +) + +func kemSchemeKeyToCurveID(s kem.Scheme) CurveID { + switch s.Name() { + case "Kyber512-X25519": + return X25519Kyber512Draft00 + case "Kyber768-X25519": + return X25519Kyber768Draft00 + default: + return invalidCurveID + } +} + +// Extract CurveID from clientKeySharePrivate +func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID { + switch v := ks.(type) { + case kem.PrivateKey: + ret := kemSchemeKeyToCurveID(v.Scheme()) + if ret == invalidCurveID { + panic("cfkem: internal error: don't know CurveID for this KEM") + } + return ret + case ecdheParameters: + return v.CurveID() + default: + panic("cfkem: internal error: unknown clientKeySharePrivate") + } +} + +// Returns scheme by CurveID if supported by Circl +func curveIdToCirclScheme(id CurveID) kem.Scheme { + switch id { + case X25519Kyber512Draft00: + return hybrid.Kyber512X25519() + case X25519Kyber768Draft00: + return hybrid.Kyber768X25519() + } + return nil +} + +// Generate a new shared secret and encapsulates it for the packed +// public key in ppk using randomness from rnd. +func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) ( + ct, ss []byte, alert alert, err error) { + pk, err := scheme.UnmarshalBinaryPublicKey(ppk) + if err != nil { + return nil, nil, alertIllegalParameter, fmt.Errorf("unpack pk: %w", err) + } + seed := make([]byte, scheme.EncapsulationSeedSize()) + if _, err := io.ReadFull(rnd, seed); err != nil { + return nil, nil, alertInternalError, fmt.Errorf("random: %w", err) + } + ct, ss, err = scheme.EncapsulateDeterministically(pk, seed) + return ct, ss, alertIllegalParameter, err +} + +// Generate a new keypair using randomness from rnd. +func generateKemKeyPair(scheme kem.Scheme, rnd io.Reader) ( + kem.PublicKey, kem.PrivateKey, error) { + seed := make([]byte, scheme.SeedSize()) + if _, err := io.ReadFull(rnd, seed); err != nil { + return nil, nil, err + } + pk, sk := scheme.DeriveKeyPair(seed) + return pk, sk, nil +} + +// Events. We cannot use the same approach as used in our plain Go fork +// as we cannot change tls.Config, tls.ConnectionState, etc. Also we do +// not want to maintain a fork of quic-go itself as well. This seems +// the simplest option. + +// CFEvent. There are two events: one emitted on HRR and one emitted +type CFEvent interface { + // Common to all events + ServerSide() bool // true if server-side; false if on client-side + + // HRR event. Emitted when an HRR happened. + IsHRR() bool // true if this is an HRR event + + // Handshake event. + IsHandshake() bool // true if this is a handshake event. + Duration() time.Duration // how long did the handshake take? + KEX() tls.CurveID // which kex was established? +} + +type CFEventHandler func(CFEvent) + +// Registers a handler to be called when a CFEvent is emitted; returns +// the previous handler. +func SetCFEventHandler(handler CFEventHandler) CFEventHandler { + cfEventMux.Lock() + ret := cfEventHandler + cfEventHandler = handler + cfEventMux.Unlock() + return ret +} + +func raiseCFEvent(ev CFEvent) { + cfEventMux.Lock() + handler := cfEventHandler + cfEventMux.Unlock() + if handler != nil { + handler(ev) + } +} + +var ( + cfEventMux sync.Mutex + cfEventHandler CFEventHandler +) + +type cfEventHRR struct{ serverSide bool } + +func (*cfEventHRR) IsHRR() bool { return true } +func (ev *cfEventHRR) ServerSide() bool { return ev.serverSide } +func (*cfEventHRR) IsHandshake() bool { return false } +func (ev *cfEventHRR) Duration() time.Duration { panic("wrong event") } +func (ev *cfEventHRR) KEX() tls.CurveID { panic("wrong event") } + +type cfEventHandshake struct { + serverSide bool + duration time.Duration + kex tls.CurveID +} + +func (*cfEventHandshake) IsHRR() bool { return false } +func (ev *cfEventHandshake) ServerSide() bool { return ev.serverSide } +func (*cfEventHandshake) IsHandshake() bool { return true } +func (ev *cfEventHandshake) Duration() time.Duration { return ev.duration } +func (ev *cfEventHandshake) KEX() tls.CurveID { return ev.kex } diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/common.go b/vendor/github.com/marten-seemann/qtls-go1-18/common.go index 4c9aeeb4..d4334f9a 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/common.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/common.go @@ -345,6 +345,7 @@ type clientSessionState struct { // goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not // SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which // are supported via this interface. +// //go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/marten-seemann/qtls-go1-17 ClientSessionCache" type ClientSessionCache = tls.ClientSessionCache diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client.go b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client.go index ab691d56..4d026c66 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client.go @@ -38,7 +38,7 @@ type clientHandshakeState struct { session *clientSessionState } -func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { +func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error) { config := c.config if len(config.ServerName) == 0 && !config.InsecureSkipVerify { return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") @@ -137,7 +137,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms } - var params ecdheParameters + var secret clientKeySharePrivate if hello.supportedVersions[0] == VersionTLS13 { var suites []uint16 for _, suiteID := range configCipherSuites { @@ -158,21 +158,37 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { } curveID := config.curvePreferences()[0] - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + if scheme := curveIdToCirclScheme(curveID); scheme != nil { + pk, sk, err := generateKemKeyPair(scheme, config.rand()) + if err != nil { + return nil, nil, fmt.Errorf("generateKemKeyPair %s: %w", + scheme.Name(), err) + } + packedPk, err := pk.MarshalBinary() + if err != nil { + return nil, nil, fmt.Errorf("pack circl public key %s: %w", + scheme.Name(), err) + } + hello.keyShares = []keyShare{{group: curveID, data: packedPk}} + secret = sk + } else { + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, nil, err + } + hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + secret = params } - params, err = generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, nil, err - } - hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil { hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello) } - return hello, params, nil + return hello, secret, nil } func (c *Conn) clientHandshake(ctx context.Context) (err error) { @@ -258,14 +274,14 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { if c.vers == VersionTLS13 { hs := &clientHandshakeStateTLS13{ - c: c, - ctx: ctx, - serverHello: serverHello, - hello: hello, - ecdheParams: ecdheParams, - session: session, - earlySecret: earlySecret, - binderKey: binderKey, + c: c, + ctx: ctx, + serverHello: serverHello, + hello: hello, + keySharePrivate: ecdheParams, + session: session, + earlySecret: earlySecret, + binderKey: binderKey, } // In TLS 1.3, session tickets are delivered after the handshake. diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client_tls13.go index 0de59fc1..349d3f20 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_client_tls13.go @@ -12,10 +12,12 @@ import ( "crypto/rsa" "encoding/binary" "errors" + "fmt" "hash" "sync/atomic" "time" + circlKem "github.com/cloudflare/circl/kem" "golang.org/x/crypto/cryptobyte" ) @@ -24,7 +26,8 @@ type clientHandshakeStateTLS13 struct { ctx context.Context serverHello *serverHelloMsg hello *clientHelloMsg - ecdheParams ecdheParameters + + keySharePrivate clientKeySharePrivate session *clientSessionState earlySecret []byte @@ -44,6 +47,8 @@ type clientHandshakeStateTLS13 struct { func (hs *clientHandshakeStateTLS13) handshake() error { c := hs.c + startTime := time.Now() + // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, // sections 4.1.2 and 4.1.3. if c.handshakes > 0 { @@ -52,7 +57,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } // Consistency check on the presence of a keyShare and its parameters. - if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { + if hs.keySharePrivate == nil || len(hs.hello.keyShares) != 1 { return c.sendAlert(alertInternalError) } @@ -103,6 +108,12 @@ func (hs *clientHandshakeStateTLS13) handshake() error { return err } + raiseCFEvent(&cfEventHandshake{ + serverSide: false, + duration: time.Since(startTime), + kex: hs.serverHello.serverShare.group, + }) + atomic.StoreUint32(&c.handshakeStatus, 1) return nil @@ -180,6 +191,8 @@ func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { c := hs.c + raiseCFEvent(&cfEventHRR{serverSide: false}) + // The first ClientHello gets double-hashed into the transcript upon a // HelloRetryRequest. (The idea is that the server might offload transcript // storage to the client in the cookie.) See RFC 8446, Section 4.4.1. @@ -221,21 +234,38 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: server selected unsupported group") } - if hs.ecdheParams.CurveID() == curveID { + if clientKeySharePrivateCurveID(hs.keySharePrivate) == curveID { c.sendAlert(alertIllegalParameter) return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share") } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") + if scheme := curveIdToCirclScheme(curveID); scheme != nil { + pk, sk, err := generateKemKeyPair(scheme, c.config.rand()) + if err != nil { + c.sendAlert(alertInternalError) + return fmt.Errorf("HRR generateKeyPair %s: %w", + scheme.Name(), err) + } + packedPk, err := pk.MarshalBinary() + if err != nil { + c.sendAlert(alertInternalError) + return fmt.Errorf("HRR pack circl public key %s: %w", + scheme.Name(), err) + } + hs.keySharePrivate = sk + hs.hello.keyShares = []keyShare{{group: curveID, data: packedPk}} + } else { + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + c.sendAlert(alertInternalError) + return errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(c.config.rand(), curveID) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.keySharePrivate = params + hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } - params, err := generateECDHEParameters(c.config.rand(), curveID) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.ecdheParams = params - hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } hs.hello.raw = nil @@ -314,7 +344,7 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: server did not send a key share") } - if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { + if hs.serverHello.serverShare.group != clientKeySharePrivateCurveID(hs.keySharePrivate) { c.sendAlert(alertIllegalParameter) return errors.New("tls: server selected unsupported group") } @@ -352,7 +382,18 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error { func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { c := hs.c - sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) + var sharedKey []byte + if params, ok := hs.keySharePrivate.(ecdheParameters); ok { + sharedKey = params.SharedKey(hs.serverHello.serverShare.data) + } else if sk, ok := hs.keySharePrivate.(circlKem.PrivateKey); ok { + var err error + sharedKey, err = sk.Scheme().Decapsulate(sk, hs.serverHello.serverShare.data) + if err != nil { + c.sendAlert(alertIllegalParameter) + return fmt.Errorf("%s decaps: %w", sk.Scheme().Name(), err) + } + } + if sharedKey == nil { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid server key share") diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go index dd8d801e..58fbb8d0 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server_tls13.go @@ -11,6 +11,7 @@ import ( "crypto/hmac" "crypto/rsa" "errors" + "fmt" "hash" "io" "sync/atomic" @@ -46,6 +47,8 @@ type serverHandshakeStateTLS13 struct { func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c + startTime := time.Now() + // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. if err := hs.processClientHello(); err != nil { return err @@ -79,6 +82,12 @@ func (hs *serverHandshakeStateTLS13) handshake() error { return err } + raiseCFEvent(&cfEventHandshake{ + serverSide: true, + duration: time.Since(startTime), + kex: hs.hello.serverShare.group, + }) + atomic.StoreUint32(&c.handshakeStatus, 1) return nil @@ -206,17 +215,27 @@ GroupSelection: clientKeyShare = &hs.clientHello.keyShares[0] } - if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok { + if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && curveIdToCirclScheme(selectedGroup) == nil && !ok { c.sendAlert(alertInternalError) return errors.New("tls: CurvePreferences includes unsupported curve") } - params, err := generateECDHEParameters(c.config.rand(), selectedGroup) - if err != nil { - c.sendAlert(alertInternalError) - return err + if kem := curveIdToCirclScheme(selectedGroup); kem != nil { + ct, ss, alert, err := encapsulateForKem(kem, c.config.rand(), clientKeyShare.data) + if err != nil { + c.sendAlert(alert) + return fmt.Errorf("%s encap: %w", kem.Name(), err) + } + hs.hello.serverShare = keyShare{group: selectedGroup, data: ct} + hs.sharedKey = ss + } else { + params, err := generateECDHEParameters(c.config.rand(), selectedGroup) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} + hs.sharedKey = params.SharedKey(clientKeyShare.data) } - hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} - hs.sharedKey = params.SharedKey(clientKeyShare.data) if hs.sharedKey == nil { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid client key share") @@ -437,6 +456,8 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { c := hs.c + raiseCFEvent(&cfEventHRR{serverSide: true}) + // The first ClientHello gets double-hashed into the transcript upon a // HelloRetryRequest. See RFC 8446, Section 4.4.1. hs.transcript.Write(hs.clientHello.marshal()) diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/key_agreement.go b/vendor/github.com/marten-seemann/qtls-go1-18/key_agreement.go index 453a8dcf..8a6fd692 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/key_agreement.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/key_agreement.go @@ -168,7 +168,7 @@ type ecdheKeyAgreement struct { func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveID CurveID for _, c := range clientHello.supportedCurves { - if config.supportsCurve(c) { + if config.supportsCurve(c) && curveIdToCirclScheme(c) == nil { curveID = c break } diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/cfkem.go b/vendor/github.com/marten-seemann/qtls-go1-19/cfkem.go new file mode 100644 index 00000000..dc2f2302 --- /dev/null +++ b/vendor/github.com/marten-seemann/qtls-go1-19/cfkem.go @@ -0,0 +1,170 @@ +// Copyright 2022 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. +// +// Glue to add Circl's (post-quantum) hybrid KEMs. +// +// To enable set CurvePreferences with the desired scheme as the first element: +// +// import ( +// "github.com/cloudflare/circl/kem/tls" +// "github.com/cloudflare/circl/kem/hybrid" +// +// [...] +// +// config.CurvePreferences = []tls.CurveID{ +// qtls.X25519Kyber512Draft00, +// qtls.X25519, +// qtls.P256, +// } + +package qtls + +import ( + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/hybrid" + + "crypto/tls" + "fmt" + "io" + "sync" + "time" +) + +// Either ecdheParameters or kem.PrivateKey +type clientKeySharePrivate interface{} + +var ( + X25519Kyber512Draft00 = CurveID(0xfe30) + X25519Kyber768Draft00 = CurveID(0xfe31) + invalidCurveID = CurveID(0) +) + +func kemSchemeKeyToCurveID(s kem.Scheme) CurveID { + switch s.Name() { + case "Kyber512-X25519": + return X25519Kyber512Draft00 + case "Kyber768-X25519": + return X25519Kyber768Draft00 + default: + return invalidCurveID + } +} + +// Extract CurveID from clientKeySharePrivate +func clientKeySharePrivateCurveID(ks clientKeySharePrivate) CurveID { + switch v := ks.(type) { + case kem.PrivateKey: + ret := kemSchemeKeyToCurveID(v.Scheme()) + if ret == invalidCurveID { + panic("cfkem: internal error: don't know CurveID for this KEM") + } + return ret + case ecdheParameters: + return v.CurveID() + default: + panic("cfkem: internal error: unknown clientKeySharePrivate") + } +} + +// Returns scheme by CurveID if supported by Circl +func curveIdToCirclScheme(id CurveID) kem.Scheme { + switch id { + case X25519Kyber512Draft00: + return hybrid.Kyber512X25519() + case X25519Kyber768Draft00: + return hybrid.Kyber768X25519() + } + return nil +} + +// Generate a new shared secret and encapsulates it for the packed +// public key in ppk using randomness from rnd. +func encapsulateForKem(scheme kem.Scheme, rnd io.Reader, ppk []byte) ( + ct, ss []byte, alert alert, err error) { + pk, err := scheme.UnmarshalBinaryPublicKey(ppk) + if err != nil { + return nil, nil, alertIllegalParameter, fmt.Errorf("unpack pk: %w", err) + } + seed := make([]byte, scheme.EncapsulationSeedSize()) + if _, err := io.ReadFull(rnd, seed); err != nil { + return nil, nil, alertInternalError, fmt.Errorf("random: %w", err) + } + ct, ss, err = scheme.EncapsulateDeterministically(pk, seed) + return ct, ss, alertIllegalParameter, err +} + +// Generate a new keypair using randomness from rnd. +func generateKemKeyPair(scheme kem.Scheme, rnd io.Reader) ( + kem.PublicKey, kem.PrivateKey, error) { + seed := make([]byte, scheme.SeedSize()) + if _, err := io.ReadFull(rnd, seed); err != nil { + return nil, nil, err + } + pk, sk := scheme.DeriveKeyPair(seed) + return pk, sk, nil +} + +// Events. We cannot use the same approach as used in our plain Go fork +// as we cannot change tls.Config, tls.ConnectionState, etc. Also we do +// not want to maintain a fork of quic-go itself as well. This seems +// the simplest option. + +// CFEvent. There are two events: one emitted on HRR and one emitted +type CFEvent interface { + // Common to all events + ServerSide() bool // true if server-side; false if on client-side + + // HRR event. Emitted when an HRR happened. + IsHRR() bool // true if this is an HRR event + + // Handshake event. + IsHandshake() bool // true if this is a handshake event. + Duration() time.Duration // how long did the handshake take? + KEX() tls.CurveID // which kex was established? +} + +type CFEventHandler func(CFEvent) + +// Registers a handler to be called when a CFEvent is emitted; returns +// the previous handler. +func SetCFEventHandler(handler CFEventHandler) CFEventHandler { + cfEventMux.Lock() + ret := cfEventHandler + cfEventHandler = handler + cfEventMux.Unlock() + return ret +} + +func raiseCFEvent(ev CFEvent) { + cfEventMux.Lock() + handler := cfEventHandler + cfEventMux.Unlock() + if handler != nil { + handler(ev) + } +} + +var ( + cfEventMux sync.Mutex + cfEventHandler CFEventHandler +) + +type cfEventHRR struct{ serverSide bool } + +func (*cfEventHRR) IsHRR() bool { return true } +func (ev *cfEventHRR) ServerSide() bool { return ev.serverSide } +func (*cfEventHRR) IsHandshake() bool { return false } +func (ev *cfEventHRR) Duration() time.Duration { panic("wrong event") } +func (ev *cfEventHRR) KEX() tls.CurveID { panic("wrong event") } + +type cfEventHandshake struct { + serverSide bool + duration time.Duration + kex tls.CurveID +} + +func (*cfEventHandshake) IsHRR() bool { return false } +func (ev *cfEventHandshake) ServerSide() bool { return ev.serverSide } +func (*cfEventHandshake) IsHandshake() bool { return true } +func (ev *cfEventHandshake) Duration() time.Duration { return ev.duration } +func (ev *cfEventHandshake) KEX() tls.CurveID { return ev.kex } diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/common.go b/vendor/github.com/marten-seemann/qtls-go1-19/common.go index 6670ce2f..3b8de4c5 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/common.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/common.go @@ -345,6 +345,7 @@ type clientSessionState struct { // goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not // SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which // are supported via this interface. +// //go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/marten-seemann/qtls-go1-17 ClientSessionCache" type ClientSessionCache = tls.ClientSessionCache diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go index 4407683a..65aae636 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client.go @@ -40,7 +40,7 @@ type clientHandshakeState struct { var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme -func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { +func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error) { config := c.config if len(config.ServerName) == 0 && !config.InsecureSkipVerify { return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") @@ -142,7 +142,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms } - var params ecdheParameters + var secret clientKeySharePrivate if hello.supportedVersions[0] == VersionTLS13 { var suites []uint16 for _, suiteID := range configCipherSuites { @@ -163,21 +163,37 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { } curveID := config.curvePreferences()[0] - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + if scheme := curveIdToCirclScheme(curveID); scheme != nil { + pk, sk, err := generateKemKeyPair(scheme, config.rand()) + if err != nil { + return nil, nil, fmt.Errorf("generateKemKeyPair %s: %w", + scheme.Name(), err) + } + packedPk, err := pk.MarshalBinary() + if err != nil { + return nil, nil, fmt.Errorf("pack circl public key %s: %w", + scheme.Name(), err) + } + hello.keyShares = []keyShare{{group: curveID, data: packedPk}} + secret = sk + } else { + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, nil, err + } + hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} + secret = params } - params, err = generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, nil, err - } - hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil { hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello) } - return hello, params, nil + return hello, secret, nil } func (c *Conn) clientHandshake(ctx context.Context) (err error) { @@ -263,14 +279,14 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { if c.vers == VersionTLS13 { hs := &clientHandshakeStateTLS13{ - c: c, - ctx: ctx, - serverHello: serverHello, - hello: hello, - ecdheParams: ecdheParams, - session: session, - earlySecret: earlySecret, - binderKey: binderKey, + c: c, + ctx: ctx, + serverHello: serverHello, + hello: hello, + keySharePrivate: ecdheParams, + session: session, + earlySecret: earlySecret, + binderKey: binderKey, } // In TLS 1.3, session tickets are delivered after the handshake. diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go index 7f05f2c6..7d470c42 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_client_tls13.go @@ -12,10 +12,12 @@ import ( "crypto/rsa" "encoding/binary" "errors" + "fmt" "hash" "sync/atomic" "time" + circlKem "github.com/cloudflare/circl/kem" "golang.org/x/crypto/cryptobyte" ) @@ -24,7 +26,8 @@ type clientHandshakeStateTLS13 struct { ctx context.Context serverHello *serverHelloMsg hello *clientHelloMsg - ecdheParams ecdheParameters + + keySharePrivate clientKeySharePrivate session *clientSessionState earlySecret []byte @@ -44,6 +47,8 @@ type clientHandshakeStateTLS13 struct { func (hs *clientHandshakeStateTLS13) handshake() error { c := hs.c + startTime := time.Now() + if needFIPS() { return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") } @@ -56,7 +61,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } // Consistency check on the presence of a keyShare and its parameters. - if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { + if hs.keySharePrivate == nil || len(hs.hello.keyShares) != 1 { return c.sendAlert(alertInternalError) } @@ -107,6 +112,12 @@ func (hs *clientHandshakeStateTLS13) handshake() error { return err } + raiseCFEvent(&cfEventHandshake{ + serverSide: false, + duration: time.Since(startTime), + kex: hs.serverHello.serverShare.group, + }) + atomic.StoreUint32(&c.handshakeStatus, 1) return nil @@ -184,6 +195,8 @@ func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { c := hs.c + raiseCFEvent(&cfEventHRR{serverSide: false}) + // The first ClientHello gets double-hashed into the transcript upon a // HelloRetryRequest. (The idea is that the server might offload transcript // storage to the client in the cookie.) See RFC 8446, Section 4.4.1. @@ -225,21 +238,38 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: server selected unsupported group") } - if hs.ecdheParams.CurveID() == curveID { + if clientKeySharePrivateCurveID(hs.keySharePrivate) == curveID { c.sendAlert(alertIllegalParameter) return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share") } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") + if scheme := curveIdToCirclScheme(curveID); scheme != nil { + pk, sk, err := generateKemKeyPair(scheme, c.config.rand()) + if err != nil { + c.sendAlert(alertInternalError) + return fmt.Errorf("HRR generateKeyPair %s: %w", + scheme.Name(), err) + } + packedPk, err := pk.MarshalBinary() + if err != nil { + c.sendAlert(alertInternalError) + return fmt.Errorf("HRR pack circl public key %s: %w", + scheme.Name(), err) + } + hs.keySharePrivate = sk + hs.hello.keyShares = []keyShare{{group: curveID, data: packedPk}} + } else { + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + c.sendAlert(alertInternalError) + return errors.New("tls: CurvePreferences includes unsupported curve") + } + params, err := generateECDHEParameters(c.config.rand(), curveID) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.keySharePrivate = params + hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } - params, err := generateECDHEParameters(c.config.rand(), curveID) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.ecdheParams = params - hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} } hs.hello.raw = nil @@ -318,7 +348,7 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: server did not send a key share") } - if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { + if hs.serverHello.serverShare.group != clientKeySharePrivateCurveID(hs.keySharePrivate) { c.sendAlert(alertIllegalParameter) return errors.New("tls: server selected unsupported group") } @@ -356,7 +386,18 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error { func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { c := hs.c - sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) + var sharedKey []byte + if params, ok := hs.keySharePrivate.(ecdheParameters); ok { + sharedKey = params.SharedKey(hs.serverHello.serverShare.data) + } else if sk, ok := hs.keySharePrivate.(circlKem.PrivateKey); ok { + var err error + sharedKey, err = sk.Scheme().Decapsulate(sk, hs.serverHello.serverShare.data) + if err != nil { + c.sendAlert(alertIllegalParameter) + return fmt.Errorf("%s decaps: %w", sk.Scheme().Name(), err) + } + } + if sharedKey == nil { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid server key share") diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go index e3db8063..9fb32c35 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server_tls13.go @@ -11,6 +11,7 @@ import ( "crypto/hmac" "crypto/rsa" "errors" + "fmt" "hash" "io" "sync/atomic" @@ -46,6 +47,8 @@ type serverHandshakeStateTLS13 struct { func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c + startTime := time.Now() + if needFIPS() { return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") } @@ -83,6 +86,12 @@ func (hs *serverHandshakeStateTLS13) handshake() error { return err } + raiseCFEvent(&cfEventHandshake{ + serverSide: true, + duration: time.Since(startTime), + kex: hs.hello.serverShare.group, + }) + atomic.StoreUint32(&c.handshakeStatus, 1) return nil @@ -210,17 +219,27 @@ GroupSelection: clientKeyShare = &hs.clientHello.keyShares[0] } - if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok { + if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && curveIdToCirclScheme(selectedGroup) == nil && !ok { c.sendAlert(alertInternalError) return errors.New("tls: CurvePreferences includes unsupported curve") } - params, err := generateECDHEParameters(c.config.rand(), selectedGroup) - if err != nil { - c.sendAlert(alertInternalError) - return err + if kem := curveIdToCirclScheme(selectedGroup); kem != nil { + ct, ss, alert, err := encapsulateForKem(kem, c.config.rand(), clientKeyShare.data) + if err != nil { + c.sendAlert(alert) + return fmt.Errorf("%s encap: %w", kem.Name(), err) + } + hs.hello.serverShare = keyShare{group: selectedGroup, data: ct} + hs.sharedKey = ss + } else { + params, err := generateECDHEParameters(c.config.rand(), selectedGroup) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} + hs.sharedKey = params.SharedKey(clientKeyShare.data) } - hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} - hs.sharedKey = params.SharedKey(clientKeyShare.data) if hs.sharedKey == nil { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid client key share") @@ -441,6 +460,8 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { c := hs.c + raiseCFEvent(&cfEventHRR{serverSide: true}) + // The first ClientHello gets double-hashed into the transcript upon a // HelloRetryRequest. See RFC 8446, Section 4.4.1. hs.transcript.Write(hs.clientHello.marshal()) diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go b/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go index 453a8dcf..8a6fd692 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/key_agreement.go @@ -168,7 +168,7 @@ type ecdheKeyAgreement struct { func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveID CurveID for _, c := range clientHello.supportedCurves { - if config.supportsCurve(c) { + if config.supportsCurve(c) && curveIdToCirclScheme(c) == nil { curveID = c break } diff --git a/vendor/golang.org/x/crypto/AUTHORS b/vendor/golang.org/x/crypto/AUTHORS deleted file mode 100644 index 2b00ddba..00000000 --- a/vendor/golang.org/x/crypto/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at https://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/golang.org/x/crypto/CONTRIBUTORS deleted file mode 100644 index 1fbd3e97..00000000 --- a/vendor/golang.org/x/crypto/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at https://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go index cda3fdd3..bc62161d 100644 --- a/vendor/golang.org/x/crypto/curve25519/curve25519.go +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -9,7 +9,8 @@ package curve25519 // import "golang.org/x/crypto/curve25519" import ( "crypto/subtle" - "fmt" + "errors" + "strconv" "golang.org/x/crypto/curve25519/internal/field" ) @@ -124,10 +125,10 @@ func X25519(scalar, point []byte) ([]byte, error) { func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { var in [32]byte if l := len(scalar); l != 32 { - return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32) + return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32") } if l := len(point); l != 32 { - return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32) + return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32") } copy(in[:], scalar) if &point[0] == &Basepoint[0] { @@ -138,7 +139,7 @@ func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { copy(base[:], point) ScalarMult(dst, &in, &base) if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 { - return nil, fmt.Errorf("bad input point: low order point") + return nil, errors.New("bad input point: low order point") } } return dst[:], nil diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index a69e2249..4600c207 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -460,6 +460,8 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { // certKeyAlgoNames is a mapping from known certificate algorithm names to the // corresponding public key signature algorithm. +// +// This map must be kept in sync with the one in agent/client.go. var certKeyAlgoNames = map[string]string{ CertAlgoRSAv01: KeyAlgoRSA, CertAlgoRSASHA256v01: KeyAlgoRSASHA256, diff --git a/vendor/golang.org/x/sys/cpu/cpu_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_arm64.go index 87dd5e30..bbaba18b 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_arm64.go @@ -41,13 +41,10 @@ func archInit() { switch runtime.GOOS { case "freebsd": readARM64Registers() - case "linux", "netbsd": + case "linux", "netbsd", "openbsd": doinit() default: - // Most platforms don't seem to allow reading these registers. - // - // OpenBSD: - // See https://golang.org/issue/31746 + // Many platforms don't seem to allow reading these registers. setMinimalFeatures() } } diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go new file mode 100644 index 00000000..85b64d5c --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "unsafe" +) + +// Minimal copy of functionality from x/sys/unix so the cpu package can call +// sysctl without depending on x/sys/unix. + +const ( + // From OpenBSD's sys/sysctl.h. + _CTL_MACHDEP = 7 + + // From OpenBSD's machine/cpu.h. + _CPU_ID_AA64ISAR0 = 2 + _CPU_ID_AA64ISAR1 = 3 +) + +// Implemented in the runtime package (runtime/sys_openbsd3.go) +func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) + +//go:linkname syscall_syscall6 syscall.syscall6 + +func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + _, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if errno != 0 { + return errno + } + return nil +} + +var libc_sysctl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_sysctl sysctl "libc.so" + +func sysctlUint64(mib []uint32) (uint64, bool) { + var out uint64 + nout := unsafe.Sizeof(out) + if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil { + return 0, false + } + return out, true +} + +func doinit() { + setMinimalFeatures() + + // Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl. + isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0}) + if !ok { + return + } + isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1}) + if !ok { + return + } + parseARM64SystemRegisters(isar0, isar1, 0) + + Initialized = true +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s new file mode 100644 index 00000000..054ba05d --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_openbsd_arm64.s @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_sysctl(SB) + +GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) diff --git a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go index f8c484f5..f3cde129 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_other_arm64.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux && !netbsd && arm64 -// +build !linux,!netbsd,arm64 +//go:build !linux && !netbsd && !openbsd && arm64 +// +build !linux,!netbsd,!openbsd,arm64 package cpu diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index dcef4de6..2cd0e916 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -73,12 +73,12 @@ aix_ppc64) darwin_amd64) mkerrors="$mkerrors -m64" mktypes="GOARCH=$GOARCH go tool cgo -godefs" - mkasm="go run mkasm_darwin.go" + mkasm="go run mkasm.go" ;; darwin_arm64) mkerrors="$mkerrors -m64" mktypes="GOARCH=$GOARCH go tool cgo -godefs" - mkasm="go run mkasm_darwin.go" + mkasm="go run mkasm.go" ;; dragonfly_amd64) mkerrors="$mkerrors -m64" @@ -232,5 +232,5 @@ esac if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; fi - if [ -n "$mkasm" ]; then echo "$mkasm $GOARCH"; fi + if [ -n "$mkasm" ]; then echo "$mkasm $GOOS $GOARCH"; fi ) | $run diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go index e2a30e88..2db1b51e 100644 --- a/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -253,7 +253,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle var empty bool if len(oob) > 0 { // send at least one normal byte - empty := emptyIovecs(iov) + empty = emptyIovecs(iov) if empty { var iova [1]Iovec iova[0].Base = &dummy diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index c437fc5d..eda42671 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -363,7 +363,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle var empty bool if len(oob) > 0 { // send at least one normal byte - empty := emptyIovecs(iov) + empty = emptyIovecs(iov) if empty { var iova [1]Iovec iova[0].Base = &dummy diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 5e4a94f7..ecb0f27f 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1541,7 +1541,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle var dummy byte var empty bool if len(oob) > 0 { - empty := emptyIovecs(iov) + empty = emptyIovecs(iov) if empty { var sockType int sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE) diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 274e2dab..36c0dfc7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include -m32 +// mkerrors.sh -Wall -Werror -static -I/tmp/386/include -m32 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux // +build 386,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/386/include -m32 _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 95b6eeed..4ff94270 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include -m64 +// mkerrors.sh -Wall -Werror -static -I/tmp/amd64/include -m64 // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux // +build amd64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/amd64/include -m64 _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 918cd130..3eaa0fb7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/arm/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux // +build arm,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/arm/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 3907dc5a..d7995bdc 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include -fsigned-char +// mkerrors.sh -Wall -Werror -static -I/tmp/arm64/include -fsigned-char // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux // +build arm64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 03d5c105..928e24c2 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/loong64/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux // +build loong64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/loong64/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index bd794e01..179bffb4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/mips/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux // +build mips,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/mips/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 6c741b05..1fba17bd 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/mips64/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux // +build mips64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/mips64/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 807b8cd2..b77dde31 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/mips64le/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux // +build mips64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/mips64le/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index a39e4f5c..78c6c751 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/mipsle/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux // +build mipsle,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/mipsle/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index c0fcda86..1c0d31f0 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/ppc/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux // +build ppc,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index f3b72407..959dd9bb 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/ppc64/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux // +build ppc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 72f2a45d..5a873cdb 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/ppc64le/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux // +build ppc64le,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64le/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 45b214b4..e336d141 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/riscv64/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux // +build riscv64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/riscv64/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 1897f207..390c01d9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include -fsigned-char +// mkerrors.sh -Wall -Werror -static -I/tmp/s390x/include -fsigned-char // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux // +build s390x,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/s390x/include -fsigned-char _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 1fb7a395..98a6e5f1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -1,11 +1,11 @@ -// mkerrors.sh -Wall -Werror -static -I/tmp/include +// mkerrors.sh -Wall -Werror -static -I/tmp/sparc64/include // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux // +build sparc64,linux // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go +// cgo -godefs -- -Wall -Werror -static -I/tmp/sparc64/include _const.go package unix diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.s index d6c3e25c..f5bb40ed 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.1_13.s @@ -1,4 +1,4 @@ -// go run mkasm_darwin.go amd64 +// go run mkasm.go darwin amd64 // Code generated by the command above; DO NOT EDIT. //go:build go1.13 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index 7e308a47..b41467a0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -1,4 +1,4 @@ -// go run mkasm_darwin.go amd64 +// go run mkasm.go darwin amd64 // Code generated by the command above; DO NOT EDIT. //go:build go1.12 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.s index 35798972..0c3f76bc 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.1_13.s @@ -1,4 +1,4 @@ -// go run mkasm_darwin.go arm64 +// go run mkasm.go darwin arm64 // Code generated by the command above; DO NOT EDIT. //go:build go1.13 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index b09e5bb0..e1f9204a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -1,4 +1,4 @@ -// go run mkasm_darwin.go arm64 +// go run mkasm.go darwin arm64 // Code generated by the command above; DO NOT EDIT. //go:build go1.12 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 62192e1d..c9c4ad03 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -m32 /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/386/include -m32 /tmp/386/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 490aab5d..12ff3417 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -m64 /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/amd64/include -m64 /tmp/amd64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index aca17b6f..c3fb5e77 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/arm/include /tmp/arm/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 54b4dfa5..358c847a 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/arm64/include -fsigned-char /tmp/arm64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index 44a764c9..81c4849b 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/loong64/include /tmp/loong64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 65a99efc..202a57e9 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/mips/include /tmp/mips/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 841c8a66..1fbceb52 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/mips64/include /tmp/mips64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index e26a7c76..b4ffb7a2 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/mips64le/include /tmp/mips64le/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index 26447260..867985f9 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/mipsle/include /tmp/mipsle/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index 26aefc18..a8cce69e 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/ppc/include /tmp/ppc/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 8d4cd9d9..d44c5b39 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/ppc64/include /tmp/ppc64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index 3b405d1f..4214dd9c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/ppc64le/include /tmp/ppc64le/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 3a9c96b2..3e594a8c 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/riscv64/include /tmp/riscv64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 8ffa6646..7ea46520 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/s390x/include -fsigned-char /tmp/s390x/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 6a39640e..92f628ef 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -1,4 +1,4 @@ -// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h +// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/sparc64/include /tmp/sparc64/include/asm/unistd.h // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 86984798..ff688116 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -945,6 +945,9 @@ type PerfEventAttr struct { Aux_watermark uint32 Sample_max_stack uint16 _ uint16 + Aux_sample_size uint32 + _ uint32 + Sig_data uint64 } type PerfEventMmapPage struct { @@ -1463,6 +1466,11 @@ const ( IFLA_ALT_IFNAME = 0x35 IFLA_PERM_ADDRESS = 0x36 IFLA_PROTO_DOWN_REASON = 0x37 + IFLA_PARENT_DEV_NAME = 0x38 + IFLA_PARENT_DEV_BUS_NAME = 0x39 + IFLA_GRO_MAX_SIZE = 0x3a + IFLA_TSO_MAX_SIZE = 0x3b + IFLA_TSO_MAX_SEGS = 0x3c IFLA_PROTO_DOWN_REASON_UNSPEC = 0x0 IFLA_PROTO_DOWN_REASON_MASK = 0x1 IFLA_PROTO_DOWN_REASON_VALUE = 0x2 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go index 7551af48..26360440 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_386.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_386.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/386/cgo -- -Wall -Werror -static -I/tmp/386/include -m32 linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build 386 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go index 3e738ac0..8187489d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/amd64/cgo -- -Wall -Werror -static -I/tmp/amd64/include -m64 linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build amd64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go index 6183eef4..d1612335 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/arm/cgo -- -Wall -Werror -static -I/tmp/arm/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go index 968cecb1..c28e5556 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/arm64/cgo -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build arm64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go index 8fe4c522..187061f9 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/loong64/cgo -- -Wall -Werror -static -I/tmp/loong64/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build loong64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go index 11426a30..36912991 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/mips/cgo -- -Wall -Werror -static -I/tmp/mips/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go index ad1c3b3d..7473468d 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/mips64/cgo -- -Wall -Werror -static -I/tmp/mips64/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go index 15fd84e4..ed944852 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/mips64le/cgo -- -Wall -Werror -static -I/tmp/mips64le/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mips64le && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go index 49c49825..0892a73a 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/mipsle/cgo -- -Wall -Werror -static -I/tmp/mipsle/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build mipsle && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go index cd36d0da..e1dd4833 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/ppc/cgo -- -Wall -Werror -static -I/tmp/ppc/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go index 8c6fce03..d9f654c7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/ppc64/cgo -- -Wall -Werror -static -I/tmp/ppc64/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go index 20910f2a..74acda9f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/ppc64le/cgo -- -Wall -Werror -static -I/tmp/ppc64le/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build ppc64le && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 71b7b333..50ebe69e 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/riscv64/cgo -- -Wall -Werror -static -I/tmp/riscv64/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build riscv64 && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go index 71184cc2..75b34c25 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/s390x/cgo -- -Wall -Werror -static -I/tmp/s390x/include -fsigned-char linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build s390x && linux diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go index 06156285..429c3bf7 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go @@ -1,4 +1,4 @@ -// cgo -godefs -- -Wall -Werror -static -I/tmp/include linux/types.go | go run mkpost.go +// cgo -godefs -objdir=/tmp/sparc64/cgo -- -Wall -Werror -static -I/tmp/sparc64/include linux/types.go | go run mkpost.go // Code generated by the command above; see README.md. DO NOT EDIT. //go:build sparc64 && linux diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index be3ec2bd..e2791381 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -417,6 +417,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation //sys GetModuleFileNameEx(process Handle, module Handle, filename *uint16, size uint32) (err error) = psapi.GetModuleFileNameExW //sys GetModuleBaseName(process Handle, module Handle, baseName *uint16, size uint32) (err error) = psapi.GetModuleBaseNameW +//sys QueryWorkingSetEx(process Handle, pv uintptr, cb uint32) (err error) = psapi.QueryWorkingSetEx // NT Native APIs //sys rtlNtStatusToDosErrorNoTeb(ntstatus NTStatus) (ret syscall.Errno) = ntdll.RtlNtStatusToDosErrorNoTeb @@ -971,6 +972,32 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) { return unsafe.Pointer(&sa.raw), sl, nil } +type RawSockaddrBth struct { + AddressFamily [2]byte + BtAddr [8]byte + ServiceClassId [16]byte + Port [4]byte +} + +type SockaddrBth struct { + BtAddr uint64 + ServiceClassId GUID + Port uint32 + + raw RawSockaddrBth +} + +func (sa *SockaddrBth) sockaddr() (unsafe.Pointer, int32, error) { + family := AF_BTH + sa.raw = RawSockaddrBth{ + AddressFamily: *(*[2]byte)(unsafe.Pointer(&family)), + BtAddr: *(*[8]byte)(unsafe.Pointer(&sa.BtAddr)), + Port: *(*[4]byte)(unsafe.Pointer(&sa.Port)), + ServiceClassId: *(*[16]byte)(unsafe.Pointer(&sa.ServiceClassId)), + } + return unsafe.Pointer(&sa.raw), int32(unsafe.Sizeof(sa.raw)), nil +} + func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { switch rsa.Addr.Family { case AF_UNIX: @@ -1707,3 +1734,71 @@ func LoadResourceData(module, resInfo Handle) (data []byte, err error) { h.Cap = int(size) return } + +// PSAPI_WORKING_SET_EX_BLOCK contains extended working set information for a page. +type PSAPI_WORKING_SET_EX_BLOCK uint64 + +// Valid returns the validity of this page. +// If this bit is 1, the subsequent members are valid; otherwise they should be ignored. +func (b PSAPI_WORKING_SET_EX_BLOCK) Valid() bool { + return (b & 1) == 1 +} + +// ShareCount is the number of processes that share this page. The maximum value of this member is 7. +func (b PSAPI_WORKING_SET_EX_BLOCK) ShareCount() uint64 { + return b.intField(1, 3) +} + +// Win32Protection is the memory protection attributes of the page. For a list of values, see +// https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants +func (b PSAPI_WORKING_SET_EX_BLOCK) Win32Protection() uint64 { + return b.intField(4, 11) +} + +// Shared returns the shared status of this page. +// If this bit is 1, the page can be shared. +func (b PSAPI_WORKING_SET_EX_BLOCK) Shared() bool { + return (b & (1 << 15)) == 1 +} + +// Node is the NUMA node. The maximum value of this member is 63. +func (b PSAPI_WORKING_SET_EX_BLOCK) Node() uint64 { + return b.intField(16, 6) +} + +// Locked returns the locked status of this page. +// If this bit is 1, the virtual page is locked in physical memory. +func (b PSAPI_WORKING_SET_EX_BLOCK) Locked() bool { + return (b & (1 << 22)) == 1 +} + +// LargePage returns the large page status of this page. +// If this bit is 1, the page is a large page. +func (b PSAPI_WORKING_SET_EX_BLOCK) LargePage() bool { + return (b & (1 << 23)) == 1 +} + +// Bad returns the bad status of this page. +// If this bit is 1, the page is has been reported as bad. +func (b PSAPI_WORKING_SET_EX_BLOCK) Bad() bool { + return (b & (1 << 31)) == 1 +} + +// intField extracts an integer field in the PSAPI_WORKING_SET_EX_BLOCK union. +func (b PSAPI_WORKING_SET_EX_BLOCK) intField(start, length int) uint64 { + var mask PSAPI_WORKING_SET_EX_BLOCK + for pos := start; pos < start+length; pos++ { + mask |= (1 << pos) + } + + masked := b & mask + return uint64(masked >> start) +} + +// PSAPI_WORKING_SET_EX_INFORMATION contains extended working set information for a process. +type PSAPI_WORKING_SET_EX_INFORMATION struct { + // The virtual address. + VirtualAddress Pointer + // A PSAPI_WORKING_SET_EX_BLOCK union that indicates the attributes of the page at VirtualAddress. + VirtualAttributes PSAPI_WORKING_SET_EX_BLOCK +} diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 678262cd..52d4742c 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -408,6 +408,7 @@ var ( procGetModuleBaseNameW = modpsapi.NewProc("GetModuleBaseNameW") procGetModuleFileNameExW = modpsapi.NewProc("GetModuleFileNameExW") procGetModuleInformation = modpsapi.NewProc("GetModuleInformation") + procQueryWorkingSetEx = modpsapi.NewProc("QueryWorkingSetEx") procSubscribeServiceChangeNotifications = modsechost.NewProc("SubscribeServiceChangeNotifications") procUnsubscribeServiceChangeNotifications = modsechost.NewProc("UnsubscribeServiceChangeNotifications") procGetUserNameExW = modsecur32.NewProc("GetUserNameExW") @@ -3504,6 +3505,14 @@ func GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb return } +func QueryWorkingSetEx(process Handle, pv uintptr, cb uint32) (err error) { + r1, _, e1 := syscall.Syscall(procQueryWorkingSetEx.Addr(), 3, uintptr(process), uintptr(pv), uintptr(cb)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) { ret = procSubscribeServiceChangeNotifications.Find() if ret != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c3ca424..0ae2b93a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -26,6 +26,28 @@ github.com/cloudflare/brotli-go/brotli github.com/cloudflare/brotli-go/common github.com/cloudflare/brotli-go/dec github.com/cloudflare/brotli-go/enc +# github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 +## explicit; go 1.16 +github.com/cloudflare/circl/dh/x25519 +github.com/cloudflare/circl/dh/x448 +github.com/cloudflare/circl/internal/conv +github.com/cloudflare/circl/internal/sha3 +github.com/cloudflare/circl/kem +github.com/cloudflare/circl/kem/hybrid +github.com/cloudflare/circl/kem/kyber/kyber1024 +github.com/cloudflare/circl/kem/kyber/kyber512 +github.com/cloudflare/circl/kem/kyber/kyber768 +github.com/cloudflare/circl/math/fp25519 +github.com/cloudflare/circl/math/fp448 +github.com/cloudflare/circl/pke/kyber/internal/common +github.com/cloudflare/circl/pke/kyber/internal/common/params +github.com/cloudflare/circl/pke/kyber/kyber1024 +github.com/cloudflare/circl/pke/kyber/kyber1024/internal +github.com/cloudflare/circl/pke/kyber/kyber512 +github.com/cloudflare/circl/pke/kyber/kyber512/internal +github.com/cloudflare/circl/pke/kyber/kyber768 +github.com/cloudflare/circl/pke/kyber/kyber768/internal +github.com/cloudflare/circl/simd/keccakf1600 # github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc ## explicit github.com/cloudflare/golibs/lrucache @@ -171,10 +193,10 @@ github.com/marten-seemann/qtls-go1-16 # github.com/marten-seemann/qtls-go1-17 v0.1.2 ## explicit; go 1.17 github.com/marten-seemann/qtls-go1-17 -# github.com/marten-seemann/qtls-go1-18 v0.1.2 +# github.com/marten-seemann/qtls-go1-18 v0.1.2 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af ## explicit; go 1.18 github.com/marten-seemann/qtls-go1-18 -# github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 +# github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af ## explicit; go 1.19 github.com/marten-seemann/qtls-go1-19 # github.com/mattn/go-colorable v0.1.8 @@ -320,7 +342,7 @@ go.opentelemetry.io/proto/otlp/trace/v1 go.uber.org/automaxprocs/internal/cgroups go.uber.org/automaxprocs/internal/runtime go.uber.org/automaxprocs/maxprocs -# golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f +# golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa ## explicit; go 1.17 golang.org/x/crypto/blake2b golang.org/x/crypto/blowfish @@ -371,7 +393,7 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ## explicit golang.org/x/sync/errgroup -# golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 +# golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -554,3 +576,5 @@ zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 # gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 +# github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af +# github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af From e3803335206bef2cc87196302920096010a71828 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 7 Sep 2022 15:06:06 -0700 Subject: [PATCH 192/238] TUN-6688: Update RegisterUdpSession capnproto to include trace context --- Makefile | 2 +- connection/quic.go | 2 +- connection/quic_test.go | 2 +- quic/quic_protocol.go | 8 +- quic/quic_protocol_test.go | 95 ++++--- tunnelrpc/pogs/sessionrpc.go | 24 +- tunnelrpc/tunnelrpc.capnp | 5 +- tunnelrpc/tunnelrpc.capnp.go | 502 +++++++++++++++++++---------------- 8 files changed, 361 insertions(+), 279 deletions(-) diff --git a/Makefile b/Makefile index 0fc54935..effcadba 100644 --- a/Makefile +++ b/Makefile @@ -291,7 +291,7 @@ github-mac-upload: .PHONY: tunnelrpc-deps tunnelrpc-deps: which capnp # https://capnproto.org/install.html - which capnpc-go # go get zombiezen.com/go/capnproto2/capnpc-go + which capnpc-go # go install zombiezen.com/go/capnproto2/capnpc-go@latest capnp compile -ogo tunnelrpc/tunnelrpc.capnp .PHONY: quic-deps diff --git a/connection/quic.go b/connection/quic.go index 10361088..d1b126da 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -258,7 +258,7 @@ func (q *QUICConnection) handleRPCStream(rpcStream *quicpogs.RPCServerStream) er } // RegisterUdpSession is the RPC method invoked by edge to register and run a session -func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration) error { +func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) error { // Each session is a series of datagram from an eyeball to a dstIP:dstPort. // (src port, dst IP, dst port) uniquely identifies a session, so it needs a dedicated connected socket. originProxy, err := ingress.DialUDP(dstIP, dstPort) diff --git a/connection/quic_test.go b/connection/quic_test.go index c860400e..26b71e48 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -652,7 +652,7 @@ type mockSessionRPCServer struct { calledUnregisterChan chan struct{} } -func (s mockSessionRPCServer) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration) error { +func (s mockSessionRPCServer) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) error { return fmt.Errorf("mockSessionRPCServer doesn't implement RegisterUdpSession") } diff --git a/quic/quic_protocol.go b/quic/quic_protocol.go index 776b2eec..8c7baf87 100644 --- a/quic/quic_protocol.go +++ b/quic/quic_protocol.go @@ -247,12 +247,8 @@ func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, logger * }, nil } -func (rcs *RPCClientStream) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfterHint time.Duration) error { - resp, err := rcs.client.RegisterUdpSession(ctx, sessionID, dstIP, dstPort, closeIdleAfterHint) - if err != nil { - return err - } - return resp.Err +func (rcs *RPCClientStream) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfterHint time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) { + return rcs.client.RegisterUdpSession(ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext) } func (rcs *RPCClientStream) UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, message string) error { diff --git a/quic/quic_protocol_test.go b/quic/quic_protocol_test.go index a801b63e..9693a808 100644 --- a/quic/quic_protocol_test.go +++ b/quic/quic_protocol_test.go @@ -110,45 +110,74 @@ func TestConnectResponseMeta(t *testing.T) { } func TestRegisterUdpSession(t *testing.T) { - clientStream, serverStream := newMockRPCStreams() - unregisterMessage := "closed by eyeball" - sessionRPCServer := mockSessionRPCServer{ - sessionID: uuid.New(), - dstIP: net.IP{172, 16, 0, 1}, - dstPort: 8000, - closeIdleAfter: testCloseIdleAfterHint, - unregisterMessage: unregisterMessage, + + var tests = []struct { + name string + sessionRPCServer mockSessionRPCServer + }{ + { + name: "RegisterUdpSession (no trace context)", + sessionRPCServer: mockSessionRPCServer{ + sessionID: uuid.New(), + dstIP: net.IP{172, 16, 0, 1}, + dstPort: 8000, + closeIdleAfter: testCloseIdleAfterHint, + unregisterMessage: unregisterMessage, + traceContext: "", + }, + }, + { + name: "RegisterUdpSession (with trace context)", + sessionRPCServer: mockSessionRPCServer{ + sessionID: uuid.New(), + dstIP: net.IP{172, 16, 0, 1}, + dstPort: 8000, + closeIdleAfter: testCloseIdleAfterHint, + unregisterMessage: unregisterMessage, + traceContext: "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1", + }, + }, } - logger := zerolog.Nop() - sessionRegisteredChan := make(chan struct{}) - go func() { - protocol, err := DetermineProtocol(serverStream) - assert.NoError(t, err) - rpcServerStream, err := NewRPCServerStream(serverStream, protocol) - assert.NoError(t, err) - err = rpcServerStream.Serve(sessionRPCServer, nil, &logger) - assert.NoError(t, err) - serverStream.Close() - close(sessionRegisteredChan) - }() + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + logger := zerolog.Nop() + clientStream, serverStream := newMockRPCStreams() + sessionRegisteredChan := make(chan struct{}) + go func() { + protocol, err := DetermineProtocol(serverStream) + assert.NoError(t, err) + rpcServerStream, err := NewRPCServerStream(serverStream, protocol) + assert.NoError(t, err) + err = rpcServerStream.Serve(test.sessionRPCServer, nil, &logger) + assert.NoError(t, err) - rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, &logger) - assert.NoError(t, err) + serverStream.Close() + close(sessionRegisteredChan) + }() - assert.NoError(t, rpcClientStream.RegisterUdpSession(context.Background(), sessionRPCServer.sessionID, sessionRPCServer.dstIP, sessionRPCServer.dstPort, testCloseIdleAfterHint)) + rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, &logger) + assert.NoError(t, err) - // Different sessionID, the RPC server should reject the registraion - assert.Error(t, rpcClientStream.RegisterUdpSession(context.Background(), uuid.New(), sessionRPCServer.dstIP, sessionRPCServer.dstPort, testCloseIdleAfterHint)) + reg, err := rpcClientStream.RegisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext) + assert.NoError(t, err) + assert.NoError(t, reg.Err) - assert.NoError(t, rpcClientStream.UnregisterUdpSession(context.Background(), sessionRPCServer.sessionID, unregisterMessage)) + // Different sessionID, the RPC server should reject the registraion + reg, err = rpcClientStream.RegisterUdpSession(context.Background(), uuid.New(), test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext) + assert.NoError(t, err) + assert.Error(t, reg.Err) - // Different sessionID, the RPC server should reject the unregistraion - assert.Error(t, rpcClientStream.UnregisterUdpSession(context.Background(), uuid.New(), unregisterMessage)) + assert.NoError(t, rpcClientStream.UnregisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, unregisterMessage)) - rpcClientStream.Close() - <-sessionRegisteredChan + // Different sessionID, the RPC server should reject the unregistraion + assert.Error(t, rpcClientStream.UnregisterUdpSession(context.Background(), uuid.New(), unregisterMessage)) + + rpcClientStream.Close() + <-sessionRegisteredChan + }) + } } func TestManageConfiguration(t *testing.T) { @@ -198,9 +227,10 @@ type mockSessionRPCServer struct { dstPort uint16 closeIdleAfter time.Duration unregisterMessage string + traceContext string } -func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration) error { +func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) error { if s.sessionID != sessionID { return fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID) } @@ -213,6 +243,9 @@ func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uu if s.closeIdleAfter != closeIdleAfter { return fmt.Errorf("expect closeIdleAfter %d, got %d", s.closeIdleAfter, closeIdleAfter) } + if s.traceContext != traceContext { + return fmt.Errorf("expect traceContext %s, got %s", s.traceContext, traceContext) + } return nil } diff --git a/tunnelrpc/pogs/sessionrpc.go b/tunnelrpc/pogs/sessionrpc.go index 5fa1642c..378fc205 100644 --- a/tunnelrpc/pogs/sessionrpc.go +++ b/tunnelrpc/pogs/sessionrpc.go @@ -14,7 +14,7 @@ import ( ) type SessionManager interface { - RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration) error + RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) error UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, message string) error } @@ -50,12 +50,19 @@ func (i SessionManager_PogsImpl) RegisterUdpSession(p tunnelrpc.SessionManager_r closeIdleAfterHint := time.Duration(p.Params.CloseAfterIdleHint()) + traceContext, err := p.Params.TraceContext() + if err != nil { + return err + } + resp := RegisterUdpSessionResponse{} - registrationErr := i.impl.RegisterUdpSession(p.Ctx, sessionID, dstIP, dstPort, closeIdleAfterHint) + registrationErr := i.impl.RegisterUdpSession(p.Ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext) if registrationErr != nil { resp.Err = registrationErr } + // TUN-6689: Add spans to return path for RegisterUdpSession + result, err := p.Results.NewResult() if err != nil { return err @@ -85,13 +92,17 @@ func (i SessionManager_PogsImpl) UnregisterUdpSession(p tunnelrpc.SessionManager } type RegisterUdpSessionResponse struct { - Err error + Err error + Spans []byte // Spans in protobuf format } func (p *RegisterUdpSessionResponse) Marshal(s tunnelrpc.RegisterUdpSessionResponse) error { if p.Err != nil { return s.SetErr(p.Err.Error()) } + if err := s.SetSpans(p.Spans); err != nil { + return err + } return nil } @@ -103,6 +114,10 @@ func (p *RegisterUdpSessionResponse) Unmarshal(s tunnelrpc.RegisterUdpSessionRes if respErr != "" { p.Err = fmt.Errorf(respErr) } + p.Spans, err = s.Spans() + if err != nil { + return err + } return nil } @@ -116,7 +131,7 @@ func (c SessionManager_PogsClient) Close() error { return c.Conn.Close() } -func (c SessionManager_PogsClient) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration) (*RegisterUdpSessionResponse, error) { +func (c SessionManager_PogsClient) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) (*RegisterUdpSessionResponse, error) { client := tunnelrpc.SessionManager{Client: c.Client} promise := client.RegisterUdpSession(ctx, func(p tunnelrpc.SessionManager_registerUdpSession_Params) error { if err := p.SetSessionId(sessionID[:]); err != nil { @@ -127,6 +142,7 @@ func (c SessionManager_PogsClient) RegisterUdpSession(ctx context.Context, sessi } p.SetDstPort(dstPort) p.SetCloseAfterIdleHint(int64(closeAfterIdleHint)) + p.SetTraceContext(traceContext) return nil }) result, err := promise.Result().Struct() diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 3d2d1dd3..c24a7476 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -148,12 +148,13 @@ interface TunnelServer extends (RegistrationServer) { struct RegisterUdpSessionResponse { err @0 :Text; + spans @1 :Data; } interface SessionManager { # Let the edge decide closeAfterIdle to make sure cloudflared doesn't close session before the edge closes its side - registerUdpSession @0 (sessionId :Data, dstIp :Data, dstPort: UInt16, closeAfterIdleHint: Int64) -> (result :RegisterUdpSessionResponse); - unregisterUdpSession @1 (sessionId :Data, message: Text) -> (); + registerUdpSession @0 (sessionId :Data, dstIp :Data, dstPort :UInt16, closeAfterIdleHint :Int64, traceContext :Text = "") -> (result :RegisterUdpSessionResponse); + unregisterUdpSession @1 (sessionId :Data, message :Text) -> (); } struct UpdateConfigurationResponse { diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index a198ca15..79261fce 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -3593,12 +3593,12 @@ type RegisterUdpSessionResponse struct{ capnp.Struct } const RegisterUdpSessionResponse_TypeID = 0xab6d5210c1f26687 func NewRegisterUdpSessionResponse(s *capnp.Segment) (RegisterUdpSessionResponse, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) return RegisterUdpSessionResponse{st}, err } func NewRootRegisterUdpSessionResponse(s *capnp.Segment) (RegisterUdpSessionResponse, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}) return RegisterUdpSessionResponse{st}, err } @@ -3631,12 +3631,26 @@ func (s RegisterUdpSessionResponse) SetErr(v string) error { return s.Struct.SetText(0, v) } +func (s RegisterUdpSessionResponse) Spans() ([]byte, error) { + p, err := s.Struct.Ptr(1) + return []byte(p.Data()), err +} + +func (s RegisterUdpSessionResponse) HasSpans() bool { + p, err := s.Struct.Ptr(1) + return p.IsValid() || err != nil +} + +func (s RegisterUdpSessionResponse) SetSpans(v []byte) error { + return s.Struct.SetData(1, v) +} + // RegisterUdpSessionResponse_List is a list of RegisterUdpSessionResponse. type RegisterUdpSessionResponse_List struct{ capnp.List } // NewRegisterUdpSessionResponse creates a new list of RegisterUdpSessionResponse. func NewRegisterUdpSessionResponse_List(s *capnp.Segment, sz int32) (RegisterUdpSessionResponse_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz) return RegisterUdpSessionResponse_List{l}, err } @@ -3681,7 +3695,7 @@ func (c SessionManager) RegisterUdpSession(ctx context.Context, params func(Sess Options: capnp.NewCallOptions(opts), } if params != nil { - call.ParamsSize = capnp.ObjectSize{DataSize: 16, PointerCount: 2} + call.ParamsSize = capnp.ObjectSize{DataSize: 16, PointerCount: 3} call.ParamsFunc = func(s capnp.Struct) error { return params(SessionManager_registerUdpSession_Params{Struct: s}) } } return SessionManager_registerUdpSession_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} @@ -3776,12 +3790,12 @@ type SessionManager_registerUdpSession_Params struct{ capnp.Struct } const SessionManager_registerUdpSession_Params_TypeID = 0x904e297b87fbecea func NewSessionManager_registerUdpSession_Params(s *capnp.Segment) (SessionManager_registerUdpSession_Params, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 16, PointerCount: 2}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 16, PointerCount: 3}) return SessionManager_registerUdpSession_Params{st}, err } func NewRootSessionManager_registerUdpSession_Params(s *capnp.Segment) (SessionManager_registerUdpSession_Params, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 16, PointerCount: 2}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 16, PointerCount: 3}) return SessionManager_registerUdpSession_Params{st}, err } @@ -3839,12 +3853,31 @@ func (s SessionManager_registerUdpSession_Params) SetCloseAfterIdleHint(v int64) s.Struct.SetUint64(8, uint64(v)) } +func (s SessionManager_registerUdpSession_Params) TraceContext() (string, error) { + p, err := s.Struct.Ptr(2) + return p.Text(), err +} + +func (s SessionManager_registerUdpSession_Params) HasTraceContext() bool { + p, err := s.Struct.Ptr(2) + return p.IsValid() || err != nil +} + +func (s SessionManager_registerUdpSession_Params) TraceContextBytes() ([]byte, error) { + p, err := s.Struct.Ptr(2) + return p.TextBytes(), err +} + +func (s SessionManager_registerUdpSession_Params) SetTraceContext(v string) error { + return s.Struct.SetText(2, v) +} + // SessionManager_registerUdpSession_Params_List is a list of SessionManager_registerUdpSession_Params. type SessionManager_registerUdpSession_Params_List struct{ capnp.List } // NewSessionManager_registerUdpSession_Params creates a new list of SessionManager_registerUdpSession_Params. func NewSessionManager_registerUdpSession_Params_List(s *capnp.Segment, sz int32) (SessionManager_registerUdpSession_Params_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 16, PointerCount: 2}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 16, PointerCount: 3}, sz) return SessionManager_registerUdpSession_Params_List{l}, err } @@ -4424,7 +4457,7 @@ func (c CloudflaredServer) RegisterUdpSession(ctx context.Context, params func(S Options: capnp.NewCallOptions(opts), } if params != nil { - call.ParamsSize = capnp.ObjectSize{DataSize: 16, PointerCount: 2} + call.ParamsSize = capnp.ObjectSize{DataSize: 16, PointerCount: 3} call.ParamsFunc = func(s capnp.Struct) error { return params(SessionManager_registerUdpSession_Params{Struct: s}) } } return SessionManager_registerUdpSession_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} @@ -4533,231 +4566,234 @@ func CloudflaredServer_Methods(methods []server.Method, s CloudflaredServer_Serv return methods } -const schema_db8274f9144abc7e = "x\xda\xccZ}t\x1c\xd5u\xbfwfW#\x19\xad" + - "G\xc3,\x96\xb5\xc7B\x8d\x8e]j'\x06d\xd7)" + - "u\xd3H2\xb2\x83\x84?4\xbbV\x0e1&\x87\xd1" + - "\xee\x934\xea\xee\xcc23+l\x07\xe2\x0fl\x0c\x1c" + - "B0\xb1\xf9pBc\x9b\xd2\x9e\x98$\x85`\x9a\xba" + - "\x07Z\x9c\x86\x10Cpp\x0e\xa4&\x86\xa6\x89\xe3\xb6" + - "\xf8\x98R\x0cn\x8e\xdb\x98\xe9\xb93;\x1f\xda]$" + - "+\xce\x1f\xf9ou\xe7\xbe\xf7\xee\xfb\xbd\xdf\xfdx\xf7" + - "\xe9\xea;\x1a\xba\xb8\x8e\xf8F\x11@9\x10\xafs\xd8" + - "\xbc\x9fl\xd83\xe7\x9f\xb7\x80\x92Bt\xbe\xf8l_" + - "\xf2\x9c\xbd\xe58\xc4y\x01`\xe1\xe3\xc2\x06\x94\x0f\x0a" + - "\x02\x80\xfc\x8c\xf0\x1f\x80\xce\x9d3\x9e\xfc\xfa\xe3Kw" + - "\xde\x01R\x8a\x0f\x95\x01\x17>R\xdf\x87\xf2\xb7\xeaI" + - "\xf3\x1b\xf5\xdb\xe5\x86\x06\x01\xc0\xb9^\xba\xea\xc6\xe4\xab" + - "GH;:u\x8c\xa6~\xaf~\x1e\xcaHj\xf2\xf9" + - "z\x9a\xfaS\x85\x1f\xef\xfb\xe4\xae\x97\xb7\x82\x94\xe2\xc6" + - "M\xfdv\xc3\x06\x94\xcf\xbb\x9a\xe7\x1aV\x01:\xef\xef" + - "\x9c\xf9\xc4\xde#?\xdc\x06\xd2\x15\x08eK\xa5i?" + - "C@y\xce\xb4\xbf\x05t^9{\xe3\x07\x07~\xb0" + - "\xe8N\x90\xe6\x92\x02\x92\xc2\x0b\xd3\xda9@\xf9\xadi" + - "\x9d\x80\xce\xa9\xd3\xff\xb7\xfd\x0bsW\xde\x0f\xca\\\xe4" + - "\x00\xe2\x1ci\x9c\x9f\x96\"\x8d\xcb.!k:\x97\xbd" + - "r0\xb5\xf0\xc1\x9d\x15\xa6\xbb\x8ag/\x99\x87r\xbc" + - "\x91\x0c\xc2\xc6[\x01\x9dO\xff\xc9\xd3\xf7\xf5<\xb8i" + - "\x17HW\x05\xeb\xb1\xc654\xdb\xe6FZ\xef\x7f\xa6" + - "\x7f\xf5H\xe9\xda\xef>X6\xc8\x9deo\xe36\xe7\xe6\xef\xbd\xf0\xf4C\xa0\xccG" + - "t\xde\x1c\xfc\xf8\xeb\xfc\xa3\xfb\x8f\xc3\x00\x0ad\xdf\xc2" + - "\x96\xc4>\xda\xdd\xdc\x04\xe9\xfe\xf8\x13\xcf\xfe\xc3\xfdO" + - "o\xff*(W \x02\xb8h~)\xf1\xbf\xa4\xb07" + - "A\xab\xed<\xf6\xdc\xca\xc2\x8e\xdd\xfb<|\xdc\xef\x87" + - "\x13\x1c\x071gk\xef\xaf\x0b\x03\x8fe\x1e+#\x17" + - "\xa7O\xcf%\xce \xe0\xc2\xa3\x896\x04t\x16\xfd\xec" + - "\xe4\xaa\x15\xdf\x19\xfa\x9b\xc8\xd8w\xa6o\xa0\xb1\xdb\x87" + - "\xce\x1cjJ\x17\x9e\xa8@\xc4\xdd\xec\xdb\xd3\xf7\xa3\x8c" + - "\xa2{\x98\xd3\xc9\x84o]~}\xc3\xba\x93\xcb\x9e\x04" + - "i\xbe?M\xab\x98\xa6i\xc6^z\xec\x0f\xe7\xbct" + - "\xebS\xa0\\\x85\x01X-\xf4\x0d\xe5E\"\xed/v" + - "|\xce\xfe\xe7~~\xdf\x81*\x8e\xed\x107\xa0\xfc\xb8" + - "\xbb\xca^\xf13\xf2Q\xfa\xe5\xc4n\xe2?T\x1f\xfe" + - "\xa7\x03\x95\xfcu1>(\x0e\xa2\xfc\x8a\xe8\" \xba" + - "\xfb\xbb\xe7\xd0\xee\x8f\xd7\x7f\xfd\xfdgj\xaa\xbf\xd74" + - "\x88r\\r\x0fV\"\"]\xd6\x8bo>\xdf\x11\xfb" + - "n\x94i\x8fK\xa7\x08\xea\x83\xaeB\xeb;K\x12\xfa" + - "\xbb[\x9e\xaf\x00\xc5U\xfc\xdc\xa5}(\x17.\xa5\xd9" + - "\xb4KI9\xf6\xc9\xd1\xed\xd2\x89\x9f\xbe\xe0\x81\xe2\xed" + - "<.\x8f\xd2\xce?&\x13j}7~\xe5\x81\xf8\xc9" + - "\xaf\xbcH\xc6E| Nn\xb5\xb0[6Q\x1e\x90" + - "\xe9\xa7\"7\xf3\x80N\xea\xc9?\xfb\xf6\x92\xdc\x1b/" + - "\xd7\xe0\xa8|t\xc6\x19\xf9\xad\x19\xf4\xeb\x8d\x19\x04\xea" + - "\x89\xf9O}\xe1\xed/\x1d}\xad\xbc\x13w\xedE\xcd" + - ".iz\x9bi\xedsk\xf7\\\xaf97\x1c\xaf\x04" + - "\xc6\xd5\xd4\x9a\xbf\x83\xf2\xe6f\x9a\xee\xf6f\x9a.`" + - "h-\xed_4\x8f\xa2|\xd6\xd5~\xcf\x9d\x9b;\xa9" + - "\xb6l\xfa\xe9\xa7\xdf\x8c\x90\xeal\xf3/\x11b\xce\xca" + - "\xcf\xde8\xdap\xfb\x89\x13Q\xb3\xdenv\x01>\xef" + - "\x0e\xfd\xc7\x7fyh\xe4\xa6o\x1f9\x19%\xd2L\x93" + - "\x88\xf4_\x7f}\xea\xcb\xa7\x0b\xb9\x7fw]\xc6?\x9c" + - "\x96\x99\x8b\x09\xce\x8e\x99\xe4\xc3\xcdm\x89\xa5\xed\xc7\xfa" + - "OE\xf1N\xb4,!\x859-4\xf9\xa2\x9b\xbb\xd9" + - "\xdakn8U\xc5\xb4\xa5-\x8bQ\x1ehq\xb1n" + - "\xd9\x8e\xb2\x96j\x06p\xc6\xfen\xc7\x0dO|\x7f\xe5" + - "\x19\xcf\x8b][>\x97Z@\xb6\xdc\xf7\xc5\x9eU\x7f" + - "\xda~\xe8Lt\x1bJ\x8a\xfcJf)Zi\xe8\x9a" + - "\xd3\x9f\x99s\xdf\x0f\xce\xd4r\x9em\xa9y(\xefJ" + - "\x11\\;H\xf9\xdde\x7f\xf9ZJL}P\x01m" + - "\x1d\xe9>\x93\x1aE\xf9p\xca\x0dh\xa9\x17\x89\xd0{" + - "\xffj\xdf\xbf\x9e;r\xdd\xd9\xaa=\x1c\x9cE\xdc\x9f" + - "E\xd3\x1e\x9e%\xc8\x87g]\x01\xe0\xdcy\xfc\xf3\xeb" + - "~r\xc7\xfbg+\x19\xe6\x1arhV\x1a\xe5\xd7\xdd" + - "\x11Gg\x11a\x1fZ\xfd\x9f\x1bO\xef\x9a\xf1\xeb\xaa" + - "\xb9oi\x1dEy[+inn}Q\xbe\xecr" + - "\xf2\xc4W\x85\xc7:z6\xbe|.rT\xe7[\xfb" + - "\x08\x9e\x07\x85\xaf\x9d\xd8\xf4\xf3\xcf\xff&\x0a\xcf\xb9\xd6" + - "_\x12<\x89\xcb\x09\x9e\xdb\xde}\xe4\xba/\xaf\xfd\xe6" + - "\x87\x11\x82\xcc\xbf|\x0b\x0d\xb5K\xba\xce\xf2f1\x96" + - "\xbd\xca\xff\x99\xbd2\xab\x16\xf5\xe2\xe2\xee\x92=\xc2t" + - "[\xcb\xaa6K\xb3N\xabh\xe8\x16\xebGT\x9a\xf8" + - "\x18@\x0c\x01$u\x14@\xb9\x99G%\xcf\xa1\x84\x98" + - "$\xa2H\x1a\x09GxTl\x0e%\x8eKR\x84\x95" + - "ni\x07P\xf2<*\xeb8D>\x89<\x80Tz" + - "\x00@Y\xc7\xa3\xb2\x95C\xa7\xc8\xcc\x82\xaa3\x1dD" + - "{\xa9ib#p\xd8\x08\xe8\x98\xcc6\xd7\xab\x83y" + - "\x10YD,\x8c\xdejc\x028L\x00:#F\xc9" + - "\xb4\x06t\x1b\xb5|\x9a\x0d\x99\xcc\xc2\x11\xac\x03\x0e\xeb" + - "\x00'\xda^\x86Y\x96f\xe8+T]\x1df&\x00" + - "\xed\xac\x9e\x8f\x03\x04\xd9\x0b\xfd<'u\xec\x06N\x9a" + - "/`\x98i\xd0'\xab\xf4\xb1\xfd\xc0I\xad\x82c\xb2" + - "a\xcd\xb2\x99\x89\x03\xb9\xa2;7o\xe8]\xe8\x94t" + - "\xef\x032\xd3\xfb \xd2\xaa]\xd8\x8f\xa1u|\xb5u" + - "\xd7\xe65\xa6\xdbb\xaf>dT@\xdeW\x0b\xf2\xbe" + - "2\xe4[#\x90o^\x02\xa0\xdc\xc6\xa3r\x17\x87\x12" + - "_\xc6|\xdb<\x00e\x13\x8f\xca\xbd\x1c:Yw\x91" + - "\xde\x1c\x00\x04h\x0e1\xd5.\x99\xcc\"\xd9t\xc0~" + - "\x1e]\xd0\xa7\x03n\x1cc&\xd9\xee\x1f\x82\xa8\x9a\xd9" + - "\x91\xe0\xa0&@z\xe9:\xcd\xb25}x\xb5+\xef" + - "\xec7\xf2Zv=\xed\xaa\xd1\xb5\xb3u1\x00\xa2t" + - "\xd9\x1a\x00\xe4$i\x09@\xa76\xac\x1b&sr\x9a" + - "\x955t\x9d\x01\x9f\xb57\x0e\xaayU\xcf\xb2`\xa1" + - "\xba\xea\x85\xbc\x052\xcc\x1cc\xe6\x95j\x84\xbe\xb3\xfb" + - "US\xe5\x0b\x96\xd2\x18\xe0\xb8t\x0d\x80\xd2\xc3\xa3\xd2" + - "\x1f\xc1q\x05\xe1\xb8\x9cG\xe5\x86\x08\x8e\x03\x84c?" + - "\x8f\xcaZ\x0e\x1d\xc3\xd4\x865\xfdZ\x06\xbc\x19e\xa0" + - "e\xebj\x81\x11fe<6\x1aE[3t\x0b\x9b" + - "\xc2\xac\x03\x88M\x11\xa4\x84\xc98y\xa5O)\x9fQ" + - "\x86>;\xcd\xac\x92\x90\xb7-%\x16\xec$\xb1\x18@" + - "\xa9\xe7QIr\xd8i2\xab\x94\xb7\xb1),'~" + - "\x17\xab\xfa\xf0Eh\x98\xaeE\xc3\x05\x00J\x8eG\xa5" + - "\xc8!\x96\xd1+,\x89D\x03\x1e=\x16\xde\xb2\x1b@" + - "\xb1yT6q\xe8X\xde\"\xbd\x809\x1f\xd1\xb6\x9c" + - "e\xf7\x16\xfd\xbf6\xe6,\xbb\xdf0m\x14\x80C\x01" + - "\x88\xb7\x86\xc5\xba\x87\xc8\xa7zsyv\x9d\xc6\xeb6" + - "\xc6\x81\xc38L\xe8T\x1e?D\x0al\x9e\xb7\xfb\xbb" + - "\x99Kd\xf8#\x1e\x95?\x8e\xec\xa6\x83\xe2\xd8\xd5<" + - "*\x9f\xe2\xd0Q\xb3Y\xa3\xa4\xdb\xab\x81W\x87+8" + - "\x9fa fM\x16\xd2\xc1_\xb6\xbe\x86[\x1b\xfa\x90" + - "6\\2U;\x02x\xa9\x98Sm6\xee\x93{\xce" + - "y\xfe\x02\xce9\xa8+\xa6|\xce~d\xaa8i\xd1" + - "T\x0bV\x14\x9bt-l\xe8T?\xc1\xa3rM\xed" + - "\x03\xdcX`\x96\xa5\x0e\xb3\xaa\xf0\x10\xaf\x89\x89\xce\xb2" + - "\xb4\xeb4\xf3\x92\xcc\x95&\xb3\x84R\xde&+\x1a\x1d" + - "\xc73\x83\xb85\x9bG\xe5j\x0e\x13\xf8\xa1\xe3\xd91" + - "\xff\x81\xf0\x8c\xda\x98i\x1a&6\x85I\xb8\x0cI\xb6" + - "\xbc\x00\x1az\x0f\xb3U-\x8f\xe4\x96A\xa5Z\x01\xdc" + - "dq%\x84\xcd\x13\xcf\xee$\xef(\x8c;)\xa2w" + - "\x13\x8f\xca,\x0e\x9daS\xcd\xb2~f\xa2f\xe4V" + - "\xaa\xba\x91\xe1Y\xb6\x8a\xac\xd3\xa7\xba\xa8\xcb\x0f\xdb\x82" + - "`\xd4\xc4\xe3MV\x06\xa1<\xbc\xbf\xcd\xb39\x19\xd8" + - "|{{\x98\x8c\x83c\xde<\x18f\x8b \x1e\xdeM" + - "\xcer\x17\x8f\xca\xceH^\xd9A\x91\xf3~\x1e\x95\xaf" + - "q(\xc5bI\x8c\x01H\x8f\x10Kv\xf2\xa8\xec\xe1" + - "\xc6\xa7l6\xc6t\xbbG\x1b\x06\x81Y\xa1\x94L\xec" + - "\xd1\x86\x19\xf0\xd6\xc5\xc6\xd6\xfaI\xf00\x06-#\xcf" + - "l\xd6\xc3\xb2y\x95\\n\x8cy\xdf\xcbd\xf4\x0fu" + - "\"\xde\xa6\xab\xbc\x87\xf8+\xfaUR\x84\x0e\xed\xa1\xe3" + - "\x0a,R\xdcL`\xad7\xb9\x17\x0c\xaa8\x10zL" + - "\x99\x07h\xfdN\x82\x8e\xbbg\x1c\xe7\xfcKB\xaf\xf3" + - "I1\x7fq\x18\x10\x82\x9a \x06\x1c\xc6\x00;\xb3\xee" + - "\x84U\xa106\x99U\x9d\x9eY\x1epT\x84\xf9\xf7" + - "X\xf4/\xff\x92\xb4\x0f8)!8\xbe\xe5\xe8\x8f\x17" + - "\xaa\x0a\xaa\xd8DQfU\xd1\xd6\x04C\xb7h\xad\x08" + - "\xff\x17\xd7\xe2\xbf\x19\xf2\xdfOhwo\x89\xd2\xbf\x9c" + - "\xd0v\xec\x0e\x99.\xc58\x8f\xfe\x8f\xee\x03P\xf6\xf0" + - "\xa8|\x93\xc3N\xaf\xd6\xc2\xa6\xb0iS\xa6\xacWQ" + - ",7\xa0-\xab\xe6\xc3\xa4\xe7\x98\xac\x98W\xb3l)" + - "\x96\xab'@\x04\x0e\xd1\xf5\x93B\xd1d\x96\x85\x9a\xa1" + - "+%5\xaf\xf1\xf6\xfa\xa0\xe2\xd5K\x85~\x93\x8di" + - "h\x94\xacn\xdbf\x05\xa1h[\x17R\x0f\x87\x00Q" + - "\x90\x14\xb4\xbc\x0bP\xa4`\xa2\xca\xb1\x8bGey\x04" + - "\xa0^\xca\x91\xd7\xf1\xa8\xac\x0e\x01R\xbe\x07\xa0\xac\xe6" + - "Q\xb9\x99C\xb1T\xd2\x82\xa4\xe0\xe4\x8d\xac{\xda " + - "\xaeT\x0b\x95\xb9\xa1\xd7\xe2\xd2\xac`\xd8,\xbf\xde\xe3" + - "h.\xdc\xf1\x85\xc6\xe6\x8a \xe9'\xb3\xdf\xa7\xaao" + - "\xe2\x8b\x16\x81\x03\x15\xb8\xb7\xd7\xc2}Ad\x1f\xbe\xc9" + - "+\x06\xc3}\x08\x7f\xc1\xd6\xfbV\xb5\xb1\x02%=\x1f" + - "\xee\xf2f\xbaA\xb8>\xd4\x99j,r}p\xb9\x91" + - "U\xf3\x95!D\xacL\x86\xd1\xb2\xe5\xc2\xc3Ct\xd1" + - "U\xc56\x17V\x02\xe6\x1a\x7fby=\xf6\x01d\xd6" + - "!\x8f\x99\xad\x18b#o\xc6%\x00\x99\xdbH~\x17" + - "\x86\xf0\xc8\xdb0\x05\x90\xd9D\xf2{1\xb8\x85\xcaw" + - "\xe3~\x80\xcc\xbd$~\x98\xd4c\xbc\xeb\xbd\xf2.w" + - "\xfa\x9d$\xdfC\xf2x,\x89q\x00\xf9Q\x9c\x07\x90" + - "y\x98\xe4\x07H^\xc7%\xb1\x0e@~\x0aG\x012" + - "O\x92\xfcY\x92\x0b\xf1$]\xc4\xe5\x83h\x02d\xfe" + - "\x9e\xe4\xdf'y\xfd\xcc$\xd6\x03\xc8\x87\\\xf9\xf3$" + - "\xff\x11\xc9\x1bZ\x92\xd8\x00 \x1f\xc6-\x00\x99\x1f\x92" + - "\xfc5\x92O\xc3$N\x03\x90\x8f\xe2n\x80\xcck$" + - "\xff7\x92_R\x97\xc4K\x00\xe4\xb7\\{\x8e\x91\xfc" + - "W$o\x8c%\xb1\x11@\xfe\x05\xee\x03\xc8\xfc\x8a\xe4" + - "\xffM\xf2\x84\x90\xc4\x04\x80\xfc\x8e\xbb\xaf\xd3$\xaf\xe7" + - "*.\x81>\x8d+nz\xbca\x05dP\xc9$D\xeb\xc4\xa9\x8d" + - "N3K\xbc\x90\xb3\x08\xfb\x9b\x93\xd7\xb9S\x89\xd6i" + - "\xd6\xe6ra\xa2[c\x8d\xf9j\xd4\xa1\xfe%(\xd2" + - "! r\xaf\xe5Q\x19\x89\x90\x9b\xf5\xd5\xe8\x10\xa4\xc3" + - "\xd6\xa0\xc4s\xe5\xde \xa5\xdf\"\x8f\xcam\x1c\x8aj" + - "\xc9\x1e\xc1\xa6\xf0\xcdh\x1c\x08\xe3\xdbW\xc4\xf5^=" + - "\xc7\x00\xd7\xf9\xee\x1aI\xca\xc1c\xc6o\x05\xe3G\x96" + - "\xdf\x16Lz\x80AC\xbfb\xe5\x8f\xecRtz\x8b" + - "\x12og\xba\x05\xb1\xffV\x82~3\\zj\x03p" + - "\xd27\x04\x0c\x1f\x00\xd0\xef\xf7K\x8f\x9a\xc0I\xbb\x04" + - "\xe4\x82\xe7,\xf4\x9f\xad\xa4\xbb\xef\x01N\xda& \x1f" + - "\xbcF\xa1\xdf \xeeX?\x0d\x81\x93n\x170\x16<" + - "\x03\xa2\xdf^\x96n\x19\x05N\xd2\x04\x8c\x07\x0f]\xe8" + - "\xbf{H7m\x01N\x1a\x08\xdb\xa0\xd0\xe9\xed\xa3\x0b" + - "\x1d\x9f\xf3\xd0\xe6\xb2~|S\xd4\xd3\x02\xe8B\xc7\xbf" + - "\x94\xf1\x1fu+s\xb5\xfc\xbe\x1e\x88Y\xd5f]T" + - "({\x01\x0e\xcb\x11\x0e\xbaP\x89a\xa4\xbb\x0ep\xb1" + - "]\x91*?\x99b!\xea\x8f\xff-c._\xcbj" + - "Z'\xe8\x0fG\xe6\xa5\x8a\xbc\x91Ge&7I\xc1" + - "]3tz\x06\xfb\xe4\x17i0\xcd\xff\x07\xc1\xfcG" + - ")\\\xff\x88G\xe5X\xc4\xad_'\xe1\xab<*o" + - "F\xca\xd17\xc8\xd7\x8f\xf1\xa8|\x10\xb6\xfc\xdf\xbb\x07" + - "@\xf9\x80\xc7t\xa4\xd2\x92\xce\x93\xe2o\xa8\x1eq\xeb" + - ",\xf4\xea\xac8>\x00\x90\xa9\xa7:%\xe9\xd6Y1" + - "\xaf\xce\x92p\x10 \xd3D\xf2Y\xd1:\xab\x05\xd7\x00" + - "df\x92|6\x8e\xbfh\x0b%3,\x7f\xf3\xc6\xf0" + - "rM\xaf\x99\xbc\xfd7\x08\xb4\x97\xa9Z\xbed2\xa8" + - "\xbc\x82\xf4\xf6D\xca\x19\xefq\xc2\xebCf\x88\x849" + - "\xb4\x82\x1e\xe5\x14Z\x1c\x13e\xb2\xbcQ\xca\x0d\xe5U" + - "\x93\xe52\xcc\x14\xbc\x80\xd0\xcf\xc7\x95z\x8c\xfc\xb3\x00" + - "@\xf8\xa8\x1b!\xfb\x84\x99q\xa9i\x1ahV\\5" + - "\x16\x84W\x8d\xe0\xa6\xb1&\xbc\xe1I\\W\xf9\x8a7" + - "\x18^\x8e\xda\xb2j\xc9bU\x98\x00\xcf\xcc\xa0\xcde" + - "\x8d\x18\xa5|.\xcd@\xb0\xcd\xf5U\xb7\xba\xd8d\xd1" + - "W\xf4#a\xa3\x1b\x09\xfd\x97I\xf4\x1f %e7" + - "p\xd2\x0a\x8a\x84\xfe#\x19\xfa/\xe4R\xf7~\xe0\xa4" + - "?\xa7H\xe8\xbf\x0f\xa3\xff\xe8)u\xbc\x04\x9c\xd4\x11" + - "y\xbb\xf1\xf1\xa9z\xbb\xf1>\xb8\xfe@\x1f\xca\x09\x95" + - "\xab\xcc\xa8\x14\xa1\xa2\x8d\x88\x8b\xe8\xecx\x095r\x9c" + - "Sz\xf0\xb8\xe0w\x82\xe0\xffS*bN\xc3\xc5v" + - "\xd0\xfc\xd4\xf8\xff\x01\x00\x00\xff\xff|\xba\xdf\xe8" +const schema_db8274f9144abc7e = "x\xda\xccZ{t\x1c\xe5u\xbfwfW#\x19\xc9" + + "\xaba\x96H\x96#\xab\xd1\xb1Kqb@v\x9d\x82" + + "\x9bF\x92\x91\x1cV\xf8\xa1\xd9\xb5s\xa8\xb1s\x18\xed" + + "~\x92F\x9d\x9dYff\x85\xe5\xe0\xd886\x06\x0e" + + "\x10 6`'n\x8c\x1d\xda\x83\x09)\x0e\xa6)=" + + "\xa4\xc5i\x1c\x12H\x1c\x9c\x03\xa9\x89I\xd3\xc6q[" + + "\xfb\x98R\x0c4\xc7m\xcc\xf4\xdc\x99\x9d\x87v\x17=" + + " \x7f\xe4\xbf\xd5\x9d\xfb=\xee\xef\xfb\xdd\xc7w?]" + + "}\xa0\xae\x8b\xeb\x88oN\x00\xc8\x87\xe35\x0e\x9b\xff" + + "\xd3\x8d\xfb\xe6\xfd\xd3V\x90[\x10\x9d/<\xd7\x97\xbc" + + "`o=\x09q^\x00X\xf4\x98\xb0\x11\xa5g\x05\x01" + + "@zF\xf8\x0f@\xe7\x8e\x8f<\xf5\xb5\xc7zw~" + + "\x11\xc4\x16>T\x06\\\xb4\xbb\xb6\x0f\xa5'kI\xf3" + + "\xf1\xda\x1dR]\x9d\x00\xe0\xdc ^uS\xf2\xe5c" + + "\xa4\x1d\x9d:FS\xbfU;\x1f%$5\xe9b-" + + "M\xfd\xa9\xfcO\xf6\x7fr\xd7K\xdb@l\xe1\xc6M" + + "}\xa6n#J\x17]\xcd\x0bu\xab\x00\x9d\xb7w6" + + "?\xf1\xe8\xb1\x1fl\x07\xf1r\x84\xd2N\xc5\x19?G" + + "@i\xde\x8c\xbf\x01t~\xfc\xeeM\xef\x1c\xfe\xfe\xe2" + + ";@\xbc\x82\x14\x90\x14\x8e\xceh\xe7\x00\xa5_\xcc\xe8" + + "\x04t\xce\x9e\xfb\xbf\x1d\x9f\xbfb\xe5\xfd _\x81\x9c" + + "?\xc5\xc5\x19-\x1c\xe0\xa2\xcb.iC@\xa7s\xd9" + + "\x8f\x9fmY\xf4\xd0\xce\xb2\xbds\xa4ym\xfd|\x94" + + "R\xf5\xb4\xa3\xde\xfa[\x01\x9dO\xff\xc9\xd3\xf7\xf5<" + + "\xb4e\x17\x88W\x05\x0b>^\xbf\x96\x16\xf1\xbd\x95\xe7" + + "=/v\xf7\xf2Z\xcbB\xda\xcb}_\xe8Yum" + + "\xfb\x91\xf3Q3\x8e\xb7\x9cw]\xb2\x85V\x1a\xbc\xe6" + + "\xdcg\xe6\xdd\xf7\xfd\xf3eG\xe5*\xc6g\xcfG\xe9" + + "\xb2\xd9\x04\x978\xbb\x13\xf0\xcde\x7f\xf9JK\xa2\xe5" + + "\x9d2hkHw\xf1\xec\x11\x94R\xa4\xbb\xa8w\xf6" + + "\x0bD\xe8G\xbf\xbe\xff_.\x1c\xbb\xfe\xdd\x0a\x1b\xae" + + "m%\xee\xb7\xd2\xb4\xa9VAJ\xb5^\x0e\xe0\xdcq" + + "\xf2s\x1b~\xfa\xc5\xb7\xdf-g\x98\xbb\x91\xee\xd64" + + "Jk\xdc\x11r+\x11\xf6\xe1\xd5\xff\xb9\xf9\xdc\xae\x8f" + + "\xfc\xa6b\xee\xb7ZGP\x8a\xcf!M\x9c\xf3\x82\xb4" + + "\x8b~9/\x0b\x07:z6\xbft!rT\x9b\xe6" + + "\xf4\x11<\x0f\x09_=\xb5\xe5\x97\x9f\xfbm\x14\x9e\xb1" + + "9\xbf\"x\xee\x9dC\xf0\xdc\xf6\xe6\xee\xeb\xbf\xb4\xee" + + "\x1b\xefE\x08\xf2\xe4\x9c\xad4\xd4.\xea:\xd3\xccB" + + ",{\x95\xff3{eV)\xe8\x85%\xddE{\x98" + + "\xe9\xb6\x9aUl\x96f\x9dV\xc1\xd0-\xd6\x8f(7" + + "\xf21\x80\x18\x02\x88\xca\x08\x80|3\x8f\xb2\xc6\xa1\x88" + + "\x98$\xa2\x88*\x09\x87y\x94m\x0eE\x8eKR\x84" + + "\x15oi\x07\x905\x1e\xe5\x0d\x1c\"\x9fD\x1e@," + + ">\x08 o\xe0Q\xde\xc6\xa1S`f^\xd1\x99\x0e" + + "\x09\xbb\xd74\xb1\x1e8\xac\x07tLf\x9bc\xca\x80" + + "\x06\x09\x16\x11\x0b#\xb7\xda\xd8\x00\x1c6\x00:\xc3F" + + "\xd1\xb4\xd6\xe86\xaaZ\x9a\x0d\x9a\xcc\xc2a\xac\x01\x0e" + + "k\x00'2/\xc3,K5\xf4\x15\x8a\xae\x0c1\x13" + + "\x80,\xab\xe5\xe3\x00A\xfaB?\xd1\x89\x1d{\x80\x13" + + "\x17\x08\x18f\x1a\xf4\xc9*~\xec pb\xab\xe0\x98" + + "lH\xb5lf\xe2\x9a\\\xc1\x9d\x9b7\xf4.t\x8a" + + "\xba\xf7\x01\x99\xe9}H\xd0\xaa]\xd8\x8f\xe1\xee\xf8\xca" + + "\xdd]\xa7\xa9L\xb7\x13)}\xd0(\x83\xbc\xaf\x1a\xe4" + + "}%\xc8\xb7E \xbf})\x80|\x1b\x8f\xf2\x9d\x1c" + + "\x8a|\x09\xf3\xed\xf3\x01\xe4-<\xca\xf7p\xe8d\xdd" + + "ER9\x00\x08\xd0\x1cd\x8a]4\x99E\xb2\x99\x80" + + "\xfd<\xba\xa0\xcf\x04\xdc<\xcaL\xda\xbb\x7f\x08\x09\xc5" + + "\xcc\x0e\x07\x075\x01\xd2\xbd\x1bT\xcbV\xf5\xa1\xd5\xae" + + "\xbc\xb3\xdf\xd0\xd4\xec\x18YU\xef\xee\xb3u\x09\x00\xa2" + + "x\xd9Z\x00\xe4Dq)@\xa7:\xa4\x1b&sr" + + "\xaa\x955t\x9d\x01\x9f\xb57\x0f(\x9a\xa2gY\xb0" + + "PM\xe5B\xde\x02\x19f\x8e2\xf3J%B\xdf\xb9" + + "\xfd\x8a\xa9\xf0yK\xae\x0fp\xec]\x0b \xf7\xf0(" + + "\xf7Gp\\A8.\xe7Q\xbe1\x82\xe3\x1a\xc2\xb1" + + "\x9fGy\x1d\x87\x8ea\xaaC\xaa~\x1d\x03\xde\x8c2" + + "\xd0\xb2u%\xcf\x08\xb3\x12\x1e\x9b\x8d\x82\xad\x1a\xba\x85" + + "\x8da\xd6\x01\xc4\xc6\x08R\xc2d\x9c\xbc\xd2\xa7\x94\xcf" + + "(C\x9f\x9bfVQ\xd0lK\x8e\x05\x964,\x01" + + "\x90ky\x94\x93\x1cv\x9a\xcc*j66\x86\xe5\xc4" + + "\xefbU\x1f\xbed\xb0\xe8\xa6t\x84\\>|\xdb\x17" + + "\x86\xe4\xc2\x12zw\x11z\xdbx\x94\xef'\x16\xa2\xc7" + + "\xc2{\xf7\x00\xc8\xf7\xf3(\x7f\x95C1\xc6%1\x86" + + "(\xee\xa6\xb8\xf1\x08\x8f\xf2\xd79t,o\xe5\x14`" + + "\xce\x87\xb9-g\xd9\xa9\x82\xff\xd7\xe6\x9ce\xf7\x1b\xa6" + + "\x8d\x02p(\x00\x91\xd9\xb0X\xf7 9Z*\xa7\xb1" + + "\xebU^\xb71\x0e\x1c\xc6\xc9zS\xc9\xb2\xeb\x0c\x8a" + + ".l\x83]:$\x10q\x06\xc0D^\xe8\x11*A" + + "\x91\xd0\x0b\x0f\xbe\xf9W\x10{\xfe\x88G\xf9\x8f#\xe6" + + "w\x90\x01W\xf3(\x7f\x8aCG\xc9f\x8d\xa2n\xaf" + + "\x06^\x19*s\x92\x0c\x83D\xd6d!\x7f\xfcek" + + "\xab\xc4\x01C\x1fT\x87\x8a\xa6bGN\xa8X\xc8)" + + "6\x1b\xf7\xc9%\x86\xc6O\x81\x18A!2mb\xf8" + + "\xa1\xac\x8c\x1a\x09S\xc9[Ql\xd2\xd5\xb0!\x1a|" + + "\x82G\xf9\x9a\xea\x87\xbb9\xcf,K\x19b\x15\xf1$" + + "^\x15\x13\x9de\xc9\xea4\xf3\xb2\xd2\x95&\xb3\x84\xa2" + + "f\xd3.\xea\x1d\xc7\xdb\x06\x91q.\x8f\xf2\xd5\x1c6" + + "\xe0{\x8e\xb7\x8f\x05\x0f\x86g\xd4\xc6L\xd30\xb11" + + "\xcc\xda%H\xb2\xa5\x05\xd0\xd0{\x98\xad\xa8\x1a\x92\x1f" + + "\x07\xa5m\x19p\x93\x05\xa2\x106O<\xb7\x93\xdc)" + + "?\xee\xa4\xc8\x1f\x1ay\x94?\xca\xa13D\\\xedg" + + "&\xaaFn\xa5\xa2\x1b\x19\x9eeC\"\x97V\x9a9" + + "\xddE]~\xd8\x16\x04\xa3&\x1eo\xb2\x12\x08\xa5\xe1" + + "\xfdm\xde\x9e#\x11\xa0=\xcc\xde\xc11\xdf>\x10F" + + "\x80 \x80\xdeE\xcer'\x8f\xf2\xceH\"z\xa0/" + + "\x1a\x02bI\x8c\x01\x88\xbb\x89%;y\x94\xf7q\xe3" + + "s<\x1be\xba\xdd\xa3\x0e\x81\xc0\xacPJ[\xecQ" + + "\x87\x18\xf0\xd6\x87\x0d\xc6\xb5\x93\xe0a\x0cX\x86\xc6l" + + "\xd6\xc3\xb2\x9aB.7\xca\xbc\xef%2\xfa\x87:\x11" + + "o\xd3\x15\xdeC\xfcM\xf8eU\xc4\x83\xdaC\xea\x06" + + "\xd0.X\x18\xba\x95\xc0\xc2Z\xa8\xcd*(\xba5\x95" + + "X\xe2\xad\xef\xc5\x8b\x0a\x9a\x84NU\xa2\x0aZ\xbf\x93" + + "\xb8\xe4\xc2\x82\xe3\xe2\xc3\xd2\xd0\xba\xc0\xb8%\xa1qA" + + "\x9d\x11\x03\x0ec\x80\x9dYw\xc2\x0a\x0bc\x93\xed\xaa" + + "\xd3\xdb\x16a\x1bs\x0b;\xffn\x8c~CA\x14\xf7" + + "\x03'6\x08\x8e\xbfs\xf4\xc7\x0b\x15EZl\xa2@" + + "\xb4\xaa`\xab\x82\xa1[\xb4V\xc4E\x96Ts\x11\xb3" + + "J\x92\xdc\x1a\xf5\x90R\x92|`O\xe8\x0c^\x92\x04" + + "\x10\xf7\xee\x07\x90\xf7\xf1(\x7f\x83\xc3N\xaf~\xc3\xc6" + + "\xb0\x13Tb\xb5W\xa5,7\xa0-\xabha\xcet" + + "LV\xd0\x94,\xeb\xc5RE\x06\x88\xc0!\xba\xae\x94" + + "/\x98\xcc\xb2P5t\xb9\xa8h*o\x8f\x05U\xb4" + + "^\xcc\xf7\x9blTE\xa3hu\xdb6\xcb\x0b\x05\xdb" + + "\x9aJ\x8d\x1d\x02DqTP5\x17\xa0H\x11F\xd5" + + "h\x17\x8f\xf2\xf2\x08@)J\xa3\xd7\xf3(\xaf\x0e\x01" + + "\x92\xbf\x0b \xaf\xe6Q\xbe\x99\xc3D\xb1\xa8\x06y\xc3" + + "\xd1\x8c\xac{\xda\x90X\xa9\xe4\xcb\xd3G\xca\xe2\xd2," + + "o\xd8L\x1b\xf38\x9a\x0b-\x9ej\xf8.\x8b\xa3~" + + "\xbe\xfb}\xaa$'\xbe\xbc\x118P\x86{{5\xdc" + + "\x17F\xec\xf0\xb7\xbcb \xb4C\xf8\x0b6\x16D\x1d" + + "\x96\xa7\xbc\xe8\xc3]2\xa6\x1b\x84\x1bB\x9d\xe9\xc6\"" + + "\xd7\x07\x97\x1bYE+\x0f!\x89\xf2|\x19\xadl\xa6" + + "\x1e\x1e\xa2\x8b\xae*\xb4\xb9\xb0\x120\xd7\xf8\x13Kc" + + "\xd8\x07\x90\xd9\x80.`\xf8\xa8\x80\xfe\x1b\x82\xb8\xd7\x04N" + + "\xdc% \x17<\x91\xa1\xff\x14&\xdeu7p\xe2v" + + "\x01\xf9\xe0\x85\x0b\xfd\xa6s\xc7\xd8\x0c\x04N\xdc$`" + + ",x[D\xbfe-\xde2\x02\x9c\xa8\x0a\x18\x0f\x1e" + + "\xcf\xd0\x7fK\x11\xd7o\x05N\\\x13\xb6V\xa1\xd3\xb3" + + "\xa3\x0b\x1d\x9f\xf3\xd0\xe6\xb2~|\xa3\xd5\xd3\x02\xe8B" + + "\xc7\xbf\xb7\xf1\xefwqs\xb5\xfc^!$\xb2\x8a\xcd" + + "\xba\xa8P\xf6\x02\x1c\x96\"\x1ct\xa1\x1c\xc3H\xc7>" + + "\xd27\xfa\x80\x8d\x93\x0a?\x99f!\xea\x8f\xff\x801" + + "\x97\xaf\xb6kZ'\xe89G\xe6\xa5\x8a\xbc\x9eG\xb9" + + "\x99\x9b\xa4\xe0\xae\x1a:\xbd\x0d\xfb\xe4O\xd0`\x9a\xff" + + "\x0f\x82\xf9\x8fS\xb8\xfe\x11\x8f\xf2\x89\x88[\xbfJ\xc2" + + "\x97y\x94_\x8f\x94\xa3\xaf\x91\xaf\x9f\xe0Q~'|" + + "Fx\xebn\x00\xf9\x1d\x1e\xd3\x91JK\xbcH\x8a\xbf" + + "\xa5z\xc4\xad\xb3\xd0\xab\xb3\xe2\xf8 @\xa6\x96\xea\x94" + + "\xa4[g\xc5\xbc:K\xc4\x01\x80L#\xc9?\x1a\xad" + + "\xb3f\xe1Z\x80L3\xc9\xe7\xe2\xf8k\xb7P4\xc3" + + "\xf2W3\x86\x96\xabz\xd5\xe4\xed\xbfk\xa0\xbdLQ" + + "\xb5\xa2\xc9\xa0\xfc\x0a\x92\xea\x89\x943\xde\x83\x87\xd7\xc6" + + "\xcc\x10\x09sh\x05-\xceitA&\xcad\x9aQ" + + "\xcc\x0dj\x8a\xc9r\x19f\x0a^@\xe8\xe7\xe3r-" + + "F\xfe\x03\x01 |(\x8e\x90}\xc2\xcc\xd8k\x9a\x06" + + "\x9aeW\x8d\x85\xe1U#\xb8i\xac\x0dox\"\xd7" + + "U\xba\xe2\x0d\x84\x97\xa3\xb6\xacR\xb4X\x05&\xc03" + + "3\xe8\x84Y\xc3FQ\xcb\xa5\x19\x08\xb69Vq\xab" + + "\x8bM\x16}\x13~$\xacw#\xa1\xff\xda\x89\xfe\xa3" + + "\xa6(\xef\x01N\\A\x91\xd0\x7fxC\xff\xd5]\xec" + + ">\x08\x9c\xf8g\x14\x09\xfd7g\xf4\x1fR\xc5\x8e\x17" + + "\x81\x13;\"\xefA>>\x15\xefA\xde\x07\xd7\x1f\xe8" + + "C)\xa1r\xe5\x19\x95\"T\xb4\x11\xf1!:;^" + + "B\x8d\x1c\xe7\xb4\x1eQ\xa6\xfc\xf6\x10\xfc\xd3KY\xcc" + + "\xa9\xfb\xb0M6?5\xfe\x7f\x00\x00\x00\xff\xff\xda\xbc" + + "\xea\xa1" func init() { schemas.Register(schema_db8274f9144abc7e, From 2ffff0687badb658e1ef9bbf45955f90a700dbcc Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 2 Sep 2022 17:29:50 +0100 Subject: [PATCH 193/238] TUN-6696: Refactor flow into funnel and close idle funnels A funnel is an abstraction for 1 source to many destinations. As part of this refactoring, shared logic between Darwin and Linux are moved into icmp_posix --- connection/quic.go | 23 ++- ingress/icmp_darwin.go | 180 +++++++++--------- ingress/icmp_generic.go | 2 +- ingress/icmp_linux.go | 297 ++++++++++-------------------- ingress/icmp_linux_test.go | 52 ------ ingress/icmp_posix.go | 142 ++++++++++++++ ingress/icmp_posix_test.go | 76 ++++++++ ingress/icmp_windows.go | 13 +- ingress/icmp_windows_test.go | 3 +- ingress/origin_icmp_proxy.go | 30 ++- ingress/origin_icmp_proxy_test.go | 66 +++++-- packet/flow.go | 94 ---------- packet/funnel.go | 191 +++++++++++++++++++ 13 files changed, 682 insertions(+), 487 deletions(-) delete mode 100644 ingress/icmp_linux_test.go create mode 100644 ingress/icmp_posix.go create mode 100644 ingress/icmp_posix_test.go delete mode 100644 packet/flow.go create mode 100644 packet/funnel.go diff --git a/connection/quic.go b/connection/quic.go index d1b126da..a696192c 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -7,6 +7,7 @@ import ( "io" "net" "net/http" + "net/netip" "strconv" "strings" "sync/atomic" @@ -346,13 +347,31 @@ func (pr *packetRouter) serve(ctx context.Context) error { continue } - if err := pr.icmpProxy.Request(icmpPacket, pr.muxer); err != nil { - pr.logger.Err(err).Str("src", icmpPacket.Src.String()).Str("dst", icmpPacket.Dst.String()).Msg("Failed to send ICMP packet") + flowPipe := muxerResponder{muxer: pr.muxer} + if err := pr.icmpProxy.Request(icmpPacket, &flowPipe); err != nil { + pr.logger.Err(err). + Str("src", icmpPacket.Src.String()). + Str("dst", icmpPacket.Dst.String()). + Interface("type", icmpPacket.Type). + Msg("Failed to send ICMP packet") continue } } } +// muxerResponder wraps DatagramMuxerV2 to satisfy the packet.FunnelUniPipe interface +type muxerResponder struct { + muxer *quicpogs.DatagramMuxerV2 +} + +func (mr *muxerResponder) SendPacket(dst netip.Addr, pk packet.RawPacket) error { + return mr.muxer.SendPacket(pk) +} + +func (mr *muxerResponder) Close() error { + return nil +} + // streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to // the client. type streamReadWriteAcker struct { diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 2b1c9cdb..8e69202c 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -2,6 +2,11 @@ package ingress +// This file implements ICMPProxy for Darwin. It uses a non-privileged ICMP socket to send echo requests and listen for +// echo replies. The source IP of the requests are rewritten to the bind IP of the socket and the socket reads all +// messages, so we use echo ID to distinguish the replies. Each (source IP, destination IP, echo ID) is assigned a +// unique echo ID. + import ( "context" "fmt" @@ -10,9 +15,8 @@ import ( "net/netip" "strconv" "sync" + "time" - "github.com/google/gopacket/layers" - "github.com/pkg/errors" "github.com/rs/zerolog" "golang.org/x/net/icmp" @@ -20,13 +24,14 @@ import ( ) // TODO: TUN-6654 Extend support to IPv6 -// On Darwin, a non-privileged ICMP socket can read messages from all echo IDs, so we use it for all sources. type icmpProxy struct { - // TODO: TUN-6588 clean up flows - srcFlowTracker *packet.FlowTracker - echoIDTracker *echoIDTracker - conn *icmp.PacketConn - logger *zerolog.Logger + srcFunnelTracker *packet.FunnelTracker + echoIDTracker *echoIDTracker + conn *icmp.PacketConn + // Response is handled in one-by-one, so encoder can be shared between funnels + encoder *packet.Encoder + logger *zerolog.Logger + idleTimeout time.Duration } // echoIDTracker tracks which ID has been assigned. It first loops through assignment from lastAssignment to then end, @@ -92,8 +97,8 @@ func (eit *echoIDTracker) release(srcIP netip.Addr, id uint16) bool { eit.lock.Lock() defer eit.lock.Unlock() - currentID, ok := eit.srcIPMapping[srcIP] - if ok && id == currentID { + currentID, exists := eit.srcIPMapping[srcIP] + if exists && id == currentID { delete(eit.srcIPMapping, srcIP) eit.assignment[id] = false return true @@ -101,39 +106,68 @@ func (eit *echoIDTracker) release(srcIP netip.Addr, id uint16) bool { return false } -type echoFlowID uint16 +type echoFunnelID uint16 -func (snf echoFlowID) Type() string { +func (snf echoFunnelID) Type() string { return "echoID" } -func (snf echoFlowID) String() string { +func (snf echoFunnelID) String() string { return strconv.FormatUint(uint64(snf), 10) } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { conn, err := newICMPConn(listenIP) if err != nil { return nil, err } return &icmpProxy{ - srcFlowTracker: packet.NewFlowTracker(), - echoIDTracker: newEchoIDTracker(), - conn: conn, - logger: logger, + srcFunnelTracker: packet.NewFunnelTracker(), + echoIDTracker: newEchoIDTracker(), + encoder: packet.NewEncoder(), + conn: conn, + logger: logger, + idleTimeout: idleTimeout, }, nil } -func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { if pk == nil { return errPacketNil } - switch body := pk.Message.Body.(type) { - case *icmp.Echo: - return ip.sendICMPEchoRequest(pk, body, responder) - default: - return fmt.Errorf("sending ICMP %s is not implemented", pk.Type) + // TODO: TUN-6744 assign unique flow per (src, echo ID) + echoID, exists := ip.echoIDTracker.get(pk.Src) + if !exists { + originalEcho, err := getICMPEcho(pk.Message) + if err != nil { + return err + } + echoID, exists = ip.echoIDTracker.assign(pk.Src) + if !exists { + return fmt.Errorf("failed to assign unique echo ID") + } + funnelID := echoFunnelID(echoID) + originSender := originSender{ + conn: ip.conn, + echoIDTracker: ip.echoIDTracker, + srcIP: pk.Src, + echoID: echoID, + } + icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, int(echoID), originalEcho.ID, ip.encoder) + if replaced := ip.srcFunnelTracker.Register(funnelID, icmpFlow); replaced { + ip.logger.Info().Str("src", pk.Src.String()).Msg("Replaced funnel") + } + return icmpFlow.sendToDst(pk.Dst, pk.Message) } + funnel, exists := ip.srcFunnelTracker.Get(echoFunnelID(echoID)) + if !exists { + return packet.ErrFunnelNotFound + } + icmpFlow, err := toICMPEchoFlow(funnel) + if err != nil { + return err + } + return icmpFlow.sendToDst(pk.Dst, pk.Message) } // Serve listens for responses to the requests until context is done @@ -142,88 +176,54 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { <-ctx.Done() ip.conn.Close() }() + go func() { + ip.srcFunnelTracker.ScheduleCleanup(ctx, ip.idleTimeout) + }() buf := make([]byte, mtu) - encoder := packet.NewEncoder() for { n, src, err := ip.conn.ReadFrom(buf) if err != nil { return err } - // TODO: TUN-6654 Check for IPv6 - msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), buf[:n]) - if err != nil { - ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to parse ICMP message") - continue - } - switch body := msg.Body.(type) { - case *icmp.Echo: - if err := ip.handleEchoResponse(encoder, msg, body); err != nil { - ip.logger.Error().Err(err). - Str("src", src.String()). - Str("flowID", echoFlowID(body.ID).String()). - Msg("Failed to handle ICMP response") - continue - } - default: - ip.logger.Warn(). - Str("icmpType", fmt.Sprintf("%s", msg.Type)). - Msgf("Responding to this type of ICMP is not implemented") + if err := ip.handleResponse(src, buf[:n]); err != nil { + ip.logger.Err(err).Str("src", src.String()).Msg("Failed to handle ICMP response") continue } } } -func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { - echoID, ok := ip.echoIDTracker.get(pk.Src) - if !ok { - echoID, ok = ip.echoIDTracker.assign(pk.Src) - if !ok { - return fmt.Errorf("failed to assign unique echo ID") - } - flowID := echoFlowID(echoID) - flow := packet.Flow{ - Src: pk.Src, - Dst: pk.Dst, - Responder: responder, - } - if replaced := ip.srcFlowTracker.Register(flowID, &flow, true); replaced { - ip.logger.Info().Str("src", flow.Src.String()).Str("dst", flow.Dst.String()).Msg("Replaced flow") - } - } - - echo.ID = int(echoID) - var pseudoHeader []byte = nil - serializedMsg, err := pk.Marshal(pseudoHeader) +func (ip *icmpProxy) handleResponse(from net.Addr, rawMsg []byte) error { + reply, err := parseReply(from, rawMsg) if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") + return err } - // The address needs to be of type UDPAddr when conn is created without priviledge - _, err = ip.conn.WriteTo(serializedMsg, &net.UDPAddr{ - IP: pk.Dst.AsSlice(), + funnel, exists := ip.srcFunnelTracker.Get(echoFunnelID(reply.echo.ID)) + if !exists { + return packet.ErrFunnelNotFound + } + icmpFlow, err := toICMPEchoFlow(funnel) + if err != nil { + return err + } + return icmpFlow.returnToSrc(reply) +} + +// originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface +type originSender struct { + conn *icmp.PacketConn + echoIDTracker *echoIDTracker + srcIP netip.Addr + echoID uint16 +} + +func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error { + _, err := os.conn.WriteTo(pk.Data, &net.UDPAddr{ + IP: dst.AsSlice(), }) return err } -func (ip *icmpProxy) handleEchoResponse(encoder *packet.Encoder, msg *icmp.Message, echo *icmp.Echo) error { - flowID := echoFlowID(echo.ID) - flow, ok := ip.srcFlowTracker.Get(flowID) - if !ok { - return fmt.Errorf("flow not found") - } - icmpPacket := packet.ICMP{ - IP: &packet.IP{ - Src: flow.Dst, - Dst: flow.Src, - Protocol: layers.IPProtocol(msg.Type.Protocol()), - }, - Message: msg, - } - serializedPacket, err := encoder.Encode(&icmpPacket) - if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") - } - if err := flow.Responder.SendPacket(serializedPacket); err != nil { - return errors.Wrap(err, "Failed to send packet to the edge") - } +func (os *originSender) Close() error { + os.echoIDTracker.release(os.srcIP, os.echoID) return nil } diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index 976387f9..c1d92c70 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -10,6 +10,6 @@ import ( "github.com/rs/zerolog" ) -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { return nil, fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) } diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index f7e2d936..f0917e2e 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -2,16 +2,18 @@ package ingress +// This file implements ICMPProxy for Linux. Each (source IP, destination IP, echo ID) opens a non-privileged ICMP socket. +// The source IP of the requests are rewritten to the bind IP of the socket and echo ID rewritten to the port number of +// the socket. The kernel ensures the socket only reads replies whose echo ID matches the port number. +// For more information about the socket, see https://man7.org/linux/man-pages/man7/icmp.7.html and https://lwn.net/Articles/422330/ + import ( "context" "fmt" "net" "net/netip" - "sync" - "sync/atomic" "time" - "github.com/google/gopacket/layers" "github.com/pkg/errors" "github.com/rs/zerolog" "golang.org/x/net/icmp" @@ -19,30 +21,27 @@ import ( "github.com/cloudflare/cloudflared/packet" ) -// The request echo ID is rewritten to the port of the socket. The kernel uses the reply echo ID to demultiplex -// We can open a socket for each source so multiple sources requesting the same destination doesn't collide type icmpProxy struct { - srcToFlowTracker *srcToFlowTracker + srcFunnelTracker *packet.FunnelTracker listenIP netip.Addr logger *zerolog.Logger - shutdownC chan struct{} + idleTimeout time.Duration } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { if err := testPermission(listenIP); err != nil { return nil, err } return &icmpProxy{ - srcToFlowTracker: newSrcToConnTracker(), + srcFunnelTracker: packet.NewFunnelTracker(), listenIP: listenIP, logger: logger, - shutdownC: make(chan struct{}), + idleTimeout: idleTimeout, }, nil } func testPermission(listenIP netip.Addr) error { // Opens a non-privileged ICMP socket. On Linux the group ID of the process needs to be in ping_group_range - // For more information, see https://man7.org/linux/man-pages/man7/icmp.7.html and https://lwn.net/Articles/422330/ conn, err := newICMPConn(listenIP) if err != nil { // TODO: TUN-6715 check if cloudflared is in ping_group_range if the check failed. If not log instruction to @@ -54,213 +53,105 @@ func testPermission(listenIP netip.Addr) error { return nil } -func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { if pk == nil { return errPacketNil } - echo, err := getICMPEcho(pk) - if err != nil { - return err - } - return ip.sendICMPEchoRequest(pk, echo, responder) -} - -func (ip *icmpProxy) Serve(ctx context.Context) error { - <-ctx.Done() - close(ip.shutdownC) - return ctx.Err() -} - -func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error { - icmpFlow, ok := ip.srcToFlowTracker.get(pk.Src) - if ok { - return icmpFlow.send(pk) - } - - conn, err := newICMPConn(ip.listenIP) - if err != nil { - return err - } - flow := packet.Flow{ - Src: pk.Src, - Dst: pk.Dst, - Responder: responder, - } - icmpFlow = newICMPFlow(conn, &flow, uint16(echo.ID), ip.logger) - go func() { - defer ip.srcToFlowTracker.delete(pk.Src) - - if err := icmpFlow.serve(ip.shutdownC, defaultCloseAfterIdle); err != nil { - ip.logger.Debug().Err(err).Uint16("flowID", icmpFlow.echoID).Msg("flow terminated") - } - }() - ip.srcToFlowTracker.set(pk.Src, icmpFlow) - return icmpFlow.send(pk) -} - -type srcIPFlowID netip.Addr - -func (sifd srcIPFlowID) Type() string { - return "srcIP" -} - -func (sifd srcIPFlowID) String() string { - return netip.Addr(sifd).String() -} - -type srcToFlowTracker struct { - lock sync.RWMutex - // srcIPToConn tracks source IP to ICMP connection - srcToFlow map[netip.Addr]*icmpFlow -} - -func newSrcToConnTracker() *srcToFlowTracker { - return &srcToFlowTracker{ - srcToFlow: make(map[netip.Addr]*icmpFlow), - } -} - -func (sft *srcToFlowTracker) get(srcIP netip.Addr) (*icmpFlow, bool) { - sft.lock.RLock() - defer sft.lock.RUnlock() - - flow, ok := sft.srcToFlow[srcIP] - return flow, ok -} - -func (sft *srcToFlowTracker) set(srcIP netip.Addr, flow *icmpFlow) { - sft.lock.Lock() - defer sft.lock.Unlock() - - sft.srcToFlow[srcIP] = flow -} - -func (sft *srcToFlowTracker) delete(srcIP netip.Addr) { - sft.lock.Lock() - defer sft.lock.Unlock() - - delete(sft.srcToFlow, srcIP) -} - -type icmpFlow struct { - conn *icmp.PacketConn - flow *packet.Flow - echoID uint16 - // last active unix time. Unit is seconds - lastActive int64 - logger *zerolog.Logger -} - -func newICMPFlow(conn *icmp.PacketConn, flow *packet.Flow, echoID uint16, logger *zerolog.Logger) *icmpFlow { - return &icmpFlow{ - conn: conn, - flow: flow, - echoID: echoID, - lastActive: time.Now().Unix(), - logger: logger, - } -} - -func (f *icmpFlow) serve(shutdownC chan struct{}, closeAfterIdle time.Duration) error { - errC := make(chan error) - go func() { - errC <- f.listenResponse() - }() - - checkIdleTicker := time.NewTicker(closeAfterIdle) - defer f.conn.Close() - defer checkIdleTicker.Stop() - for { - select { - case err := <-errC: - return err - case <-shutdownC: - return nil - case <-checkIdleTicker.C: - now := time.Now().Unix() - lastActive := atomic.LoadInt64(&f.lastActive) - if now > lastActive+int64(closeAfterIdle.Seconds()) { - return errFlowInactive - } - } - } -} - -func (f *icmpFlow) send(pk *packet.ICMP) error { - f.updateLastActive() - - // For IPv4, the pseudoHeader is not used because the checksum is always calculated - var pseudoHeader []byte = nil - serializedMsg, err := pk.Marshal(pseudoHeader) - if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") - } - // The address needs to be of type UDPAddr when conn is created without priviledge - _, err = f.conn.WriteTo(serializedMsg, &net.UDPAddr{ - IP: pk.Dst.AsSlice(), - }) - return err -} - -func (f *icmpFlow) listenResponse() error { - buf := make([]byte, mtu) - encoder := packet.NewEncoder() - for { - n, src, err := f.conn.ReadFrom(buf) + funnelID := srcIPFunnelID(pk.Src) + funnel, exists := ip.srcFunnelTracker.Get(funnelID) + if !exists { + originalEcho, err := getICMPEcho(pk.Message) if err != nil { return err } - f.updateLastActive() + conn, err := newICMPConn(ip.listenIP) + if err != nil { + return errors.Wrap(err, "failed to open ICMP socket") + } + localUDPAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return fmt.Errorf("ICMP listener address %s is not net.UDPAddr", conn.LocalAddr()) + } + originSender := originSender{conn: conn} + echoID := localUDPAddr.Port + icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, echoID, originalEcho.ID, packet.NewEncoder()) + if replaced := ip.srcFunnelTracker.Register(funnelID, icmpFlow); replaced { + ip.logger.Info().Str("src", pk.Src.String()).Msg("Replaced funnel") + } + if err := icmpFlow.sendToDst(pk.Dst, pk.Message); err != nil { + return errors.Wrap(err, "failed to send ICMP echo request") + } + go func() { + defer ip.srcFunnelTracker.Unregister(funnelID, icmpFlow) + if err := ip.listenResponse(icmpFlow, conn); err != nil { + ip.logger.Err(err). + Str("funnelID", funnelID.String()). + Int("echoID", echoID). + Msg("Failed to listen for ICMP echo response") + } + }() + return nil + } + icmpFlow, err := toICMPEchoFlow(funnel) + if err != nil { + return err + } + if err := icmpFlow.sendToDst(pk.Dst, pk.Message); err != nil { + return errors.Wrap(err, "failed to send ICMP echo request") + } + return nil +} - if err := f.handleResponse(encoder, src, buf[:n]); err != nil { - f.logger.Err(err).Str("dst", src.String()).Msg("Failed to handle ICMP response") +func (ip *icmpProxy) Serve(ctx context.Context) error { + ip.srcFunnelTracker.ScheduleCleanup(ctx, ip.idleTimeout) + return ctx.Err() +} + +func (ip *icmpProxy) listenResponse(flow *icmpEchoFlow, conn *icmp.PacketConn) error { + buf := make([]byte, mtu) + for { + n, src, err := conn.ReadFrom(buf) + if err != nil { + return err + } + + if err := ip.handleResponse(flow, src, buf[:n]); err != nil { + ip.logger.Err(err).Str("dst", src.String()).Msg("Failed to handle ICMP response") continue } } } -func (f *icmpFlow) handleResponse(encoder *packet.Encoder, from net.Addr, rawPacket []byte) error { - // TODO: TUN-6654 Check for IPv6 - msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), rawPacket) +func (ip *icmpProxy) handleResponse(flow *icmpEchoFlow, from net.Addr, rawMsg []byte) error { + reply, err := parseReply(from, rawMsg) if err != nil { return err } - - echo, ok := msg.Body.(*icmp.Echo) - if !ok { - return fmt.Errorf("received unexpected icmp type %s from non-privileged ICMP socket", msg.Type) - } - - addrPort, err := netip.ParseAddrPort(from.String()) - if err != nil { - return err - } - icmpPacket := packet.ICMP{ - IP: &packet.IP{ - Src: addrPort.Addr(), - Dst: f.flow.Src, - Protocol: layers.IPProtocol(msg.Type.Protocol()), - }, - Message: &icmp.Message{ - Type: msg.Type, - Code: msg.Code, - Body: &icmp.Echo{ - ID: int(f.echoID), - Seq: echo.Seq, - Data: echo.Data, - }, - }, - } - serializedPacket, err := encoder.Encode(&icmpPacket) - if err != nil { - return errors.Wrap(err, "Failed to encode ICMP message") - } - if err := f.flow.Responder.SendPacket(serializedPacket); err != nil { - return errors.Wrap(err, "Failed to send packet to the edge") - } - return nil + return flow.returnToSrc(reply) } -func (f *icmpFlow) updateLastActive() { - atomic.StoreInt64(&f.lastActive, time.Now().Unix()) +// originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface +type originSender struct { + conn *icmp.PacketConn +} + +func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error { + _, err := os.conn.WriteTo(pk.Data, &net.UDPAddr{ + IP: dst.AsSlice(), + }) + return err +} + +func (os *originSender) Close() error { + return os.conn.Close() +} + +type srcIPFunnelID netip.Addr + +func (sifd srcIPFunnelID) Type() string { + return "srcIP" +} + +func (sifd srcIPFunnelID) String() string { + return netip.Addr(sifd).String() } diff --git a/ingress/icmp_linux_test.go b/ingress/icmp_linux_test.go deleted file mode 100644 index 40aee7a3..00000000 --- a/ingress/icmp_linux_test.go +++ /dev/null @@ -1,52 +0,0 @@ -//go:build linux - -package ingress - -import ( - "errors" - "net" - "net/netip" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/cloudflare/cloudflared/packet" -) - -func TestCloseIdleFlow(t *testing.T) { - const ( - echoID = 19234 - idleTimeout = time.Millisecond * 100 - ) - conn, err := newICMPConn(localhostIP) - require.NoError(t, err) - flow := packet.Flow{ - Src: netip.MustParseAddr("172.16.0.1"), - } - icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger) - shutdownC := make(chan struct{}) - flowErr := make(chan error) - go func() { - flowErr <- icmpFlow.serve(shutdownC, idleTimeout) - }() - - require.Equal(t, errFlowInactive, <-flowErr) -} - -func TestCloseConnStopFlow(t *testing.T) { - const ( - echoID = 19234 - ) - conn, err := newICMPConn(localhostIP) - require.NoError(t, err) - flow := packet.Flow{ - Src: netip.MustParseAddr("172.16.0.1"), - } - icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger) - shutdownC := make(chan struct{}) - conn.Close() - - err = icmpFlow.serve(shutdownC, defaultCloseAfterIdle) - require.True(t, errors.Is(err, net.ErrClosed)) -} diff --git a/ingress/icmp_posix.go b/ingress/icmp_posix.go new file mode 100644 index 00000000..442017bb --- /dev/null +++ b/ingress/icmp_posix.go @@ -0,0 +1,142 @@ +//go:build darwin || linux + +package ingress + +// This file extracts logic shared by Linux and Darwin implementation if ICMPProxy. + +import ( + "fmt" + "net" + "net/netip" + + "github.com/google/gopacket/layers" + "golang.org/x/net/icmp" + + "github.com/cloudflare/cloudflared/packet" +) + +// Opens a non-privileged ICMP socket on Linux and Darwin +func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) { + network := "udp6" + if listenIP.Is4() { + network = "udp4" + } + return icmp.ListenPacket(network, listenIP.String()) +} + +func netipAddr(addr net.Addr) (netip.Addr, bool) { + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + return netip.Addr{}, false + } + return netip.AddrFromSlice(udpAddr.IP) +} + +type flowID struct { + srcIP netip.Addr + echoID int +} + +func (fi *flowID) Type() string { + return "srcIP_echoID" +} + +func (fi *flowID) String() string { + return fmt.Sprintf("%s:%d", fi.srcIP, fi.echoID) +} + +// icmpEchoFlow implements the packet.Funnel interface. +type icmpEchoFlow struct { + *packet.RawPacketFunnel + assignedEchoID int + originalEchoID int + // it's up to the user to ensure respEncoder is not used concurrently + respEncoder *packet.Encoder +} + +func newICMPEchoFlow(src netip.Addr, sendPipe, returnPipe packet.FunnelUniPipe, assignedEchoID, originalEchoID int, respEncoder *packet.Encoder) *icmpEchoFlow { + return &icmpEchoFlow{ + RawPacketFunnel: packet.NewRawPacketFunnel(src, sendPipe, returnPipe), + assignedEchoID: assignedEchoID, + originalEchoID: originalEchoID, + respEncoder: respEncoder, + } +} + +// sendToDst rewrites the echo ID to the one assigned to this flow +func (ief *icmpEchoFlow) sendToDst(dst netip.Addr, msg *icmp.Message) error { + originalEcho, err := getICMPEcho(msg) + if err != nil { + return err + } + sendMsg := icmp.Message{ + Type: msg.Type, + Code: msg.Code, + Body: &icmp.Echo{ + ID: ief.assignedEchoID, + Seq: originalEcho.Seq, + Data: originalEcho.Data, + }, + } + // For IPv4, the pseudoHeader is not used because the checksum is always calculated + var pseudoHeader []byte = nil + serializedPacket, err := sendMsg.Marshal(pseudoHeader) + if err != nil { + return err + } + return ief.SendToDst(dst, packet.RawPacket{Data: serializedPacket}) +} + +// returnToSrc rewrites the echo ID to the original echo ID from the eyeball +func (ief *icmpEchoFlow) returnToSrc(reply *echoReply) error { + reply.echo.ID = ief.originalEchoID + reply.msg.Body = reply.echo + pk := packet.ICMP{ + IP: &packet.IP{ + Src: reply.from, + Dst: ief.Src, + Protocol: layers.IPProtocol(reply.msg.Type.Protocol()), + }, + Message: reply.msg, + } + serializedPacket, err := ief.respEncoder.Encode(&pk) + if err != nil { + return err + } + return ief.ReturnToSrc(serializedPacket) +} + +type echoReply struct { + from netip.Addr + msg *icmp.Message + echo *icmp.Echo +} + +func parseReply(from net.Addr, rawMsg []byte) (*echoReply, error) { + // TODO: TUN-6654 Check for IPv6 + msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), rawMsg) + if err != nil { + return nil, err + } + echo, err := getICMPEcho(msg) + if err != nil { + return nil, err + } + fromAddr, ok := netipAddr(from) + if !ok { + return nil, fmt.Errorf("cannot convert %s to netip.Addr", from) + } + return &echoReply{ + from: fromAddr, + msg: msg, + echo: echo, + }, nil +} + +func toICMPEchoFlow(funnel packet.Funnel) (*icmpEchoFlow, error) { + icmpFlow, ok := funnel.(*icmpEchoFlow) + if !ok { + return nil, fmt.Errorf("%v is not *ICMPEchoFunnel", funnel) + } + return icmpFlow, nil +} diff --git a/ingress/icmp_posix_test.go b/ingress/icmp_posix_test.go new file mode 100644 index 00000000..397aa524 --- /dev/null +++ b/ingress/icmp_posix_test.go @@ -0,0 +1,76 @@ +//go:build darwin || linux + +package ingress + +import ( + "context" + "os" + "testing" + "time" + + "github.com/google/gopacket/layers" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + + "github.com/cloudflare/cloudflared/packet" +) + +func TestFunnelIdleTimeout(t *testing.T) { + const ( + idleTimeout = time.Second + echoID = 42573 + startSeq = 8129 + ) + logger := zerolog.New(os.Stderr) + proxy, err := newICMPProxy(localhostIP, &logger, idleTimeout) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + + proxyDone := make(chan struct{}) + go func() { + proxy.Serve(ctx) + close(proxyDone) + }() + + // Send a packet to register the flow + pk := packet.ICMP{ + IP: &packet.IP{ + Src: localhostIP, + Dst: localhostIP, + Protocol: layers.IPProtocolICMPv4, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: echoID, + Seq: startSeq, + Data: []byte(t.Name()), + }, + }, + } + responder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte), + } + require.NoError(t, proxy.Request(&pk, &responder)) + responder.validate(t, &pk) + + // Send second request, should reuse the funnel + require.NoError(t, proxy.Request(&pk, nil)) + responder.validate(t, &pk) + + time.Sleep(idleTimeout * 2) + newResponder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte), + } + require.NoError(t, proxy.Request(&pk, &newResponder)) + newResponder.validate(t, &pk) + + cancel() + <-proxyDone +} diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index 36eb90d5..ac34819c 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -15,6 +15,7 @@ import ( "runtime/debug" "sync" "syscall" + "time" "unsafe" "github.com/google/gopacket/layers" @@ -143,7 +144,7 @@ type icmpProxy struct { encoderPool sync.Pool } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { handle, _, err := IcmpCreateFile_proc.Call() // Windows procedure calls always return non-nil error constructed from the result of GetLastError. // Caller need to inspect the primary returned value @@ -167,7 +168,7 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { return ctx.Err() } -func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error { +func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { if pk == nil { return errPacketNil } @@ -176,7 +177,7 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) er ip.logger.Error().Interface("error", r).Msgf("Recover panic from sending icmp request/response, error %s", debug.Stack()) } }() - echo, err := getICMPEcho(pk) + echo, err := getICMPEcho(pk.Message) if err != nil { return err } @@ -193,7 +194,7 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) er return nil } -func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo, resp *echoResp, responder packet.FlowResponder) error { +func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo, resp *echoResp, responder packet.FunnelUniPipe) error { var replyType icmp.Type if request.Dst.Is4() { replyType = ipv4.ICMPTypeEchoReply @@ -222,7 +223,7 @@ func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo if err != nil { return err } - return responder.SendPacket(serializedPacket) + return responder.SendPacket(request.Src, serializedPacket) } func (ip *icmpProxy) encodeICMPReply(pk *packet.ICMP) (packet.RawPacket, error) { @@ -262,7 +263,7 @@ func (ip *icmpProxy) icmpSendEcho(dst netip.Addr, echo *icmp.Echo) (*echoResp, e } replyCount, _, err := IcmpSendEcho_proc.Call(ip.handle, uintptr(inAddr), uintptr(unsafe.Pointer(&echo.Data[0])), uintptr(dataSize), noIPHeaderOption, uintptr(unsafe.Pointer(&replyBuf[0])), - replySize, icmpTimeoutMs) + replySize, icmpRequestTimeoutMs) if replyCount == 0 { // status is returned in 5th to 8th byte of reply buffer if status, err := unmarshalIPStatus(replyBuf[4:8]); err == nil { diff --git a/ingress/icmp_windows_test.go b/ingress/icmp_windows_test.go index 633ddf47..d6aae071 100644 --- a/ingress/icmp_windows_test.go +++ b/ingress/icmp_windows_test.go @@ -9,6 +9,7 @@ import ( "io" "net/netip" "testing" + "time" "unsafe" "golang.org/x/net/icmp" @@ -72,7 +73,7 @@ func TestParseEchoReply(t *testing.T) { // TestSendEchoErrors makes sure icmpSendEcho handles error cases func TestSendEchoErrors(t *testing.T) { - proxy, err := newICMPProxy(localhostIP, &noopLogger) + proxy, err := newICMPProxy(localhostIP, &noopLogger, time.Second) require.NoError(t, err) winProxy := proxy.(*icmpProxy) diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index f2a4180c..50245c7b 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -13,14 +13,15 @@ import ( ) const ( - defaultCloseAfterIdle = time.Second * 15 - mtu = 1500 - icmpTimeoutMs = 1000 + // funnelIdleTimeout controls how long to wait to close a funnel without send/return + funnelIdleTimeout = time.Second * 10 + mtu = 1500 + // icmpRequestTimeoutMs controls how long to wait for a reply + icmpRequestTimeoutMs = 1000 ) var ( - errFlowInactive = fmt.Errorf("flow is inactive") - errPacketNil = fmt.Errorf("packet is nil") + errPacketNil = fmt.Errorf("packet is nil") ) // ICMPProxy sends ICMP messages and listens for their responses @@ -28,26 +29,17 @@ type ICMPProxy interface { // Serve starts listening for responses to the requests until context is done Serve(ctx context.Context) error // Request sends an ICMP message - Request(pk *packet.ICMP, responder packet.FlowResponder) error + Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error } func NewICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { - return newICMPProxy(listenIP, logger) + return newICMPProxy(listenIP, logger, funnelIdleTimeout) } -// Opens a non-privileged ICMP socket on Linux and Darwin -func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) { - network := "udp6" - if listenIP.Is4() { - network = "udp4" - } - return icmp.ListenPacket(network, listenIP.String()) -} - -func getICMPEcho(pk *packet.ICMP) (*icmp.Echo, error) { - echo, ok := pk.Message.Body.(*icmp.Echo) +func getICMPEcho(msg *icmp.Message) (*icmp.Echo, error) { + echo, ok := msg.Body.(*icmp.Echo) if !ok { - return nil, fmt.Errorf("expect ICMP echo, got %s", pk.Type) + return nil, fmt.Errorf("expect ICMP echo, got %s", msg.Type) } return echo, nil } diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index 7a1ebc89..fc6010c1 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -3,6 +3,7 @@ package ingress import ( "context" "fmt" + "net" "net/netip" "testing" @@ -47,26 +48,48 @@ func TestICMPProxyEcho(t *testing.T) { respChan: make(chan []byte, 1), } - ip := packet.IP{ - Src: localhostIP, - Dst: localhostIP, - Protocol: layers.IPProtocolICMPv4, + ips := []packet.IP{ + { + Src: localhostIP, + Dst: localhostIP, + Protocol: layers.IPProtocolICMPv4, + }, } - for i := 0; i < endSeq; i++ { - pk := packet.ICMP{ - IP: &ip, - Message: &icmp.Message{ - Type: ipv4.ICMPTypeEcho, - Code: 0, - Body: &icmp.Echo{ - ID: echoID, - Seq: i, - Data: []byte(fmt.Sprintf("icmp echo seq %d", i)), - }, - }, + + addrs, err := net.InterfaceAddrs() + require.NoError(t, err) + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok { + ip := ipnet.IP + if !ipnet.IP.IsLoopback() && ip.IsPrivate() && ip.To4() != nil { + localIP := netip.MustParseAddr(ipnet.IP.String()) + ips = append(ips, packet.IP{ + Src: localIP, + Dst: localIP, + Protocol: layers.IPProtocolICMPv4, + }) + break + } + } + } + + for seq := 0; seq < endSeq; seq++ { + for i, ip := range ips { + pk := packet.ICMP{ + IP: &ip, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: echoID + i, + Seq: seq, + Data: []byte(fmt.Sprintf("icmp echo seq %d", seq)), + }, + }, + } + require.NoError(t, proxy.Request(&pk, &responder)) + responder.validate(t, &pk) } - require.NoError(t, proxy.Request(&pk, &responder)) - responder.validate(t, &pk) } cancel() <-proxyDone @@ -123,13 +146,18 @@ type echoFlowResponder struct { respChan chan []byte } -func (efr *echoFlowResponder) SendPacket(pk packet.RawPacket) error { +func (efr *echoFlowResponder) SendPacket(dst netip.Addr, pk packet.RawPacket) error { copiedPacket := make([]byte, len(pk.Data)) copy(copiedPacket, pk.Data) efr.respChan <- copiedPacket return nil } +func (efr *echoFlowResponder) Close() error { + close(efr.respChan) + return nil +} + func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { pk := <-efr.respChan decoded, err := efr.decoder.Decode(packet.RawPacket{Data: pk}) diff --git a/packet/flow.go b/packet/flow.go deleted file mode 100644 index 188f3063..00000000 --- a/packet/flow.go +++ /dev/null @@ -1,94 +0,0 @@ -package packet - -import ( - "errors" - "fmt" - "net/netip" - "sync" -) - -var ( - ErrFlowNotFound = errors.New("flow not found") -) - -// FlowID represents a key type that can be used by FlowTracker -type FlowID interface { - // Type returns the name of the type that implements the FlowID - Type() string - fmt.Stringer -} - -type Flow struct { - Src netip.Addr - Dst netip.Addr - Responder FlowResponder -} - -func isSameFlow(f1, f2 *Flow) bool { - if f1 == nil || f2 == nil { - return false - } - return *f1 == *f2 -} - -// FlowResponder sends response packets to the flow -type FlowResponder interface { - // SendPacket returns a packet to the flow. It must not modify the packet, - // and after return it must not read the packet - SendPacket(pk RawPacket) error -} - -// FlowTracker tracks flow from the perspective of eyeball to origin -type FlowTracker struct { - lock sync.RWMutex - flows map[FlowID]*Flow -} - -func NewFlowTracker() *FlowTracker { - return &FlowTracker{ - flows: make(map[FlowID]*Flow), - } -} - -func (sft *FlowTracker) Get(id FlowID) (*Flow, bool) { - sft.lock.RLock() - defer sft.lock.RUnlock() - flow, ok := sft.flows[id] - return flow, ok -} - -// Registers a flow. If shouldReplace = true, replace the current flow -func (sft *FlowTracker) Register(id FlowID, flow *Flow, shouldReplace bool) (replaced bool) { - sft.lock.Lock() - defer sft.lock.Unlock() - currentFlow, ok := sft.flows[id] - if !ok { - sft.flows[id] = flow - return false - } - - if shouldReplace && !isSameFlow(currentFlow, flow) { - sft.flows[id] = flow - return true - } - return false -} - -// Unregisters a flow. If force = true, delete it even if it maps to a different flow -func (sft *FlowTracker) Unregister(id FlowID, flow *Flow, force bool) (forceDeleted bool) { - sft.lock.Lock() - defer sft.lock.Unlock() - currentFlow, ok := sft.flows[id] - if !ok { - return false - } - if isSameFlow(currentFlow, flow) { - delete(sft.flows, id) - return false - } - if force { - delete(sft.flows, id) - return true - } - return false -} diff --git a/packet/funnel.go b/packet/funnel.go new file mode 100644 index 00000000..0cb1667a --- /dev/null +++ b/packet/funnel.go @@ -0,0 +1,191 @@ +package packet + +import ( + "context" + "errors" + "fmt" + "net/netip" + "sync" + "sync/atomic" + "time" +) + +var ( + ErrFunnelNotFound = errors.New("funnel not found") +) + +// Funnel is an abstraction to pipe from 1 src to 1 or more destinations +type Funnel interface { + // SendToDst sends a raw packet to a destination + SendToDst(dst netip.Addr, pk RawPacket) error + // ReturnToSrc returns a raw packet to the source + ReturnToSrc(pk RawPacket) error + // LastActive returns the last time SendToDst or ReturnToSrc is called + LastActive() time.Time + // Close closes the funnel. Further call to SendToDst or ReturnToSrc should return an error + Close() error + // Equal compares if 2 funnels are equivalent + Equal(other Funnel) bool +} + +// FunnelUniPipe is a unidirectional pipe for sending raw packets +type FunnelUniPipe interface { + // SendPacket sends a packet to/from the funnel. It must not modify the packet, + // and after return it must not read the packet + SendPacket(dst netip.Addr, pk RawPacket) error + Close() error +} + +// RawPacketFunnel is an implementation of Funnel that sends raw packets. It can be embedded in other structs to +// satisfy the Funnel interface. +type RawPacketFunnel struct { + Src netip.Addr + // last active unix time. Unit is seconds + lastActive int64 + sendPipe FunnelUniPipe + returnPipe FunnelUniPipe +} + +func NewRawPacketFunnel(src netip.Addr, sendPipe, returnPipe FunnelUniPipe) *RawPacketFunnel { + return &RawPacketFunnel{ + Src: src, + lastActive: time.Now().Unix(), + sendPipe: sendPipe, + returnPipe: returnPipe, + } +} + +func (rpf *RawPacketFunnel) SendToDst(dst netip.Addr, pk RawPacket) error { + rpf.updateLastActive() + return rpf.sendPipe.SendPacket(dst, pk) +} + +func (rpf *RawPacketFunnel) ReturnToSrc(pk RawPacket) error { + rpf.updateLastActive() + return rpf.returnPipe.SendPacket(rpf.Src, pk) +} + +func (rpf *RawPacketFunnel) updateLastActive() { + atomic.StoreInt64(&rpf.lastActive, time.Now().Unix()) +} + +func (rpf *RawPacketFunnel) LastActive() time.Time { + lastActive := atomic.LoadInt64(&rpf.lastActive) + return time.Unix(lastActive, 0) +} + +func (rpf *RawPacketFunnel) Close() error { + sendPipeErr := rpf.sendPipe.Close() + returnPipeErr := rpf.returnPipe.Close() + if sendPipeErr != nil { + return sendPipeErr + } + if returnPipeErr != nil { + return returnPipeErr + } + return nil +} + +func (rpf *RawPacketFunnel) Equal(other Funnel) bool { + otherRawFunnel, ok := other.(*RawPacketFunnel) + if !ok { + return false + } + if rpf.Src != otherRawFunnel.Src { + return false + } + if rpf.sendPipe != otherRawFunnel.sendPipe { + return false + } + if rpf.returnPipe != otherRawFunnel.returnPipe { + return false + } + return true +} + +// FunnelID represents a key type that can be used by FunnelTracker +type FunnelID interface { + // Type returns the name of the type that implements the FunnelID + Type() string + fmt.Stringer +} + +// FunnelTracker tracks funnel from the perspective of eyeball to origin +type FunnelTracker struct { + lock sync.RWMutex + funnels map[FunnelID]Funnel +} + +func NewFunnelTracker() *FunnelTracker { + return &FunnelTracker{ + funnels: make(map[FunnelID]Funnel), + } +} + +func (ft *FunnelTracker) ScheduleCleanup(ctx context.Context, idleTimeout time.Duration) { + checkIdleTicker := time.NewTicker(idleTimeout) + defer checkIdleTicker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-checkIdleTicker.C: + ft.cleanup(idleTimeout) + } + } +} + +func (ft *FunnelTracker) cleanup(idleTimeout time.Duration) { + ft.lock.Lock() + defer ft.lock.Unlock() + + now := time.Now() + for id, funnel := range ft.funnels { + lastActive := funnel.LastActive() + if now.After(lastActive.Add(idleTimeout)) { + funnel.Close() + delete(ft.funnels, id) + } + } +} + +func (ft *FunnelTracker) Get(id FunnelID) (Funnel, bool) { + ft.lock.RLock() + defer ft.lock.RUnlock() + funnel, ok := ft.funnels[id] + return funnel, ok +} + +// Registers a funnel. It replaces the current funnel. +func (ft *FunnelTracker) Register(id FunnelID, funnel Funnel) (replaced bool) { + ft.lock.Lock() + defer ft.lock.Unlock() + currentFunnel, exists := ft.funnels[id] + if !exists { + ft.funnels[id] = funnel + return false + } + replaced = !currentFunnel.Equal(funnel) + if replaced { + currentFunnel.Close() + } + ft.funnels[id] = funnel + return replaced +} + +// Unregisters a funnel if the funnel equals to the current funnel +func (ft *FunnelTracker) Unregister(id FunnelID, funnel Funnel) (deleted bool) { + ft.lock.Lock() + defer ft.lock.Unlock() + currentFunnel, exists := ft.funnels[id] + if !exists { + return true + } + if currentFunnel.Equal(funnel) { + currentFunnel.Close() + delete(ft.funnels, id) + return true + } + return false +} From a65f8bce7f0fcc5d4f584c1d02f07369e9278e52 Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 9 Sep 2022 14:47:46 +0100 Subject: [PATCH 194/238] TUN-6749: Fix icmp_generic build --- cfsetup.yaml | 4 ++-- ingress/icmp_generic.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cfsetup.yaml b/cfsetup.yaml index 71fadc1b..468ce636 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -221,13 +221,13 @@ stretch: &stretch - build-essential - python3 - genisoimage - - jetez pre-cache: - ln -s /usr/bin/genisoimage /usr/bin/mkisofs post-cache: + - export CGO_ENABLED=0 - export GOOS=freebsd - export GOARCH=amd64 - - make cloudflared-junos + - make cloudflared publish-junos: build_dir: *build_dir builddeps: diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index c1d92c70..781dc65b 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -6,6 +6,7 @@ import ( "fmt" "net/netip" "runtime" + "time" "github.com/rs/zerolog" ) From bf3d70d1d26b1ea5bc7e2ab5fd07fa98e7dc5dc2 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 6 Sep 2022 13:46:21 +0100 Subject: [PATCH 195/238] TUN-6654: Support ICMPv6 on Linux and Darwin --- ingress/icmp_darwin.go | 46 ++++++++-- ingress/icmp_linux.go | 24 +++--- ingress/icmp_posix.go | 15 ++-- ingress/icmp_windows.go | 3 + ingress/origin_icmp_proxy.go | 67 ++++++++++++++- ingress/origin_icmp_proxy_test.go | 136 ++++++++++++++++++++++-------- supervisor/supervisor.go | 9 +- 7 files changed, 232 insertions(+), 68 deletions(-) diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 8e69202c..ace04f23 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -23,7 +23,6 @@ import ( "github.com/cloudflare/cloudflared/packet" ) -// TODO: TUN-6654 Extend support to IPv6 type icmpProxy struct { srcFunnelTracker *packet.FunnelTracker echoIDTracker *echoIDTracker @@ -180,25 +179,56 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { ip.srcFunnelTracker.ScheduleCleanup(ctx, ip.idleTimeout) }() buf := make([]byte, mtu) + icmpDecoder := packet.NewICMPDecoder() for { - n, src, err := ip.conn.ReadFrom(buf) + n, from, err := ip.conn.ReadFrom(buf) if err != nil { return err } - if err := ip.handleResponse(src, buf[:n]); err != nil { - ip.logger.Err(err).Str("src", src.String()).Msg("Failed to handle ICMP response") + reply, err := parseReply(from, buf[:n]) + if err != nil { + ip.logger.Debug().Err(err).Str("dst", from.String()).Msg("Failed to parse ICMP reply, continue to parse as full packet") + // In unit test, we found out when the listener listens on 0.0.0.0, the socket reads the full packet after + // the second reply + if err := ip.handleFullPacket(icmpDecoder, buf[:n]); err != nil { + ip.logger.Err(err).Str("dst", from.String()).Msg("Failed to parse ICMP reply as full packet") + } + continue + } + if !isEchoReply(reply.msg) { + ip.logger.Debug().Str("dst", from.String()).Msgf("Drop ICMP %s from reply", reply.msg.Type) + continue + } + if ip.sendReply(reply); err != nil { + ip.logger.Error().Err(err).Str("dst", from.String()).Msg("Failed to send ICMP reply") continue } } } -func (ip *icmpProxy) handleResponse(from net.Addr, rawMsg []byte) error { - reply, err := parseReply(from, rawMsg) +func (ip *icmpProxy) handleFullPacket(decoder *packet.ICMPDecoder, rawPacket []byte) error { + icmpPacket, err := decoder.Decode(packet.RawPacket{Data: rawPacket}) if err != nil { return err } - funnel, exists := ip.srcFunnelTracker.Get(echoFunnelID(reply.echo.ID)) - if !exists { + echo, err := getICMPEcho(icmpPacket.Message) + if err != nil { + return err + } + reply := echoReply{ + from: icmpPacket.Src, + msg: icmpPacket.Message, + echo: echo, + } + if ip.sendReply(&reply); err != nil { + return err + } + return nil +} + +func (ip *icmpProxy) sendReply(reply *echoReply) error { + funnel, ok := ip.srcFunnelTracker.Get(echoFunnelID(reply.echo.ID)) + if !ok { return packet.ErrFunnelNotFound } icmpFlow, err := toICMPEchoFlow(funnel) diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index f0917e2e..21da6ce1 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -110,26 +110,26 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { func (ip *icmpProxy) listenResponse(flow *icmpEchoFlow, conn *icmp.PacketConn) error { buf := make([]byte, mtu) for { - n, src, err := conn.ReadFrom(buf) + n, from, err := conn.ReadFrom(buf) if err != nil { return err } - - if err := ip.handleResponse(flow, src, buf[:n]); err != nil { - ip.logger.Err(err).Str("dst", src.String()).Msg("Failed to handle ICMP response") + reply, err := parseReply(from, buf[:n]) + if err != nil { + ip.logger.Error().Err(err).Str("dst", from.String()).Msg("Failed to parse ICMP reply") + continue + } + if !isEchoReply(reply.msg) { + ip.logger.Debug().Str("dst", from.String()).Msgf("Drop ICMP %s from reply", reply.msg.Type) + continue + } + if err := flow.returnToSrc(reply); err != nil { + ip.logger.Err(err).Str("dst", from.String()).Msg("Failed to send ICMP reply") continue } } } -func (ip *icmpProxy) handleResponse(flow *icmpEchoFlow, from net.Addr, rawMsg []byte) error { - reply, err := parseReply(from, rawMsg) - if err != nil { - return err - } - return flow.returnToSrc(reply) -} - // originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface type originSender struct { conn *icmp.PacketConn diff --git a/ingress/icmp_posix.go b/ingress/icmp_posix.go index 442017bb..927c02f9 100644 --- a/ingress/icmp_posix.go +++ b/ingress/icmp_posix.go @@ -113,8 +113,15 @@ type echoReply struct { } func parseReply(from net.Addr, rawMsg []byte) (*echoReply, error) { - // TODO: TUN-6654 Check for IPv6 - msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), rawMsg) + fromAddr, ok := netipAddr(from) + if !ok { + return nil, fmt.Errorf("cannot convert %s to netip.Addr", from) + } + proto := layers.IPProtocolICMPv4 + if fromAddr.Is6() { + proto = layers.IPProtocolICMPv6 + } + msg, err := icmp.ParseMessage(int(proto), rawMsg) if err != nil { return nil, err } @@ -122,10 +129,6 @@ func parseReply(from net.Addr, rawMsg []byte) (*echoReply, error) { if err != nil { return nil, err } - fromAddr, ok := netipAddr(from) - if !ok { - return nil, fmt.Errorf("cannot convert %s to netip.Addr", from) - } return &echoReply{ from: fromAddr, msg: msg, diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index ac34819c..f5b5ac48 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -145,6 +145,9 @@ type icmpProxy struct { } func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { + if listenIP.Is6() { + return nil, fmt.Errorf("ICMPv6 not implemented for Windows") + } handle, _, err := IcmpCreateFile_proc.Call() // Windows procedure calls always return non-nil error constructed from the result of GetLastError. // Caller need to inspect the primary returned value diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index 50245c7b..c114eb75 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -8,6 +8,8 @@ import ( "github.com/rs/zerolog" "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" "github.com/cloudflare/cloudflared/packet" ) @@ -32,8 +34,65 @@ type ICMPProxy interface { Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error } -func NewICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) { - return newICMPProxy(listenIP, logger, funnelIdleTimeout) +type icmpRouter struct { + ipv4Proxy ICMPProxy + ipv6Proxy ICMPProxy +} + +// NewICMPProxy doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only +// support one of them +func NewICMPProxy(logger *zerolog.Logger) (ICMPProxy, error) { + // TODO: TUN-6741: don't bind to all interface + ipv4Proxy, ipv4Err := newICMPProxy(netip.IPv4Unspecified(), logger, funnelIdleTimeout) + ipv6Proxy, ipv6Err := newICMPProxy(netip.IPv6Unspecified(), logger, funnelIdleTimeout) + if ipv4Err != nil && ipv6Err != nil { + return nil, fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err) + } + if ipv4Err != nil { + logger.Warn().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created") + ipv4Proxy = nil + } + if ipv6Err != nil { + logger.Warn().Err(ipv6Err).Msg("failed to create ICMPv6 proxy, only ICMPv4 proxy is created") + ipv6Proxy = nil + } + return &icmpRouter{ + ipv4Proxy: ipv4Proxy, + ipv6Proxy: ipv6Proxy, + }, nil +} + +func (ir *icmpRouter) Serve(ctx context.Context) error { + if ir.ipv4Proxy != nil && ir.ipv6Proxy != nil { + errC := make(chan error, 2) + go func() { + errC <- ir.ipv4Proxy.Serve(ctx) + }() + go func() { + errC <- ir.ipv6Proxy.Serve(ctx) + }() + return <-errC + } + if ir.ipv4Proxy != nil { + return ir.ipv4Proxy.Serve(ctx) + } + if ir.ipv6Proxy != nil { + return ir.ipv6Proxy.Serve(ctx) + } + return fmt.Errorf("ICMPv4 proxy and ICMPv6 proxy are both nil") +} + +func (ir *icmpRouter) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { + if pk.Dst.Is4() { + if ir.ipv4Proxy != nil { + return ir.ipv4Proxy.Request(pk, responder) + } + return fmt.Errorf("ICMPv4 proxy was not instantiated") + } + if ir.ipv6Proxy != nil { + return ir.ipv6Proxy.Request(pk, responder) + } + return fmt.Errorf("ICMPv6 proxy was not instantiated") } func getICMPEcho(msg *icmp.Message) (*icmp.Echo, error) { @@ -43,3 +102,7 @@ func getICMPEcho(msg *icmp.Message) (*icmp.Echo, error) { } return echo, nil } + +func isEchoReply(msg *icmp.Message) bool { + return msg.Type == ipv4.ICMPTypeEchoReply || msg.Type == ipv6.ICMPTypeEchoReply +} diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index fc6010c1..d3573894 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -5,6 +5,8 @@ import ( "fmt" "net" "net/netip" + "runtime" + "strings" "testing" "github.com/google/gopacket/layers" @@ -12,13 +14,15 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" "github.com/cloudflare/cloudflared/packet" ) var ( - noopLogger = zerolog.Nop() - localhostIP = netip.MustParseAddr("127.0.0.1") + noopLogger = zerolog.Nop() + localhostIP = netip.MustParseAddr("127.0.0.1") + localhostIPv6 = netip.MustParseAddr("::1") ) // TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the @@ -28,12 +32,20 @@ var ( // is allowed in ping_group_range. See the following gist for how to do that: // https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md func TestICMPProxyEcho(t *testing.T) { + testICMPProxyEcho(t, true) + if runtime.GOOS == "windows" { + t.Skip("TODO: TUN-6743: test ICMPv6 on Windows") + } + testICMPProxyEcho(t, false) +} + +func testICMPProxyEcho(t *testing.T, sendIPv4 bool) { const ( echoID = 36571 - endSeq = 100 + endSeq = 20 ) - proxy, err := NewICMPProxy(localhostIP, &noopLogger) + proxy, err := NewICMPProxy(&noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) @@ -48,37 +60,30 @@ func TestICMPProxyEcho(t *testing.T) { respChan: make(chan []byte, 1), } - ips := []packet.IP{ - { - Src: localhostIP, - Dst: localhostIP, - Protocol: layers.IPProtocolICMPv4, - }, + protocol := layers.IPProtocolICMPv6 + if sendIPv4 { + protocol = layers.IPProtocolICMPv4 } - - addrs, err := net.InterfaceAddrs() - require.NoError(t, err) - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok { - ip := ipnet.IP - if !ipnet.IP.IsLoopback() && ip.IsPrivate() && ip.To4() != nil { - localIP := netip.MustParseAddr(ipnet.IP.String()) - ips = append(ips, packet.IP{ - Src: localIP, - Dst: localIP, - Protocol: layers.IPProtocolICMPv4, - }) - break - } + localIPs := getLocalIPs(t, sendIPv4) + ips := make([]*packet.IP, len(localIPs)) + for i, localIP := range localIPs { + ips[i] = &packet.IP{ + Src: localIP, + Dst: localIP, + Protocol: protocol, } } + var icmpType icmp.Type = ipv6.ICMPTypeEchoRequest + if sendIPv4 { + icmpType = ipv4.ICMPTypeEcho + } for seq := 0; seq < endSeq; seq++ { for i, ip := range ips { pk := packet.ICMP{ - IP: &ip, + IP: ip, Message: &icmp.Message{ - Type: ipv4.ICMPTypeEcho, + Type: icmpType, Code: 0, Body: &icmp.Echo{ ID: echoID + i, @@ -121,19 +126,52 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { }, }, } - proxy, err := NewICMPProxy(localhostIP, &noopLogger) + testICMPProxyRejectNotEcho(t, localhostIP, msgs) + msgsV6 := []icmp.Message{ + { + Type: ipv6.ICMPTypeDestinationUnreachable, + Code: 3, + Body: &icmp.DstUnreach{ + Data: []byte("original packet"), + }, + }, + { + Type: ipv6.ICMPTypeTimeExceeded, + Code: 0, + Body: &icmp.TimeExceeded{ + Data: []byte("original packet"), + }, + }, + { + Type: ipv6.ICMPTypePacketTooBig, + Code: 0, + Body: &icmp.PacketTooBig{ + MTU: 1280, + Data: []byte("original packet"), + }, + }, + } + testICMPProxyRejectNotEcho(t, localhostIPv6, msgsV6) +} + +func testICMPProxyRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.Message) { + proxy, err := NewICMPProxy(&noopLogger) require.NoError(t, err) responder := echoFlowResponder{ decoder: packet.NewICMPDecoder(), respChan: make(chan []byte), } + protocol := layers.IPProtocolICMPv4 + if srcDstIP.Is6() { + protocol = layers.IPProtocolICMPv6 + } for _, m := range msgs { pk := packet.ICMP{ IP: &packet.IP{ - Src: localhostIP, - Dst: localhostIP, - Protocol: layers.IPProtocolICMPv4, + Src: srcDstIP, + Dst: srcDstIP, + Protocol: protocol, }, Message: &m, } @@ -166,8 +204,40 @@ func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { require.Equal(t, decoded.Dst, echoReq.Src) require.Equal(t, echoReq.Protocol, decoded.Protocol) - require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type) + if echoReq.Type == ipv4.ICMPTypeEcho { + require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type) + } else { + require.Equal(t, ipv6.ICMPTypeEchoReply, decoded.Type) + } require.Equal(t, 0, decoded.Code) - require.NotZero(t, decoded.Checksum) + if echoReq.Type == ipv4.ICMPTypeEcho { + require.NotZero(t, decoded.Checksum) + } else { + // For ICMPv6, the kernel will compute the checksum during transmission unless pseudo header is not nil + require.Zero(t, decoded.Checksum) + } + require.Equal(t, echoReq.Body, decoded.Body) } + +func getLocalIPs(t *testing.T, ipv4 bool) []netip.Addr { + interfaces, err := net.Interfaces() + require.NoError(t, err) + localIPs := []netip.Addr{} + for _, i := range interfaces { + // Skip TUN devices + if strings.Contains(i.Name, "tun") { + continue + } + addrs, err := i.Addrs() + require.NoError(t, err) + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && (ipnet.IP.IsPrivate() || ipnet.IP.IsLoopback()) { + if (ipv4 && ipnet.IP.To4() != nil) || (!ipv4 && ipnet.IP.To4() == nil) { + localIPs = append(localIPs, netip.MustParseAddr(ipnet.IP.String())) + } + } + } + } + return localIPs +} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 52ce46a4..fc80ed1c 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net/netip" "strings" "time" @@ -117,12 +116,8 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato connAwareLogger: log, } if useDatagramV2(config) { - // TODO: TUN-6654 listen for IPv6 and decide if it should listen on specific IP - listenIP, err := netip.ParseAddr("0.0.0.0") - if err != nil { - return nil, err - } - icmpProxy, err := ingress.NewICMPProxy(listenIP, config.Log) + // TODO: TUN-6701: Decouple upgrade of datagram v2 and using icmp proxy + icmpProxy, err := ingress.NewICMPProxy(config.Log) if err != nil { log.Logger().Warn().Err(err).Msg("Failed to create icmp proxy, will continue to use datagram v1") } else { From 30c529e7301eff1a2bb8a20afdc83064635bce6b Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 8 Sep 2022 10:15:50 +0100 Subject: [PATCH 196/238] TUN-6743: Support ICMPv6 echo on Windows --- ingress/icmp_windows.go | 263 ++++++++++++++++++++++++++---- ingress/icmp_windows_test.go | 99 ++++++++++- ingress/origin_icmp_proxy_test.go | 4 - 3 files changed, 330 insertions(+), 36 deletions(-) diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index f5b5ac48..c1ebb511 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -29,15 +29,23 @@ import ( ) const ( + // Value defined in https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw + AF_INET6 = 23 icmpEchoReplyCode = 0 + nullParameter = uintptr(0) ) var ( - Iphlpapi = syscall.NewLazyDLL("Iphlpapi.dll") - IcmpCreateFile_proc = Iphlpapi.NewProc("IcmpCreateFile") - IcmpSendEcho_proc = Iphlpapi.NewProc("IcmpSendEcho") - echoReplySize = unsafe.Sizeof(echoReply{}) - endian = binary.LittleEndian + Iphlpapi = syscall.NewLazyDLL("Iphlpapi.dll") + IcmpCreateFile_proc = Iphlpapi.NewProc("IcmpCreateFile") + Icmp6CreateFile_proc = Iphlpapi.NewProc("Icmp6CreateFile") + IcmpSendEcho_proc = Iphlpapi.NewProc("IcmpSendEcho") + Icmp6SendEcho_proc = Iphlpapi.NewProc("Icmp6SendEcho2") + echoReplySize = unsafe.Sizeof(echoReply{}) + echoV6ReplySize = unsafe.Sizeof(echoV6Reply{}) + icmpv6ErrMessageSize = 8 + ioStatusBlockSize = unsafe.Sizeof(ioStatusBlock{}) + endian = binary.LittleEndian ) // IP_STATUS code, see https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-icmp_echo_reply32#members @@ -68,6 +76,16 @@ const ( generalFailure = 11050 ) +// Additional IP_STATUS codes for ICMPv6 https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-icmpv6_echo_reply_lh#members +const ( + ipv6DestUnreachable ipStatus = iota + 11040 + ipv6TimeExceeded + ipv6BadHeader + ipv6UnrecognizedNextHeader + ipv6ICMPError + ipv6DestScopeMismatch +) + func (is ipStatus) String() string { switch is { case success: @@ -108,6 +126,18 @@ func (is ipStatus) String() string { return "The IP option was too big" case badDestination: return "Bad destination" + case ipv6DestUnreachable: + return "IPv6 destination unreachable" + case ipv6TimeExceeded: + return "IPv6 time exceeded" + case ipv6BadHeader: + return "IPv6 bad IP header" + case ipv6UnrecognizedNextHeader: + return "IPv6 unrecognized next header" + case ipv6ICMPError: + return "IPv6 ICMP error" + case ipv6DestScopeMismatch: + return "IPv6 destination scope ID mismatch" case generalFailure: return "The ICMP packet might be malformed" default: @@ -136,27 +166,88 @@ type echoReply struct { Options ipOption } +type echoV6Reply struct { + Address ipv6AddrEx + Status ipStatus + RoundTripTime uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/ipexport/ns-ipexport-ipv6_address_ex +// All the fields are in network byte order. The memory alignment is 4 bytes +type ipv6AddrEx struct { + port uint16 + // flowInfo is uint32. Because of field alignment, when we cast reply buffer to ipv6AddrEx, it starts at the 5th byte + // But looking at the raw bytes, flowInfo starts at the 3rd byte. We device flowInfo into 2 uint16 so it's aligned + flowInfoUpper uint16 + flowInfoLower uint16 + addr [8]uint16 + scopeID uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 +type sockAddrIn6 struct { + family int16 + // Can't embed ipv6AddrEx, that changes the memory alignment + port uint16 + flowInfo uint32 + addr [16]byte + scopeID uint32 +} + +func newSockAddrIn6(addr netip.Addr) (*sockAddrIn6, error) { + if !addr.Is6() { + return nil, fmt.Errorf("%s is not IPv6", addr) + } + return &sockAddrIn6{ + family: AF_INET6, + port: 10, + addr: addr.As16(), + }, nil +} + +// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block#syntax +type ioStatusBlock struct { + // The first field is an union of NTSTATUS and PVOID. NTSTATUS is int32 while PVOID depends on the platform. + // We model it as uintptr whose size depends on if the platform is 32-bit or 64-bit + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 + statusOrPointer uintptr + information uintptr +} + type icmpProxy struct { // An open handle that can send ICMP requests https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpcreatefile handle uintptr - logger *zerolog.Logger + // This is a ICMPv6 if srcSocketAddr is not nil + srcSocketAddr *sockAddrIn6 + logger *zerolog.Logger // A pool of reusable *packet.Encoder encoderPool sync.Pool } func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { - if listenIP.Is6() { - return nil, fmt.Errorf("ICMPv6 not implemented for Windows") + var ( + srcSocketAddr *sockAddrIn6 + handle uintptr + err error + ) + if listenIP.Is4() { + handle, _, err = IcmpCreateFile_proc.Call() + } else { + srcSocketAddr, err = newSockAddrIn6(listenIP) + if err != nil { + return nil, err + } + handle, _, err = Icmp6CreateFile_proc.Call() } - handle, _, err := IcmpCreateFile_proc.Call() // Windows procedure calls always return non-nil error constructed from the result of GetLastError. // Caller need to inspect the primary returned value if syscall.Handle(handle) == syscall.InvalidHandle { return nil, errors.Wrap(err, "invalid ICMP handle") } return &icmpProxy{ - handle: handle, - logger: logger, + handle: handle, + srcSocketAddr: srcSocketAddr, + logger: logger, encoderPool: sync.Pool{ New: func() any { return packet.NewEncoder() @@ -180,24 +271,25 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er ip.logger.Error().Interface("error", r).Msgf("Recover panic from sending icmp request/response, error %s", debug.Stack()) } }() + echo, err := getICMPEcho(pk.Message) if err != nil { return err } - - resp, err := ip.icmpSendEcho(pk.Dst, echo) + respData, err := ip.icmpEchoRoundtrip(pk.Dst, echo) if err != nil { - return errors.Wrap(err, "failed to send/receive ICMP echo") + ip.logger.Err(err).Msg("ICMP echo roundtrip failed") + return err } - err = ip.handleEchoResponse(pk, echo, resp, responder) + err = ip.handleEchoReply(pk, echo, respData, responder) if err != nil { return errors.Wrap(err, "failed to handle ICMP echo reply") } return nil } -func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo, resp *echoResp, responder packet.FunnelUniPipe) error { +func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, data []byte, responder packet.FunnelUniPipe) error { var replyType icmp.Type if request.Dst.Is4() { replyType = ipv4.ICMPTypeEchoReply @@ -217,7 +309,7 @@ func (ip *icmpProxy) handleEchoResponse(request *packet.ICMP, echoReq *icmp.Echo Body: &icmp.Echo{ ID: echoReq.ID, Seq: echoReq.Seq, - Data: resp.data, + Data: data, }, }, } @@ -239,10 +331,31 @@ func (ip *icmpProxy) encodeICMPReply(pk *packet.ICMP) (packet.RawPacket, error) return encoder.Encode(pk) } +func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte, error) { + if dst.Is6() { + if ip.srcSocketAddr == nil { + return nil, fmt.Errorf("cannot send ICMPv6 using ICMPv4 proxy") + } + resp, err := ip.icmp6SendEcho(dst, echo) + if err != nil { + return nil, errors.Wrap(err, "failed to send/receive ICMPv6 echo") + } + return resp.data, nil + } + if ip.srcSocketAddr != nil { + return nil, fmt.Errorf("cannot send ICMPv4 using ICMPv6 proxy") + } + resp, err := ip.icmpSendEcho(dst, echo) + if err != nil { + return nil, errors.Wrap(err, "failed to send/receive ICMPv4 echo") + } + return resp.data, nil +} + /* Wrapper to call https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmpsendecho Parameters: - - IcmpHandle: + - IcmpHandle: Handle created by IcmpCreateFile - DestinationAddress: IPv4 in the form of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax - RequestData: A pointer to echo data - RequestSize: Number of bytes in buffer pointed by echo data @@ -259,18 +372,25 @@ func (ip *icmpProxy) icmpSendEcho(dst netip.Addr, echo *icmp.Echo) (*echoResp, e dataSize := len(echo.Data) replySize := echoReplySize + uintptr(dataSize) replyBuf := make([]byte, replySize) - noIPHeaderOption := uintptr(0) + noIPHeaderOption := nullParameter inAddr, err := inAddrV4(dst) if err != nil { return nil, err } - replyCount, _, err := IcmpSendEcho_proc.Call(ip.handle, uintptr(inAddr), uintptr(unsafe.Pointer(&echo.Data[0])), - uintptr(dataSize), noIPHeaderOption, uintptr(unsafe.Pointer(&replyBuf[0])), - replySize, icmpRequestTimeoutMs) + replyCount, _, err := IcmpSendEcho_proc.Call( + ip.handle, + uintptr(inAddr), + uintptr(unsafe.Pointer(&echo.Data[0])), + uintptr(dataSize), + noIPHeaderOption, + uintptr(unsafe.Pointer(&replyBuf[0])), + replySize, + icmpRequestTimeoutMs, + ) if replyCount == 0 { // status is returned in 5th to 8th byte of reply buffer - if status, err := unmarshalIPStatus(replyBuf[4:8]); err == nil { - return nil, fmt.Errorf("received ip status: %s", status) + if status, parseErr := unmarshalIPStatus(replyBuf[4:8]); parseErr == nil && status != success { + return nil, errors.Wrapf(err, "received ip status: %s", status) } return nil, errors.Wrap(err, "did not receive ICMP echo reply") } else if replyCount > 1 { @@ -279,6 +399,15 @@ func (ip *icmpProxy) icmpSendEcho(dst netip.Addr, echo *icmp.Echo) (*echoResp, e return newEchoResp(replyBuf) } +// Third definition of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax is address in uint32 +func inAddrV4(ip netip.Addr) (uint32, error) { + if !ip.Is4() { + return 0, fmt.Errorf("%s is not IPv4", ip) + } + v4 := ip.As4() + return endian.Uint32(v4[:]), nil +} + type echoResp struct { reply *echoReply data []byte @@ -304,13 +433,87 @@ func newEchoResp(replyBuf []byte) (*echoResp, error) { }, nil } -// Third definition of https://docs.microsoft.com/en-us/windows/win32/api/inaddr/ns-inaddr-in_addr#syntax is address in uint32 -func inAddrV4(ip netip.Addr) (uint32, error) { - if !ip.Is4() { - return 0, fmt.Errorf("%s is not IPv4", ip) +/* + Wrapper to call https://docs.microsoft.com/en-us/windows/win32/api/icmpapi/nf-icmpapi-icmp6sendecho2 + Parameters: + - IcmpHandle: Handle created by Icmp6CreateFile + - Event (optional): Event object to be signaled when a reply arrives + - ApcRoutine (optional): Routine to call when the calling thread is in an alertable thread and a reply arrives + - ApcContext (optional): Optional parameter to ApcRoutine + - SourceAddress: Source address of the request + - DestinationAddress: Destination address of the request + - RequestData: A pointer to echo data + - RequestSize: Number of bytes in buffer pointed by echo data + - RequestOptions (optional): A pointer to the IPv6 header options + - ReplyBuffer: A pointer to the buffer for echoReply, options and data + - ReplySize: Number of bytes allocated for ReplyBuffer + - Timeout: Timeout in milliseconds to wait for a reply + Returns: + - the number of replies in uint32 + To retain the reference allocated objects, conversion from pointer to uintptr must happen as arguments to the + syscall function +*/ + +func (ip *icmpProxy) icmp6SendEcho(dst netip.Addr, echo *icmp.Echo) (*echoV6Resp, error) { + dstAddr, err := newSockAddrIn6(dst) + if err != nil { + return nil, err } - v4 := ip.As4() - return endian.Uint32(v4[:]), nil + dataSize := len(echo.Data) + // Reply buffer needs to be big enough to hold an echoV6Reply, echo data, 8 bytes for ICMP error message + // and ioStatusBlock + replySize := echoV6ReplySize + uintptr(dataSize) + uintptr(icmpv6ErrMessageSize) + ioStatusBlockSize + replyBuf := make([]byte, replySize) + noEvent := nullParameter + noApcRoutine := nullParameter + noAppCtx := nullParameter + noIPHeaderOption := nullParameter + replyCount, _, err := Icmp6SendEcho_proc.Call( + ip.handle, + noEvent, + noApcRoutine, + noAppCtx, + uintptr(unsafe.Pointer(ip.srcSocketAddr)), + uintptr(unsafe.Pointer(dstAddr)), + uintptr(unsafe.Pointer(&echo.Data[0])), + uintptr(dataSize), + noIPHeaderOption, + uintptr(unsafe.Pointer(&replyBuf[0])), + replySize, + icmpRequestTimeoutMs, + ) + if replyCount == 0 { + // status is in the 4 bytes after ipv6AddrEx. The reply buffer size is at least size of ipv6AddrEx + 4 + if status, parseErr := unmarshalIPStatus(replyBuf[unsafe.Sizeof(ipv6AddrEx{}) : unsafe.Sizeof(ipv6AddrEx{})+4]); parseErr == nil && status != success { + return nil, fmt.Errorf("received ip status: %s", status) + } + return nil, errors.Wrap(err, "did not receive ICMP echo reply") + } else if replyCount > 1 { + ip.logger.Warn().Msgf("Received %d ICMP echo replies, only sending 1 back", replyCount) + } + return newEchoV6Resp(replyBuf, dataSize) +} + +type echoV6Resp struct { + reply *echoV6Reply + data []byte +} + +func newEchoV6Resp(replyBuf []byte, dataSize int) (*echoV6Resp, error) { + if len(replyBuf) == 0 { + return nil, fmt.Errorf("reply buffer is empty") + } + reply := *(*echoV6Reply)(unsafe.Pointer(&replyBuf[0])) + if reply.Status != success { + return nil, fmt.Errorf("status %d", reply.Status) + } + if uintptr(len(replyBuf)) < unsafe.Sizeof(reply)+uintptr(dataSize) { + return nil, fmt.Errorf("reply buffer size %d is too small to hold reply size %d + data size %d", len(replyBuf), echoV6ReplySize, dataSize) + } + return &echoV6Resp{ + reply: &reply, + data: replyBuf[echoV6ReplySize : echoV6ReplySize+uintptr(dataSize)], + }, nil } func unmarshalIPStatus(replyBuf []byte) (ipStatus, error) { diff --git a/ingress/icmp_windows_test.go b/ingress/icmp_windows_test.go index d6aae071..5361ac44 100644 --- a/ingress/icmp_windows_test.go +++ b/ingress/icmp_windows_test.go @@ -71,9 +71,68 @@ func TestParseEchoReply(t *testing.T) { } } +// TestParseEchoV6Reply tests parsing raw bytes from icmp6SendEcho into echoV6Resp +func TestParseEchoV6Reply(t *testing.T) { + dst := netip.MustParseAddr("2606:3600:4500::3333").As16() + var addr [8]uint16 + for i := 0; i < 8; i++ { + addr[i] = binary.BigEndian.Uint16(dst[i*2 : i*2+2]) + } + + validReplyData := []byte(t.Name()) + validReply := echoV6Reply{ + Address: ipv6AddrEx{ + addr: addr, + }, + Status: success, + RoundTripTime: 25, + } + + destHostUnreachableReply := validReply + destHostUnreachableReply.Status = ipv6DestUnreachable + + tests := []struct { + testCase string + replyBuf []byte + expectedReply *echoV6Reply + expectedData []byte + }{ + { + testCase: "empty buffer", + }, + { + testCase: "status not success", + replyBuf: destHostUnreachableReply.marshal(t, []byte{}), + }, + { + testCase: "valid reply", + replyBuf: validReply.marshal(t, validReplyData), + expectedReply: &validReply, + expectedData: validReplyData, + }, + } + + for _, test := range tests { + resp, err := newEchoV6Resp(test.replyBuf, len(test.expectedData)) + if test.expectedReply == nil { + require.Error(t, err) + require.Nil(t, resp) + } else { + require.NoError(t, err) + require.Equal(t, resp.reply, test.expectedReply) + require.True(t, bytes.Equal(resp.data, test.expectedData)) + } + } +} + // TestSendEchoErrors makes sure icmpSendEcho handles error cases func TestSendEchoErrors(t *testing.T) { - proxy, err := newICMPProxy(localhostIP, &noopLogger, time.Second) + testSendEchoErrors(t, netip.IPv4Unspecified()) + testSendEchoErrors(t, netip.IPv6Unspecified()) +} + +func testSendEchoErrors(t *testing.T, listenIP netip.Addr) { + proxy, err := newICMPProxy(listenIP, &noopLogger, time.Second) require.NoError(t, err) winProxy := proxy.(*icmpProxy) @@ -83,7 +142,10 @@ func TestSendEchoErrors(t *testing.T) { Data: []byte(t.Name()), } documentIP := netip.MustParseAddr("192.0.2.200") - resp, err := winProxy.icmpSendEcho(documentIP, &echo) + if listenIP.Is6() { + documentIP = netip.MustParseAddr("2001:db8::1") + } + resp, err := winProxy.icmpEchoRoundtrip(documentIP, &echo) require.Error(t, err) require.Nil(t, resp) } @@ -138,3 +200,36 @@ func marshalPointer(buf io.Writer, ptr uintptr) error { return fmt.Errorf("unexpected pointer size %d", size) } } + +func (er *echoV6Reply) marshal(t *testing.T, data []byte) []byte { + buf := new(bytes.Buffer) + + for _, field := range []any{ + er.Address.port, + er.Address.flowInfoUpper, + er.Address.flowInfoLower, + er.Address.addr, + er.Address.scopeID, + } { + require.NoError(t, binary.Write(buf, endian, field)) + } + + padSize := buf.Len() % int(unsafe.Alignof(er)) + padding := make([]byte, padSize) + n, err := buf.Write(padding) + require.NoError(t, err) + require.Equal(t, padSize, n) + + for _, field := range []any{ + er.Status, + er.RoundTripTime, + } { + require.NoError(t, binary.Write(buf, endian, field)) + } + + n, err = buf.Write(data) + require.NoError(t, err) + require.Equal(t, len(data), n) + + return buf.Bytes() +} diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index d3573894..55379dcb 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "net/netip" - "runtime" "strings" "testing" @@ -33,9 +32,6 @@ var ( // https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md func TestICMPProxyEcho(t *testing.T) { testICMPProxyEcho(t, true) - if runtime.GOOS == "windows" { - t.Skip("TODO: TUN-6743: test ICMPv6 on Windows") - } testICMPProxyEcho(t, false) } From f5f3e6a4530a9597b9d742b35a404d0caefc93da Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 8 Sep 2022 21:42:11 -0700 Subject: [PATCH 197/238] TUN-6689: Utilize new RegisterUDPSession to begin tracing --- connection/quic.go | 28 ++++++++++++++++++++++++---- connection/quic_test.go | 7 ++++--- quic/quic_protocol_test.go | 14 +++++++------- tracing/client.go | 31 ++++++++++++++++++++++--------- tracing/tracing.go | 25 ++++++++++++++++++++++++- tunnelrpc/pogs/sessionrpc.go | 15 ++++++++++----- 6 files changed, 91 insertions(+), 29 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index a696192c..640c7be2 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -17,6 +17,8 @@ import ( "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" "github.com/rs/zerolog" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "golang.org/x/sync/errgroup" "github.com/cloudflare/cloudflared/datagramsession" @@ -259,24 +261,42 @@ func (q *QUICConnection) handleRPCStream(rpcStream *quicpogs.RPCServerStream) er } // RegisterUdpSession is the RPC method invoked by edge to register and run a session -func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) error { +func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) { + traceCtx := tracing.NewTracedContext(ctx, traceContext, q.logger) + ctx, registerSpan := traceCtx.Tracer().Start(traceCtx, "register-session", trace.WithAttributes( + attribute.String("session-id", sessionID.String()), + attribute.String("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)), + )) // Each session is a series of datagram from an eyeball to a dstIP:dstPort. // (src port, dst IP, dst port) uniquely identifies a session, so it needs a dedicated connected socket. originProxy, err := ingress.DialUDP(dstIP, dstPort) if err != nil { q.logger.Err(err).Msgf("Failed to create udp proxy to %s:%d", dstIP, dstPort) - return err + tracing.EndWithErrorStatus(registerSpan, err) + return nil, err } + registerSpan.SetAttributes( + attribute.Bool("socket-bind-success", true), + attribute.String("src", originProxy.LocalAddr().String()), + ) + session, err := q.sessionManager.RegisterSession(ctx, sessionID, originProxy) if err != nil { q.logger.Err(err).Str("sessionID", sessionID.String()).Msgf("Failed to register udp session") - return err + tracing.EndWithErrorStatus(registerSpan, err) + return nil, err } go q.serveUDPSession(session, closeAfterIdleHint) q.logger.Debug().Str("sessionID", sessionID.String()).Str("src", originProxy.LocalAddr().String()).Str("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)).Msgf("Registered session") - return nil + tracing.End(registerSpan) + + resp := tunnelpogs.RegisterUdpSessionResponse{ + Spans: traceCtx.GetProtoSpans(), + } + + return &resp, nil } func (q *QUICConnection) serveUDPSession(session *datagramsession.Session, closeAfterIdleHint time.Duration) { diff --git a/connection/quic_test.go b/connection/quic_test.go index 26b71e48..8904eeeb 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -26,6 +26,7 @@ import ( "github.com/cloudflare/cloudflared/datagramsession" quicpogs "github.com/cloudflare/cloudflared/quic" "github.com/cloudflare/cloudflared/tracing" + "github.com/cloudflare/cloudflared/tunnelrpc/pogs" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -554,7 +555,7 @@ func TestNopCloserReadWriterCloseAfterEOF(t *testing.T) { require.Equal(t, n, 9) // force another read to read eof - n, err = readerWriter.Read(buffer) + _, err = readerWriter.Read(buffer) require.Equal(t, err, io.EOF) // close @@ -652,8 +653,8 @@ type mockSessionRPCServer struct { calledUnregisterChan chan struct{} } -func (s mockSessionRPCServer) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) error { - return fmt.Errorf("mockSessionRPCServer doesn't implement RegisterUdpSession") +func (s mockSessionRPCServer) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) (*pogs.RegisterUdpSessionResponse, error) { + return nil, fmt.Errorf("mockSessionRPCServer doesn't implement RegisterUdpSession") } func (s mockSessionRPCServer) UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, reason string) error { diff --git a/quic/quic_protocol_test.go b/quic/quic_protocol_test.go index 9693a808..64943298 100644 --- a/quic/quic_protocol_test.go +++ b/quic/quic_protocol_test.go @@ -230,23 +230,23 @@ type mockSessionRPCServer struct { traceContext string } -func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) error { +func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) { if s.sessionID != sessionID { - return fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID) + return nil, fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID) } if !s.dstIP.Equal(dstIP) { - return fmt.Errorf("expect destination IP %s, got %s", s.dstIP, dstIP) + return nil, fmt.Errorf("expect destination IP %s, got %s", s.dstIP, dstIP) } if s.dstPort != dstPort { - return fmt.Errorf("expect destination port %d, got %d", s.dstPort, dstPort) + return nil, fmt.Errorf("expect destination port %d, got %d", s.dstPort, dstPort) } if s.closeIdleAfter != closeIdleAfter { - return fmt.Errorf("expect closeIdleAfter %d, got %d", s.closeIdleAfter, closeIdleAfter) + return nil, fmt.Errorf("expect closeIdleAfter %d, got %d", s.closeIdleAfter, closeIdleAfter) } if s.traceContext != traceContext { - return fmt.Errorf("expect traceContext %s, got %s", s.traceContext, traceContext) + return nil, fmt.Errorf("expect traceContext %s, got %s", s.traceContext, traceContext) } - return nil + return &tunnelpogs.RegisterUdpSessionResponse{}, nil } func (s mockSessionRPCServer) UnregisterUdpSession(_ context.Context, sessionID uuid.UUID, message string) error { diff --git a/tracing/client.go b/tracing/client.go index ffc477e1..7cfb1a43 100644 --- a/tracing/client.go +++ b/tracing/client.go @@ -24,6 +24,9 @@ type InMemoryClient interface { // Spans returns a copy of the list of in-memory stored spans as a base64 // encoded otlp protobuf string. Spans() (string, error) + // ProtoSpans returns a copy of the list of in-memory stored spans as otlp + // protobuf byte array. + ProtoSpans() ([]byte, error) } // InMemoryOtlpClient is a client implementation for otlptrace.Client @@ -55,21 +58,26 @@ func (mc *InMemoryOtlpClient) UploadTraces(_ context.Context, protoSpans []*trac // Spans returns the list of in-memory stored spans as a base64 encoded otlp protobuf string. func (mc *InMemoryOtlpClient) Spans() (string, error) { - mc.mu.Lock() - defer mc.mu.Unlock() - if len(mc.spans) <= 0 { - return "", errNoTraces - } - pbRequest := &coltracepb.ExportTraceServiceRequest{ - ResourceSpans: mc.spans, - } - data, err := proto.Marshal(pbRequest) + data, err := mc.ProtoSpans() if err != nil { return "", err } return base64.StdEncoding.EncodeToString(data), nil } +// ProtoSpans returns the list of in-memory stored spans as the protobuf byte array. +func (mc *InMemoryOtlpClient) ProtoSpans() ([]byte, error) { + mc.mu.Lock() + defer mc.mu.Unlock() + if len(mc.spans) <= 0 { + return nil, errNoTraces + } + pbRequest := &coltracepb.ExportTraceServiceRequest{ + ResourceSpans: mc.spans, + } + return proto.Marshal(pbRequest) +} + // NoopOtlpClient is a client implementation for otlptrace.Client that does nothing type NoopOtlpClient struct{} @@ -89,3 +97,8 @@ func (mc *NoopOtlpClient) UploadTraces(_ context.Context, _ []*tracepb.ResourceS func (mc *NoopOtlpClient) Spans() (string, error) { return "", errNoopTracer } + +// Spans always returns no traces error +func (mc *NoopOtlpClient) ProtoSpans() ([]byte, error) { + return nil, errNoopTracer +} diff --git a/tracing/tracing.go b/tracing/tracing.go index 1788b0ef..54625fc8 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -93,7 +93,7 @@ type TracedContext struct { *cfdTracer } -// NewTracedHTTPRequest creates a new tracer for the current HTTP request context. +// NewTracedContext creates a new tracer for the current context. func NewTracedContext(ctx context.Context, traceContext string, log *zerolog.Logger) *TracedContext { ctx, exists := extractTraceFromString(ctx, traceContext) if !exists { @@ -155,6 +155,24 @@ func (cft *cfdTracer) GetSpans() (enc string) { return } +// GetProtoSpans returns the spans as the otlp traces in protobuf byte array. +func (cft *cfdTracer) GetProtoSpans() (proto []byte) { + proto, err := cft.exporter.ProtoSpans() + switch err { + case nil: + break + case errNoTraces: + cft.log.Trace().Err(err).Msgf("expected traces to be available") + return + case errNoopTracer: + return // noop tracer has no traces + default: + cft.log.Debug().Err(err) + return + } + return +} + // AddSpans assigns spans as base64 encoded protobuf otlp traces to provided // HTTP headers. func (cft *cfdTracer) AddSpans(headers http.Header) { @@ -171,6 +189,11 @@ func (cft *cfdTracer) AddSpans(headers http.Header) { headers[CanonicalCloudflaredTracingHeader] = []string{enc} } +// End will set the OK status for the span and then end it. +func End(span trace.Span) { + endSpan(span, -1, codes.Ok, nil) +} + // EndWithErrorStatus will set a status for the span and then end it. func EndWithErrorStatus(span trace.Span, err error) { endSpan(span, -1, codes.Error, err) diff --git a/tunnelrpc/pogs/sessionrpc.go b/tunnelrpc/pogs/sessionrpc.go index 378fc205..5bbe47a3 100644 --- a/tunnelrpc/pogs/sessionrpc.go +++ b/tunnelrpc/pogs/sessionrpc.go @@ -14,7 +14,11 @@ import ( ) type SessionManager interface { - RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) error + // RegisterUdpSession is the call provided to cloudflared to handle an incoming + // capnproto RegisterUdpSession request from the edge. + RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeAfterIdleHint time.Duration, traceContext string) (*RegisterUdpSessionResponse, error) + // UnregisterUdpSession is the call provided to cloudflared to handle an incoming + // capnproto UnregisterUdpSession request from the edge. UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, message string) error } @@ -55,14 +59,15 @@ func (i SessionManager_PogsImpl) RegisterUdpSession(p tunnelrpc.SessionManager_r return err } - resp := RegisterUdpSessionResponse{} - registrationErr := i.impl.RegisterUdpSession(p.Ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext) + resp, registrationErr := i.impl.RegisterUdpSession(p.Ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext) if registrationErr != nil { + // Make sure to assign a response even if one is not returned from register + if resp == nil { + resp = &RegisterUdpSessionResponse{} + } resp.Err = registrationErr } - // TUN-6689: Add spans to return path for RegisterUdpSession - result, err := p.Results.NewResult() if err != nil { return err From 8a53c1aa1da210a1912fe89d37a5c04bf4c9be30 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 13 Sep 2022 14:00:54 +0100 Subject: [PATCH 198/238] TUN-6592: Decrement TTL and return ICMP time exceed if it's 0 --- connection/quic.go | 76 +++++------------- ingress/icmp_darwin.go | 2 +- ingress/icmp_generic.go | 19 ++++- ingress/icmp_linux.go | 2 +- ingress/icmp_posix.go | 1 + ingress/icmp_windows.go | 3 +- ingress/icmp_windows_test.go | 3 +- ingress/origin_icmp_proxy.go | 20 ++--- ingress/origin_icmp_proxy_test.go | 28 ++++--- packet/decoder.go | 4 +- packet/decoder_test.go | 5 +- packet/funnel_test.go | 16 ++++ packet/packet.go | 69 ++++++++++++++-- packet/packet_test.go | 104 ++++++++++++++++++++++++ packet/router.go | 126 ++++++++++++++++++++++++++++++ packet/router_test.go | 125 +++++++++++++++++++++++++++++ supervisor/supervisor.go | 12 +-- supervisor/tunnel.go | 6 +- 18 files changed, 515 insertions(+), 106 deletions(-) create mode 100644 packet/funnel_test.go create mode 100644 packet/packet_test.go create mode 100644 packet/router.go create mode 100644 packet/router_test.go diff --git a/connection/quic.go b/connection/quic.go index 640c7be2..86a5228c 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -51,7 +51,7 @@ type QUICConnection struct { sessionManager datagramsession.Manager // datagramMuxer mux/demux datagrams from quic connection datagramMuxer quicpogs.BaseDatagramMuxer - packetRouter *packetRouter + packetRouter *packet.Router controlStreamHandler ControlStreamHandler connOptions *tunnelpogs.ConnectionOptions } @@ -65,7 +65,7 @@ func NewQUICConnection( connOptions *tunnelpogs.ConnectionOptions, controlStreamHandler ControlStreamHandler, logger *zerolog.Logger, - icmpProxy ingress.ICMPProxy, + icmpRouter packet.ICMPRouter, ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { @@ -75,15 +75,12 @@ func NewQUICConnection( sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) var ( datagramMuxer quicpogs.BaseDatagramMuxer - pr *packetRouter + pr *packet.Router ) - if icmpProxy != nil { - pr = &packetRouter{ - muxer: quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan), - icmpProxy: icmpProxy, - logger: logger, - } - datagramMuxer = pr.muxer + if icmpRouter != nil { + datagramMuxerV2 := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) + pr = packet.NewRouter(datagramMuxerV2, &returnPipe{muxer: datagramMuxerV2}, icmpRouter, logger) + datagramMuxer = datagramMuxerV2 } else { datagramMuxer = quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan) } @@ -139,7 +136,7 @@ func (q *QUICConnection) Serve(ctx context.Context) error { if q.packetRouter != nil { errGroup.Go(func() error { defer cancel() - return q.packetRouter.serve(ctx) + return q.packetRouter.Serve(ctx) }) } @@ -348,50 +345,6 @@ func (q *QUICConnection) UpdateConfiguration(ctx context.Context, version int32, return q.orchestrator.UpdateConfig(version, config) } -type packetRouter struct { - muxer *quicpogs.DatagramMuxerV2 - icmpProxy ingress.ICMPProxy - logger *zerolog.Logger -} - -func (pr *packetRouter) serve(ctx context.Context) error { - icmpDecoder := packet.NewICMPDecoder() - for { - pk, err := pr.muxer.ReceivePacket(ctx) - if err != nil { - return err - } - icmpPacket, err := icmpDecoder.Decode(pk) - if err != nil { - pr.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram") - continue - } - - flowPipe := muxerResponder{muxer: pr.muxer} - if err := pr.icmpProxy.Request(icmpPacket, &flowPipe); err != nil { - pr.logger.Err(err). - Str("src", icmpPacket.Src.String()). - Str("dst", icmpPacket.Dst.String()). - Interface("type", icmpPacket.Type). - Msg("Failed to send ICMP packet") - continue - } - } -} - -// muxerResponder wraps DatagramMuxerV2 to satisfy the packet.FunnelUniPipe interface -type muxerResponder struct { - muxer *quicpogs.DatagramMuxerV2 -} - -func (mr *muxerResponder) SendPacket(dst netip.Addr, pk packet.RawPacket) error { - return mr.muxer.SendPacket(pk) -} - -func (mr *muxerResponder) Close() error { - return nil -} - // streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to // the client. type streamReadWriteAcker struct { @@ -538,3 +491,16 @@ func (np *nopCloserReadWriter) Close() error { return nil } + +// returnPipe wraps DatagramMuxerV2 to satisfy the packet.FunnelUniPipe interface +type returnPipe struct { + muxer *quicpogs.DatagramMuxerV2 +} + +func (rp *returnPipe) SendPacket(dst netip.Addr, pk packet.RawPacket) error { + return rp.muxer.SendPacket(pk) +} + +func (rp *returnPipe) Close() error { + return nil +} diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index ace04f23..73ac4de1 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -115,7 +115,7 @@ func (snf echoFunnelID) String() string { return strconv.FormatUint(uint64(snf), 10) } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { conn, err := newICMPConn(listenIP) if err != nil { return nil, err diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index 781dc65b..0e481006 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -3,14 +3,29 @@ package ingress import ( + "context" "fmt" "net/netip" "runtime" "time" "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/packet" ) -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { - return nil, fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) +var errICMPProxyNotImplemented = fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) + +type icmpProxy struct{} + +func (ip icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { + return errICMPProxyNotImplemented +} + +func (ip *icmpProxy) Serve(ctx context.Context) error { + return errICMPProxyNotImplemented +} + +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { + return nil, errICMPProxyNotImplemented } diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index 21da6ce1..3b7ae828 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -28,7 +28,7 @@ type icmpProxy struct { idleTimeout time.Duration } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { if err := testPermission(listenIP); err != nil { return nil, err } diff --git a/ingress/icmp_posix.go b/ingress/icmp_posix.go index 927c02f9..a2a0a5b9 100644 --- a/ingress/icmp_posix.go +++ b/ingress/icmp_posix.go @@ -96,6 +96,7 @@ func (ief *icmpEchoFlow) returnToSrc(reply *echoReply) error { Src: reply.from, Dst: ief.Src, Protocol: layers.IPProtocol(reply.msg.Type.Protocol()), + TTL: packet.DefaultTTL, }, Message: reply.msg, } diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index c1ebb511..baba8d4d 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -224,7 +224,7 @@ type icmpProxy struct { encoderPool sync.Pool } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (ICMPProxy, error) { +func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { var ( srcSocketAddr *sockAddrIn6 handle uintptr @@ -302,6 +302,7 @@ func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, d Src: request.Dst, Dst: request.Src, Protocol: layers.IPProtocol(request.Type.Protocol()), + TTL: packet.DefaultTTL, }, Message: &icmp.Message{ Type: replyType, diff --git a/ingress/icmp_windows_test.go b/ingress/icmp_windows_test.go index 5361ac44..05f7e620 100644 --- a/ingress/icmp_windows_test.go +++ b/ingress/icmp_windows_test.go @@ -134,7 +134,6 @@ func TestSendEchoErrors(t *testing.T) { func testSendEchoErrors(t *testing.T, listenIP netip.Addr) { proxy, err := newICMPProxy(listenIP, &noopLogger, time.Second) require.NoError(t, err) - winProxy := proxy.(*icmpProxy) echo := icmp.Echo{ ID: 6193, @@ -145,7 +144,7 @@ func testSendEchoErrors(t *testing.T, listenIP netip.Addr) { if listenIP.Is6() { documentIP = netip.MustParseAddr("2001:db8::1") } - resp, err := winProxy.icmpEchoRoundtrip(documentIP, &echo) + resp, err := proxy.icmpEchoRoundtrip(documentIP, &echo) require.Error(t, err) require.Nil(t, resp) } diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index c114eb75..4bd0c0fe 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -26,22 +26,14 @@ var ( errPacketNil = fmt.Errorf("packet is nil") ) -// ICMPProxy sends ICMP messages and listens for their responses -type ICMPProxy interface { - // Serve starts listening for responses to the requests until context is done - Serve(ctx context.Context) error - // Request sends an ICMP message - Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error -} - type icmpRouter struct { - ipv4Proxy ICMPProxy - ipv6Proxy ICMPProxy + ipv4Proxy *icmpProxy + ipv6Proxy *icmpProxy } -// NewICMPProxy doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only +// NewICMPRouter doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only // support one of them -func NewICMPProxy(logger *zerolog.Logger) (ICMPProxy, error) { +func NewICMPRouter(logger *zerolog.Logger) (*icmpRouter, error) { // TODO: TUN-6741: don't bind to all interface ipv4Proxy, ipv4Err := newICMPProxy(netip.IPv4Unspecified(), logger, funnelIdleTimeout) ipv6Proxy, ipv6Err := newICMPProxy(netip.IPv6Unspecified(), logger, funnelIdleTimeout) @@ -49,11 +41,11 @@ func NewICMPProxy(logger *zerolog.Logger) (ICMPProxy, error) { return nil, fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err) } if ipv4Err != nil { - logger.Warn().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created") + logger.Debug().Err(ipv4Err).Msg("failed to create ICMPv4 proxy, only ICMPv6 proxy is created") ipv4Proxy = nil } if ipv6Err != nil { - logger.Warn().Err(ipv6Err).Msg("failed to create ICMPv6 proxy, only ICMPv4 proxy is created") + logger.Debug().Err(ipv6Err).Msg("failed to create ICMPv6 proxy, only ICMPv4 proxy is created") ipv6Proxy = nil } return &icmpRouter{ diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index 55379dcb..5684a608 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -30,24 +30,24 @@ var ( // Note: if this test fails on your device under Linux, then most likely you need to make sure that your user // is allowed in ping_group_range. See the following gist for how to do that: // https://github.com/ValentinBELYN/icmplib/blob/main/docs/6-use-icmplib-without-privileges.md -func TestICMPProxyEcho(t *testing.T) { - testICMPProxyEcho(t, true) - testICMPProxyEcho(t, false) +func TestICMPRouterEcho(t *testing.T) { + testICMPRouterEcho(t, true) + testICMPRouterEcho(t, false) } -func testICMPProxyEcho(t *testing.T, sendIPv4 bool) { +func testICMPRouterEcho(t *testing.T, sendIPv4 bool) { const ( echoID = 36571 endSeq = 20 ) - proxy, err := NewICMPProxy(&noopLogger) + router, err := NewICMPRouter(&noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) go func() { - proxy.Serve(ctx) + router.Serve(ctx) close(proxyDone) }() @@ -67,6 +67,7 @@ func testICMPProxyEcho(t *testing.T, sendIPv4 bool) { Src: localIP, Dst: localIP, Protocol: protocol, + TTL: packet.DefaultTTL, } } @@ -88,7 +89,7 @@ func testICMPProxyEcho(t *testing.T, sendIPv4 bool) { }, }, } - require.NoError(t, proxy.Request(&pk, &responder)) + require.NoError(t, router.Request(&pk, &responder)) responder.validate(t, &pk) } } @@ -97,7 +98,7 @@ func testICMPProxyEcho(t *testing.T, sendIPv4 bool) { } // TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo -func TestICMPProxyRejectNotEcho(t *testing.T) { +func TestICMPRouterRejectNotEcho(t *testing.T) { msgs := []icmp.Message{ { Type: ipv4.ICMPTypeDestinationUnreachable, @@ -122,7 +123,7 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { }, }, } - testICMPProxyRejectNotEcho(t, localhostIP, msgs) + testICMPRouterRejectNotEcho(t, localhostIP, msgs) msgsV6 := []icmp.Message{ { Type: ipv6.ICMPTypeDestinationUnreachable, @@ -147,11 +148,11 @@ func TestICMPProxyRejectNotEcho(t *testing.T) { }, }, } - testICMPProxyRejectNotEcho(t, localhostIPv6, msgsV6) + testICMPRouterRejectNotEcho(t, localhostIPv6, msgsV6) } -func testICMPProxyRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.Message) { - proxy, err := NewICMPProxy(&noopLogger) +func testICMPRouterRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.Message) { + router, err := NewICMPRouter(&noopLogger) require.NoError(t, err) responder := echoFlowResponder{ @@ -168,10 +169,11 @@ func testICMPProxyRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.M Src: srcDstIP, Dst: srcDstIP, Protocol: protocol, + TTL: packet.DefaultTTL, }, Message: &m, } - require.Error(t, proxy.Request(&pk, &responder)) + require.Error(t, router.Request(&pk, &responder)) } } diff --git a/packet/decoder.go b/packet/decoder.go index 147cbd16..2737b18e 100644 --- a/packet/decoder.go +++ b/packet/decoder.go @@ -16,8 +16,8 @@ func FindProtocol(p []byte) (layers.IPProtocol, error) { } switch version { case 4: - if len(p) < ipv4HeaderLen { - return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4HeaderLen, len(p)) + if len(p) < ipv4MinHeaderLen { + return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4MinHeaderLen, len(p)) } // Protocol is in the 10th byte of IPv4 header return layers.IPProtocol(p[9]), nil diff --git a/packet/decoder_test.go b/packet/decoder_test.go index 6db377dd..c07896e4 100644 --- a/packet/decoder_test.go +++ b/packet/decoder_test.go @@ -61,11 +61,13 @@ func TestDecodeICMP(t *testing.T) { Src: netip.MustParseAddr("172.16.0.1"), Dst: netip.MustParseAddr("10.0.0.1"), Protocol: layers.IPProtocolICMPv4, + TTL: DefaultTTL, } ipv6Packet = IP{ Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), Protocol: layers.IPProtocolICMPv6, + TTL: DefaultTTL, } icmpID = 100 icmpSeq = 52819 @@ -171,7 +173,7 @@ func TestDecodeBadPackets(t *testing.T) { SrcIP: srcIPv4, DstIP: dstIPv4, Protocol: layers.IPProtocolICMPv4, - TTL: defaultTTL, + TTL: DefaultTTL, } icmpLayer := layers.ICMPv4{ TypeCode: layers.CreateICMPv4TypeCode(uint8(ipv4.ICMPTypeEcho), 0), @@ -231,6 +233,7 @@ func assertIPLayer(t *testing.T, expected, actual *IP) { require.Equal(t, expected.Src, actual.Src) require.Equal(t, expected.Dst, actual.Dst) require.Equal(t, expected.Protocol, actual.Protocol) + require.Equal(t, expected.TTL, actual.TTL) } type UDP struct { diff --git a/packet/funnel_test.go b/packet/funnel_test.go new file mode 100644 index 00000000..08dc291f --- /dev/null +++ b/packet/funnel_test.go @@ -0,0 +1,16 @@ +package packet + +import "net/netip" + +type mockFunnelUniPipe struct { + uniPipe chan RawPacket +} + +func (mfui *mockFunnelUniPipe) SendPacket(dst netip.Addr, pk RawPacket) error { + mfui.uniPipe <- pk + return nil +} + +func (mfui *mockFunnelUniPipe) Close() error { + return nil +} diff --git a/packet/packet.go b/packet/packet.go index 62b32ed8..b691790f 100644 --- a/packet/packet.go +++ b/packet/packet.go @@ -7,12 +7,20 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) const ( - defaultTTL uint8 = 64 - ipv4HeaderLen = 20 - ipv6HeaderLen = 40 + ipv4MinHeaderLen = 20 + ipv6HeaderLen = 40 + ipv4MinMTU = 576 + ipv6MinMTU = 1280 + icmpHeaderLen = 8 + // https://www.rfc-editor.org/rfc/rfc792 and https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 define 2 codes. + // 0 = ttl exceed in transit, 1 = fragment reassembly time exceeded + icmpTTLExceedInTransitCode = 0 + DefaultTTL uint8 = 255 ) // Packet represents an IP packet or a packet that is encapsulated by IP @@ -28,6 +36,7 @@ type IP struct { Src netip.Addr Dst netip.Addr Protocol layers.IPProtocol + TTL uint8 } func newIPv4(ipLayer *layers.IPv4) (*IP, error) { @@ -43,6 +52,7 @@ func newIPv4(ipLayer *layers.IPv4) (*IP, error) { Src: src, Dst: dst, Protocol: ipLayer.Protocol, + TTL: ipLayer.TTL, }, nil } @@ -59,6 +69,7 @@ func newIPv6(ipLayer *layers.IPv6) (*IP, error) { Src: src, Dst: dst, Protocol: ipLayer.NextHeader, + TTL: ipLayer.HopLimit, }, nil } @@ -78,7 +89,7 @@ func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) { SrcIP: ip.Src.AsSlice(), DstIP: ip.Dst.AsSlice(), Protocol: layers.IPProtocol(ip.Protocol), - TTL: defaultTTL, + TTL: ip.TTL, }, }, nil } else { @@ -88,7 +99,7 @@ func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) { SrcIP: ip.Src.AsSlice(), DstIP: ip.Dst.AsSlice(), NextHeader: layers.IPProtocol(ip.Protocol), - HopLimit: defaultTTL, + HopLimit: ip.TTL, }, }, nil } @@ -113,3 +124,51 @@ func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) { icmpLayer := gopacket.Payload(msg) return append(ipLayers, icmpLayer), nil } + +func NewICMPTTLExceedPacket(originalIP *IP, originalPacket RawPacket, routerIP netip.Addr) *ICMP { + var ( + protocol layers.IPProtocol + icmpType icmp.Type + ) + if originalIP.Dst.Is4() { + protocol = layers.IPProtocolICMPv4 + icmpType = ipv4.ICMPTypeTimeExceeded + } else { + protocol = layers.IPProtocolICMPv6 + icmpType = ipv6.ICMPTypeTimeExceeded + } + return &ICMP{ + IP: &IP{ + Src: routerIP, + Dst: originalIP.Src, + Protocol: protocol, + TTL: DefaultTTL, + }, + Message: &icmp.Message{ + Type: icmpType, + Code: icmpTTLExceedInTransitCode, + Body: &icmp.TimeExceeded{ + Data: originalDatagram(originalPacket, originalIP.Dst.Is4()), + }, + }, + } +} + +// originalDatagram returns a slice of the original datagram for ICMP error messages +// https://www.rfc-editor.org/rfc/rfc1812#section-4.3.2.3 suggests to copy as much without exceeding 576 bytes. +// https://datatracker.ietf.org/doc/html/rfc4443#section-3.3 suggests to copy as much without exceeding 1280 bytes +func originalDatagram(originalPacket RawPacket, isIPv4 bool) []byte { + var upperBound int + if isIPv4 { + upperBound = ipv4MinMTU - ipv4MinHeaderLen - icmpHeaderLen + if upperBound > len(originalPacket.Data) { + upperBound = len(originalPacket.Data) + } + } else { + upperBound = ipv6MinMTU - ipv6HeaderLen - icmpHeaderLen + if upperBound > len(originalPacket.Data) { + upperBound = len(originalPacket.Data) + } + } + return originalPacket.Data[:upperBound] +} diff --git a/packet/packet_test.go b/packet/packet_test.go new file mode 100644 index 00000000..8b0b314f --- /dev/null +++ b/packet/packet_test.go @@ -0,0 +1,104 @@ +package packet + +import ( + "bytes" + "net/netip" + "testing" + + "github.com/google/gopacket/layers" + "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +func TestNewICMPTTLExceedPacket(t *testing.T) { + ipv4Packet := IP{ + Src: netip.MustParseAddr("192.168.1.1"), + Dst: netip.MustParseAddr("10.0.0.1"), + Protocol: layers.IPProtocolICMPv4, + TTL: 0, + } + icmpV4Packet := ICMP{ + IP: &ipv4Packet, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: 25821, + Seq: 58129, + Data: []byte("test ttl=0"), + }, + }, + } + assertTTLExceedPacket(t, &icmpV4Packet) + icmpV4Packet.Body = &icmp.Echo{ + ID: 3487, + Seq: 19183, + Data: make([]byte, ipv4MinMTU), + } + assertTTLExceedPacket(t, &icmpV4Packet) + ipv6Packet := IP{ + Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), + Protocol: layers.IPProtocolICMPv6, + TTL: 0, + } + icmpV6Packet := ICMP{ + IP: &ipv6Packet, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, + Code: 0, + Body: &icmp.Echo{ + ID: 25821, + Seq: 58129, + Data: []byte("test ttl=0"), + }, + }, + } + assertTTLExceedPacket(t, &icmpV6Packet) + icmpV6Packet.Body = &icmp.Echo{ + ID: 1497, + Seq: 39284, + Data: make([]byte, ipv6MinMTU), + } + assertTTLExceedPacket(t, &icmpV6Packet) +} + +func assertTTLExceedPacket(t *testing.T, pk *ICMP) { + encoder := NewEncoder() + rawPacket, err := encoder.Encode(pk) + require.NoError(t, err) + + minMTU := ipv4MinMTU + headerLen := ipv4MinHeaderLen + routerIP := netip.MustParseAddr("172.16.0.3") + if pk.Dst.Is6() { + minMTU = ipv6MinMTU + headerLen = ipv6HeaderLen + routerIP = netip.MustParseAddr("fd51:2391:697:f4ee::3") + } + + ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, routerIP) + require.Equal(t, routerIP, ttlExceedPacket.Src) + require.Equal(t, pk.Src, ttlExceedPacket.Dst) + require.Equal(t, pk.Protocol, ttlExceedPacket.Protocol) + require.Equal(t, DefaultTTL, ttlExceedPacket.TTL) + + timeExceed, ok := ttlExceedPacket.Body.(*icmp.TimeExceeded) + require.True(t, ok) + if len(rawPacket.Data) > minMTU { + require.True(t, bytes.Equal(timeExceed.Data, rawPacket.Data[:minMTU-headerLen-icmpHeaderLen])) + } else { + require.True(t, bytes.Equal(timeExceed.Data, rawPacket.Data)) + } + + rawTTLExceedPacket, err := encoder.Encode(ttlExceedPacket) + require.NoError(t, err) + if len(rawPacket.Data) > minMTU { + require.Len(t, rawTTLExceedPacket.Data, minMTU) + } else { + require.Len(t, rawTTLExceedPacket.Data, headerLen+icmpHeaderLen+len(rawPacket.Data)) + require.True(t, bytes.Equal(rawPacket.Data, rawTTLExceedPacket.Data[headerLen+icmpHeaderLen:])) + } +} diff --git a/packet/router.go b/packet/router.go new file mode 100644 index 00000000..83acc393 --- /dev/null +++ b/packet/router.go @@ -0,0 +1,126 @@ +package packet + +import ( + "context" + "net" + "net/netip" + + "github.com/rs/zerolog" +) + +var ( + // Source IP in documentation range to return ICMP error messages if we can't determine the IP of this machine + icmpv4ErrFallbackSrc = netip.MustParseAddr("192.0.2.30") + icmpv6ErrFallbackSrc = netip.MustParseAddr("2001:db8::") +) + +// ICMPRouter sends ICMP messages and listens for their responses +type ICMPRouter interface { + // Serve starts listening for responses to the requests until context is done + Serve(ctx context.Context) error + // Request sends an ICMP message + Request(pk *ICMP, responder FunnelUniPipe) error +} + +// Upstream of raw packets +type Upstream interface { + // ReceivePacket waits for the next raw packet from upstream + ReceivePacket(ctx context.Context) (RawPacket, error) +} + +type Router struct { + upstream Upstream + returnPipe FunnelUniPipe + icmpProxy ICMPRouter + ipv4Src netip.Addr + ipv6Src netip.Addr + logger *zerolog.Logger +} + +func NewRouter(upstream Upstream, returnPipe FunnelUniPipe, icmpProxy ICMPRouter, logger *zerolog.Logger) *Router { + ipv4Src, err := findLocalAddr(net.ParseIP("1.1.1.1"), 53) + if err != nil { + logger.Warn().Err(err).Msgf("Failed to determine the IPv4 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv4ErrFallbackSrc) + ipv4Src = icmpv4ErrFallbackSrc + } + ipv6Src, err := findLocalAddr(net.ParseIP("2606:4700:4700::1111"), 53) + if err != nil { + logger.Warn().Err(err).Msgf("Failed to determine the IPv6 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv6ErrFallbackSrc) + ipv6Src = icmpv6ErrFallbackSrc + } + return &Router{ + upstream: upstream, + returnPipe: returnPipe, + icmpProxy: icmpProxy, + ipv4Src: ipv4Src, + ipv6Src: ipv6Src, + logger: logger, + } +} + +func (r *Router) Serve(ctx context.Context) error { + icmpDecoder := NewICMPDecoder() + encoder := NewEncoder() + for { + rawPacket, err := r.upstream.ReceivePacket(ctx) + if err != nil { + return err + } + icmpPacket, err := icmpDecoder.Decode(rawPacket) + if err != nil { + r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram") + continue + } + + if icmpPacket.TTL <= 1 { + if err := r.sendTTLExceedMsg(icmpPacket, rawPacket, encoder); err != nil { + r.logger.Err(err).Msg("Failed to return ICMP TTL exceed error") + } + continue + } + icmpPacket.TTL-- + + if err := r.icmpProxy.Request(icmpPacket, r.returnPipe); err != nil { + r.logger.Err(err). + Str("src", icmpPacket.Src.String()). + Str("dst", icmpPacket.Dst.String()). + Interface("type", icmpPacket.Type). + Msg("Failed to send ICMP packet") + continue + } + } +} + +func (r *Router) sendTTLExceedMsg(pk *ICMP, rawPacket RawPacket, encoder *Encoder) error { + var srcIP netip.Addr + if pk.Dst.Is4() { + srcIP = r.ipv4Src + } else { + srcIP = r.ipv6Src + } + ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP) + + encodedTTLExceed, err := encoder.Encode(ttlExceedPacket) + if err != nil { + return err + } + return r.returnPipe.SendPacket(pk.Src, encodedTTLExceed) +} + +// findLocalAddr tries to dial UDP and returns the local address picked by the OS +func findLocalAddr(dst net.IP, port int) (netip.Addr, error) { + udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{ + IP: dst, + Port: port, + }) + if err != nil { + return netip.Addr{}, err + } + defer udpConn.Close() + localAddrPort, err := netip.ParseAddrPort(udpConn.LocalAddr().String()) + if err != nil { + return netip.Addr{}, err + } + localAddr := localAddrPort.Addr() + return localAddr, nil +} diff --git a/packet/router_test.go b/packet/router_test.go new file mode 100644 index 00000000..14e49986 --- /dev/null +++ b/packet/router_test.go @@ -0,0 +1,125 @@ +package packet + +import ( + "bytes" + "context" + "fmt" + "net/netip" + "testing" + + "github.com/google/gopacket/layers" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +var ( + noopLogger = zerolog.Nop() +) + +func TestRouterReturnTTLExceed(t *testing.T) { + upstream := &mockUpstream{ + source: make(chan RawPacket), + } + returnPipe := &mockFunnelUniPipe{ + uniPipe: make(chan RawPacket), + } + router := NewRouter(upstream, returnPipe, &mockICMPRouter{}, &noopLogger) + ctx, cancel := context.WithCancel(context.Background()) + routerStopped := make(chan struct{}) + go func() { + router.Serve(ctx) + close(routerStopped) + }() + + pk := ICMP{ + IP: &IP{ + Src: netip.MustParseAddr("192.168.1.1"), + Dst: netip.MustParseAddr("10.0.0.1"), + Protocol: layers.IPProtocolICMPv4, + TTL: 1, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: 12481, + Seq: 8036, + Data: []byte("TTL exceed"), + }, + }, + } + assertTTLExceed(t, &pk, router.ipv4Src, upstream, returnPipe) + pk = ICMP{ + IP: &IP{ + Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"), + Protocol: layers.IPProtocolICMPv6, + TTL: 1, + }, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, + Code: 0, + Body: &icmp.Echo{ + ID: 42583, + Seq: 7039, + Data: []byte("TTL exceed"), + }, + }, + } + assertTTLExceed(t, &pk, router.ipv6Src, upstream, returnPipe) + + cancel() + <-routerStopped +} + +func assertTTLExceed(t *testing.T, originalPacket *ICMP, expectedSrc netip.Addr, upstream *mockUpstream, returnPipe *mockFunnelUniPipe) { + encoder := NewEncoder() + rawPacket, err := encoder.Encode(originalPacket) + require.NoError(t, err) + + upstream.source <- rawPacket + resp := <-returnPipe.uniPipe + decoder := NewICMPDecoder() + decoded, err := decoder.Decode(resp) + require.NoError(t, err) + + require.Equal(t, expectedSrc, decoded.Src) + require.Equal(t, originalPacket.Src, decoded.Dst) + require.Equal(t, originalPacket.Protocol, decoded.Protocol) + require.Equal(t, DefaultTTL, decoded.TTL) + if originalPacket.Dst.Is4() { + require.Equal(t, ipv4.ICMPTypeTimeExceeded, decoded.Type) + } else { + require.Equal(t, ipv6.ICMPTypeTimeExceeded, decoded.Type) + } + require.Equal(t, 0, decoded.Code) + timeExceed, ok := decoded.Body.(*icmp.TimeExceeded) + require.True(t, ok) + require.True(t, bytes.Equal(rawPacket.Data, timeExceed.Data)) +} + +type mockUpstream struct { + source chan RawPacket +} + +func (ms *mockUpstream) ReceivePacket(ctx context.Context) (RawPacket, error) { + select { + case <-ctx.Done(): + return RawPacket{}, ctx.Err() + case pk := <-ms.source: + return pk, nil + } +} + +type mockICMPRouter struct{} + +func (mir mockICMPRouter) Serve(ctx context.Context) error { + return fmt.Errorf("Serve not implemented by mockICMPRouter") +} + +func (mir mockICMPRouter) Request(pk *ICMP, responder FunnelUniPipe) error { + return fmt.Errorf("Request not implemented by mockICMPRouter") +} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index fc80ed1c..ebd0e449 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -117,11 +117,11 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato } if useDatagramV2(config) { // TODO: TUN-6701: Decouple upgrade of datagram v2 and using icmp proxy - icmpProxy, err := ingress.NewICMPProxy(config.Log) + icmpRouter, err := ingress.NewICMPRouter(config.Log) if err != nil { - log.Logger().Warn().Err(err).Msg("Failed to create icmp proxy, will continue to use datagram v1") + log.Logger().Warn().Err(err).Msg("Failed to create icmp router, will continue to use datagram v1") } else { - edgeTunnelServer.icmpProxy = icmpProxy + edgeTunnelServer.icmpRouter = icmpRouter } } @@ -152,10 +152,10 @@ func (s *Supervisor) Run( ctx context.Context, connectedSignal *signal.Signal, ) error { - if s.edgeTunnelServer.icmpProxy != nil { + if s.edgeTunnelServer.icmpRouter != nil { go func() { - if err := s.edgeTunnelServer.icmpProxy.Serve(ctx); err != nil { - s.log.Logger().Err(err).Msg("icmp proxy terminated") + if err := s.edgeTunnelServer.icmpRouter.Serve(ctx); err != nil { + s.log.Logger().Err(err).Msg("icmp router terminated") } }() } diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 34f6cecc..22fb55e3 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -20,8 +20,8 @@ import ( "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/orchestration" + "github.com/cloudflare/cloudflared/packet" quicpogs "github.com/cloudflare/cloudflared/quic" "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" @@ -200,7 +200,7 @@ type EdgeTunnelServer struct { reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} tracker *tunnelstate.ConnTracker - icmpProxy ingress.ICMPProxy + icmpRouter packet.ICMPRouter connAwareLogger *ConnAwareLogger } @@ -661,7 +661,7 @@ func (e *EdgeTunnelServer) serveQUIC( connOptions, controlStreamHandler, connLogger.Logger(), - e.icmpProxy) + e.icmpRouter) if err != nil { if e.config.NeedPQ { handlePQTunnelError(err, e.config) From e454994e3ed3b708580a00eea76f190598cff30e Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 16 Sep 2022 10:14:05 +0100 Subject: [PATCH 199/238] TUN-6767: Build ICMP proxy for Windows only when CGO is enabled --- ingress/icmp_generic.go | 4 ++-- ingress/icmp_windows.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index 0e481006..c685a2f4 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -1,4 +1,4 @@ -//go:build !darwin && !linux && !windows +//go:build !darwin && !linux && (!windows || !cgo) package ingress @@ -14,7 +14,7 @@ import ( "github.com/cloudflare/cloudflared/packet" ) -var errICMPProxyNotImplemented = fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS) +var errICMPProxyNotImplemented = fmt.Errorf("ICMP proxy is not implemented on %s %s", runtime.GOOS, runtime.GOARCH) type icmpProxy struct{} diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index baba8d4d..4170d5fc 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -1,4 +1,4 @@ -//go:build windows +//go:build windows && cgo package ingress From b639b6627a2760f8a747c29d526e3f323662c8ae Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 9 Sep 2022 16:48:42 +0100 Subject: [PATCH 200/238] TUN-6744: On posix platforms, assign unique echo ID per (src, dst, echo ID) This also refactor FunnelTracker to provide a GetOrRegister method to prevent race condition --- ingress/icmp_darwin.go | 112 +++++++++++++++++------------- ingress/icmp_darwin_test.go | 82 +++++++++++++++------- ingress/icmp_linux.go | 73 ++++++++++--------- ingress/icmp_posix.go | 15 ++-- ingress/icmp_windows.go | 21 +++--- ingress/origin_icmp_proxy_test.go | 86 +++++++++++++++++++++++ packet/funnel.go | 17 +++-- 7 files changed, 268 insertions(+), 138 deletions(-) diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 73ac4de1..78d0a87f 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -37,9 +37,9 @@ type icmpProxy struct { // then from the beginning to lastAssignment. // ICMP echo are short lived. By the time an ID is revisited, it should have been released. type echoIDTracker struct { - lock sync.RWMutex - // maps the source IP to an echo ID obtained from assignment - srcIPMapping map[netip.Addr]uint16 + lock sync.Mutex + // maps the source IP, destination IP and original echo ID to a unique echo ID obtained from assignment + mapping map[flow3Tuple]uint16 // assignment tracks if an ID is assigned using index as the ID // The size of the array is math.MaxUint16 because echo ID is 2 bytes assignment [math.MaxUint16]bool @@ -49,20 +49,18 @@ type echoIDTracker struct { func newEchoIDTracker() *echoIDTracker { return &echoIDTracker{ - srcIPMapping: make(map[netip.Addr]uint16), + mapping: make(map[flow3Tuple]uint16), } } -func (eit *echoIDTracker) get(srcIP netip.Addr) (uint16, bool) { - eit.lock.RLock() - defer eit.lock.RUnlock() - id, ok := eit.srcIPMapping[srcIP] - return id, ok -} - -func (eit *echoIDTracker) assign(srcIP netip.Addr) (uint16, bool) { +// Get assignment or assign a new ID. +func (eit *echoIDTracker) getOrAssign(key flow3Tuple) (id uint16, success bool) { eit.lock.Lock() defer eit.lock.Unlock() + id, exists := eit.mapping[key] + if exists { + return id, true + } if eit.nextAssignment == math.MaxUint16 { eit.nextAssignment = 0 @@ -71,14 +69,14 @@ func (eit *echoIDTracker) assign(srcIP netip.Addr) (uint16, bool) { for i, assigned := range eit.assignment[eit.nextAssignment:] { if !assigned { echoID := uint16(i) + eit.nextAssignment - eit.set(srcIP, echoID) + eit.set(key, echoID) return echoID, true } } for i, assigned := range eit.assignment[0:eit.nextAssignment] { if !assigned { echoID := uint16(i) - eit.set(srcIP, echoID) + eit.set(key, echoID) return echoID, true } } @@ -86,20 +84,20 @@ func (eit *echoIDTracker) assign(srcIP netip.Addr) (uint16, bool) { } // Caller should hold the lock -func (eit *echoIDTracker) set(srcIP netip.Addr, echoID uint16) { - eit.assignment[echoID] = true - eit.srcIPMapping[srcIP] = echoID - eit.nextAssignment = echoID + 1 +func (eit *echoIDTracker) set(key flow3Tuple, assignedEchoID uint16) { + eit.assignment[assignedEchoID] = true + eit.mapping[key] = assignedEchoID + eit.nextAssignment = assignedEchoID + 1 } -func (eit *echoIDTracker) release(srcIP netip.Addr, id uint16) bool { +func (eit *echoIDTracker) release(key flow3Tuple, assigned uint16) bool { eit.lock.Lock() defer eit.lock.Unlock() - currentID, exists := eit.srcIPMapping[srcIP] - if exists && id == currentID { - delete(eit.srcIPMapping, srcIP) - eit.assignment[id] = false + currentEchoID, exists := eit.mapping[key] + if exists && assigned == currentEchoID { + delete(eit.mapping, key) + eit.assignment[assigned] = false return true } return false @@ -134,33 +132,46 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er if pk == nil { return errPacketNil } + originalEcho, err := getICMPEcho(pk.Message) + if err != nil { + return err + } + echoIDTrackerKey := flow3Tuple{ + srcIP: pk.Src, + dstIP: pk.Dst, + originalEchoID: originalEcho.ID, + } // TODO: TUN-6744 assign unique flow per (src, echo ID) - echoID, exists := ip.echoIDTracker.get(pk.Src) - if !exists { + assignedEchoID, success := ip.echoIDTracker.getOrAssign(echoIDTrackerKey) + if !success { + return fmt.Errorf("failed to assign unique echo ID") + } + newFunnelFunc := func() (packet.Funnel, error) { originalEcho, err := getICMPEcho(pk.Message) if err != nil { - return err + return nil, err } - echoID, exists = ip.echoIDTracker.assign(pk.Src) - if !exists { - return fmt.Errorf("failed to assign unique echo ID") - } - funnelID := echoFunnelID(echoID) originSender := originSender{ - conn: ip.conn, - echoIDTracker: ip.echoIDTracker, - srcIP: pk.Src, - echoID: echoID, + conn: ip.conn, + echoIDTracker: ip.echoIDTracker, + echoIDTrackerKey: echoIDTrackerKey, + assignedEchoID: assignedEchoID, } - icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, int(echoID), originalEcho.ID, ip.encoder) - if replaced := ip.srcFunnelTracker.Register(funnelID, icmpFlow); replaced { - ip.logger.Info().Str("src", pk.Src.String()).Msg("Replaced funnel") - } - return icmpFlow.sendToDst(pk.Dst, pk.Message) + icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, int(assignedEchoID), originalEcho.ID, ip.encoder) + return icmpFlow, nil } - funnel, exists := ip.srcFunnelTracker.Get(echoFunnelID(echoID)) - if !exists { - return packet.ErrFunnelNotFound + funnelID := echoFunnelID(assignedEchoID) + funnel, isNew, err := ip.srcFunnelTracker.GetOrRegister(funnelID, newFunnelFunc) + if err != nil { + return err + } + if isNew { + ip.logger.Debug(). + Str("src", pk.Src.String()). + Str("dst", pk.Dst.String()). + Int("originalEchoID", originalEcho.ID). + Int("assignedEchoID", int(assignedEchoID)). + Msg("New flow") } icmpFlow, err := toICMPEchoFlow(funnel) if err != nil { @@ -199,7 +210,7 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { ip.logger.Debug().Str("dst", from.String()).Msgf("Drop ICMP %s from reply", reply.msg.Type) continue } - if ip.sendReply(reply); err != nil { + if err := ip.sendReply(reply); err != nil { ip.logger.Error().Err(err).Str("dst", from.String()).Msg("Failed to send ICMP reply") continue } @@ -227,7 +238,8 @@ func (ip *icmpProxy) handleFullPacket(decoder *packet.ICMPDecoder, rawPacket []b } func (ip *icmpProxy) sendReply(reply *echoReply) error { - funnel, ok := ip.srcFunnelTracker.Get(echoFunnelID(reply.echo.ID)) + funnelID := echoFunnelID(reply.echo.ID) + funnel, ok := ip.srcFunnelTracker.Get(funnelID) if !ok { return packet.ErrFunnelNotFound } @@ -240,10 +252,10 @@ func (ip *icmpProxy) sendReply(reply *echoReply) error { // originSender wraps icmp.PacketConn to implement packet.FunnelUniPipe interface type originSender struct { - conn *icmp.PacketConn - echoIDTracker *echoIDTracker - srcIP netip.Addr - echoID uint16 + conn *icmp.PacketConn + echoIDTracker *echoIDTracker + echoIDTrackerKey flow3Tuple + assignedEchoID uint16 } func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error { @@ -254,6 +266,6 @@ func (os *originSender) SendPacket(dst netip.Addr, pk packet.RawPacket) error { } func (os *originSender) Close() error { - os.echoIDTracker.release(os.srcIP, os.echoID) + os.echoIDTracker.release(os.echoIDTrackerKey, os.assignedEchoID) return nil } diff --git a/ingress/icmp_darwin_test.go b/ingress/icmp_darwin_test.go index 8baccb58..90b92710 100644 --- a/ingress/icmp_darwin_test.go +++ b/ingress/icmp_darwin_test.go @@ -12,80 +12,110 @@ import ( func TestSingleEchoIDTracker(t *testing.T) { tracker := newEchoIDTracker() - srcIP := netip.MustParseAddr("127.0.0.1") - echoID, ok := tracker.get(srcIP) - require.False(t, ok) - require.Equal(t, uint16(0), echoID) + key := flow3Tuple{ + srcIP: netip.MustParseAddr("172.16.0.1"), + dstIP: netip.MustParseAddr("172.16.0.2"), + originalEchoID: 5182, + } // not assigned yet, so nothing to release - require.False(t, tracker.release(srcIP, echoID)) + require.False(t, tracker.release(key, 0)) - echoID, ok = tracker.assign(srcIP) + echoID, ok := tracker.getOrAssign(key) require.True(t, ok) require.Equal(t, uint16(0), echoID) - echoID, ok = tracker.get(srcIP) + // Second time should return the same echo ID + echoID, ok = tracker.getOrAssign(key) require.True(t, ok) require.Equal(t, uint16(0), echoID) // releasing a different ID returns false - require.False(t, tracker.release(srcIP, 1999)) - require.True(t, tracker.release(srcIP, echoID)) + require.False(t, tracker.release(key, 1999)) + require.True(t, tracker.release(key, echoID)) // releasing the second time returns false - require.False(t, tracker.release(srcIP, echoID)) - - echoID, ok = tracker.get(srcIP) - require.False(t, ok) - require.Equal(t, uint16(0), echoID) + require.False(t, tracker.release(key, echoID)) // Move to the next IP - echoID, ok = tracker.assign(srcIP) + echoID, ok = tracker.getOrAssign(key) require.True(t, ok) require.Equal(t, uint16(1), echoID) } func TestFullEchoIDTracker(t *testing.T) { + var ( + dstIP = netip.MustParseAddr("192.168.0.1") + originalEchoID = 41820 + ) tracker := newEchoIDTracker() - firstIP := netip.MustParseAddr("172.16.0.1") - srcIP := firstIP + firstSrcIP := netip.MustParseAddr("172.16.0.1") + srcIP := firstSrcIP for i := uint16(0); i < math.MaxUint16; i++ { - echoID, ok := tracker.assign(srcIP) + key := flow3Tuple{ + srcIP: srcIP, + dstIP: dstIP, + originalEchoID: originalEchoID, + } + echoID, ok := tracker.getOrAssign(key) require.True(t, ok) require.Equal(t, i, echoID) - echoID, ok = tracker.get(srcIP) + echoID, ok = tracker.get(key) require.True(t, ok) require.Equal(t, i, echoID) + srcIP = srcIP.Next() } + key := flow3Tuple{ + srcIP: srcIP.Next(), + dstIP: dstIP, + originalEchoID: originalEchoID, + } // All echo IDs are assigned - echoID, ok := tracker.assign(srcIP.Next()) + echoID, ok := tracker.getOrAssign(key) require.False(t, ok) require.Equal(t, uint16(0), echoID) - srcIP = firstIP + srcIP = firstSrcIP for i := uint16(0); i < math.MaxUint16; i++ { - ok := tracker.release(srcIP, i) + key := flow3Tuple{ + srcIP: srcIP, + dstIP: dstIP, + originalEchoID: originalEchoID, + } + ok := tracker.release(key, i) require.True(t, ok) - echoID, ok = tracker.get(srcIP) + echoID, ok = tracker.get(key) require.False(t, ok) require.Equal(t, uint16(0), echoID) srcIP = srcIP.Next() } // The IDs are assignable again - srcIP = firstIP + srcIP = firstSrcIP for i := uint16(0); i < math.MaxUint16; i++ { - echoID, ok := tracker.assign(srcIP) + key := flow3Tuple{ + srcIP: srcIP, + dstIP: dstIP, + originalEchoID: originalEchoID, + } + echoID, ok := tracker.getOrAssign(key) require.True(t, ok) require.Equal(t, i, echoID) - echoID, ok = tracker.get(srcIP) + echoID, ok = tracker.get(key) require.True(t, ok) require.Equal(t, i, echoID) srcIP = srcIP.Next() } } + +func (eit *echoIDTracker) get(key flow3Tuple) (id uint16, exist bool) { + eit.lock.Lock() + defer eit.lock.Unlock() + id, exists := eit.mapping[key] + return id, exists +} diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index 3b7ae828..5f122e49 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -57,45 +57,57 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er if pk == nil { return errPacketNil } - funnelID := srcIPFunnelID(pk.Src) - funnel, exists := ip.srcFunnelTracker.Get(funnelID) - if !exists { - originalEcho, err := getICMPEcho(pk.Message) - if err != nil { - return err - } + originalEcho, err := getICMPEcho(pk.Message) + if err != nil { + return err + } + newConnChan := make(chan *icmp.PacketConn, 1) + newFunnelFunc := func() (packet.Funnel, error) { conn, err := newICMPConn(ip.listenIP) if err != nil { - return errors.Wrap(err, "failed to open ICMP socket") + return nil, errors.Wrap(err, "failed to open ICMP socket") } + newConnChan <- conn localUDPAddr, ok := conn.LocalAddr().(*net.UDPAddr) if !ok { - return fmt.Errorf("ICMP listener address %s is not net.UDPAddr", conn.LocalAddr()) + return nil, fmt.Errorf("ICMP listener address %s is not net.UDPAddr", conn.LocalAddr()) } originSender := originSender{conn: conn} echoID := localUDPAddr.Port icmpFlow := newICMPEchoFlow(pk.Src, &originSender, responder, echoID, originalEcho.ID, packet.NewEncoder()) - if replaced := ip.srcFunnelTracker.Register(funnelID, icmpFlow); replaced { - ip.logger.Info().Str("src", pk.Src.String()).Msg("Replaced funnel") - } - if err := icmpFlow.sendToDst(pk.Dst, pk.Message); err != nil { - return errors.Wrap(err, "failed to send ICMP echo request") - } - go func() { - defer ip.srcFunnelTracker.Unregister(funnelID, icmpFlow) - if err := ip.listenResponse(icmpFlow, conn); err != nil { - ip.logger.Err(err). - Str("funnelID", funnelID.String()). - Int("echoID", echoID). - Msg("Failed to listen for ICMP echo response") - } - }() - return nil + return icmpFlow, nil + } + funnelID := flow3Tuple{ + srcIP: pk.Src, + dstIP: pk.Dst, + originalEchoID: originalEcho.ID, + } + funnel, isNew, err := ip.srcFunnelTracker.GetOrRegister(funnelID, newFunnelFunc) + if err != nil { + return err } icmpFlow, err := toICMPEchoFlow(funnel) if err != nil { return err } + if isNew { + ip.logger.Debug(). + Str("src", pk.Src.String()). + Str("dst", pk.Dst.String()). + Int("originalEchoID", originalEcho.ID). + Msg("New flow") + conn := <-newConnChan + go func() { + defer ip.srcFunnelTracker.Unregister(funnelID, icmpFlow) + if err := ip.listenResponse(icmpFlow, conn); err != nil { + ip.logger.Debug().Err(err). + Str("src", pk.Src.String()). + Str("dst", pk.Dst.String()). + Int("originalEchoID", originalEcho.ID). + Msg("Failed to listen for ICMP echo response") + } + }() + } if err := icmpFlow.sendToDst(pk.Dst, pk.Message); err != nil { return errors.Wrap(err, "failed to send ICMP echo request") } @@ -146,12 +158,11 @@ func (os *originSender) Close() error { return os.conn.Close() } -type srcIPFunnelID netip.Addr - -func (sifd srcIPFunnelID) Type() string { - return "srcIP" +// Only linux uses flow3Tuple as FunnelID +func (ft flow3Tuple) Type() string { + return "srcIP_dstIP_echoID" } -func (sifd srcIPFunnelID) String() string { - return netip.Addr(sifd).String() +func (ft flow3Tuple) String() string { + return fmt.Sprintf("%s:%s:%d", ft.srcIP, ft.dstIP, ft.originalEchoID) } diff --git a/ingress/icmp_posix.go b/ingress/icmp_posix.go index a2a0a5b9..5c3c62b3 100644 --- a/ingress/icmp_posix.go +++ b/ingress/icmp_posix.go @@ -32,17 +32,10 @@ func netipAddr(addr net.Addr) (netip.Addr, bool) { return netip.AddrFromSlice(udpAddr.IP) } -type flowID struct { - srcIP netip.Addr - echoID int -} - -func (fi *flowID) Type() string { - return "srcIP_echoID" -} - -func (fi *flowID) String() string { - return fmt.Sprintf("%s:%d", fi.srcIP, fi.echoID) +type flow3Tuple struct { + srcIP netip.Addr + dstIP netip.Addr + originalEchoID int } // icmpEchoFlow implements the packet.Funnel interface. diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index 4170d5fc..8ad313b5 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -315,23 +315,22 @@ func (ip *icmpProxy) handleEchoReply(request *packet.ICMP, echoReq *icmp.Echo, d }, } - serializedPacket, err := ip.encodeICMPReply(&pk) + cachedEncoder := ip.encoderPool.Get() + // The encoded packet is a slice to of the encoder, so we shouldn't return the encoder back to the pool until + // the encoded packet is sent. + defer ip.encoderPool.Put(cachedEncoder) + encoder, ok := cachedEncoder.(*packet.Encoder) + if !ok { + return fmt.Errorf("encoderPool returned %T, expect *packet.Encoder", cachedEncoder) + } + + serializedPacket, err := encoder.Encode(&pk) if err != nil { return err } return responder.SendPacket(request.Src, serializedPacket) } -func (ip *icmpProxy) encodeICMPReply(pk *packet.ICMP) (packet.RawPacket, error) { - cachedEncoder := ip.encoderPool.Get() - defer ip.encoderPool.Put(cachedEncoder) - encoder, ok := cachedEncoder.(*packet.Encoder) - if !ok { - return packet.RawPacket{}, fmt.Errorf("encoderPool returned %T, expect *packet.Encoder", cachedEncoder) - } - return encoder.Encode(pk) -} - func (ip *icmpProxy) icmpEchoRoundtrip(dst netip.Addr, echo *icmp.Echo) ([]byte, error) { if dst.Is6() { if ip.srcSocketAddr == nil { diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index 5684a608..b961b7d4 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -6,6 +6,7 @@ import ( "net" "net/netip" "strings" + "sync" "testing" "github.com/google/gopacket/layers" @@ -97,6 +98,91 @@ func testICMPRouterEcho(t *testing.T, sendIPv4 bool) { <-proxyDone } +// TestConcurrentRequests makes sure icmpRouter can send concurrent requests to the same destination with different +// echo ID. This simulates concurrent ping to the same destination. +func TestConcurrentRequestsToSameDst(t *testing.T) { + const ( + concurrentPings = 5 + endSeq = 5 + ) + + router, err := NewICMPRouter(&noopLogger) + require.NoError(t, err) + + proxyDone := make(chan struct{}) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + router.Serve(ctx) + close(proxyDone) + }() + + var wg sync.WaitGroup + // icmpv4 and icmpv6 each has concurrentPings + wg.Add(concurrentPings * 2) + for i := 0; i < concurrentPings; i++ { + echoID := 38451 + i + go func() { + defer wg.Done() + responder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte, 1), + } + for seq := 0; seq < endSeq; seq++ { + pk := &packet.ICMP{ + IP: &packet.IP{ + Src: localhostIP, + Dst: localhostIP, + Protocol: layers.IPProtocolICMPv4, + TTL: packet.DefaultTTL, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: echoID, + Seq: seq, + Data: []byte(fmt.Sprintf("icmpv4 echo id %d, seq %d", echoID, seq)), + }, + }, + } + require.NoError(t, router.Request(pk, &responder)) + responder.validate(t, pk) + } + }() + go func() { + defer wg.Done() + responder := echoFlowResponder{ + decoder: packet.NewICMPDecoder(), + respChan: make(chan []byte, 1), + } + for seq := 0; seq < endSeq; seq++ { + pk := &packet.ICMP{ + IP: &packet.IP{ + Src: localhostIPv6, + Dst: localhostIPv6, + Protocol: layers.IPProtocolICMPv6, + TTL: packet.DefaultTTL, + }, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, + Code: 0, + Body: &icmp.Echo{ + ID: echoID, + Seq: seq, + Data: []byte(fmt.Sprintf("icmpv6 echo id %d, seq %d", echoID, seq)), + }, + }, + } + require.NoError(t, router.Request(pk, &responder)) + responder.validate(t, pk) + } + }() + } + wg.Wait() + cancel() + <-proxyDone +} + // TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo func TestICMPRouterRejectNotEcho(t *testing.T) { msgs := []icmp.Message{ diff --git a/packet/funnel.go b/packet/funnel.go index 0cb1667a..f0124070 100644 --- a/packet/funnel.go +++ b/packet/funnel.go @@ -158,20 +158,19 @@ func (ft *FunnelTracker) Get(id FunnelID) (Funnel, bool) { } // Registers a funnel. It replaces the current funnel. -func (ft *FunnelTracker) Register(id FunnelID, funnel Funnel) (replaced bool) { +func (ft *FunnelTracker) GetOrRegister(id FunnelID, newFunnelFunc func() (Funnel, error)) (funnel Funnel, new bool, err error) { ft.lock.Lock() defer ft.lock.Unlock() currentFunnel, exists := ft.funnels[id] - if !exists { - ft.funnels[id] = funnel - return false + if exists { + return currentFunnel, false, nil } - replaced = !currentFunnel.Equal(funnel) - if replaced { - currentFunnel.Close() + newFunnel, err := newFunnelFunc() + if err != nil { + return nil, false, err } - ft.funnels[id] = funnel - return replaced + ft.funnels[id] = newFunnel + return newFunnel, true, nil } // Unregisters a funnel if the funnel equals to the current funnel From 013bdbd10c228aa38db09ecf843c7539fe5b38cf Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Fri, 2 Sep 2022 11:29:46 -0700 Subject: [PATCH 201/238] TUN-6718: Bump go and go-boring 1.18.6 --- Makefile | 2 +- cfsetup.yaml | 8 ++++---- dev.Dockerfile | 2 +- go.mod | 2 +- go.sum | 4 ++-- vendor/golang.org/x/net/bpf/vm_instructions.go | 4 ++-- vendor/golang.org/x/net/context/go17.go | 4 ++-- vendor/golang.org/x/net/http2/server.go | 3 +++ vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go | 1 + vendor/modules.txt | 2 +- 10 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index effcadba..f0c469b0 100644 --- a/Makefile +++ b/Makefile @@ -302,7 +302,7 @@ quic-deps: .PHONY: vet vet: - go vet -mod=vendor ./... + go vet -v -mod=vendor ./... .PHONY: goimports goimports: diff --git a/cfsetup.yaml b/cfsetup.yaml index 468ce636..a09e7f77 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -1,5 +1,5 @@ -pinned_go: &pinned_go go=1.18.5-1 -pinned_go_fips: &pinned_go_fips go-boring=1.18.5-2 +pinned_go: &pinned_go go=1.18.6-1 +pinned_go_fips: &pinned_go_fips go-boring=1.18.6-1 build_dir: &build_dir /cfsetup_build default-flavor: bullseye @@ -255,8 +255,8 @@ centos-7: pre-cache: - yum install -y fakeroot - yum upgrade -y binutils-2.27-44.base.el7.x86_64 - - wget https://go.dev/dl/go1.18.5.linux-amd64.tar.gz -P /tmp/ - - tar -C /usr/local -xzf /tmp/go1.18.5.linux-amd64.tar.gz + - wget https://go.dev/dl/go1.18.6.linux-amd64.tar.gz -P /tmp/ + - tar -C /usr/local -xzf /tmp/go1.18.6.linux-amd64.tar.gz post-cache: - export PATH=$PATH:/usr/local/go/bin - export GOOS=linux diff --git a/dev.Dockerfile b/dev.Dockerfile index f2e34bbb..d02e04bf 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18.1 as builder +FROM golang:1.18 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 WORKDIR /go/src/github.com/cloudflare/cloudflared/ diff --git a/go.mod b/go.mod index 02c0bf76..466994b2 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( go.opentelemetry.io/proto/otlp v0.15.0 go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.0.0-20220812174116-3211cb980234 + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 diff --git a/go.sum b/go.sum index 7f2012a8..8800a932 100644 --- a/go.sum +++ b/go.sum @@ -715,8 +715,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/vendor/golang.org/x/net/bpf/vm_instructions.go b/vendor/golang.org/x/net/bpf/vm_instructions.go index cf8947c3..0aa307c0 100644 --- a/vendor/golang.org/x/net/bpf/vm_instructions.go +++ b/vendor/golang.org/x/net/bpf/vm_instructions.go @@ -94,7 +94,7 @@ func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value u func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) { offset := int(ins.Off) - size := int(ins.Size) + size := ins.Size return loadCommon(in, offset, size) } @@ -121,7 +121,7 @@ func loadExtension(ins LoadExtension, in []byte) uint32 { func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) { offset := int(ins.Off) + int(regX) - size := int(ins.Size) + size := ins.Size return loadCommon(in, offset, size) } diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index 0a54bdbc..2cb9c408 100644 --- a/vendor/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -32,7 +32,7 @@ var DeadlineExceeded = context.DeadlineExceeded // call cancel as soon as the operations running in this Context complete. func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { ctx, f := context.WithCancel(parent) - return ctx, CancelFunc(f) + return ctx, f } // WithDeadline returns a copy of the parent context with the deadline adjusted @@ -46,7 +46,7 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { // call cancel as soon as the operations running in this Context complete. func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { ctx, f := context.WithDeadline(parent, deadline) - return ctx, CancelFunc(f) + return ctx, f } // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index aa3b0864..fd873b9a 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -1371,6 +1371,9 @@ func (sc *serverConn) startGracefulShutdownInternal() { func (sc *serverConn) goAway(code ErrCode) { sc.serveG.check() if sc.inGoAway { + if sc.goAwayCode == ErrCodeNo { + sc.goAwayCode = code + } return } sc.inGoAway = true diff --git a/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go index fc65e62f..eaa896cb 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go @@ -5,6 +5,7 @@ package socket import ( + "net" "syscall" "unsafe" ) diff --git a/vendor/modules.txt b/vendor/modules.txt index 0ae2b93a..edebf8e8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -367,7 +367,7 @@ golang.org/x/crypto/ssh/terminal ## explicit; go 1.12 golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.0.0-20220812174116-3211cb980234 +# golang.org/x/net v0.0.0-20220909164309-bea034e7d591 ## explicit; go 1.17 golang.org/x/net/bpf golang.org/x/net/context From de00396669b9af5bbec6c94b7de97dd76cacfce9 Mon Sep 17 00:00:00 2001 From: cthuang Date: Mon, 19 Sep 2022 12:36:25 +0100 Subject: [PATCH 202/238] TUN-6778: Cleanup logs about ICMP --- ingress/icmp_darwin.go | 2 +- supervisor/supervisor.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 78d0a87f..352b5c54 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -202,7 +202,7 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { // In unit test, we found out when the listener listens on 0.0.0.0, the socket reads the full packet after // the second reply if err := ip.handleFullPacket(icmpDecoder, buf[:n]); err != nil { - ip.logger.Err(err).Str("dst", from.String()).Msg("Failed to parse ICMP reply as full packet") + ip.logger.Debug().Err(err).Str("dst", from.String()).Msg("Failed to parse ICMP reply as full packet") } continue } diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index ebd0e449..252a532c 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net" "strings" "time" @@ -155,7 +156,11 @@ func (s *Supervisor) Run( if s.edgeTunnelServer.icmpRouter != nil { go func() { if err := s.edgeTunnelServer.icmpRouter.Serve(ctx); err != nil { - s.log.Logger().Err(err).Msg("icmp router terminated") + if errors.Is(err, net.ErrClosed) { + s.log.Logger().Info().Err(err).Msg("icmp router terminated") + } else { + s.log.Logger().Err(err).Msg("icmp router terminated") + } } }() } From a0b6ba9b8d0f724020156b02a303e442a6bc4d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 19 Sep 2022 12:34:26 +0100 Subject: [PATCH 203/238] TUN-6779: cloudflared should also use the root CAs from system pool to validate edge certificate --- tlsconfig/certreloader.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tlsconfig/certreloader.go b/tlsconfig/certreloader.go index 6394fbac..7c0514b0 100644 --- a/tlsconfig/certreloader.go +++ b/tlsconfig/certreloader.go @@ -131,7 +131,10 @@ func CreateTunnelConfig(c *cli.Context, serverName string) (*tls.Config, error) } if tlsConfig.RootCAs == nil { - rootCAPool := x509.NewCertPool() + rootCAPool, err := x509.SystemCertPool() + if err != nil { + return nil, errors.Wrap(err, "unable to get x509 system cert pool") + } cfRootCA, err := GetCloudflareRootCA() if err != nil { return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool") From b457cca1e59c464aac548a9fd852a19ab3ae45c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 19 Sep 2022 12:47:18 +0100 Subject: [PATCH 204/238] TUN-6780: Add support for certReload to also include support for client certificates --- tlsconfig/certreloader.go | 9 +++++++++ tlsconfig/tlsconfig.go | 24 +++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/tlsconfig/certreloader.go b/tlsconfig/certreloader.go index 7c0514b0..f2edce78 100644 --- a/tlsconfig/certreloader.go +++ b/tlsconfig/certreloader.go @@ -40,12 +40,21 @@ func NewCertReloader(certPath, keyPath string) (*CertReloader, error) { } // Cert returns the TLS certificate most recently read by the CertReloader. +// This method works as a direct utility method for tls.Config#Cert. func (cr *CertReloader) Cert(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { cr.Lock() defer cr.Unlock() return cr.certificate, nil } +// ClientCert returns the TLS certificate most recently read by the CertReloader. +// This method works as a direct utility method for tls.Config#ClientCert. +func (cr *CertReloader) ClientCert(certRequestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { + cr.Lock() + defer cr.Unlock() + return cr.certificate, nil +} + // LoadCert loads a TLS certificate from the CertReloader's specified filepath. // Call this after writing a new certificate to the disk (e.g. after renewing a certificate) func (cr *CertReloader) LoadCert() error { diff --git a/tlsconfig/tlsconfig.go b/tlsconfig/tlsconfig.go index fd255175..46490373 100644 --- a/tlsconfig/tlsconfig.go +++ b/tlsconfig/tlsconfig.go @@ -12,15 +12,16 @@ import ( // Config is the user provided parameters to create a tls.Config type TLSParameters struct { - Cert string - Key string - GetCertificate *CertReloader - ClientCAs []string - RootCAs []string - ServerName string - CurvePreferences []tls.CurveID - MinVersion uint16 // min tls version. If zero, TLS1.0 is defined as minimum. - MaxVersion uint16 // max tls version. If zero, last TLS version is used defined as limit (currently TLS1.3) + Cert string + Key string + GetCertificate *CertReloader + GetClientCertificate *CertReloader + ClientCAs []string + RootCAs []string + ServerName string + CurvePreferences []tls.CurveID + MinVersion uint16 // min tls version. If zero, TLS1.0 is defined as minimum. + MaxVersion uint16 // max tls version. If zero, last TLS version is used defined as limit (currently TLS1.3) } // GetConfig returns a TLS configuration according to the Config set by the user. @@ -43,6 +44,11 @@ func GetConfig(p *TLSParameters) (*tls.Config, error) { tlsconfig.GetCertificate = p.GetCertificate.Cert } + if p.GetClientCertificate != nil { + // GetClientCertificate is called when using an HTTP client library and mTLS is required. + tlsconfig.GetClientCertificate = p.GetClientCertificate.ClientCert + } + if len(p.ClientCAs) > 0 { // set of root certificate authorities that servers use if required to verify a client certificate // by the policy in ClientAuth From b1995b4dd16b9936134fa2c0207cef2f62d37256 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 20 Sep 2022 09:10:47 +0100 Subject: [PATCH 205/238] TUN-6777: Fix race condition in TestFunnelIdleTimeout --- ingress/origin_icmp_proxy_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index b961b7d4..6046c575 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -264,11 +264,14 @@ func testICMPRouterRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp. } type echoFlowResponder struct { + lock sync.Mutex decoder *packet.ICMPDecoder respChan chan []byte } func (efr *echoFlowResponder) SendPacket(dst netip.Addr, pk packet.RawPacket) error { + efr.lock.Lock() + defer efr.lock.Unlock() copiedPacket := make([]byte, len(pk.Data)) copy(copiedPacket, pk.Data) efr.respChan <- copiedPacket @@ -276,6 +279,8 @@ func (efr *echoFlowResponder) SendPacket(dst netip.Addr, pk packet.RawPacket) er } func (efr *echoFlowResponder) Close() error { + efr.lock.Lock() + defer efr.lock.Unlock() close(efr.respChan) return nil } From 3d345d37487c8fe141ffbb473ef50cc46b46f491 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 20 Sep 2022 09:01:35 +0100 Subject: [PATCH 206/238] TUN-6595: Enable datagramv2 and icmp proxy by default --- cmd/cloudflared/tunnel/configuration.go | 2 +- supervisor/supervisor.go | 26 ++++++------------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index f8930357..ef9a7e28 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -44,7 +44,7 @@ var ( LogFieldHostname = "hostname" secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag} - defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders} + defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders, supervisor.FeatureDatagramV2} configFlags = []string{"autoupdate-freq", "no-autoupdate", "retries", "protocol", "loglevel", "transport-loglevel", "origincert", "metrics", "metrics-update-freq", "edge-ip-version"} ) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 252a532c..04f5536e 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -116,14 +116,12 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato gracefulShutdownC: gracefulShutdownC, connAwareLogger: log, } - if useDatagramV2(config) { - // TODO: TUN-6701: Decouple upgrade of datagram v2 and using icmp proxy - icmpRouter, err := ingress.NewICMPRouter(config.Log) - if err != nil { - log.Logger().Warn().Err(err).Msg("Failed to create icmp router, will continue to use datagram v1") - } else { - edgeTunnelServer.icmpRouter = icmpRouter - } + + icmpRouter, err := ingress.NewICMPRouter(config.Log) + if err != nil { + log.Logger().Warn().Err(err).Msg("Failed to create icmp router, ICMP proxy feature is disabled") + } else { + edgeTunnelServer.icmpRouter = icmpRouter } useReconnectToken := false @@ -435,15 +433,3 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int) registrationOptions.NumPreviousAttempts = uint8(numPreviousAttempts) return rpcClient.Authenticate(ctx, s.config.ClassicTunnel, registrationOptions) } - -func useDatagramV2(config *TunnelConfig) bool { - if config.NamedTunnel == nil { - return false - } - for _, feature := range config.NamedTunnel.Client.Features { - if feature == FeatureDatagramV2 { - return true - } - } - return false -} From db4564e5b91d69279941651dd99b3f3c4740ee24 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 20 Sep 2022 15:14:31 +0100 Subject: [PATCH 207/238] TUN-6773: Add access based configuration to ingress.OriginRequestConfig This PR adds some access related configurations to OriginRequestConfig. This will eventually get validated to be part of Ingress.Rule. --- config/configuration.go | 13 +++++++++++++ ingress/config.go | 6 ++++++ ingress/rule_test.go | 8 ++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/config/configuration.go b/config/configuration.go index 5112d1ec..94e6467d 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -229,6 +229,19 @@ type OriginRequestConfig struct { IPRules []IngressIPRule `yaml:"ipRules" json:"ipRules,omitempty"` // Attempt to connect to origin with HTTP/2 Http2Origin *bool `yaml:"http2Origin" json:"http2Origin,omitempty"` + // Access holds all access related configs + Access *AccessConfig `yaml:"access" json:"access,omitempty"` +} + +type AccessConfig struct { + // Enabled when set to true will fail every request that does not arrive through an access authenticated endpoint. + Enabled bool + + // TeamName is the organization team name to get the public key certificates for. + TeamName string `yaml:"teamName" json:"teamName,omitempty"` + + // AudTag is the AudTag to verify access JWT against. + AudTag []string `yaml:"audTag" json:"audTag,omitempty"` } type IngressIPRule struct { diff --git a/ingress/config.go b/ingress/config.go index e6eed0bf..de094861 100644 --- a/ingress/config.go +++ b/ingress/config.go @@ -265,6 +265,9 @@ func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig { if c.Http2Origin != nil { out.Http2Origin = *c.Http2Origin } + if c.Access != nil { + out.Access = *c.Access + } return out } @@ -310,6 +313,9 @@ type OriginRequestConfig struct { IPRules []ipaccess.Rule `yaml:"ipRules" json:"ipRules"` // Attempt to connect to origin with HTTP/2 Http2Origin bool `yaml:"http2Origin" json:"http2Origin"` + + // Access holds all access related configs + Access config.AccessConfig `yaml:"access" json:"access,omitempty"` } func (defaults *OriginRequestConfig) setConnectTimeout(overrides config.OriginRequestConfig) { diff --git a/ingress/rule_test.go b/ingress/rule_test.go index 279d7911..d399b1be 100644 --- a/ingress/rule_test.go +++ b/ingress/rule_test.go @@ -182,25 +182,25 @@ func TestMarshalJSON(t *testing.T) { { name: "Nil", path: nil, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, want: true, }, { name: "Nil regex", path: &Regexp{Regexp: nil}, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, want: true, }, { name: "Empty", path: &Regexp{Regexp: regexp.MustCompile("")}, - expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, + expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, want: true, }, { name: "Basic", path: &Regexp{Regexp: regexp.MustCompile("/echo")}, - expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false}}`, + expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, want: true, }, } From b0f0741a9b0630a5b8b4e0030757b84e2b2d1233 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 15 Sep 2022 14:26:02 -0700 Subject: [PATCH 208/238] TUN-6590: Use Windows Teamcity agent to build binary --- .teamcity/package-windows.sh | 17 +++++++++++++++++ Makefile | 9 ++++++++- build-packages.sh | 9 --------- cfsetup.yaml | 31 +++++++++++++++++++++++-------- 4 files changed, 48 insertions(+), 18 deletions(-) create mode 100755 .teamcity/package-windows.sh diff --git a/.teamcity/package-windows.sh b/.teamcity/package-windows.sh new file mode 100755 index 00000000..9d92a21f --- /dev/null +++ b/.teamcity/package-windows.sh @@ -0,0 +1,17 @@ +VERSION=$(git describe --tags --always --match "[0-9][0-9][0-9][0-9].*.*") +echo $VERSION + +export TARGET_OS=windows +# This controls the directory the built artifacts go into +export ARTIFACT_DIR=built_artifacts/ +mkdir -p $ARTIFACT_DIR +windowsArchs=("amd64" "386") +for arch in ${windowsArchs[@]}; do + export TARGET_ARCH=$arch + # Copy exe into final directory + cp ./artifacts/cloudflared-windows-$arch.exe $ARTIFACT_DIR/cloudflared-windows-$arch.exe + cp ./artifacts/cloudflared-windows-$arch.exe ./cloudflared.exe + make cloudflared-msi + # Copy msi into final directory + mv cloudflared-$VERSION-$arch.msi $ARTIFACT_DIR/cloudflared-windows-$arch.msi +done \ No newline at end of file diff --git a/Makefile b/Makefile index f0c469b0..555d57e2 100644 --- a/Makefile +++ b/Makefile @@ -198,7 +198,7 @@ cloudflared-pkg: cloudflared cloudflared.1 $(call build_package,osxpkg) .PHONY: cloudflared-msi -cloudflared-msi: cloudflared +cloudflared-msi: wixl --define Version=$(VERSION) --define Path=$(EXECUTABLE_PATH) --output cloudflared-$(VERSION)-$(TARGET_ARCH).msi cloudflared.wxs .PHONY: cloudflared-darwin-amd64.tgz @@ -288,6 +288,13 @@ github-mac-upload: python3 github_release.py --path artifacts/cloudflared-darwin-amd64.tgz --release-version $(VERSION) --name cloudflared-darwin-amd64.tgz python3 github_release.py --path artifacts/cloudflared-amd64.pkg --release-version $(VERSION) --name cloudflared-amd64.pkg +.PHONY: github-windows-upload +github-windows-upload: + python3 github_release.py --path built_artifacts/cloudflared-windows-amd64.exe --release-version $(VERSION) --name cloudflared-windows-amd64.exe + python3 github_release.py --path built_artifacts/cloudflared-windows-amd64.msi --release-version $(VERSION) --name cloudflared-windows-amd64.msi + python3 github_release.py --path built_artifacts/cloudflared-windows-386.exe --release-version $(VERSION) --name cloudflared-windows-386.exe + python3 github_release.py --path built_artifacts/cloudflared-windows-386.msi --release-version $(VERSION) --name cloudflared-windows-386.msi + .PHONY: tunnelrpc-deps tunnelrpc-deps: which capnp # https://capnproto.org/install.html diff --git a/build-packages.sh b/build-packages.sh index 97c5b681..6ca0bcd8 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -7,15 +7,6 @@ export CGO_ENABLED=0 # This controls the directory the built artifacts go into export ARTIFACT_DIR=built_artifacts/ mkdir -p $ARTIFACT_DIR -windowsArchs=("amd64" "386") -export TARGET_OS=windows -for arch in ${windowsArchs[@]}; do - export TARGET_ARCH=$arch - make cloudflared-msi - mv ./cloudflared.exe $ARTIFACT_DIR/cloudflared-windows-$arch.exe - mv cloudflared-$VERSION-$arch.msi $ARTIFACT_DIR/cloudflared-windows-$arch.msi -done - linuxArchs=("386" "amd64" "arm" "armhf" "arm64") export TARGET_OS=linux diff --git a/cfsetup.yaml b/cfsetup.yaml index a09e7f77..585771b1 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -133,7 +133,7 @@ stretch: &stretch - make publish-deb github-release-macos-amd64: build_dir: *build_dir - builddeps: + builddeps: &build_pygithub - *pinned_go - build-essential - python3-dev @@ -145,6 +145,27 @@ stretch: &stretch - pip3 install pygithub==1.55 post-cache: - make github-mac-upload + github-release-windows: + build_dir: *build_dir + builddeps: + - *pinned_go + - build-essential + - python3-dev + - libffi-dev + - python3-setuptools + - python3-pip + - wget + # libmsi and libgcab are libraries the wixl binary depends on. + - libmsi-dev + - libgcab-dev + pre-cache: + - wget https://github.com/sudarshan-reddy/msitools/releases/download/v0.101b/wixl -P /usr/local/bin + - chmod a+x /usr/local/bin/wixl + - pip3 install pynacl==1.4.0 + - pip3 install pygithub==1.55 + post-cache: + - .teamcity/package-windows.sh + - make github-windows-upload test: build_dir: *build_dir builddeps: @@ -204,13 +225,7 @@ stretch: &stretch - .teamcity/update-homebrew-core.sh github-message-release: build_dir: *build_dir - builddeps: - - *pinned_go - - build-essential - - python3-dev - - libffi-dev - - python3-setuptools - - python3-pip + builddeps: *build_pygithub pre-cache: *install_pygithub post-cache: - make github-message From e9a2c85671d9aed46cb1b816fd02dd223804e440 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 21 Sep 2022 12:52:59 -0700 Subject: [PATCH 209/238] Release 2022.9.1 --- RELEASE_NOTES | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 7cbd9706..190cee88 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,25 @@ +2022.9.1 +- 2022-09-20 TUN-6777: Fix race condition in TestFunnelIdleTimeout +- 2022-09-20 TUN-6595: Enable datagramv2 and icmp proxy by default +- 2022-09-20 TUN-6773: Add access based configuration to ingress.OriginRequestConfig +- 2022-09-19 TUN-6778: Cleanup logs about ICMP +- 2022-09-19 TUN-6779: cloudflared should also use the root CAs from system pool to validate edge certificate +- 2022-09-19 TUN-6780: Add support for certReload to also include support for client certificates +- 2022-09-16 TUN-6767: Build ICMP proxy for Windows only when CGO is enabled +- 2022-09-15 TUN-6590: Use Windows Teamcity agent to build binary +- 2022-09-13 TUN-6592: Decrement TTL and return ICMP time exceed if it's 0 +- 2022-09-09 TUN-6749: Fix icmp_generic build +- 2022-09-09 TUN-6744: On posix platforms, assign unique echo ID per (src, dst, echo ID) +- 2022-09-08 TUN-6743: Support ICMPv6 echo on Windows +- 2022-09-08 TUN-6689: Utilize new RegisterUDPSession to begin tracing +- 2022-09-07 TUN-6688: Update RegisterUdpSession capnproto to include trace context +- 2022-09-06 TUN-6740: Detect no UDP packets allowed and fallback from QUIC in that case +- 2022-09-06 TUN-6654: Support ICMPv6 on Linux and Darwin +- 2022-09-02 TUN-6696: Refactor flow into funnel and close idle funnels +- 2022-09-02 TUN-6718: Bump go and go-boring 1.18.6 +- 2022-08-29 TUN-6531: Implement ICMP proxy for Windows using IcmpSendEcho +- 2022-08-24 RTG-1339 Support post-quantum hybrid key exchange + 2022.9.0 - 2022-09-05 TUN-6737: Fix datagramV2Type should be declared in its own block so it starts at 0 - 2022-09-01 TUN-6725: Fix testProxySSEAllData From de07da02cd744ddeeab7bf346898e4a40876cf40 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 21 Sep 2022 15:17:44 +0100 Subject: [PATCH 210/238] TUN-6772: Add a JWT Validator as an ingress verifier This adds a new verifier interface that can be attached to ingress.Rule. This would act as a middleware layer that gets executed at the start of proxy.ProxyHTTP. A jwt validator implementation for this verifier is also provided. The validator downloads the public key from the access teams endpoint and uses it to verify the JWT sent to cloudflared with the audtag (clientID) information provided in the config. --- go.mod | 11 +- go.sum | 90 ++- ingress/middleware/jwtvalidator.go | 66 +++ ingress/middleware/middleware.go | 10 + ingress/rule.go | 5 + vendor/github.com/coreos/go-oidc/v3/LICENSE | 202 +++++++ vendor/github.com/coreos/go-oidc/v3/NOTICE | 5 + .../github.com/coreos/go-oidc/v3/oidc/jose.go | 16 + .../github.com/coreos/go-oidc/v3/oidc/jwks.go | 248 ++++++++ .../github.com/coreos/go-oidc/v3/oidc/oidc.go | 522 ++++++++++++++++ .../coreos/go-oidc/v3/oidc/verify.go | 344 +++++++++++ .../golang/protobuf/jsonpb/decode.go | 524 ++++++++++++++++ .../golang/protobuf/jsonpb/encode.go | 559 ++++++++++++++++++ .../github.com/golang/protobuf/jsonpb/json.go | 69 +++ vendor/golang.org/x/oauth2/AUTHORS | 3 - vendor/golang.org/x/oauth2/CONTRIBUTORS | 3 - vendor/golang.org/x/sync/errgroup/errgroup.go | 72 ++- vendor/golang.org/x/xerrors/doc.go | 3 +- vendor/golang.org/x/xerrors/fmt.go | 3 + vendor/golang.org/x/xerrors/wrap.go | 6 + .../grpc/balancer/balancer.go | 3 +- .../grpc/balancer_conn_wrappers.go | 326 ++++++---- .../grpc/channelz/channelz.go | 36 ++ vendor/google.golang.org/grpc/clientconn.go | 346 +++++------ vendor/google.golang.org/grpc/dialoptions.go | 52 +- .../grpc/encoding/encoding.go | 2 +- .../balancer/gracefulswitch/gracefulswitch.go | 382 ++++++++++++ .../grpc/internal/binarylog/binarylog.go | 91 +-- .../grpc/internal/binarylog/env_config.go | 6 +- .../grpc/internal/binarylog/method_logger.go | 28 +- .../grpc/internal/channelz/funcs.go | 159 +++-- .../grpc/internal/channelz/id.go | 75 +++ .../grpc/internal/channelz/logging.go | 91 ++- .../grpc/internal/channelz/types.go | 23 +- .../grpc/internal/internal.go | 6 + .../grpc/internal/metadata/metadata.go | 46 ++ .../grpc/internal/pretty/pretty.go | 82 +++ .../grpc/internal/transport/controlbuf.go | 6 + .../grpc/internal/transport/http2_client.go | 31 +- .../grpc/internal/transport/http2_server.go | 93 +-- .../grpc/internal/transport/transport.go | 5 +- .../grpc/metadata/metadata.go | 8 +- .../google.golang.org/grpc/picker_wrapper.go | 8 +- vendor/google.golang.org/grpc/pickfirst.go | 128 ++-- .../grpc/resolver/resolver.go | 8 +- .../grpc/resolver_conn_wrapper.go | 23 +- vendor/google.golang.org/grpc/server.go | 81 ++- .../google.golang.org/grpc/service_config.go | 3 + vendor/google.golang.org/grpc/stream.go | 227 ++++--- vendor/google.golang.org/grpc/version.go | 2 +- vendor/modules.txt | 23 +- 51 files changed, 4371 insertions(+), 790 deletions(-) create mode 100644 ingress/middleware/jwtvalidator.go create mode 100644 ingress/middleware/middleware.go create mode 100644 vendor/github.com/coreos/go-oidc/v3/LICENSE create mode 100644 vendor/github.com/coreos/go-oidc/v3/NOTICE create mode 100644 vendor/github.com/coreos/go-oidc/v3/oidc/jose.go create mode 100644 vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go create mode 100644 vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go create mode 100644 vendor/github.com/coreos/go-oidc/v3/oidc/verify.go create mode 100644 vendor/github.com/golang/protobuf/jsonpb/decode.go create mode 100644 vendor/github.com/golang/protobuf/jsonpb/encode.go create mode 100644 vendor/github.com/golang/protobuf/jsonpb/json.go delete mode 100644 vendor/golang.org/x/oauth2/AUTHORS delete mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTORS create mode 100644 vendor/google.golang.org/grpc/channelz/channelz.go create mode 100644 vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go create mode 100644 vendor/google.golang.org/grpc/internal/channelz/id.go create mode 100644 vendor/google.golang.org/grpc/internal/pretty/pretty.go diff --git a/go.mod b/go.mod index 466994b2..bdd0d711 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/net v0.0.0-20220909164309-bea034e7d591 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/protobuf v1.28.0 @@ -56,6 +56,7 @@ require ( github.com/cheekybits/genny v1.0.0 // indirect github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 // indirect github.com/coredns/caddy v1.1.1 // indirect + github.com/coreos/go-oidc/v3 v3.4.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -89,13 +90,13 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect - google.golang.org/grpc v1.45.0 // indirect + google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 // indirect + google.golang.org/grpc v1.47.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 8800a932..1c5d35d9 100644 --- a/go.sum +++ b/go.sum @@ -28,14 +28,23 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -45,6 +54,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= @@ -133,11 +143,14 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/coredns v1.8.7 h1:wVMjAnyFnY7Mc18AFO+9qbGD6ODPtdVUIlzoWrHr3hk= github.com/coredns/coredns v1.8.7/go.mod h1:bFmbgEfeRz5aizL2VsQ5LRlsvJuXWkgG/MWG9zxqjVM= +github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g= +github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -171,6 +184,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= @@ -292,8 +306,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -325,14 +340,19 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -714,6 +734,12 @@ golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -734,8 +760,13 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -747,8 +778,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -819,9 +851,19 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs= golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -907,8 +949,11 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -942,7 +987,16 @@ google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.64.0/go.mod h1:931CdxA8Rm4t6zqTFGSsgwbAEZ2+GMYurbndwSimebM= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -997,6 +1051,7 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1016,8 +1071,27 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb h1:ZrsicilzPCS/Xr8qtBZZLpy4P9TYXAfl49ctG1/5tgw= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 h1:4SPz2GL2CXJt28MTF8V6Ap/9ZiVbQlJeGSd9qtA7DLs= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1052,8 +1126,12 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/ingress/middleware/jwtvalidator.go b/ingress/middleware/jwtvalidator.go new file mode 100644 index 00000000..8bdab7e1 --- /dev/null +++ b/ingress/middleware/jwtvalidator.go @@ -0,0 +1,66 @@ +package middleware + +import ( + "context" + "fmt" + "net/http" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/pkg/errors" +) + +const ( + headerKeyAccessJWTAssertion = "Cf-Access-Jwt-Assertion" +) + +var ( + ErrNoAccessToken = errors.New("no access token provided in request") + cloudflareAccessCertsURL = "https://%s.cloudflareaccess.com" +) + +// JWTValidator is an implementation of Verifier that validates access based JWT tokens. +type JWTValidator struct { + *oidc.IDTokenVerifier + audTags []string +} + +func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTValidator { + if certsURL == "" { + certsURL = fmt.Sprintf(cloudflareAccessCertsURL, teamName) + } + certsEndpoint := fmt.Sprintf("%s/cdn-cgi/access/certs", certsURL) + + config := &oidc.Config{ + SkipClientIDCheck: true, + } + + ctx := context.Background() + keySet := oidc.NewRemoteKeySet(ctx, certsEndpoint) + verifier := oidc.NewVerifier(certsURL, keySet, config) + return &JWTValidator{ + IDTokenVerifier: verifier, + } +} + +func (v *JWTValidator) Handle(ctx context.Context, headers http.Header) error { + accessJWT := headers.Get(headerKeyAccessJWTAssertion) + if accessJWT == "" { + return ErrNoAccessToken + } + + token, err := v.IDTokenVerifier.Verify(ctx, accessJWT) + if err != nil { + return fmt.Errorf("Invalid token: %w", err) + } + + // We want atleast one audTag to match + for _, jwtAudTag := range token.Audience { + for _, acceptedAudTag := range v.audTags { + if acceptedAudTag == jwtAudTag { + return nil + } + } + } + + return fmt.Errorf("Invalid token: %w", err) +} diff --git a/ingress/middleware/middleware.go b/ingress/middleware/middleware.go new file mode 100644 index 00000000..7888dc31 --- /dev/null +++ b/ingress/middleware/middleware.go @@ -0,0 +1,10 @@ +package middleware + +import ( + "context" + "net/http" +) + +type Handler interface { + Handle(ctx context.Context, r *http.Request) error +} diff --git a/ingress/rule.go b/ingress/rule.go index 08499919..b3dd0001 100644 --- a/ingress/rule.go +++ b/ingress/rule.go @@ -20,6 +20,11 @@ type Rule struct { // address. Service OriginService `json:"service"` + // Handlers is a list of functions that acts as a middleware during ProxyHTTP + // TODO TUN-6774: Uncomment when we parse ingress to this. This serves as a demonstration on how + // we want to plug in Verifiers. + // Handlers []middleware.Handler + // Configure the request cloudflared sends to this specific origin. Config OriginRequestConfig `json:"originRequest"` } diff --git a/vendor/github.com/coreos/go-oidc/v3/LICENSE b/vendor/github.com/coreos/go-oidc/v3/LICENSE new file mode 100644 index 00000000..e06d2081 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/LICENSE @@ -0,0 +1,202 @@ +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/coreos/go-oidc/v3/NOTICE b/vendor/github.com/coreos/go-oidc/v3/NOTICE new file mode 100644 index 00000000..b39ddfa5 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2014 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go new file mode 100644 index 00000000..8afa895c --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go @@ -0,0 +1,16 @@ +package oidc + +// JOSE asymmetric signing algorithm values as defined by RFC 7518 +// +// see: https://tools.ietf.org/html/rfc7518#section-3.1 +const ( + RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 + RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 + RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 + ES256 = "ES256" // ECDSA using P-256 and SHA-256 + ES384 = "ES384" // ECDSA using P-384 and SHA-384 + ES512 = "ES512" // ECDSA using P-521 and SHA-512 + PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 + PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 + PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 +) diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go new file mode 100644 index 00000000..fdcfba81 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go @@ -0,0 +1,248 @@ +package oidc + +import ( + "context" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "errors" + "fmt" + "io/ioutil" + "net/http" + "sync" + "time" + + jose "gopkg.in/square/go-jose.v2" +) + +// StaticKeySet is a verifier that validates JWT against a static set of public keys. +type StaticKeySet struct { + // PublicKeys used to verify the JWT. Supported types are *rsa.PublicKey and + // *ecdsa.PublicKey. + PublicKeys []crypto.PublicKey +} + +// VerifySignature compares the signature against a static set of public keys. +func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { + jws, err := jose.ParseSigned(jwt) + if err != nil { + return nil, fmt.Errorf("parsing jwt: %v", err) + } + for _, pub := range s.PublicKeys { + switch pub.(type) { + case *rsa.PublicKey: + case *ecdsa.PublicKey: + default: + return nil, fmt.Errorf("invalid public key type provided: %T", pub) + } + payload, err := jws.Verify(pub) + if err != nil { + continue + } + return payload, nil + } + return nil, fmt.Errorf("no public keys able to verify jwt") +} + +// NewRemoteKeySet returns a KeySet that can validate JSON web tokens by using HTTP +// GETs to fetch JSON web token sets hosted at a remote URL. This is automatically +// used by NewProvider using the URLs returned by OpenID Connect discovery, but is +// exposed for providers that don't support discovery or to prevent round trips to the +// discovery URL. +// +// The returned KeySet is a long lived verifier that caches keys based on any +// keys change. Reuse a common remote key set instead of creating new ones as needed. +func NewRemoteKeySet(ctx context.Context, jwksURL string) *RemoteKeySet { + return newRemoteKeySet(ctx, jwksURL, time.Now) +} + +func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *RemoteKeySet { + if now == nil { + now = time.Now + } + return &RemoteKeySet{jwksURL: jwksURL, ctx: cloneContext(ctx), now: now} +} + +// RemoteKeySet is a KeySet implementation that validates JSON web tokens against +// a jwks_uri endpoint. +type RemoteKeySet struct { + jwksURL string + ctx context.Context + now func() time.Time + + // guard all other fields + mu sync.RWMutex + + // inflight suppresses parallel execution of updateKeys and allows + // multiple goroutines to wait for its result. + inflight *inflight + + // A set of cached keys. + cachedKeys []jose.JSONWebKey +} + +// inflight is used to wait on some in-flight request from multiple goroutines. +type inflight struct { + doneCh chan struct{} + + keys []jose.JSONWebKey + err error +} + +func newInflight() *inflight { + return &inflight{doneCh: make(chan struct{})} +} + +// wait returns a channel that multiple goroutines can receive on. Once it returns +// a value, the inflight request is done and result() can be inspected. +func (i *inflight) wait() <-chan struct{} { + return i.doneCh +} + +// done can only be called by a single goroutine. It records the result of the +// inflight request and signals other goroutines that the result is safe to +// inspect. +func (i *inflight) done(keys []jose.JSONWebKey, err error) { + i.keys = keys + i.err = err + close(i.doneCh) +} + +// result cannot be called until the wait() channel has returned a value. +func (i *inflight) result() ([]jose.JSONWebKey, error) { + return i.keys, i.err +} + +// paresdJWTKey is a context key that allows common setups to avoid parsing the +// JWT twice. It holds a *jose.JSONWebSignature value. +var parsedJWTKey contextKey + +// VerifySignature validates a payload against a signature from the jwks_uri. +// +// Users MUST NOT call this method directly and should use an IDTokenVerifier +// instead. This method skips critical validations such as 'alg' values and is +// only exported to implement the KeySet interface. +func (r *RemoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { + jws, ok := ctx.Value(parsedJWTKey).(*jose.JSONWebSignature) + if !ok { + var err error + jws, err = jose.ParseSigned(jwt) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt: %v", err) + } + } + return r.verify(ctx, jws) +} + +func (r *RemoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { + // We don't support JWTs signed with multiple signatures. + keyID := "" + for _, sig := range jws.Signatures { + keyID = sig.Header.KeyID + break + } + + keys := r.keysFromCache() + for _, key := range keys { + if keyID == "" || key.KeyID == keyID { + if payload, err := jws.Verify(&key); err == nil { + return payload, nil + } + } + } + + // If the kid doesn't match, check for new keys from the remote. This is the + // strategy recommended by the spec. + // + // https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys + keys, err := r.keysFromRemote(ctx) + if err != nil { + return nil, fmt.Errorf("fetching keys %v", err) + } + + for _, key := range keys { + if keyID == "" || key.KeyID == keyID { + if payload, err := jws.Verify(&key); err == nil { + return payload, nil + } + } + } + return nil, errors.New("failed to verify id token signature") +} + +func (r *RemoteKeySet) keysFromCache() (keys []jose.JSONWebKey) { + r.mu.RLock() + defer r.mu.RUnlock() + return r.cachedKeys +} + +// keysFromRemote syncs the key set from the remote set, records the values in the +// cache, and returns the key set. +func (r *RemoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) { + // Need to lock to inspect the inflight request field. + r.mu.Lock() + // If there's not a current inflight request, create one. + if r.inflight == nil { + r.inflight = newInflight() + + // This goroutine has exclusive ownership over the current inflight + // request. It releases the resource by nil'ing the inflight field + // once the goroutine is done. + go func() { + // Sync keys and finish inflight when that's done. + keys, err := r.updateKeys() + + r.inflight.done(keys, err) + + // Lock to update the keys and indicate that there is no longer an + // inflight request. + r.mu.Lock() + defer r.mu.Unlock() + + if err == nil { + r.cachedKeys = keys + } + + // Free inflight so a different request can run. + r.inflight = nil + }() + } + inflight := r.inflight + r.mu.Unlock() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-inflight.wait(): + return inflight.result() + } +} + +func (r *RemoteKeySet) updateKeys() ([]jose.JSONWebKey, error) { + req, err := http.NewRequest("GET", r.jwksURL, nil) + if err != nil { + return nil, fmt.Errorf("oidc: can't create request: %v", err) + } + + resp, err := doRequest(r.ctx, req) + if err != nil { + return nil, fmt.Errorf("oidc: get keys failed %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("unable to read response body: %v", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body) + } + + var keySet jose.JSONWebKeySet + err = unmarshalResp(resp, body, &keySet) + if err != nil { + return nil, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body) + } + return keySet.Keys, nil +} diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go new file mode 100644 index 00000000..ae73eb02 --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go @@ -0,0 +1,522 @@ +// Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package. +package oidc + +import ( + "context" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "hash" + "io/ioutil" + "mime" + "net/http" + "strings" + "time" + + "golang.org/x/oauth2" +) + +const ( + // ScopeOpenID is the mandatory scope for all OpenID Connect OAuth2 requests. + ScopeOpenID = "openid" + + // ScopeOfflineAccess is an optional scope defined by OpenID Connect for requesting + // OAuth2 refresh tokens. + // + // Support for this scope differs between OpenID Connect providers. For instance + // Google rejects it, favoring appending "access_type=offline" as part of the + // authorization request instead. + // + // See: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess + ScopeOfflineAccess = "offline_access" +) + +var ( + errNoAtHash = errors.New("id token did not have an access token hash") + errInvalidAtHash = errors.New("access token hash does not match value in ID token") +) + +type contextKey int + +var issuerURLKey contextKey + +// ClientContext returns a new Context that carries the provided HTTP client. +// +// This method sets the same context key used by the golang.org/x/oauth2 package, +// so the returned context works for that package too. +// +// myClient := &http.Client{} +// ctx := oidc.ClientContext(parentContext, myClient) +// +// // This will use the custom client +// provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") +// +func ClientContext(ctx context.Context, client *http.Client) context.Context { + return context.WithValue(ctx, oauth2.HTTPClient, client) +} + +// cloneContext copies a context's bag-of-values into a new context that isn't +// associated with its cancellation. This is used to initialize remote keys sets +// which run in the background and aren't associated with the initial context. +func cloneContext(ctx context.Context) context.Context { + cp := context.Background() + if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { + cp = ClientContext(cp, c) + } + return cp +} + +// InsecureIssuerURLContext allows discovery to work when the issuer_url reported +// by upstream is mismatched with the discovery URL. This is meant for integration +// with off-spec providers such as Azure. +// +// discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0" +// issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0" +// +// ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL) +// +// // Provider will be discovered with the discoveryBaseURL, but use issuerURL +// // for future issuer validation. +// provider, err := oidc.NewProvider(ctx, discoveryBaseURL) +// +// This is insecure because validating the correct issuer is critical for multi-tenant +// proivders. Any overrides here MUST be carefully reviewed. +func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Context { + return context.WithValue(ctx, issuerURLKey, issuerURL) +} + +func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { + client := http.DefaultClient + if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { + client = c + } + return client.Do(req.WithContext(ctx)) +} + +// Provider represents an OpenID Connect server's configuration. +type Provider struct { + issuer string + authURL string + tokenURL string + userInfoURL string + algorithms []string + + // Raw claims returned by the server. + rawClaims []byte + + remoteKeySet KeySet +} + +type providerJSON struct { + Issuer string `json:"issuer"` + AuthURL string `json:"authorization_endpoint"` + TokenURL string `json:"token_endpoint"` + JWKSURL string `json:"jwks_uri"` + UserInfoURL string `json:"userinfo_endpoint"` + Algorithms []string `json:"id_token_signing_alg_values_supported"` +} + +// supportedAlgorithms is a list of algorithms explicitly supported by this +// package. If a provider supports other algorithms, such as HS256 or none, +// those values won't be passed to the IDTokenVerifier. +var supportedAlgorithms = map[string]bool{ + RS256: true, + RS384: true, + RS512: true, + ES256: true, + ES384: true, + ES512: true, + PS256: true, + PS384: true, + PS512: true, +} + +// ProviderConfig allows creating providers when discovery isn't supported. It's +// generally easier to use NewProvider directly. +type ProviderConfig struct { + // IssuerURL is the identity of the provider, and the string it uses to sign + // ID tokens with. For example "https://accounts.google.com". This value MUST + // match ID tokens exactly. + IssuerURL string + // AuthURL is the endpoint used by the provider to support the OAuth 2.0 + // authorization endpoint. + AuthURL string + // TokenURL is the endpoint used by the provider to support the OAuth 2.0 + // token endpoint. + TokenURL string + // UserInfoURL is the endpoint used by the provider to support the OpenID + // Connect UserInfo flow. + // + // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo + UserInfoURL string + // JWKSURL is the endpoint used by the provider to advertise public keys to + // verify issued ID tokens. This endpoint is polled as new keys are made + // available. + JWKSURL string + + // Algorithms, if provided, indicate a list of JWT algorithms allowed to sign + // ID tokens. If not provided, this defaults to the algorithms advertised by + // the JWK endpoint, then the set of algorithms supported by this package. + Algorithms []string +} + +// NewProvider initializes a provider from a set of endpoints, rather than +// through discovery. +func (p *ProviderConfig) NewProvider(ctx context.Context) *Provider { + return &Provider{ + issuer: p.IssuerURL, + authURL: p.AuthURL, + tokenURL: p.TokenURL, + userInfoURL: p.UserInfoURL, + algorithms: p.Algorithms, + remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), + } +} + +// NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. +// +// The issuer is the URL identifier for the service. For example: "https://accounts.google.com" +// or "https://login.salesforce.com". +func NewProvider(ctx context.Context, issuer string) (*Provider, error) { + wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" + req, err := http.NewRequest("GET", wellKnown, nil) + if err != nil { + return nil, err + } + resp, err := doRequest(ctx, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("unable to read response body: %v", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%s: %s", resp.Status, body) + } + + var p providerJSON + err = unmarshalResp(resp, body, &p) + if err != nil { + return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) + } + + issuerURL, skipIssuerValidation := ctx.Value(issuerURLKey).(string) + if !skipIssuerValidation { + issuerURL = issuer + } + if p.Issuer != issuerURL && !skipIssuerValidation { + return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) + } + var algs []string + for _, a := range p.Algorithms { + if supportedAlgorithms[a] { + algs = append(algs, a) + } + } + return &Provider{ + issuer: issuerURL, + authURL: p.AuthURL, + tokenURL: p.TokenURL, + userInfoURL: p.UserInfoURL, + algorithms: algs, + rawClaims: body, + remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), + }, nil +} + +// Claims unmarshals raw fields returned by the server during discovery. +// +// var claims struct { +// ScopesSupported []string `json:"scopes_supported"` +// ClaimsSupported []string `json:"claims_supported"` +// } +// +// if err := provider.Claims(&claims); err != nil { +// // handle unmarshaling error +// } +// +// For a list of fields defined by the OpenID Connect spec see: +// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata +func (p *Provider) Claims(v interface{}) error { + if p.rawClaims == nil { + return errors.New("oidc: claims not set") + } + return json.Unmarshal(p.rawClaims, v) +} + +// Endpoint returns the OAuth2 auth and token endpoints for the given provider. +func (p *Provider) Endpoint() oauth2.Endpoint { + return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL} +} + +// UserInfo represents the OpenID Connect userinfo claims. +type UserInfo struct { + Subject string `json:"sub"` + Profile string `json:"profile"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + + claims []byte +} + +type userInfoRaw struct { + Subject string `json:"sub"` + Profile string `json:"profile"` + Email string `json:"email"` + // Handle providers that return email_verified as a string + // https://forums.aws.amazon.com/thread.jspa?messageID=949441󧳁 and + // https://discuss.elastic.co/t/openid-error-after-authenticating-against-aws-cognito/206018/11 + EmailVerified stringAsBool `json:"email_verified"` +} + +// Claims unmarshals the raw JSON object claims into the provided object. +func (u *UserInfo) Claims(v interface{}) error { + if u.claims == nil { + return errors.New("oidc: claims not set") + } + return json.Unmarshal(u.claims, v) +} + +// UserInfo uses the token source to query the provider's user info endpoint. +func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error) { + if p.userInfoURL == "" { + return nil, errors.New("oidc: user info endpoint is not supported by this provider") + } + + req, err := http.NewRequest("GET", p.userInfoURL, nil) + if err != nil { + return nil, fmt.Errorf("oidc: create GET request: %v", err) + } + + token, err := tokenSource.Token() + if err != nil { + return nil, fmt.Errorf("oidc: get access token: %v", err) + } + token.SetAuthHeader(req) + + resp, err := doRequest(ctx, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%s: %s", resp.Status, body) + } + + ct := resp.Header.Get("Content-Type") + mediaType, _, parseErr := mime.ParseMediaType(ct) + if parseErr == nil && mediaType == "application/jwt" { + payload, err := p.remoteKeySet.VerifySignature(ctx, string(body)) + if err != nil { + return nil, fmt.Errorf("oidc: invalid userinfo jwt signature %v", err) + } + body = payload + } + + var userInfo userInfoRaw + if err := json.Unmarshal(body, &userInfo); err != nil { + return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err) + } + return &UserInfo{ + Subject: userInfo.Subject, + Profile: userInfo.Profile, + Email: userInfo.Email, + EmailVerified: bool(userInfo.EmailVerified), + claims: body, + }, nil +} + +// IDToken is an OpenID Connect extension that provides a predictable representation +// of an authorization event. +// +// The ID Token only holds fields OpenID Connect requires. To access additional +// claims returned by the server, use the Claims method. +type IDToken struct { + // The URL of the server which issued this token. OpenID Connect + // requires this value always be identical to the URL used for + // initial discovery. + // + // Note: Because of a known issue with Google Accounts' implementation + // this value may differ when using Google. + // + // See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo + Issuer string + + // The client ID, or set of client IDs, that this token is issued for. For + // common uses, this is the client that initialized the auth flow. + // + // This package ensures the audience contains an expected value. + Audience []string + + // A unique string which identifies the end user. + Subject string + + // Expiry of the token. Ths package will not process tokens that have + // expired unless that validation is explicitly turned off. + Expiry time.Time + // When the token was issued by the provider. + IssuedAt time.Time + + // Initial nonce provided during the authentication redirect. + // + // This package does NOT provided verification on the value of this field + // and it's the user's responsibility to ensure it contains a valid value. + Nonce string + + // at_hash claim, if set in the ID token. Callers can verify an access token + // that corresponds to the ID token using the VerifyAccessToken method. + AccessTokenHash string + + // signature algorithm used for ID token, needed to compute a verification hash of an + // access token + sigAlgorithm string + + // Raw payload of the id_token. + claims []byte + + // Map of distributed claim names to claim sources + distributedClaims map[string]claimSource +} + +// Claims unmarshals the raw JSON payload of the ID Token into a provided struct. +// +// idToken, err := idTokenVerifier.Verify(rawIDToken) +// if err != nil { +// // handle error +// } +// var claims struct { +// Email string `json:"email"` +// EmailVerified bool `json:"email_verified"` +// } +// if err := idToken.Claims(&claims); err != nil { +// // handle error +// } +// +func (i *IDToken) Claims(v interface{}) error { + if i.claims == nil { + return errors.New("oidc: claims not set") + } + return json.Unmarshal(i.claims, v) +} + +// VerifyAccessToken verifies that the hash of the access token that corresponds to the iD token +// matches the hash in the id token. It returns an error if the hashes don't match. +// It is the caller's responsibility to ensure that the optional access token hash is present for the ID token +// before calling this method. See https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken +func (i *IDToken) VerifyAccessToken(accessToken string) error { + if i.AccessTokenHash == "" { + return errNoAtHash + } + var h hash.Hash + switch i.sigAlgorithm { + case RS256, ES256, PS256: + h = sha256.New() + case RS384, ES384, PS384: + h = sha512.New384() + case RS512, ES512, PS512: + h = sha512.New() + default: + return fmt.Errorf("oidc: unsupported signing algorithm %q", i.sigAlgorithm) + } + h.Write([]byte(accessToken)) // hash documents that Write will never return an error + sum := h.Sum(nil)[:h.Size()/2] + actual := base64.RawURLEncoding.EncodeToString(sum) + if actual != i.AccessTokenHash { + return errInvalidAtHash + } + return nil +} + +type idToken struct { + Issuer string `json:"iss"` + Subject string `json:"sub"` + Audience audience `json:"aud"` + Expiry jsonTime `json:"exp"` + IssuedAt jsonTime `json:"iat"` + NotBefore *jsonTime `json:"nbf"` + Nonce string `json:"nonce"` + AtHash string `json:"at_hash"` + ClaimNames map[string]string `json:"_claim_names"` + ClaimSources map[string]claimSource `json:"_claim_sources"` +} + +type claimSource struct { + Endpoint string `json:"endpoint"` + AccessToken string `json:"access_token"` +} + +type stringAsBool bool + +func (sb *stringAsBool) UnmarshalJSON(b []byte) error { + switch string(b) { + case "true", `"true"`: + *sb = true + case "false", `"false"`: + *sb = false + default: + return errors.New("invalid value for boolean") + } + return nil +} + +type audience []string + +func (a *audience) UnmarshalJSON(b []byte) error { + var s string + if json.Unmarshal(b, &s) == nil { + *a = audience{s} + return nil + } + var auds []string + if err := json.Unmarshal(b, &auds); err != nil { + return err + } + *a = auds + return nil +} + +type jsonTime time.Time + +func (j *jsonTime) UnmarshalJSON(b []byte) error { + var n json.Number + if err := json.Unmarshal(b, &n); err != nil { + return err + } + var unix int64 + + if t, err := n.Int64(); err == nil { + unix = t + } else { + f, err := n.Float64() + if err != nil { + return err + } + unix = int64(f) + } + *j = jsonTime(time.Unix(unix, 0)) + return nil +} + +func unmarshalResp(r *http.Response, body []byte, v interface{}) error { + err := json.Unmarshal(body, &v) + if err == nil { + return nil + } + ct := r.Header.Get("Content-Type") + mediaType, _, parseErr := mime.ParseMediaType(ct) + if parseErr == nil && mediaType == "application/json" { + return fmt.Errorf("got Content-Type = application/json, but could not unmarshal as JSON: %v", err) + } + return fmt.Errorf("expected Content-Type = application/json, got %q: %v", ct, err) +} diff --git a/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go new file mode 100644 index 00000000..b0dd60fa --- /dev/null +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go @@ -0,0 +1,344 @@ +package oidc + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + + "golang.org/x/oauth2" + jose "gopkg.in/square/go-jose.v2" +) + +const ( + issuerGoogleAccounts = "https://accounts.google.com" + issuerGoogleAccountsNoScheme = "accounts.google.com" +) + +// TokenExpiredError indicates that Verify failed because the token was expired. This +// error does NOT indicate that the token is not also invalid for other reasons. Other +// checks might have failed if the expiration check had not failed. +type TokenExpiredError struct { + // Expiry is the time when the token expired. + Expiry time.Time +} + +func (e *TokenExpiredError) Error() string { + return fmt.Sprintf("oidc: token is expired (Token Expiry: %v)", e.Expiry) +} + +// KeySet is a set of publc JSON Web Keys that can be used to validate the signature +// of JSON web tokens. This is expected to be backed by a remote key set through +// provider metadata discovery or an in-memory set of keys delivered out-of-band. +type KeySet interface { + // VerifySignature parses the JSON web token, verifies the signature, and returns + // the raw payload. Header and claim fields are validated by other parts of the + // package. For example, the KeySet does not need to check values such as signature + // algorithm, issuer, and audience since the IDTokenVerifier validates these values + // independently. + // + // If VerifySignature makes HTTP requests to verify the token, it's expected to + // use any HTTP client associated with the context through ClientContext. + VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) +} + +// IDTokenVerifier provides verification for ID Tokens. +type IDTokenVerifier struct { + keySet KeySet + config *Config + issuer string +} + +// NewVerifier returns a verifier manually constructed from a key set and issuer URL. +// +// It's easier to use provider discovery to construct an IDTokenVerifier than creating +// one directly. This method is intended to be used with provider that don't support +// metadata discovery, or avoiding round trips when the key set URL is already known. +// +// This constructor can be used to create a verifier directly using the issuer URL and +// JSON Web Key Set URL without using discovery: +// +// keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs") +// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config) +// +// Or a static key set (e.g. for testing): +// +// keySet := &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{pub1, pub2}} +// verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config) +// +func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier { + return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL} +} + +// Config is the configuration for an IDTokenVerifier. +type Config struct { + // Expected audience of the token. For a majority of the cases this is expected to be + // the ID of the client that initialized the login flow. It may occasionally differ if + // the provider supports the authorizing party (azp) claim. + // + // If not provided, users must explicitly set SkipClientIDCheck. + ClientID string + // If specified, only this set of algorithms may be used to sign the JWT. + // + // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this + // defaults to the set of algorithms the provider supports. Otherwise this values + // defaults to RS256. + SupportedSigningAlgs []string + + // If true, no ClientID check performed. Must be true if ClientID field is empty. + SkipClientIDCheck bool + // If true, token expiry is not checked. + SkipExpiryCheck bool + + // SkipIssuerCheck is intended for specialized cases where the the caller wishes to + // defer issuer validation. When enabled, callers MUST independently verify the Token's + // Issuer is a known good value. + // + // Mismatched issuers often indicate client mis-configuration. If mismatches are + // unexpected, evaluate if the provided issuer URL is incorrect instead of enabling + // this option. + SkipIssuerCheck bool + + // Time function to check Token expiry. Defaults to time.Now + Now func() time.Time + + // InsecureSkipSignatureCheck causes this package to skip JWT signature validation. + // It's intended for special cases where providers (such as Azure), use the "none" + // algorithm. + // + // This option can only be enabled safely when the ID Token is received directly + // from the provider after the token exchange. + // + // This option MUST NOT be used when receiving an ID Token from sources other + // than the token endpoint. + InsecureSkipSignatureCheck bool +} + +// Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs. +func (p *Provider) Verifier(config *Config) *IDTokenVerifier { + if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { + // Make a copy so we don't modify the config values. + cp := &Config{} + *cp = *config + cp.SupportedSigningAlgs = p.algorithms + config = cp + } + return NewVerifier(p.issuer, p.remoteKeySet, config) +} + +func parseJWT(p string) ([]byte, error) { + parts := strings.Split(p, ".") + if len(parts) < 2 { + return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) + } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) + } + return payload, nil +} + +func contains(sli []string, ele string) bool { + for _, s := range sli { + if s == ele { + return true + } + } + return false +} + +// Returns the Claims from the distributed JWT token +func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src claimSource) ([]byte, error) { + req, err := http.NewRequest("GET", src.Endpoint, nil) + if err != nil { + return nil, fmt.Errorf("malformed request: %v", err) + } + if src.AccessToken != "" { + req.Header.Set("Authorization", "Bearer "+src.AccessToken) + } + + resp, err := doRequest(ctx, req) + if err != nil { + return nil, fmt.Errorf("oidc: Request to endpoint failed: %v", err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("unable to read response body: %v", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("oidc: request failed: %v", resp.StatusCode) + } + + token, err := verifier.Verify(ctx, string(body)) + if err != nil { + return nil, fmt.Errorf("malformed response body: %v", err) + } + + return token.claims, nil +} + +// Verify parses a raw ID Token, verifies it's been signed by the provider, performs +// any additional checks depending on the Config, and returns the payload. +// +// Verify does NOT do nonce validation, which is the callers responsibility. +// +// See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation +// +// oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) +// if err != nil { +// // handle error +// } +// +// // Extract the ID Token from oauth2 token. +// rawIDToken, ok := oauth2Token.Extra("id_token").(string) +// if !ok { +// // handle error +// } +// +// token, err := verifier.Verify(ctx, rawIDToken) +// +func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) { + // Throw out tokens with invalid claims before trying to verify the token. This lets + // us do cheap checks before possibly re-syncing keys. + payload, err := parseJWT(rawIDToken) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt: %v", err) + } + var token idToken + if err := json.Unmarshal(payload, &token); err != nil { + return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err) + } + + distributedClaims := make(map[string]claimSource) + + //step through the token to map claim names to claim sources" + for cn, src := range token.ClaimNames { + if src == "" { + return nil, fmt.Errorf("oidc: failed to obtain source from claim name") + } + s, ok := token.ClaimSources[src] + if !ok { + return nil, fmt.Errorf("oidc: source does not exist") + } + distributedClaims[cn] = s + } + + t := &IDToken{ + Issuer: token.Issuer, + Subject: token.Subject, + Audience: []string(token.Audience), + Expiry: time.Time(token.Expiry), + IssuedAt: time.Time(token.IssuedAt), + Nonce: token.Nonce, + AccessTokenHash: token.AtHash, + claims: payload, + distributedClaims: distributedClaims, + } + + // Check issuer. + if !v.config.SkipIssuerCheck && t.Issuer != v.issuer { + // Google sometimes returns "accounts.google.com" as the issuer claim instead of + // the required "https://accounts.google.com". Detect this case and allow it only + // for Google. + // + // We will not add hooks to let other providers go off spec like this. + if !(v.issuer == issuerGoogleAccounts && t.Issuer == issuerGoogleAccountsNoScheme) { + return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.issuer, t.Issuer) + } + } + + // If a client ID has been provided, make sure it's part of the audience. SkipClientIDCheck must be true if ClientID is empty. + // + // This check DOES NOT ensure that the ClientID is the party to which the ID Token was issued (i.e. Authorized party). + if !v.config.SkipClientIDCheck { + if v.config.ClientID != "" { + if !contains(t.Audience, v.config.ClientID) { + return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.ClientID, t.Audience) + } + } else { + return nil, fmt.Errorf("oidc: invalid configuration, clientID must be provided or SkipClientIDCheck must be set") + } + } + + // If a SkipExpiryCheck is false, make sure token is not expired. + if !v.config.SkipExpiryCheck { + now := time.Now + if v.config.Now != nil { + now = v.config.Now + } + nowTime := now() + + if t.Expiry.Before(nowTime) { + return nil, &TokenExpiredError{Expiry: t.Expiry} + } + + // If nbf claim is provided in token, ensure that it is indeed in the past. + if token.NotBefore != nil { + nbfTime := time.Time(*token.NotBefore) + // Set to 5 minutes since this is what other OpenID Connect providers do to deal with clock skew. + // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.12.2/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs#L149-L153 + leeway := 5 * time.Minute + + if nowTime.Add(leeway).Before(nbfTime) { + return nil, fmt.Errorf("oidc: current time %v before the nbf (not before) time: %v", nowTime, nbfTime) + } + } + } + + if v.config.InsecureSkipSignatureCheck { + return t, nil + } + + jws, err := jose.ParseSigned(rawIDToken) + if err != nil { + return nil, fmt.Errorf("oidc: malformed jwt: %v", err) + } + + switch len(jws.Signatures) { + case 0: + return nil, fmt.Errorf("oidc: id token not signed") + case 1: + default: + return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") + } + + sig := jws.Signatures[0] + supportedSigAlgs := v.config.SupportedSigningAlgs + if len(supportedSigAlgs) == 0 { + supportedSigAlgs = []string{RS256} + } + + if !contains(supportedSigAlgs, sig.Header.Algorithm) { + return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm) + } + + t.sigAlgorithm = sig.Header.Algorithm + + ctx = context.WithValue(ctx, parsedJWTKey, jws) + gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken) + if err != nil { + return nil, fmt.Errorf("failed to verify signature: %v", err) + } + + // Ensure that the payload returned by the square actually matches the payload parsed earlier. + if !bytes.Equal(gotPayload, payload) { + return nil, errors.New("oidc: internal error, payload parsed did not match previous payload") + } + + return t, nil +} + +// Nonce returns an auth code option which requires the ID Token created by the +// OpenID Connect provider to contain the specified nonce. +func Nonce(nonce string) oauth2.AuthCodeOption { + return oauth2.SetAuthURLParam("nonce", nonce) +} diff --git a/vendor/github.com/golang/protobuf/jsonpb/decode.go b/vendor/github.com/golang/protobuf/jsonpb/decode.go new file mode 100644 index 00000000..60e82caa --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/decode.go @@ -0,0 +1,524 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonpb + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "math" + "reflect" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/encoding/protojson" + protoV2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +const wrapJSONUnmarshalV2 = false + +// UnmarshalNext unmarshals the next JSON object from d into m. +func UnmarshalNext(d *json.Decoder, m proto.Message) error { + return new(Unmarshaler).UnmarshalNext(d, m) +} + +// Unmarshal unmarshals a JSON object from r into m. +func Unmarshal(r io.Reader, m proto.Message) error { + return new(Unmarshaler).Unmarshal(r, m) +} + +// UnmarshalString unmarshals a JSON object from s into m. +func UnmarshalString(s string, m proto.Message) error { + return new(Unmarshaler).Unmarshal(strings.NewReader(s), m) +} + +// Unmarshaler is a configurable object for converting from a JSON +// representation to a protocol buffer object. +type Unmarshaler struct { + // AllowUnknownFields specifies whether to allow messages to contain + // unknown JSON fields, as opposed to failing to unmarshal. + AllowUnknownFields bool + + // AnyResolver is used to resolve the google.protobuf.Any well-known type. + // If unset, the global registry is used by default. + AnyResolver AnyResolver +} + +// JSONPBUnmarshaler is implemented by protobuf messages that customize the way +// they are unmarshaled from JSON. Messages that implement this should also +// implement JSONPBMarshaler so that the custom format can be produced. +// +// The JSON unmarshaling must follow the JSON to proto specification: +// https://developers.google.com/protocol-buffers/docs/proto3#json +// +// Deprecated: Custom types should implement protobuf reflection instead. +type JSONPBUnmarshaler interface { + UnmarshalJSONPB(*Unmarshaler, []byte) error +} + +// Unmarshal unmarshals a JSON object from r into m. +func (u *Unmarshaler) Unmarshal(r io.Reader, m proto.Message) error { + return u.UnmarshalNext(json.NewDecoder(r), m) +} + +// UnmarshalNext unmarshals the next JSON object from d into m. +func (u *Unmarshaler) UnmarshalNext(d *json.Decoder, m proto.Message) error { + if m == nil { + return errors.New("invalid nil message") + } + + // Parse the next JSON object from the stream. + raw := json.RawMessage{} + if err := d.Decode(&raw); err != nil { + return err + } + + // Check for custom unmarshalers first since they may not properly + // implement protobuf reflection that the logic below relies on. + if jsu, ok := m.(JSONPBUnmarshaler); ok { + return jsu.UnmarshalJSONPB(u, raw) + } + + mr := proto.MessageReflect(m) + + // NOTE: For historical reasons, a top-level null is treated as a noop. + // This is incorrect, but kept for compatibility. + if string(raw) == "null" && mr.Descriptor().FullName() != "google.protobuf.Value" { + return nil + } + + if wrapJSONUnmarshalV2 { + // NOTE: If input message is non-empty, we need to preserve merge semantics + // of the old jsonpb implementation. These semantics are not supported by + // the protobuf JSON specification. + isEmpty := true + mr.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool { + isEmpty = false // at least one iteration implies non-empty + return false + }) + if !isEmpty { + // Perform unmarshaling into a newly allocated, empty message. + mr = mr.New() + + // Use a defer to copy all unmarshaled fields into the original message. + dst := proto.MessageReflect(m) + defer mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + dst.Set(fd, v) + return true + }) + } + + // Unmarshal using the v2 JSON unmarshaler. + opts := protojson.UnmarshalOptions{ + DiscardUnknown: u.AllowUnknownFields, + } + if u.AnyResolver != nil { + opts.Resolver = anyResolver{u.AnyResolver} + } + return opts.Unmarshal(raw, mr.Interface()) + } else { + if err := u.unmarshalMessage(mr, raw); err != nil { + return err + } + return protoV2.CheckInitialized(mr.Interface()) + } +} + +func (u *Unmarshaler) unmarshalMessage(m protoreflect.Message, in []byte) error { + md := m.Descriptor() + fds := md.Fields() + + if jsu, ok := proto.MessageV1(m.Interface()).(JSONPBUnmarshaler); ok { + return jsu.UnmarshalJSONPB(u, in) + } + + if string(in) == "null" && md.FullName() != "google.protobuf.Value" { + return nil + } + + switch wellKnownType(md.FullName()) { + case "Any": + var jsonObject map[string]json.RawMessage + if err := json.Unmarshal(in, &jsonObject); err != nil { + return err + } + + rawTypeURL, ok := jsonObject["@type"] + if !ok { + return errors.New("Any JSON doesn't have '@type'") + } + typeURL, err := unquoteString(string(rawTypeURL)) + if err != nil { + return fmt.Errorf("can't unmarshal Any's '@type': %q", rawTypeURL) + } + m.Set(fds.ByNumber(1), protoreflect.ValueOfString(typeURL)) + + var m2 protoreflect.Message + if u.AnyResolver != nil { + mi, err := u.AnyResolver.Resolve(typeURL) + if err != nil { + return err + } + m2 = proto.MessageReflect(mi) + } else { + mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL) + if err != nil { + if err == protoregistry.NotFound { + return fmt.Errorf("could not resolve Any message type: %v", typeURL) + } + return err + } + m2 = mt.New() + } + + if wellKnownType(m2.Descriptor().FullName()) != "" { + rawValue, ok := jsonObject["value"] + if !ok { + return errors.New("Any JSON doesn't have 'value'") + } + if err := u.unmarshalMessage(m2, rawValue); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) + } + } else { + delete(jsonObject, "@type") + rawJSON, err := json.Marshal(jsonObject) + if err != nil { + return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) + } + if err = u.unmarshalMessage(m2, rawJSON); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %v: %v", typeURL, err) + } + } + + rawWire, err := protoV2.Marshal(m2.Interface()) + if err != nil { + return fmt.Errorf("can't marshal proto %v into Any.Value: %v", typeURL, err) + } + m.Set(fds.ByNumber(2), protoreflect.ValueOfBytes(rawWire)) + return nil + case "BoolValue", "BytesValue", "StringValue", + "Int32Value", "UInt32Value", "FloatValue", + "Int64Value", "UInt64Value", "DoubleValue": + fd := fds.ByNumber(1) + v, err := u.unmarshalValue(m.NewField(fd), in, fd) + if err != nil { + return err + } + m.Set(fd, v) + return nil + case "Duration": + v, err := unquoteString(string(in)) + if err != nil { + return err + } + d, err := time.ParseDuration(v) + if err != nil { + return fmt.Errorf("bad Duration: %v", err) + } + + sec := d.Nanoseconds() / 1e9 + nsec := d.Nanoseconds() % 1e9 + m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) + m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) + return nil + case "Timestamp": + v, err := unquoteString(string(in)) + if err != nil { + return err + } + t, err := time.Parse(time.RFC3339Nano, v) + if err != nil { + return fmt.Errorf("bad Timestamp: %v", err) + } + + sec := t.Unix() + nsec := t.Nanosecond() + m.Set(fds.ByNumber(1), protoreflect.ValueOfInt64(int64(sec))) + m.Set(fds.ByNumber(2), protoreflect.ValueOfInt32(int32(nsec))) + return nil + case "Value": + switch { + case string(in) == "null": + m.Set(fds.ByNumber(1), protoreflect.ValueOfEnum(0)) + case string(in) == "true": + m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(true)) + case string(in) == "false": + m.Set(fds.ByNumber(4), protoreflect.ValueOfBool(false)) + case hasPrefixAndSuffix('"', in, '"'): + s, err := unquoteString(string(in)) + if err != nil { + return fmt.Errorf("unrecognized type for Value %q", in) + } + m.Set(fds.ByNumber(3), protoreflect.ValueOfString(s)) + case hasPrefixAndSuffix('[', in, ']'): + v := m.Mutable(fds.ByNumber(6)) + return u.unmarshalMessage(v.Message(), in) + case hasPrefixAndSuffix('{', in, '}'): + v := m.Mutable(fds.ByNumber(5)) + return u.unmarshalMessage(v.Message(), in) + default: + f, err := strconv.ParseFloat(string(in), 0) + if err != nil { + return fmt.Errorf("unrecognized type for Value %q", in) + } + m.Set(fds.ByNumber(2), protoreflect.ValueOfFloat64(f)) + } + return nil + case "ListValue": + var jsonArray []json.RawMessage + if err := json.Unmarshal(in, &jsonArray); err != nil { + return fmt.Errorf("bad ListValue: %v", err) + } + + lv := m.Mutable(fds.ByNumber(1)).List() + for _, raw := range jsonArray { + ve := lv.NewElement() + if err := u.unmarshalMessage(ve.Message(), raw); err != nil { + return err + } + lv.Append(ve) + } + return nil + case "Struct": + var jsonObject map[string]json.RawMessage + if err := json.Unmarshal(in, &jsonObject); err != nil { + return fmt.Errorf("bad StructValue: %v", err) + } + + mv := m.Mutable(fds.ByNumber(1)).Map() + for key, raw := range jsonObject { + kv := protoreflect.ValueOf(key).MapKey() + vv := mv.NewValue() + if err := u.unmarshalMessage(vv.Message(), raw); err != nil { + return fmt.Errorf("bad value in StructValue for key %q: %v", key, err) + } + mv.Set(kv, vv) + } + return nil + } + + var jsonObject map[string]json.RawMessage + if err := json.Unmarshal(in, &jsonObject); err != nil { + return err + } + + // Handle known fields. + for i := 0; i < fds.Len(); i++ { + fd := fds.Get(i) + if fd.IsWeak() && fd.Message().IsPlaceholder() { + continue // weak reference is not linked in + } + + // Search for any raw JSON value associated with this field. + var raw json.RawMessage + name := string(fd.Name()) + if fd.Kind() == protoreflect.GroupKind { + name = string(fd.Message().Name()) + } + if v, ok := jsonObject[name]; ok { + delete(jsonObject, name) + raw = v + } + name = string(fd.JSONName()) + if v, ok := jsonObject[name]; ok { + delete(jsonObject, name) + raw = v + } + + field := m.NewField(fd) + // Unmarshal the field value. + if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { + continue + } + v, err := u.unmarshalValue(field, raw, fd) + if err != nil { + return err + } + m.Set(fd, v) + } + + // Handle extension fields. + for name, raw := range jsonObject { + if !strings.HasPrefix(name, "[") || !strings.HasSuffix(name, "]") { + continue + } + + // Resolve the extension field by name. + xname := protoreflect.FullName(name[len("[") : len(name)-len("]")]) + xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) + if xt == nil && isMessageSet(md) { + xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) + } + if xt == nil { + continue + } + delete(jsonObject, name) + fd := xt.TypeDescriptor() + if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { + return fmt.Errorf("extension field %q does not extend message %q", xname, m.Descriptor().FullName()) + } + + field := m.NewField(fd) + // Unmarshal the field value. + if raw == nil || (string(raw) == "null" && !isSingularWellKnownValue(fd) && !isSingularJSONPBUnmarshaler(field, fd)) { + continue + } + v, err := u.unmarshalValue(field, raw, fd) + if err != nil { + return err + } + m.Set(fd, v) + } + + if !u.AllowUnknownFields && len(jsonObject) > 0 { + for name := range jsonObject { + return fmt.Errorf("unknown field %q in %v", name, md.FullName()) + } + } + return nil +} + +func isSingularWellKnownValue(fd protoreflect.FieldDescriptor) bool { + if md := fd.Message(); md != nil { + return md.FullName() == "google.protobuf.Value" && fd.Cardinality() != protoreflect.Repeated + } + return false +} + +func isSingularJSONPBUnmarshaler(v protoreflect.Value, fd protoreflect.FieldDescriptor) bool { + if fd.Message() != nil && fd.Cardinality() != protoreflect.Repeated { + _, ok := proto.MessageV1(v.Interface()).(JSONPBUnmarshaler) + return ok + } + return false +} + +func (u *Unmarshaler) unmarshalValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { + switch { + case fd.IsList(): + var jsonArray []json.RawMessage + if err := json.Unmarshal(in, &jsonArray); err != nil { + return v, err + } + lv := v.List() + for _, raw := range jsonArray { + ve, err := u.unmarshalSingularValue(lv.NewElement(), raw, fd) + if err != nil { + return v, err + } + lv.Append(ve) + } + return v, nil + case fd.IsMap(): + var jsonObject map[string]json.RawMessage + if err := json.Unmarshal(in, &jsonObject); err != nil { + return v, err + } + kfd := fd.MapKey() + vfd := fd.MapValue() + mv := v.Map() + for key, raw := range jsonObject { + var kv protoreflect.MapKey + if kfd.Kind() == protoreflect.StringKind { + kv = protoreflect.ValueOf(key).MapKey() + } else { + v, err := u.unmarshalSingularValue(kfd.Default(), []byte(key), kfd) + if err != nil { + return v, err + } + kv = v.MapKey() + } + + vv, err := u.unmarshalSingularValue(mv.NewValue(), raw, vfd) + if err != nil { + return v, err + } + mv.Set(kv, vv) + } + return v, nil + default: + return u.unmarshalSingularValue(v, in, fd) + } +} + +var nonFinite = map[string]float64{ + `"NaN"`: math.NaN(), + `"Infinity"`: math.Inf(+1), + `"-Infinity"`: math.Inf(-1), +} + +func (u *Unmarshaler) unmarshalSingularValue(v protoreflect.Value, in []byte, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { + switch fd.Kind() { + case protoreflect.BoolKind: + return unmarshalValue(in, new(bool)) + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: + return unmarshalValue(trimQuote(in), new(int32)) + case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + return unmarshalValue(trimQuote(in), new(int64)) + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: + return unmarshalValue(trimQuote(in), new(uint32)) + case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + return unmarshalValue(trimQuote(in), new(uint64)) + case protoreflect.FloatKind: + if f, ok := nonFinite[string(in)]; ok { + return protoreflect.ValueOfFloat32(float32(f)), nil + } + return unmarshalValue(trimQuote(in), new(float32)) + case protoreflect.DoubleKind: + if f, ok := nonFinite[string(in)]; ok { + return protoreflect.ValueOfFloat64(float64(f)), nil + } + return unmarshalValue(trimQuote(in), new(float64)) + case protoreflect.StringKind: + return unmarshalValue(in, new(string)) + case protoreflect.BytesKind: + return unmarshalValue(in, new([]byte)) + case protoreflect.EnumKind: + if hasPrefixAndSuffix('"', in, '"') { + vd := fd.Enum().Values().ByName(protoreflect.Name(trimQuote(in))) + if vd == nil { + return v, fmt.Errorf("unknown value %q for enum %s", in, fd.Enum().FullName()) + } + return protoreflect.ValueOfEnum(vd.Number()), nil + } + return unmarshalValue(in, new(protoreflect.EnumNumber)) + case protoreflect.MessageKind, protoreflect.GroupKind: + err := u.unmarshalMessage(v.Message(), in) + return v, err + default: + panic(fmt.Sprintf("invalid kind %v", fd.Kind())) + } +} + +func unmarshalValue(in []byte, v interface{}) (protoreflect.Value, error) { + err := json.Unmarshal(in, v) + return protoreflect.ValueOf(reflect.ValueOf(v).Elem().Interface()), err +} + +func unquoteString(in string) (out string, err error) { + err = json.Unmarshal([]byte(in), &out) + return out, err +} + +func hasPrefixAndSuffix(prefix byte, in []byte, suffix byte) bool { + if len(in) >= 2 && in[0] == prefix && in[len(in)-1] == suffix { + return true + } + return false +} + +// trimQuote is like unquoteString but simply strips surrounding quotes. +// This is incorrect, but is behavior done by the legacy implementation. +func trimQuote(in []byte) []byte { + if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' { + in = in[1 : len(in)-1] + } + return in +} diff --git a/vendor/github.com/golang/protobuf/jsonpb/encode.go b/vendor/github.com/golang/protobuf/jsonpb/encode.go new file mode 100644 index 00000000..685c80a6 --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/encode.go @@ -0,0 +1,559 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonpb + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/encoding/protojson" + protoV2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" +) + +const wrapJSONMarshalV2 = false + +// Marshaler is a configurable object for marshaling protocol buffer messages +// to the specified JSON representation. +type Marshaler struct { + // OrigName specifies whether to use the original protobuf name for fields. + OrigName bool + + // EnumsAsInts specifies whether to render enum values as integers, + // as opposed to string values. + EnumsAsInts bool + + // EmitDefaults specifies whether to render fields with zero values. + EmitDefaults bool + + // Indent controls whether the output is compact or not. + // If empty, the output is compact JSON. Otherwise, every JSON object + // entry and JSON array value will be on its own line. + // Each line will be preceded by repeated copies of Indent, where the + // number of copies is the current indentation depth. + Indent string + + // AnyResolver is used to resolve the google.protobuf.Any well-known type. + // If unset, the global registry is used by default. + AnyResolver AnyResolver +} + +// JSONPBMarshaler is implemented by protobuf messages that customize the +// way they are marshaled to JSON. Messages that implement this should also +// implement JSONPBUnmarshaler so that the custom format can be parsed. +// +// The JSON marshaling must follow the proto to JSON specification: +// https://developers.google.com/protocol-buffers/docs/proto3#json +// +// Deprecated: Custom types should implement protobuf reflection instead. +type JSONPBMarshaler interface { + MarshalJSONPB(*Marshaler) ([]byte, error) +} + +// Marshal serializes a protobuf message as JSON into w. +func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error { + b, err := jm.marshal(m) + if len(b) > 0 { + if _, err := w.Write(b); err != nil { + return err + } + } + return err +} + +// MarshalToString serializes a protobuf message as JSON in string form. +func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) { + b, err := jm.marshal(m) + if err != nil { + return "", err + } + return string(b), nil +} + +func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) { + v := reflect.ValueOf(m) + if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { + return nil, errors.New("Marshal called with nil") + } + + // Check for custom marshalers first since they may not properly + // implement protobuf reflection that the logic below relies on. + if jsm, ok := m.(JSONPBMarshaler); ok { + return jsm.MarshalJSONPB(jm) + } + + if wrapJSONMarshalV2 { + opts := protojson.MarshalOptions{ + UseProtoNames: jm.OrigName, + UseEnumNumbers: jm.EnumsAsInts, + EmitUnpopulated: jm.EmitDefaults, + Indent: jm.Indent, + } + if jm.AnyResolver != nil { + opts.Resolver = anyResolver{jm.AnyResolver} + } + return opts.Marshal(proto.MessageReflect(m).Interface()) + } else { + // Check for unpopulated required fields first. + m2 := proto.MessageReflect(m) + if err := protoV2.CheckInitialized(m2.Interface()); err != nil { + return nil, err + } + + w := jsonWriter{Marshaler: jm} + err := w.marshalMessage(m2, "", "") + return w.buf, err + } +} + +type jsonWriter struct { + *Marshaler + buf []byte +} + +func (w *jsonWriter) write(s string) { + w.buf = append(w.buf, s...) +} + +func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error { + if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok { + b, err := jsm.MarshalJSONPB(w.Marshaler) + if err != nil { + return err + } + if typeURL != "" { + // we are marshaling this object to an Any type + var js map[string]*json.RawMessage + if err = json.Unmarshal(b, &js); err != nil { + return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err) + } + turl, err := json.Marshal(typeURL) + if err != nil { + return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err) + } + js["@type"] = (*json.RawMessage)(&turl) + if b, err = json.Marshal(js); err != nil { + return err + } + } + w.write(string(b)) + return nil + } + + md := m.Descriptor() + fds := md.Fields() + + // Handle well-known types. + const secondInNanos = int64(time.Second / time.Nanosecond) + switch wellKnownType(md.FullName()) { + case "Any": + return w.marshalAny(m, indent) + case "BoolValue", "BytesValue", "StringValue", + "Int32Value", "UInt32Value", "FloatValue", + "Int64Value", "UInt64Value", "DoubleValue": + fd := fds.ByNumber(1) + return w.marshalValue(fd, m.Get(fd), indent) + case "Duration": + const maxSecondsInDuration = 315576000000 + // "Generated output always contains 0, 3, 6, or 9 fractional digits, + // depending on required precision." + s := m.Get(fds.ByNumber(1)).Int() + ns := m.Get(fds.ByNumber(2)).Int() + if s < -maxSecondsInDuration || s > maxSecondsInDuration { + return fmt.Errorf("seconds out of range %v", s) + } + if ns <= -secondInNanos || ns >= secondInNanos { + return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos) + } + if (s > 0 && ns < 0) || (s < 0 && ns > 0) { + return errors.New("signs of seconds and nanos do not match") + } + var sign string + if s < 0 || ns < 0 { + sign, s, ns = "-", -1*s, -1*ns + } + x := fmt.Sprintf("%s%d.%09d", sign, s, ns) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + w.write(fmt.Sprintf(`"%vs"`, x)) + return nil + case "Timestamp": + // "RFC 3339, where generated output will always be Z-normalized + // and uses 0, 3, 6 or 9 fractional digits." + s := m.Get(fds.ByNumber(1)).Int() + ns := m.Get(fds.ByNumber(2)).Int() + if ns < 0 || ns >= secondInNanos { + return fmt.Errorf("ns out of range [0, %v)", secondInNanos) + } + t := time.Unix(s, ns).UTC() + // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits). + x := t.Format("2006-01-02T15:04:05.000000000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + w.write(fmt.Sprintf(`"%vZ"`, x)) + return nil + case "Value": + // JSON value; which is a null, number, string, bool, object, or array. + od := md.Oneofs().Get(0) + fd := m.WhichOneof(od) + if fd == nil { + return errors.New("nil Value") + } + return w.marshalValue(fd, m.Get(fd), indent) + case "Struct", "ListValue": + // JSON object or array. + fd := fds.ByNumber(1) + return w.marshalValue(fd, m.Get(fd), indent) + } + + w.write("{") + if w.Indent != "" { + w.write("\n") + } + + firstField := true + if typeURL != "" { + if err := w.marshalTypeURL(indent, typeURL); err != nil { + return err + } + firstField = false + } + + for i := 0; i < fds.Len(); { + fd := fds.Get(i) + if od := fd.ContainingOneof(); od != nil { + fd = m.WhichOneof(od) + i += od.Fields().Len() + if fd == nil { + continue + } + } else { + i++ + } + + v := m.Get(fd) + + if !m.Has(fd) { + if !w.EmitDefaults || fd.ContainingOneof() != nil { + continue + } + if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) { + v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars + } + } + + if !firstField { + w.writeComma() + } + if err := w.marshalField(fd, v, indent); err != nil { + return err + } + firstField = false + } + + // Handle proto2 extensions. + if md.ExtensionRanges().Len() > 0 { + // Collect a sorted list of all extension descriptor and values. + type ext struct { + desc protoreflect.FieldDescriptor + val protoreflect.Value + } + var exts []ext + m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + if fd.IsExtension() { + exts = append(exts, ext{fd, v}) + } + return true + }) + sort.Slice(exts, func(i, j int) bool { + return exts[i].desc.Number() < exts[j].desc.Number() + }) + + for _, ext := range exts { + if !firstField { + w.writeComma() + } + if err := w.marshalField(ext.desc, ext.val, indent); err != nil { + return err + } + firstField = false + } + } + + if w.Indent != "" { + w.write("\n") + w.write(indent) + } + w.write("}") + return nil +} + +func (w *jsonWriter) writeComma() { + if w.Indent != "" { + w.write(",\n") + } else { + w.write(",") + } +} + +func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error { + // "If the Any contains a value that has a special JSON mapping, + // it will be converted as follows: {"@type": xxx, "value": yyy}. + // Otherwise, the value will be converted into a JSON object, + // and the "@type" field will be inserted to indicate the actual data type." + md := m.Descriptor() + typeURL := m.Get(md.Fields().ByNumber(1)).String() + rawVal := m.Get(md.Fields().ByNumber(2)).Bytes() + + var m2 protoreflect.Message + if w.AnyResolver != nil { + mi, err := w.AnyResolver.Resolve(typeURL) + if err != nil { + return err + } + m2 = proto.MessageReflect(mi) + } else { + mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL) + if err != nil { + return err + } + m2 = mt.New() + } + + if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil { + return err + } + + if wellKnownType(m2.Descriptor().FullName()) == "" { + return w.marshalMessage(m2, indent, typeURL) + } + + w.write("{") + if w.Indent != "" { + w.write("\n") + } + if err := w.marshalTypeURL(indent, typeURL); err != nil { + return err + } + w.writeComma() + if w.Indent != "" { + w.write(indent) + w.write(w.Indent) + w.write(`"value": `) + } else { + w.write(`"value":`) + } + if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil { + return err + } + if w.Indent != "" { + w.write("\n") + w.write(indent) + } + w.write("}") + return nil +} + +func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error { + if w.Indent != "" { + w.write(indent) + w.write(w.Indent) + } + w.write(`"@type":`) + if w.Indent != "" { + w.write(" ") + } + b, err := json.Marshal(typeURL) + if err != nil { + return err + } + w.write(string(b)) + return nil +} + +// marshalField writes field description and value to the Writer. +func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { + if w.Indent != "" { + w.write(indent) + w.write(w.Indent) + } + w.write(`"`) + switch { + case fd.IsExtension(): + // For message set, use the fname of the message as the extension name. + name := string(fd.FullName()) + if isMessageSet(fd.ContainingMessage()) { + name = strings.TrimSuffix(name, ".message_set_extension") + } + + w.write("[" + name + "]") + case w.OrigName: + name := string(fd.Name()) + if fd.Kind() == protoreflect.GroupKind { + name = string(fd.Message().Name()) + } + w.write(name) + default: + w.write(string(fd.JSONName())) + } + w.write(`":`) + if w.Indent != "" { + w.write(" ") + } + return w.marshalValue(fd, v, indent) +} + +func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { + switch { + case fd.IsList(): + w.write("[") + comma := "" + lv := v.List() + for i := 0; i < lv.Len(); i++ { + w.write(comma) + if w.Indent != "" { + w.write("\n") + w.write(indent) + w.write(w.Indent) + w.write(w.Indent) + } + if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil { + return err + } + comma = "," + } + if w.Indent != "" { + w.write("\n") + w.write(indent) + w.write(w.Indent) + } + w.write("]") + return nil + case fd.IsMap(): + kfd := fd.MapKey() + vfd := fd.MapValue() + mv := v.Map() + + // Collect a sorted list of all map keys and values. + type entry struct{ key, val protoreflect.Value } + var entries []entry + mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { + entries = append(entries, entry{k.Value(), v}) + return true + }) + sort.Slice(entries, func(i, j int) bool { + switch kfd.Kind() { + case protoreflect.BoolKind: + return !entries[i].key.Bool() && entries[j].key.Bool() + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: + return entries[i].key.Int() < entries[j].key.Int() + case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: + return entries[i].key.Uint() < entries[j].key.Uint() + case protoreflect.StringKind: + return entries[i].key.String() < entries[j].key.String() + default: + panic("invalid kind") + } + }) + + w.write(`{`) + comma := "" + for _, entry := range entries { + w.write(comma) + if w.Indent != "" { + w.write("\n") + w.write(indent) + w.write(w.Indent) + w.write(w.Indent) + } + + s := fmt.Sprint(entry.key.Interface()) + b, err := json.Marshal(s) + if err != nil { + return err + } + w.write(string(b)) + + w.write(`:`) + if w.Indent != "" { + w.write(` `) + } + + if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil { + return err + } + comma = "," + } + if w.Indent != "" { + w.write("\n") + w.write(indent) + w.write(w.Indent) + } + w.write(`}`) + return nil + default: + return w.marshalSingularValue(fd, v, indent) + } +} + +func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error { + switch { + case !v.IsValid(): + w.write("null") + return nil + case fd.Message() != nil: + return w.marshalMessage(v.Message(), indent+w.Indent, "") + case fd.Enum() != nil: + if fd.Enum().FullName() == "google.protobuf.NullValue" { + w.write("null") + return nil + } + + vd := fd.Enum().Values().ByNumber(v.Enum()) + if vd == nil || w.EnumsAsInts { + w.write(strconv.Itoa(int(v.Enum()))) + } else { + w.write(`"` + string(vd.Name()) + `"`) + } + return nil + default: + switch v.Interface().(type) { + case float32, float64: + switch { + case math.IsInf(v.Float(), +1): + w.write(`"Infinity"`) + return nil + case math.IsInf(v.Float(), -1): + w.write(`"-Infinity"`) + return nil + case math.IsNaN(v.Float()): + w.write(`"NaN"`) + return nil + } + case int64, uint64: + w.write(fmt.Sprintf(`"%d"`, v.Interface())) + return nil + } + + b, err := json.Marshal(v.Interface()) + if err != nil { + return err + } + w.write(string(b)) + return nil + } +} diff --git a/vendor/github.com/golang/protobuf/jsonpb/json.go b/vendor/github.com/golang/protobuf/jsonpb/json.go new file mode 100644 index 00000000..480e2448 --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/json.go @@ -0,0 +1,69 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jsonpb provides functionality to marshal and unmarshal between a +// protocol buffer message and JSON. It follows the specification at +// https://developers.google.com/protocol-buffers/docs/proto3#json. +// +// Do not rely on the default behavior of the standard encoding/json package +// when called on generated message types as it does not operate correctly. +// +// Deprecated: Use the "google.golang.org/protobuf/encoding/protojson" +// package instead. +package jsonpb + +import ( + "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/runtime/protoimpl" +) + +// AnyResolver takes a type URL, present in an Any message, +// and resolves it into an instance of the associated message. +type AnyResolver interface { + Resolve(typeURL string) (proto.Message, error) +} + +type anyResolver struct{ AnyResolver } + +func (r anyResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) { + return r.FindMessageByURL(string(message)) +} + +func (r anyResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) { + m, err := r.Resolve(url) + if err != nil { + return nil, err + } + return protoimpl.X.MessageTypeOf(m), nil +} + +func (r anyResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { + return protoregistry.GlobalTypes.FindExtensionByName(field) +} + +func (r anyResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { + return protoregistry.GlobalTypes.FindExtensionByNumber(message, field) +} + +func wellKnownType(s protoreflect.FullName) string { + if s.Parent() == "google.protobuf" { + switch s.Name() { + case "Empty", "Any", + "BoolValue", "BytesValue", "StringValue", + "Int32Value", "UInt32Value", "FloatValue", + "Int64Value", "UInt64Value", "DoubleValue", + "Duration", "Timestamp", + "NullValue", "Struct", "Value", "ListValue": + return string(s.Name()) + } + } + return "" +} + +func isMessageSet(md protoreflect.MessageDescriptor) bool { + ms, ok := md.(interface{ IsMessageSet() bool }) + return ok && ms.IsMessageSet() +} diff --git a/vendor/golang.org/x/oauth2/AUTHORS b/vendor/golang.org/x/oauth2/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/vendor/golang.org/x/oauth2/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTORS b/vendor/golang.org/x/oauth2/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/vendor/golang.org/x/oauth2/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go index 9857fe53..4c0850a4 100644 --- a/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -8,22 +8,35 @@ package errgroup import ( "context" + "fmt" "sync" ) +type token struct{} + // A Group is a collection of goroutines working on subtasks that are part of // the same overall task. // -// A zero Group is valid and does not cancel on error. +// A zero Group is valid, has no limit on the number of active goroutines, +// and does not cancel on error. type Group struct { cancel func() wg sync.WaitGroup + sem chan token + errOnce sync.Once err error } +func (g *Group) done() { + if g.sem != nil { + <-g.sem + } + g.wg.Done() +} + // WithContext returns a new Group and an associated Context derived from ctx. // // The derived Context is canceled the first time a function passed to Go @@ -45,14 +58,19 @@ func (g *Group) Wait() error { } // Go calls the given function in a new goroutine. +// It blocks until the new goroutine can be added without the number of +// active goroutines in the group exceeding the configured limit. // // The first call to return a non-nil error cancels the group; its error will be // returned by Wait. func (g *Group) Go(f func() error) { - g.wg.Add(1) + if g.sem != nil { + g.sem <- token{} + } + g.wg.Add(1) go func() { - defer g.wg.Done() + defer g.done() if err := f(); err != nil { g.errOnce.Do(func() { @@ -64,3 +82,51 @@ func (g *Group) Go(f func() error) { } }() } + +// TryGo calls the given function in a new goroutine only if the number of +// active goroutines in the group is currently below the configured limit. +// +// The return value reports whether the goroutine was started. +func (g *Group) TryGo(f func() error) bool { + if g.sem != nil { + select { + case g.sem <- token{}: + // Note: this allows barging iff channels in general allow barging. + default: + return false + } + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() + return true +} + +// SetLimit limits the number of active goroutines in this group to at most n. +// A negative value indicates no limit. +// +// Any subsequent call to the Go method will block until it can add an active +// goroutine without exceeding the configured limit. +// +// The limit must not be modified while any goroutines in the group are active. +func (g *Group) SetLimit(n int) { + if n < 0 { + g.sem = nil + return + } + if len(g.sem) != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + } + g.sem = make(chan token, n) +} diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go index eef99d9d..2ef99f5a 100644 --- a/vendor/golang.org/x/xerrors/doc.go +++ b/vendor/golang.org/x/xerrors/doc.go @@ -5,7 +5,8 @@ // Package xerrors implements functions to manipulate errors. // // This package is based on the Go 2 proposal for error values: -// https://golang.org/design/29934-error-values +// +// https://golang.org/design/29934-error-values // // These functions were incorporated into the standard library's errors package // in Go 1.13: diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go index 829862dd..27a5d70b 100644 --- a/vendor/golang.org/x/xerrors/fmt.go +++ b/vendor/golang.org/x/xerrors/fmt.go @@ -33,6 +33,9 @@ const percentBangString = "%!" // It is invalid to include more than one %w verb or to supply it with an // operand that does not implement the error interface. The %w verb is otherwise // a synonym for %v. +// +// Note that as of Go 1.13, the fmt.Errorf function will do error formatting, +// but it will not capture a stack backtrace. func Errorf(format string, a ...interface{}) error { format = formatPlusW(format) // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go index 9a3b5103..9842758c 100644 --- a/vendor/golang.org/x/xerrors/wrap.go +++ b/vendor/golang.org/x/xerrors/wrap.go @@ -35,6 +35,8 @@ func (e noWrapper) FormatError(p Printer) (next error) { // Unwrap returns the result of calling the Unwrap method on err, if err implements // Unwrap. Otherwise, Unwrap returns nil. +// +// Deprecated: As of Go 1.13, use errors.Unwrap instead. func Unwrap(err error) error { u, ok := err.(Wrapper) if !ok { @@ -47,6 +49,8 @@ func Unwrap(err error) error { // // An error is considered to match a target if it is equal to that target or if // it implements a method Is(error) bool such that Is(target) returns true. +// +// Deprecated: As of Go 1.13, use errors.Is instead. func Is(err, target error) bool { if target == nil { return err == target @@ -77,6 +81,8 @@ func Is(err, target error) bool { // // The As method should set the target to its value and return true if err // matches the type to which target points. +// +// Deprecated: As of Go 1.13, use errors.As instead. func As(err error, target interface{}) bool { if target == nil { panic("errors: target cannot be nil") diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index bcc6f545..f7a7697c 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -27,6 +27,7 @@ import ( "net" "strings" + "google.golang.org/grpc/channelz" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal" @@ -192,7 +193,7 @@ type BuildOptions struct { // server can ignore this field. Authority string // ChannelzParentID is the parent ClientConn's channelz ID. - ChannelzParentID int64 + ChannelzParentID *channelz.Identifier // CustomUserAgent is the custom user agent set on the parent ClientConn. // The balancer should set the same custom user agent if it creates a // ClientConn. diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go index f4ea6174..b1c23eaa 100644 --- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go +++ b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go @@ -20,130 +20,178 @@ package grpc import ( "fmt" + "strings" "sync" "google.golang.org/grpc/balancer" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/internal/balancer/gracefulswitch" "google.golang.org/grpc/internal/buffer" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/resolver" ) -// scStateUpdate contains the subConn and the new state it changed to. +// ccBalancerWrapper sits between the ClientConn and the Balancer. +// +// ccBalancerWrapper implements methods corresponding to the ones on the +// balancer.Balancer interface. The ClientConn is free to call these methods +// concurrently and the ccBalancerWrapper ensures that calls from the ClientConn +// to the Balancer happen synchronously and in order. +// +// ccBalancerWrapper also implements the balancer.ClientConn interface and is +// passed to the Balancer implementations. It invokes unexported methods on the +// ClientConn to handle these calls from the Balancer. +// +// It uses the gracefulswitch.Balancer internally to ensure that balancer +// switches happen in a graceful manner. +type ccBalancerWrapper struct { + cc *ClientConn + + // Since these fields are accessed only from handleXxx() methods which are + // synchronized by the watcher goroutine, we do not need a mutex to protect + // these fields. + balancer *gracefulswitch.Balancer + curBalancerName string + + updateCh *buffer.Unbounded // Updates written on this channel are processed by watcher(). + resultCh *buffer.Unbounded // Results of calls to UpdateClientConnState() are pushed here. + closed *grpcsync.Event // Indicates if close has been called. + done *grpcsync.Event // Indicates if close has completed its work. +} + +// newCCBalancerWrapper creates a new balancer wrapper. The underlying balancer +// is not created until the switchTo() method is invoked. +func newCCBalancerWrapper(cc *ClientConn, bopts balancer.BuildOptions) *ccBalancerWrapper { + ccb := &ccBalancerWrapper{ + cc: cc, + updateCh: buffer.NewUnbounded(), + resultCh: buffer.NewUnbounded(), + closed: grpcsync.NewEvent(), + done: grpcsync.NewEvent(), + } + go ccb.watcher() + ccb.balancer = gracefulswitch.NewBalancer(ccb, bopts) + return ccb +} + +// The following xxxUpdate structs wrap the arguments received as part of the +// corresponding update. The watcher goroutine uses the 'type' of the update to +// invoke the appropriate handler routine to handle the update. + +type ccStateUpdate struct { + ccs *balancer.ClientConnState +} + type scStateUpdate struct { sc balancer.SubConn state connectivity.State err error } -// exitIdle contains no data and is just a signal sent on the updateCh in -// ccBalancerWrapper to instruct the balancer to exit idle. -type exitIdle struct{} +type exitIdleUpdate struct{} -// ccBalancerWrapper is a wrapper on top of cc for balancers. -// It implements balancer.ClientConn interface. -type ccBalancerWrapper struct { - cc *ClientConn - balancerMu sync.Mutex // synchronizes calls to the balancer - balancer balancer.Balancer - hasExitIdle bool - updateCh *buffer.Unbounded - closed *grpcsync.Event - done *grpcsync.Event - - mu sync.Mutex - subConns map[*acBalancerWrapper]struct{} +type resolverErrorUpdate struct { + err error } -func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper { - ccb := &ccBalancerWrapper{ - cc: cc, - updateCh: buffer.NewUnbounded(), - closed: grpcsync.NewEvent(), - done: grpcsync.NewEvent(), - subConns: make(map[*acBalancerWrapper]struct{}), - } - go ccb.watcher() - ccb.balancer = b.Build(ccb, bopts) - _, ccb.hasExitIdle = ccb.balancer.(balancer.ExitIdler) - return ccb +type switchToUpdate struct { + name string } -// watcher balancer functions sequentially, so the balancer can be implemented -// lock-free. +type subConnUpdate struct { + acbw *acBalancerWrapper +} + +// watcher is a long-running goroutine which reads updates from a channel and +// invokes corresponding methods on the underlying balancer. It ensures that +// these methods are invoked in a synchronous fashion. It also ensures that +// these methods are invoked in the order in which the updates were received. func (ccb *ccBalancerWrapper) watcher() { for { select { - case t := <-ccb.updateCh.Get(): + case u := <-ccb.updateCh.Get(): ccb.updateCh.Load() if ccb.closed.HasFired() { break } - switch u := t.(type) { + switch update := u.(type) { + case *ccStateUpdate: + ccb.handleClientConnStateChange(update.ccs) case *scStateUpdate: - ccb.balancerMu.Lock() - ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err}) - ccb.balancerMu.Unlock() - case *acBalancerWrapper: - ccb.mu.Lock() - if ccb.subConns != nil { - delete(ccb.subConns, u) - ccb.cc.removeAddrConn(u.getAddrConn(), errConnDrain) - } - ccb.mu.Unlock() - case exitIdle: - if ccb.cc.GetState() == connectivity.Idle { - if ei, ok := ccb.balancer.(balancer.ExitIdler); ok { - // We already checked that the balancer implements - // ExitIdle before pushing the event to updateCh, but - // check conditionally again as defensive programming. - ccb.balancerMu.Lock() - ei.ExitIdle() - ccb.balancerMu.Unlock() - } - } + ccb.handleSubConnStateChange(update) + case *exitIdleUpdate: + ccb.handleExitIdle() + case *resolverErrorUpdate: + ccb.handleResolverError(update.err) + case *switchToUpdate: + ccb.handleSwitchTo(update.name) + case *subConnUpdate: + ccb.handleRemoveSubConn(update.acbw) default: - logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", t, t) + logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", update, update) } case <-ccb.closed.Done(): } if ccb.closed.HasFired() { - ccb.balancerMu.Lock() - ccb.balancer.Close() - ccb.balancerMu.Unlock() - ccb.mu.Lock() - scs := ccb.subConns - ccb.subConns = nil - ccb.mu.Unlock() - ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil}) - ccb.done.Fire() - // Fire done before removing the addr conns. We can safely unblock - // ccb.close and allow the removeAddrConns to happen - // asynchronously. - for acbw := range scs { - ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) - } + ccb.handleClose() return } } } -func (ccb *ccBalancerWrapper) close() { - ccb.closed.Fire() - <-ccb.done.Done() -} +// updateClientConnState is invoked by grpc to push a ClientConnState update to +// the underlying balancer. +// +// Unlike other methods invoked by grpc to push updates to the underlying +// balancer, this method cannot simply push the update onto the update channel +// and return. It needs to return the error returned by the underlying balancer +// back to grpc which propagates that to the resolver. +func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { + ccb.updateCh.Put(&ccStateUpdate{ccs: ccs}) -func (ccb *ccBalancerWrapper) exitIdle() bool { - if !ccb.hasExitIdle { - return false + var res interface{} + select { + case res = <-ccb.resultCh.Get(): + ccb.resultCh.Load() + case <-ccb.closed.Done(): + // Return early if the balancer wrapper is closed while we are waiting for + // the underlying balancer to process a ClientConnState update. + return nil } - ccb.updateCh.Put(exitIdle{}) - return true + // If the returned error is nil, attempting to type assert to error leads to + // panic. So, this needs to handled separately. + if res == nil { + return nil + } + return res.(error) } -func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) { +// handleClientConnStateChange handles a ClientConnState update from the update +// channel and invokes the appropriate method on the underlying balancer. +// +// If the addresses specified in the update contain addresses of type "grpclb" +// and the selected LB policy is not "grpclb", these addresses will be filtered +// out and ccs will be modified with the updated address list. +func (ccb *ccBalancerWrapper) handleClientConnStateChange(ccs *balancer.ClientConnState) { + if ccb.curBalancerName != grpclbName { + // Filter any grpclb addresses since we don't have the grpclb balancer. + var addrs []resolver.Address + for _, addr := range ccs.ResolverState.Addresses { + if addr.Type == resolver.GRPCLB { + continue + } + addrs = append(addrs, addr) + } + ccs.ResolverState.Addresses = addrs + } + ccb.resultCh.Put(ccb.balancer.UpdateClientConnState(*ccs)) +} + +// updateSubConnState is invoked by grpc to push a subConn state update to the +// underlying balancer. +func (ccb *ccBalancerWrapper) updateSubConnState(sc balancer.SubConn, s connectivity.State, err error) { // When updating addresses for a SubConn, if the address in use is not in // the new addresses, the old ac will be tearDown() and a new ac will be // created. tearDown() generates a state change with Shutdown state, we @@ -161,44 +209,125 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co }) } -func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error { - ccb.balancerMu.Lock() - defer ccb.balancerMu.Unlock() - return ccb.balancer.UpdateClientConnState(*ccs) +// handleSubConnStateChange handles a SubConnState update from the update +// channel and invokes the appropriate method on the underlying balancer. +func (ccb *ccBalancerWrapper) handleSubConnStateChange(update *scStateUpdate) { + ccb.balancer.UpdateSubConnState(update.sc, balancer.SubConnState{ConnectivityState: update.state, ConnectionError: update.err}) +} + +func (ccb *ccBalancerWrapper) exitIdle() { + ccb.updateCh.Put(&exitIdleUpdate{}) +} + +func (ccb *ccBalancerWrapper) handleExitIdle() { + if ccb.cc.GetState() != connectivity.Idle { + return + } + ccb.balancer.ExitIdle() } func (ccb *ccBalancerWrapper) resolverError(err error) { - ccb.balancerMu.Lock() - defer ccb.balancerMu.Unlock() + ccb.updateCh.Put(&resolverErrorUpdate{err: err}) +} + +func (ccb *ccBalancerWrapper) handleResolverError(err error) { ccb.balancer.ResolverError(err) } +// switchTo is invoked by grpc to instruct the balancer wrapper to switch to the +// LB policy identified by name. +// +// ClientConn calls newCCBalancerWrapper() at creation time. Upon receipt of the +// first good update from the name resolver, it determines the LB policy to use +// and invokes the switchTo() method. Upon receipt of every subsequent update +// from the name resolver, it invokes this method. +// +// the ccBalancerWrapper keeps track of the current LB policy name, and skips +// the graceful balancer switching process if the name does not change. +func (ccb *ccBalancerWrapper) switchTo(name string) { + ccb.updateCh.Put(&switchToUpdate{name: name}) +} + +// handleSwitchTo handles a balancer switch update from the update channel. It +// calls the SwitchTo() method on the gracefulswitch.Balancer with a +// balancer.Builder corresponding to name. If no balancer.Builder is registered +// for the given name, it uses the default LB policy which is "pick_first". +func (ccb *ccBalancerWrapper) handleSwitchTo(name string) { + // TODO: Other languages use case-insensitive balancer registries. We should + // switch as well. See: https://github.com/grpc/grpc-go/issues/5288. + if strings.EqualFold(ccb.curBalancerName, name) { + return + } + + // TODO: Ensure that name is a registered LB policy when we get here. + // We currently only validate the `loadBalancingConfig` field. We need to do + // the same for the `loadBalancingPolicy` field and reject the service config + // if the specified policy is not registered. + builder := balancer.Get(name) + if builder == nil { + channelz.Warningf(logger, ccb.cc.channelzID, "Channel switches to new LB policy %q, since the specified LB policy %q was not registered", PickFirstBalancerName, name) + builder = newPickfirstBuilder() + } else { + channelz.Infof(logger, ccb.cc.channelzID, "Channel switches to new LB policy %q", name) + } + + if err := ccb.balancer.SwitchTo(builder); err != nil { + channelz.Errorf(logger, ccb.cc.channelzID, "Channel failed to build new LB policy %q: %v", name, err) + return + } + ccb.curBalancerName = builder.Name() +} + +// handleRemoveSucConn handles a request from the underlying balancer to remove +// a subConn. +// +// See comments in RemoveSubConn() for more details. +func (ccb *ccBalancerWrapper) handleRemoveSubConn(acbw *acBalancerWrapper) { + ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) +} + +func (ccb *ccBalancerWrapper) close() { + ccb.closed.Fire() + <-ccb.done.Done() +} + +func (ccb *ccBalancerWrapper) handleClose() { + ccb.balancer.Close() + ccb.done.Fire() +} + func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { if len(addrs) <= 0 { return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") } - ccb.mu.Lock() - defer ccb.mu.Unlock() - if ccb.subConns == nil { - return nil, fmt.Errorf("grpc: ClientConn balancer wrapper was closed") - } ac, err := ccb.cc.newAddrConn(addrs, opts) if err != nil { + channelz.Warningf(logger, ccb.cc.channelzID, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err) return nil, err } acbw := &acBalancerWrapper{ac: ac} acbw.ac.mu.Lock() ac.acbw = acbw acbw.ac.mu.Unlock() - ccb.subConns[acbw] = struct{}{} return acbw, nil } func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { - // The RemoveSubConn() is handled in the run() goroutine, to avoid deadlock - // during switchBalancer() if the old balancer calls RemoveSubConn() in its - // Close(). - ccb.updateCh.Put(sc) + // Before we switched the ccBalancerWrapper to use gracefulswitch.Balancer, it + // was required to handle the RemoveSubConn() method asynchronously by pushing + // the update onto the update channel. This was done to avoid a deadlock as + // switchBalancer() was holding cc.mu when calling Close() on the old + // balancer, which would in turn call RemoveSubConn(). + // + // With the use of gracefulswitch.Balancer in ccBalancerWrapper, handling this + // asynchronously is probably not required anymore since the switchTo() method + // handles the balancer switch by pushing the update onto the channel. + // TODO(easwars): Handle this inline. + acbw, ok := sc.(*acBalancerWrapper) + if !ok { + return + } + ccb.updateCh.Put(&subConnUpdate{acbw: acbw}) } func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { @@ -210,11 +339,6 @@ func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resol } func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { - ccb.mu.Lock() - defer ccb.mu.Unlock() - if ccb.subConns == nil { - return - } // Update picker before updating state. Even though the ordering here does // not matter, it can lead to multiple calls of Pick in the common start-up // case where we wait for ready and then perform an RPC. If the picker is diff --git a/vendor/google.golang.org/grpc/channelz/channelz.go b/vendor/google.golang.org/grpc/channelz/channelz.go new file mode 100644 index 00000000..a220c47c --- /dev/null +++ b/vendor/google.golang.org/grpc/channelz/channelz.go @@ -0,0 +1,36 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * 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 channelz exports internals of the channelz implementation as required +// by other gRPC packages. +// +// The implementation of the channelz spec as defined in +// https://github.com/grpc/proposal/blob/master/A14-channelz.md, is provided by +// the `internal/channelz` package. +// +// Experimental +// +// Notice: All APIs in this package are experimental and may be removed in a +// later release. +package channelz + +import "google.golang.org/grpc/internal/channelz" + +// Identifier is an opaque identifier which uniquely identifies an entity in the +// channelz database. +type Identifier = channelz.Identifier diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index f9af7891..de6d41c2 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -159,23 +159,20 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * } }() - if channelz.IsOn() { - if cc.dopts.channelzParentID != 0 { - cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target) - channelz.AddTraceEvent(logger, cc.channelzID, 0, &channelz.TraceEventDesc{ - Desc: "Channel Created", - Severity: channelz.CtInfo, - Parent: &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Nested Channel(id:%d) created", cc.channelzID), - Severity: channelz.CtInfo, - }, - }) - } else { - cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, 0, target) - channelz.Info(logger, cc.channelzID, "Channel Created") - } - cc.csMgr.channelzID = cc.channelzID + pid := cc.dopts.channelzParentID + cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, pid, target) + ted := &channelz.TraceEventDesc{ + Desc: "Channel created", + Severity: channelz.CtInfo, } + if cc.dopts.channelzParentID != nil { + ted.Parent = &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Nested Channel(id:%d) created", cc.channelzID.Int()), + Severity: channelz.CtInfo, + } + } + channelz.AddTraceEvent(logger, cc.channelzID, 1, ted) + cc.csMgr.channelzID = cc.channelzID if cc.dopts.copts.TransportCredentials == nil && cc.dopts.copts.CredsBundle == nil { return nil, errNoTransportSecurity @@ -281,7 +278,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * if creds := cc.dopts.copts.TransportCredentials; creds != nil { credsClone = creds.Clone() } - cc.balancerBuildOpts = balancer.BuildOptions{ + cc.balancerWrapper = newCCBalancerWrapper(cc, balancer.BuildOptions{ DialCreds: credsClone, CredsBundle: cc.dopts.copts.CredsBundle, Dialer: cc.dopts.copts.Dialer, @@ -289,7 +286,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * CustomUserAgent: cc.dopts.copts.UserAgent, ChannelzParentID: cc.channelzID, Target: cc.parsedTarget, - } + }) // Build the resolver. rWrapper, err := newCCResolverWrapper(cc, resolverBuilder) @@ -398,7 +395,7 @@ type connectivityStateManager struct { mu sync.Mutex state connectivity.State notifyChan chan struct{} - channelzID int64 + channelzID *channelz.Identifier } // updateState updates the connectivity.State of ClientConn. @@ -464,34 +461,36 @@ var _ ClientConnInterface = (*ClientConn)(nil) // handshakes. It also handles errors on established connections by // re-resolving the name and reconnecting. type ClientConn struct { - ctx context.Context - cancel context.CancelFunc + ctx context.Context // Initialized using the background context at dial time. + cancel context.CancelFunc // Cancelled on close. - target string - parsedTarget resolver.Target - authority string - dopts dialOptions - csMgr *connectivityStateManager - - balancerBuildOpts balancer.BuildOptions - blockingpicker *pickerWrapper + // The following are initialized at dial time, and are read-only after that. + target string // User's dial target. + parsedTarget resolver.Target // See parseTargetAndFindResolver(). + authority string // See determineAuthority(). + dopts dialOptions // Default and user specified dial options. + channelzID *channelz.Identifier // Channelz identifier for the channel. + balancerWrapper *ccBalancerWrapper // Uses gracefulswitch.balancer underneath. + // The following provide their own synchronization, and therefore don't + // require cc.mu to be held to access them. + csMgr *connectivityStateManager + blockingpicker *pickerWrapper safeConfigSelector iresolver.SafeConfigSelector + czData *channelzData + retryThrottler atomic.Value // Updated from service config. - mu sync.RWMutex - resolverWrapper *ccResolverWrapper - sc *ServiceConfig - conns map[*addrConn]struct{} - // Keepalive parameter can be updated if a GoAway is received. - mkp keepalive.ClientParameters - curBalancerName string - balancerWrapper *ccBalancerWrapper - retryThrottler atomic.Value - + // firstResolveEvent is used to track whether the name resolver sent us at + // least one update. RPCs block on this event. firstResolveEvent *grpcsync.Event - channelzID int64 // channelz unique identification number - czData *channelzData + // mu protects the following fields. + // TODO: split mu so the same mutex isn't used for everything. + mu sync.RWMutex + resolverWrapper *ccResolverWrapper // Initialized in Dial; cleared in Close. + sc *ServiceConfig // Latest service config received from the resolver. + conns map[*addrConn]struct{} // Set to nil on close. + mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway. lceMu sync.Mutex // protects lastConnectionError lastConnectionError error @@ -536,14 +535,7 @@ func (cc *ClientConn) GetState() connectivity.State { // Notice: This API is EXPERIMENTAL and may be changed or removed in a later // release. func (cc *ClientConn) Connect() { - cc.mu.Lock() - defer cc.mu.Unlock() - if cc.balancerWrapper != nil && cc.balancerWrapper.exitIdle() { - return - } - for ac := range cc.conns { - go ac.connect() - } + cc.balancerWrapper.exitIdle() } func (cc *ClientConn) scWatcher() { @@ -623,9 +615,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { // with the new addresses. cc.maybeApplyDefaultServiceConfig(nil) - if cc.balancerWrapper != nil { - cc.balancerWrapper.resolverError(err) - } + cc.balancerWrapper.resolverError(err) // No addresses are valid with err set; return early. cc.mu.Unlock() @@ -653,16 +643,10 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { cc.applyServiceConfigAndBalancer(sc, configSelector, s.Addresses) } else { ret = balancer.ErrBadResolverState - if cc.balancerWrapper == nil { - var err error - if s.ServiceConfig.Err != nil { - err = status.Errorf(codes.Unavailable, "error parsing service config: %v", s.ServiceConfig.Err) - } else { - err = status.Errorf(codes.Unavailable, "illegal service config type: %T", s.ServiceConfig.Config) - } - cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{cc.sc}) - cc.blockingpicker.updatePicker(base.NewErrPicker(err)) - cc.csMgr.updateState(connectivity.TransientFailure) + if cc.sc == nil { + // Apply the failing LB only if we haven't received valid service config + // from the name resolver in the past. + cc.applyFailingLB(s.ServiceConfig) cc.mu.Unlock() return ret } @@ -670,24 +654,12 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { } var balCfg serviceconfig.LoadBalancingConfig - if cc.dopts.balancerBuilder == nil && cc.sc != nil && cc.sc.lbConfig != nil { + if cc.sc != nil && cc.sc.lbConfig != nil { balCfg = cc.sc.lbConfig.cfg } - - cbn := cc.curBalancerName bw := cc.balancerWrapper cc.mu.Unlock() - if cbn != grpclbName { - // Filter any grpclb addresses since we don't have the grpclb balancer. - for i := 0; i < len(s.Addresses); { - if s.Addresses[i].Type == resolver.GRPCLB { - copy(s.Addresses[i:], s.Addresses[i+1:]) - s.Addresses = s.Addresses[:len(s.Addresses)-1] - continue - } - i++ - } - } + uccsErr := bw.updateClientConnState(&balancer.ClientConnState{ResolverState: s, BalancerConfig: balCfg}) if ret == nil { ret = uccsErr // prefer ErrBadResolver state since any other error is @@ -696,56 +668,28 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error { return ret } -// switchBalancer starts the switching from current balancer to the balancer -// with the given name. -// -// It will NOT send the current address list to the new balancer. If needed, -// caller of this function should send address list to the new balancer after -// this function returns. +// applyFailingLB is akin to configuring an LB policy on the channel which +// always fails RPCs. Here, an actual LB policy is not configured, but an always +// erroring picker is configured, which returns errors with information about +// what was invalid in the received service config. A config selector with no +// service config is configured, and the connectivity state of the channel is +// set to TransientFailure. // // Caller must hold cc.mu. -func (cc *ClientConn) switchBalancer(name string) { - if strings.EqualFold(cc.curBalancerName, name) { - return - } - - channelz.Infof(logger, cc.channelzID, "ClientConn switching balancer to %q", name) - if cc.dopts.balancerBuilder != nil { - channelz.Info(logger, cc.channelzID, "ignoring balancer switching: Balancer DialOption used instead") - return - } - if cc.balancerWrapper != nil { - // Don't hold cc.mu while closing the balancers. The balancers may call - // methods that require cc.mu (e.g. cc.NewSubConn()). Holding the mutex - // would cause a deadlock in that case. - cc.mu.Unlock() - cc.balancerWrapper.close() - cc.mu.Lock() - } - - builder := balancer.Get(name) - if builder == nil { - channelz.Warningf(logger, cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName) - channelz.Infof(logger, cc.channelzID, "failed to get balancer builder for: %v, using pick_first instead", name) - builder = newPickfirstBuilder() +func (cc *ClientConn) applyFailingLB(sc *serviceconfig.ParseResult) { + var err error + if sc.Err != nil { + err = status.Errorf(codes.Unavailable, "error parsing service config: %v", sc.Err) } else { - channelz.Infof(logger, cc.channelzID, "Channel switches to new LB policy %q", name) + err = status.Errorf(codes.Unavailable, "illegal service config type: %T", sc.Config) } - - cc.curBalancerName = builder.Name() - cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts) + cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil}) + cc.blockingpicker.updatePicker(base.NewErrPicker(err)) + cc.csMgr.updateState(connectivity.TransientFailure) } func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) { - cc.mu.Lock() - if cc.conns == nil { - cc.mu.Unlock() - return - } - // TODO(bar switching) send updates to all balancer wrappers when balancer - // gracefully switching is supported. - cc.balancerWrapper.handleSubConnStateChange(sc, s, err) - cc.mu.Unlock() + cc.balancerWrapper.updateSubConnState(sc, s, err) } // newAddrConn creates an addrConn for addrs and adds it to cc.conns. @@ -768,17 +712,21 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub cc.mu.Unlock() return nil, ErrClientConnClosing } - if channelz.IsOn() { - ac.channelzID = channelz.RegisterSubChannel(ac, cc.channelzID, "") - channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ - Desc: "Subchannel Created", - Severity: channelz.CtInfo, - Parent: &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelzID), - Severity: channelz.CtInfo, - }, - }) + + var err error + ac.channelzID, err = channelz.RegisterSubChannel(ac, cc.channelzID, "") + if err != nil { + return nil, err } + channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ + Desc: "Subchannel created", + Severity: channelz.CtInfo, + Parent: &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelzID.Int()), + Severity: channelz.CtInfo, + }, + }) + cc.conns[ac] = struct{}{} cc.mu.Unlock() return ac, nil @@ -853,16 +801,31 @@ func (ac *addrConn) connect() error { return nil } +func equalAddresses(a, b []resolver.Address) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if !v.Equal(b[i]) { + return false + } + } + return true +} + // tryUpdateAddrs tries to update ac.addrs with the new addresses list. // -// If ac is Connecting, it returns false. The caller should tear down the ac and -// create a new one. Note that the backoff will be reset when this happens. -// // If ac is TransientFailure, it updates ac.addrs and returns true. The updated // addresses will be picked up by retry in the next iteration after backoff. // // If ac is Shutdown or Idle, it updates ac.addrs and returns true. // +// If the addresses is the same as the old list, it does nothing and returns +// true. +// +// If ac is Connecting, it returns false. The caller should tear down the ac and +// create a new one. Note that the backoff will be reset when this happens. +// // If ac is Ready, it checks whether current connected address of ac is in the // new addrs list. // - If true, it updates ac.addrs and returns true. The ac will keep using @@ -879,6 +842,10 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool { return true } + if equalAddresses(ac.addrs, addrs) { + return true + } + if ac.state == connectivity.Connecting { return false } @@ -959,14 +926,10 @@ func (cc *ClientConn) healthCheckConfig() *healthCheckConfig { } func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, func(balancer.DoneInfo), error) { - t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{ + return cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{ Ctx: ctx, FullMethodName: method, }) - if err != nil { - return nil, nil, toRPCErr(err) - } - return t, done, nil } func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector, addrs []resolver.Address) { @@ -991,35 +954,26 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSel cc.retryThrottler.Store((*retryThrottler)(nil)) } - if cc.dopts.balancerBuilder == nil { - // Only look at balancer types and switch balancer if balancer dial - // option is not set. - var newBalancerName string - if cc.sc != nil && cc.sc.lbConfig != nil { - newBalancerName = cc.sc.lbConfig.name - } else { - var isGRPCLB bool - for _, a := range addrs { - if a.Type == resolver.GRPCLB { - isGRPCLB = true - break - } - } - if isGRPCLB { - newBalancerName = grpclbName - } else if cc.sc != nil && cc.sc.LB != nil { - newBalancerName = *cc.sc.LB - } else { - newBalancerName = PickFirstBalancerName + var newBalancerName string + if cc.sc != nil && cc.sc.lbConfig != nil { + newBalancerName = cc.sc.lbConfig.name + } else { + var isGRPCLB bool + for _, a := range addrs { + if a.Type == resolver.GRPCLB { + isGRPCLB = true + break } } - cc.switchBalancer(newBalancerName) - } else if cc.balancerWrapper == nil { - // Balancer dial option was set, and this is the first time handling - // resolved addresses. Build a balancer with dopts.balancerBuilder. - cc.curBalancerName = cc.dopts.balancerBuilder.Name() - cc.balancerWrapper = newCCBalancerWrapper(cc, cc.dopts.balancerBuilder, cc.balancerBuildOpts) + if isGRPCLB { + newBalancerName = grpclbName + } else if cc.sc != nil && cc.sc.LB != nil { + newBalancerName = *cc.sc.LB + } else { + newBalancerName = PickFirstBalancerName + } } + cc.balancerWrapper.switchTo(newBalancerName) } func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) { @@ -1070,11 +1024,11 @@ func (cc *ClientConn) Close() error { rWrapper := cc.resolverWrapper cc.resolverWrapper = nil bWrapper := cc.balancerWrapper - cc.balancerWrapper = nil cc.mu.Unlock() + // The order of closing matters here since the balancer wrapper assumes the + // picker is closed before it is closed. cc.blockingpicker.close() - if bWrapper != nil { bWrapper.close() } @@ -1085,22 +1039,22 @@ func (cc *ClientConn) Close() error { for ac := range conns { ac.tearDown(ErrClientConnClosing) } - if channelz.IsOn() { - ted := &channelz.TraceEventDesc{ - Desc: "Channel Deleted", + ted := &channelz.TraceEventDesc{ + Desc: "Channel deleted", + Severity: channelz.CtInfo, + } + if cc.dopts.channelzParentID != nil { + ted.Parent = &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Nested channel(id:%d) deleted", cc.channelzID.Int()), Severity: channelz.CtInfo, } - if cc.dopts.channelzParentID != 0 { - ted.Parent = &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Nested channel(id:%d) deleted", cc.channelzID), - Severity: channelz.CtInfo, - } - } - channelz.AddTraceEvent(logger, cc.channelzID, 0, ted) - // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to - // the entity being deleted, and thus prevent it from being deleted right away. - channelz.RemoveEntry(cc.channelzID) } + channelz.AddTraceEvent(logger, cc.channelzID, 0, ted) + // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add + // trace reference to the entity being deleted, and thus prevent it from being + // deleted right away. + channelz.RemoveEntry(cc.channelzID) + return nil } @@ -1130,7 +1084,7 @@ type addrConn struct { backoffIdx int // Needs to be stateful for resetConnectBackoff. resetBackoff chan struct{} - channelzID int64 // channelz unique identification number. + channelzID *channelz.Identifier czData *channelzData } @@ -1284,6 +1238,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne ac.mu.Lock() defer ac.mu.Unlock() defer connClosed.Fire() + defer hcancel() if !hcStarted || hctx.Err() != nil { // We didn't start the health check or set the state to READY, so // no need to do anything else here. @@ -1294,7 +1249,6 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne // state, since there may be a new transport in this addrConn. return } - hcancel() ac.transport = nil // Refresh the name resolver ac.cc.resolveNow(resolver.ResolveNowOptions{}) @@ -1312,14 +1266,13 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline) defer cancel() - if channelz.IsOn() { - copts.ChannelzParentID = ac.channelzID - } + copts.ChannelzParentID = ac.channelzID newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, func() { prefaceReceived.Fire() }, onGoAway, onClose) if err != nil { // newTr is either nil, or closed. - channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v", addr, err) + hcancel() + channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err) return err } @@ -1332,7 +1285,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne newTr.Close(transport.ErrConnClosing) if connectCtx.Err() == context.DeadlineExceeded { err := errors.New("failed to receive server preface within timeout") - channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: %v", addr, err) + channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %s: %v", addr, err) return err } return nil @@ -1497,19 +1450,18 @@ func (ac *addrConn) tearDown(err error) { curTr.GracefulClose() ac.mu.Lock() } - if channelz.IsOn() { - channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ - Desc: "Subchannel Deleted", + channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{ + Desc: "Subchannel deleted", + Severity: channelz.CtInfo, + Parent: &channelz.TraceEventDesc{ + Desc: fmt.Sprintf("Subchannel(id:%d) deleted", ac.channelzID.Int()), Severity: channelz.CtInfo, - Parent: &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Subchanel(id:%d) deleted", ac.channelzID), - Severity: channelz.CtInfo, - }, - }) - // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to - // the entity being deleted, and thus prevent it from being deleted right away. - channelz.RemoveEntry(ac.channelzID) - } + }, + }) + // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add + // trace reference to the entity being deleted, and thus prevent it from + // being deleted right away. + channelz.RemoveEntry(ac.channelzID) ac.mu.Unlock() } diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index c4bf09f9..f2f605a1 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -20,12 +20,11 @@ package grpc import ( "context" - "fmt" "net" "time" "google.golang.org/grpc/backoff" - "google.golang.org/grpc/balancer" + "google.golang.org/grpc/channelz" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/internal" @@ -45,19 +44,17 @@ type dialOptions struct { chainUnaryInts []UnaryClientInterceptor chainStreamInts []StreamClientInterceptor - cp Compressor - dc Decompressor - bs internalbackoff.Strategy - block bool - returnLastError bool - timeout time.Duration - scChan <-chan ServiceConfig - authority string - copts transport.ConnectOptions - callOptions []CallOption - // This is used by WithBalancerName dial option. - balancerBuilder balancer.Builder - channelzParentID int64 + cp Compressor + dc Decompressor + bs internalbackoff.Strategy + block bool + returnLastError bool + timeout time.Duration + scChan <-chan ServiceConfig + authority string + copts transport.ConnectOptions + callOptions []CallOption + channelzParentID *channelz.Identifier disableServiceConfig bool disableRetry bool disableHealthCheck bool @@ -195,25 +192,6 @@ func WithDecompressor(dc Decompressor) DialOption { }) } -// WithBalancerName sets the balancer that the ClientConn will be initialized -// with. Balancer registered with balancerName will be used. This function -// panics if no balancer was registered by balancerName. -// -// The balancer cannot be overridden by balancer option specified by service -// config. -// -// Deprecated: use WithDefaultServiceConfig and WithDisableServiceConfig -// instead. Will be removed in a future 1.x release. -func WithBalancerName(balancerName string) DialOption { - builder := balancer.Get(balancerName) - if builder == nil { - panic(fmt.Sprintf("grpc.WithBalancerName: no balancer is registered for name %v", balancerName)) - } - return newFuncDialOption(func(o *dialOptions) { - o.balancerBuilder = builder - }) -} - // WithServiceConfig returns a DialOption which has a channel to read the // service configuration. // @@ -304,8 +282,8 @@ func WithReturnConnectionError() DialOption { // WithCredentialsBundle or WithPerRPCCredentials) which require transport // security is incompatible and will cause grpc.Dial() to fail. // -// Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead. -// Will be supported throughout 1.x. +// Deprecated: use WithTransportCredentials and insecure.NewCredentials() +// instead. Will be supported throughout 1.x. func WithInsecure() DialOption { return newFuncDialOption(func(o *dialOptions) { o.copts.TransportCredentials = insecure.NewCredentials() @@ -498,7 +476,7 @@ func WithAuthority(a string) DialOption { // // Notice: This API is EXPERIMENTAL and may be changed or removed in a // later release. -func WithChannelzParentID(id int64) DialOption { +func WithChannelzParentID(id *channelz.Identifier) DialOption { return newFuncDialOption(func(o *dialOptions) { o.channelzParentID = id }) diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go index 6d84f74c..18e530fc 100644 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/vendor/google.golang.org/grpc/encoding/encoding.go @@ -108,7 +108,7 @@ var registeredCodecs = make(map[string]Codec) // more details. // // NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Compressors are +// an init() function), and is not thread-safe. If multiple Codecs are // registered with the same name, the one registered last will take effect. func RegisterCodec(codec Codec) { if codec == nil { diff --git a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go new file mode 100644 index 00000000..7ba8f4d1 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go @@ -0,0 +1,382 @@ +/* + * + * Copyright 2022 gRPC authors. + * + * 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 gracefulswitch implements a graceful switch load balancer. +package gracefulswitch + +import ( + "errors" + "fmt" + "sync" + + "google.golang.org/grpc/balancer" + "google.golang.org/grpc/balancer/base" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/resolver" +) + +var errBalancerClosed = errors.New("gracefulSwitchBalancer is closed") +var _ balancer.Balancer = (*Balancer)(nil) + +// NewBalancer returns a graceful switch Balancer. +func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions) *Balancer { + return &Balancer{ + cc: cc, + bOpts: opts, + } +} + +// Balancer is a utility to gracefully switch from one balancer to +// a new balancer. It implements the balancer.Balancer interface. +type Balancer struct { + bOpts balancer.BuildOptions + cc balancer.ClientConn + + // mu protects the following fields and all fields within balancerCurrent + // and balancerPending. mu does not need to be held when calling into the + // child balancers, as all calls into these children happen only as a direct + // result of a call into the gracefulSwitchBalancer, which are also + // guaranteed to be synchronous. There is one exception: an UpdateState call + // from a child balancer when current and pending are populated can lead to + // calling Close() on the current. To prevent that racing with an + // UpdateSubConnState from the channel, we hold currentMu during Close and + // UpdateSubConnState calls. + mu sync.Mutex + balancerCurrent *balancerWrapper + balancerPending *balancerWrapper + closed bool // set to true when this balancer is closed + + // currentMu must be locked before mu. This mutex guards against this + // sequence of events: UpdateSubConnState() called, finds the + // balancerCurrent, gives up lock, updateState comes in, causes Close() on + // balancerCurrent before the UpdateSubConnState is called on the + // balancerCurrent. + currentMu sync.Mutex +} + +// swap swaps out the current lb with the pending lb and updates the ClientConn. +// The caller must hold gsb.mu. +func (gsb *Balancer) swap() { + gsb.cc.UpdateState(gsb.balancerPending.lastState) + cur := gsb.balancerCurrent + gsb.balancerCurrent = gsb.balancerPending + gsb.balancerPending = nil + go func() { + gsb.currentMu.Lock() + defer gsb.currentMu.Unlock() + cur.Close() + }() +} + +// Helper function that checks if the balancer passed in is current or pending. +// The caller must hold gsb.mu. +func (gsb *Balancer) balancerCurrentOrPending(bw *balancerWrapper) bool { + return bw == gsb.balancerCurrent || bw == gsb.balancerPending +} + +// SwitchTo initializes the graceful switch process, which completes based on +// connectivity state changes on the current/pending balancer. Thus, the switch +// process is not complete when this method returns. This method must be called +// synchronously alongside the rest of the balancer.Balancer methods this +// Graceful Switch Balancer implements. +func (gsb *Balancer) SwitchTo(builder balancer.Builder) error { + gsb.mu.Lock() + if gsb.closed { + gsb.mu.Unlock() + return errBalancerClosed + } + bw := &balancerWrapper{ + gsb: gsb, + lastState: balancer.State{ + ConnectivityState: connectivity.Connecting, + Picker: base.NewErrPicker(balancer.ErrNoSubConnAvailable), + }, + subconns: make(map[balancer.SubConn]bool), + } + balToClose := gsb.balancerPending // nil if there is no pending balancer + if gsb.balancerCurrent == nil { + gsb.balancerCurrent = bw + } else { + gsb.balancerPending = bw + } + gsb.mu.Unlock() + balToClose.Close() + // This function takes a builder instead of a balancer because builder.Build + // can call back inline, and this utility needs to handle the callbacks. + newBalancer := builder.Build(bw, gsb.bOpts) + if newBalancer == nil { + // This is illegal and should never happen; we clear the balancerWrapper + // we were constructing if it happens to avoid a potential panic. + gsb.mu.Lock() + if gsb.balancerPending != nil { + gsb.balancerPending = nil + } else { + gsb.balancerCurrent = nil + } + gsb.mu.Unlock() + return balancer.ErrBadResolverState + } + + // This write doesn't need to take gsb.mu because this field never gets read + // or written to on any calls from the current or pending. Calls from grpc + // to this balancer are guaranteed to be called synchronously, so this + // bw.Balancer field will never be forwarded to until this SwitchTo() + // function returns. + bw.Balancer = newBalancer + return nil +} + +// Returns nil if the graceful switch balancer is closed. +func (gsb *Balancer) latestBalancer() *balancerWrapper { + gsb.mu.Lock() + defer gsb.mu.Unlock() + if gsb.balancerPending != nil { + return gsb.balancerPending + } + return gsb.balancerCurrent +} + +// UpdateClientConnState forwards the update to the latest balancer created. +func (gsb *Balancer) UpdateClientConnState(state balancer.ClientConnState) error { + // The resolver data is only relevant to the most recent LB Policy. + balToUpdate := gsb.latestBalancer() + if balToUpdate == nil { + return errBalancerClosed + } + // Perform this call without gsb.mu to prevent deadlocks if the child calls + // back into the channel. The latest balancer can never be closed during a + // call from the channel, even without gsb.mu held. + return balToUpdate.UpdateClientConnState(state) +} + +// ResolverError forwards the error to the latest balancer created. +func (gsb *Balancer) ResolverError(err error) { + // The resolver data is only relevant to the most recent LB Policy. + balToUpdate := gsb.latestBalancer() + if balToUpdate == nil { + return + } + // Perform this call without gsb.mu to prevent deadlocks if the child calls + // back into the channel. The latest balancer can never be closed during a + // call from the channel, even without gsb.mu held. + balToUpdate.ResolverError(err) +} + +// ExitIdle forwards the call to the latest balancer created. +// +// If the latest balancer does not support ExitIdle, the subConns are +// re-connected to manually. +func (gsb *Balancer) ExitIdle() { + balToUpdate := gsb.latestBalancer() + if balToUpdate == nil { + return + } + // There is no need to protect this read with a mutex, as the write to the + // Balancer field happens in SwitchTo, which completes before this can be + // called. + if ei, ok := balToUpdate.Balancer.(balancer.ExitIdler); ok { + ei.ExitIdle() + return + } + for sc := range balToUpdate.subconns { + sc.Connect() + } +} + +// UpdateSubConnState forwards the update to the appropriate child. +func (gsb *Balancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { + gsb.currentMu.Lock() + defer gsb.currentMu.Unlock() + gsb.mu.Lock() + // Forward update to the appropriate child. Even if there is a pending + // balancer, the current balancer should continue to get SubConn updates to + // maintain the proper state while the pending is still connecting. + var balToUpdate *balancerWrapper + if gsb.balancerCurrent != nil && gsb.balancerCurrent.subconns[sc] { + balToUpdate = gsb.balancerCurrent + } else if gsb.balancerPending != nil && gsb.balancerPending.subconns[sc] { + balToUpdate = gsb.balancerPending + } + gsb.mu.Unlock() + if balToUpdate == nil { + // SubConn belonged to a stale lb policy that has not yet fully closed, + // or the balancer was already closed. + return + } + balToUpdate.UpdateSubConnState(sc, state) +} + +// Close closes any active child balancers. +func (gsb *Balancer) Close() { + gsb.mu.Lock() + gsb.closed = true + currentBalancerToClose := gsb.balancerCurrent + gsb.balancerCurrent = nil + pendingBalancerToClose := gsb.balancerPending + gsb.balancerPending = nil + gsb.mu.Unlock() + + currentBalancerToClose.Close() + pendingBalancerToClose.Close() +} + +// balancerWrapper wraps a balancer.Balancer, and overrides some Balancer +// methods to help cleanup SubConns created by the wrapped balancer. +// +// It implements the balancer.ClientConn interface and is passed down in that +// capacity to the wrapped balancer. It maintains a set of subConns created by +// the wrapped balancer and calls from the latter to create/update/remove +// SubConns update this set before being forwarded to the parent ClientConn. +// State updates from the wrapped balancer can result in invocation of the +// graceful switch logic. +type balancerWrapper struct { + balancer.Balancer + gsb *Balancer + + lastState balancer.State + subconns map[balancer.SubConn]bool // subconns created by this balancer +} + +func (bw *balancerWrapper) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { + if state.ConnectivityState == connectivity.Shutdown { + bw.gsb.mu.Lock() + delete(bw.subconns, sc) + bw.gsb.mu.Unlock() + } + // There is no need to protect this read with a mutex, as the write to the + // Balancer field happens in SwitchTo, which completes before this can be + // called. + bw.Balancer.UpdateSubConnState(sc, state) +} + +// Close closes the underlying LB policy and removes the subconns it created. bw +// must not be referenced via balancerCurrent or balancerPending in gsb when +// called. gsb.mu must not be held. Does not panic with a nil receiver. +func (bw *balancerWrapper) Close() { + // before Close is called. + if bw == nil { + return + } + // There is no need to protect this read with a mutex, as Close() is + // impossible to be called concurrently with the write in SwitchTo(). The + // callsites of Close() for this balancer in Graceful Switch Balancer will + // never be called until SwitchTo() returns. + bw.Balancer.Close() + bw.gsb.mu.Lock() + for sc := range bw.subconns { + bw.gsb.cc.RemoveSubConn(sc) + } + bw.gsb.mu.Unlock() +} + +func (bw *balancerWrapper) UpdateState(state balancer.State) { + // Hold the mutex for this entire call to ensure it cannot occur + // concurrently with other updateState() calls. This causes updates to + // lastState and calls to cc.UpdateState to happen atomically. + bw.gsb.mu.Lock() + defer bw.gsb.mu.Unlock() + bw.lastState = state + + if !bw.gsb.balancerCurrentOrPending(bw) { + return + } + + if bw == bw.gsb.balancerCurrent { + // In the case that the current balancer exits READY, and there is a pending + // balancer, you can forward the pending balancer's cached State up to + // ClientConn and swap the pending into the current. This is because there + // is no reason to gracefully switch from and keep using the old policy as + // the ClientConn is not connected to any backends. + if state.ConnectivityState != connectivity.Ready && bw.gsb.balancerPending != nil { + bw.gsb.swap() + return + } + // Even if there is a pending balancer waiting to be gracefully switched to, + // continue to forward current balancer updates to the Client Conn. Ignoring + // state + picker from the current would cause undefined behavior/cause the + // system to behave incorrectly from the current LB policies perspective. + // Also, the current LB is still being used by grpc to choose SubConns per + // RPC, and thus should use the most updated form of the current balancer. + bw.gsb.cc.UpdateState(state) + return + } + // This method is now dealing with a state update from the pending balancer. + // If the current balancer is currently in a state other than READY, the new + // policy can be swapped into place immediately. This is because there is no + // reason to gracefully switch from and keep using the old policy as the + // ClientConn is not connected to any backends. + if state.ConnectivityState != connectivity.Connecting || bw.gsb.balancerCurrent.lastState.ConnectivityState != connectivity.Ready { + bw.gsb.swap() + } +} + +func (bw *balancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { + bw.gsb.mu.Lock() + if !bw.gsb.balancerCurrentOrPending(bw) { + bw.gsb.mu.Unlock() + return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw) + } + bw.gsb.mu.Unlock() + + sc, err := bw.gsb.cc.NewSubConn(addrs, opts) + if err != nil { + return nil, err + } + bw.gsb.mu.Lock() + if !bw.gsb.balancerCurrentOrPending(bw) { // balancer was closed during this call + bw.gsb.cc.RemoveSubConn(sc) + bw.gsb.mu.Unlock() + return nil, fmt.Errorf("%T at address %p that called NewSubConn is deleted", bw, bw) + } + bw.subconns[sc] = true + bw.gsb.mu.Unlock() + return sc, nil +} + +func (bw *balancerWrapper) ResolveNow(opts resolver.ResolveNowOptions) { + // Ignore ResolveNow requests from anything other than the most recent + // balancer, because older balancers were already removed from the config. + if bw != bw.gsb.latestBalancer() { + return + } + bw.gsb.cc.ResolveNow(opts) +} + +func (bw *balancerWrapper) RemoveSubConn(sc balancer.SubConn) { + bw.gsb.mu.Lock() + if !bw.gsb.balancerCurrentOrPending(bw) { + bw.gsb.mu.Unlock() + return + } + bw.gsb.mu.Unlock() + bw.gsb.cc.RemoveSubConn(sc) +} + +func (bw *balancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) { + bw.gsb.mu.Lock() + if !bw.gsb.balancerCurrentOrPending(bw) { + bw.gsb.mu.Unlock() + return + } + bw.gsb.mu.Unlock() + bw.gsb.cc.UpdateAddresses(sc, addrs) +} + +func (bw *balancerWrapper) Target() string { + return bw.gsb.cc.Target() +} diff --git a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go index 5cc3aedd..0a25ce43 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go @@ -31,7 +31,7 @@ import ( // Logger is the global binary logger. It can be used to get binary logger for // each method. type Logger interface { - getMethodLogger(methodName string) *MethodLogger + GetMethodLogger(methodName string) MethodLogger } // binLogger is the global binary logger for the binary. One of this should be @@ -49,17 +49,24 @@ func SetLogger(l Logger) { binLogger = l } +// GetLogger gets the binarg logger. +// +// Only call this at init time. +func GetLogger() Logger { + return binLogger +} + // GetMethodLogger returns the methodLogger for the given methodName. // // methodName should be in the format of "/service/method". // // Each methodLogger returned by this method is a new instance. This is to // generate sequence id within the call. -func GetMethodLogger(methodName string) *MethodLogger { +func GetMethodLogger(methodName string) MethodLogger { if binLogger == nil { return nil } - return binLogger.getMethodLogger(methodName) + return binLogger.GetMethodLogger(methodName) } func init() { @@ -68,17 +75,29 @@ func init() { binLogger = NewLoggerFromConfigString(configStr) } -type methodLoggerConfig struct { +// MethodLoggerConfig contains the setting for logging behavior of a method +// logger. Currently, it contains the max length of header and message. +type MethodLoggerConfig struct { // Max length of header and message. - hdr, msg uint64 + Header, Message uint64 +} + +// LoggerConfig contains the config for loggers to create method loggers. +type LoggerConfig struct { + All *MethodLoggerConfig + Services map[string]*MethodLoggerConfig + Methods map[string]*MethodLoggerConfig + + Blacklist map[string]struct{} } type logger struct { - all *methodLoggerConfig - services map[string]*methodLoggerConfig - methods map[string]*methodLoggerConfig + config LoggerConfig +} - blacklist map[string]struct{} +// NewLoggerFromConfig builds a logger with the given LoggerConfig. +func NewLoggerFromConfig(config LoggerConfig) Logger { + return &logger{config: config} } // newEmptyLogger creates an empty logger. The map fields need to be filled in @@ -88,57 +107,57 @@ func newEmptyLogger() *logger { } // Set method logger for "*". -func (l *logger) setDefaultMethodLogger(ml *methodLoggerConfig) error { - if l.all != nil { +func (l *logger) setDefaultMethodLogger(ml *MethodLoggerConfig) error { + if l.config.All != nil { return fmt.Errorf("conflicting global rules found") } - l.all = ml + l.config.All = ml return nil } // Set method logger for "service/*". // // New methodLogger with same service overrides the old one. -func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error { - if _, ok := l.services[service]; ok { +func (l *logger) setServiceMethodLogger(service string, ml *MethodLoggerConfig) error { + if _, ok := l.config.Services[service]; ok { return fmt.Errorf("conflicting service rules for service %v found", service) } - if l.services == nil { - l.services = make(map[string]*methodLoggerConfig) + if l.config.Services == nil { + l.config.Services = make(map[string]*MethodLoggerConfig) } - l.services[service] = ml + l.config.Services[service] = ml return nil } // Set method logger for "service/method". // // New methodLogger with same method overrides the old one. -func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error { - if _, ok := l.blacklist[method]; ok { +func (l *logger) setMethodMethodLogger(method string, ml *MethodLoggerConfig) error { + if _, ok := l.config.Blacklist[method]; ok { return fmt.Errorf("conflicting blacklist rules for method %v found", method) } - if _, ok := l.methods[method]; ok { + if _, ok := l.config.Methods[method]; ok { return fmt.Errorf("conflicting method rules for method %v found", method) } - if l.methods == nil { - l.methods = make(map[string]*methodLoggerConfig) + if l.config.Methods == nil { + l.config.Methods = make(map[string]*MethodLoggerConfig) } - l.methods[method] = ml + l.config.Methods[method] = ml return nil } // Set blacklist method for "-service/method". func (l *logger) setBlacklist(method string) error { - if _, ok := l.blacklist[method]; ok { + if _, ok := l.config.Blacklist[method]; ok { return fmt.Errorf("conflicting blacklist rules for method %v found", method) } - if _, ok := l.methods[method]; ok { + if _, ok := l.config.Methods[method]; ok { return fmt.Errorf("conflicting method rules for method %v found", method) } - if l.blacklist == nil { - l.blacklist = make(map[string]struct{}) + if l.config.Blacklist == nil { + l.config.Blacklist = make(map[string]struct{}) } - l.blacklist[method] = struct{}{} + l.config.Blacklist[method] = struct{}{} return nil } @@ -148,23 +167,23 @@ func (l *logger) setBlacklist(method string) error { // // Each methodLogger returned by this method is a new instance. This is to // generate sequence id within the call. -func (l *logger) getMethodLogger(methodName string) *MethodLogger { +func (l *logger) GetMethodLogger(methodName string) MethodLogger { s, m, err := grpcutil.ParseMethod(methodName) if err != nil { grpclogLogger.Infof("binarylogging: failed to parse %q: %v", methodName, err) return nil } - if ml, ok := l.methods[s+"/"+m]; ok { - return newMethodLogger(ml.hdr, ml.msg) + if ml, ok := l.config.Methods[s+"/"+m]; ok { + return newMethodLogger(ml.Header, ml.Message) } - if _, ok := l.blacklist[s+"/"+m]; ok { + if _, ok := l.config.Blacklist[s+"/"+m]; ok { return nil } - if ml, ok := l.services[s]; ok { - return newMethodLogger(ml.hdr, ml.msg) + if ml, ok := l.config.Services[s]; ok { + return newMethodLogger(ml.Header, ml.Message) } - if l.all == nil { + if l.config.All == nil { return nil } - return newMethodLogger(l.all.hdr, l.all.msg) + return newMethodLogger(l.config.All.Header, l.config.All.Message) } diff --git a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go index d8f4e760..ab589a76 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go @@ -89,7 +89,7 @@ func (l *logger) fillMethodLoggerWithConfigString(config string) error { if err != nil { return fmt.Errorf("invalid config: %q, %v", config, err) } - if err := l.setDefaultMethodLogger(&methodLoggerConfig{hdr: hdr, msg: msg}); err != nil { + if err := l.setDefaultMethodLogger(&MethodLoggerConfig{Header: hdr, Message: msg}); err != nil { return fmt.Errorf("invalid config: %v", err) } return nil @@ -104,11 +104,11 @@ func (l *logger) fillMethodLoggerWithConfigString(config string) error { return fmt.Errorf("invalid header/message length config: %q, %v", suffix, err) } if m == "*" { - if err := l.setServiceMethodLogger(s, &methodLoggerConfig{hdr: hdr, msg: msg}); err != nil { + if err := l.setServiceMethodLogger(s, &MethodLoggerConfig{Header: hdr, Message: msg}); err != nil { return fmt.Errorf("invalid config: %v", err) } } else { - if err := l.setMethodMethodLogger(s+"/"+m, &methodLoggerConfig{hdr: hdr, msg: msg}); err != nil { + if err := l.setMethodMethodLogger(s+"/"+m, &MethodLoggerConfig{Header: hdr, Message: msg}); err != nil { return fmt.Errorf("invalid config: %v", err) } } diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go index 0cdb4183..24df0a1a 100644 --- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go +++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go @@ -48,7 +48,11 @@ func (g *callIDGenerator) reset() { var idGen callIDGenerator // MethodLogger is the sub-logger for each method. -type MethodLogger struct { +type MethodLogger interface { + Log(LogEntryConfig) +} + +type methodLogger struct { headerMaxLen, messageMaxLen uint64 callID uint64 @@ -57,8 +61,8 @@ type MethodLogger struct { sink Sink // TODO(blog): make this plugable. } -func newMethodLogger(h, m uint64) *MethodLogger { - return &MethodLogger{ +func newMethodLogger(h, m uint64) *methodLogger { + return &methodLogger{ headerMaxLen: h, messageMaxLen: m, @@ -69,8 +73,10 @@ func newMethodLogger(h, m uint64) *MethodLogger { } } -// Log creates a proto binary log entry, and logs it to the sink. -func (ml *MethodLogger) Log(c LogEntryConfig) { +// Build is an internal only method for building the proto message out of the +// input event. It's made public to enable other library to reuse as much logic +// in methodLogger as possible. +func (ml *methodLogger) Build(c LogEntryConfig) *pb.GrpcLogEntry { m := c.toProto() timestamp, _ := ptypes.TimestampProto(time.Now()) m.Timestamp = timestamp @@ -85,11 +91,15 @@ func (ml *MethodLogger) Log(c LogEntryConfig) { case *pb.GrpcLogEntry_Message: m.PayloadTruncated = ml.truncateMessage(pay.Message) } - - ml.sink.Write(m) + return m } -func (ml *MethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) { +// Log creates a proto binary log entry, and logs it to the sink. +func (ml *methodLogger) Log(c LogEntryConfig) { + ml.sink.Write(ml.Build(c)) +} + +func (ml *methodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) { if ml.headerMaxLen == maxUInt { return false } @@ -119,7 +129,7 @@ func (ml *MethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated bool) { return truncated } -func (ml *MethodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) { +func (ml *methodLogger) truncateMessage(msgPb *pb.Message) (truncated bool) { if ml.messageMaxLen == maxUInt { return false } diff --git a/vendor/google.golang.org/grpc/internal/channelz/funcs.go b/vendor/google.golang.org/grpc/internal/channelz/funcs.go index ea660a14..777cbcd7 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/funcs.go +++ b/vendor/google.golang.org/grpc/internal/channelz/funcs.go @@ -25,6 +25,7 @@ package channelz import ( "context" + "errors" "fmt" "sort" "sync" @@ -184,54 +185,77 @@ func GetServer(id int64) *ServerMetric { return db.get().GetServer(id) } -// RegisterChannel registers the given channel c in channelz database with ref -// as its reference name, and add it to the child list of its parent (identified -// by pid). pid = 0 means no parent. It returns the unique channelz tracking id -// assigned to this channel. -func RegisterChannel(c Channel, pid int64, ref string) int64 { +// RegisterChannel registers the given channel c in the channelz database with +// ref as its reference name, and adds it to the child list of its parent +// (identified by pid). pid == nil means no parent. +// +// Returns a unique channelz identifier assigned to this channel. +// +// If channelz is not turned ON, the channelz database is not mutated. +func RegisterChannel(c Channel, pid *Identifier, ref string) *Identifier { id := idGen.genID() + var parent int64 + isTopChannel := true + if pid != nil { + isTopChannel = false + parent = pid.Int() + } + + if !IsOn() { + return newIdentifer(RefChannel, id, pid) + } + cn := &channel{ refName: ref, c: c, subChans: make(map[int64]string), nestedChans: make(map[int64]string), id: id, - pid: pid, + pid: parent, trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, } - if pid == 0 { - db.get().addChannel(id, cn, true, pid) - } else { - db.get().addChannel(id, cn, false, pid) - } - return id + db.get().addChannel(id, cn, isTopChannel, parent) + return newIdentifer(RefChannel, id, pid) } -// RegisterSubChannel registers the given channel c in channelz database with ref -// as its reference name, and add it to the child list of its parent (identified -// by pid). It returns the unique channelz tracking id assigned to this subchannel. -func RegisterSubChannel(c Channel, pid int64, ref string) int64 { - if pid == 0 { - logger.Error("a SubChannel's parent id cannot be 0") - return 0 +// RegisterSubChannel registers the given subChannel c in the channelz database +// with ref as its reference name, and adds it to the child list of its parent +// (identified by pid). +// +// Returns a unique channelz identifier assigned to this subChannel. +// +// If channelz is not turned ON, the channelz database is not mutated. +func RegisterSubChannel(c Channel, pid *Identifier, ref string) (*Identifier, error) { + if pid == nil { + return nil, errors.New("a SubChannel's parent id cannot be nil") } id := idGen.genID() + if !IsOn() { + return newIdentifer(RefSubChannel, id, pid), nil + } + sc := &subChannel{ refName: ref, c: c, sockets: make(map[int64]string), id: id, - pid: pid, + pid: pid.Int(), trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())}, } - db.get().addSubChannel(id, sc, pid) - return id + db.get().addSubChannel(id, sc, pid.Int()) + return newIdentifer(RefSubChannel, id, pid), nil } // RegisterServer registers the given server s in channelz database. It returns // the unique channelz tracking id assigned to this server. -func RegisterServer(s Server, ref string) int64 { +// +// If channelz is not turned ON, the channelz database is not mutated. +func RegisterServer(s Server, ref string) *Identifier { id := idGen.genID() + if !IsOn() { + return newIdentifer(RefServer, id, nil) + } + svr := &server{ refName: ref, s: s, @@ -240,71 +264,92 @@ func RegisterServer(s Server, ref string) int64 { id: id, } db.get().addServer(id, svr) - return id + return newIdentifer(RefServer, id, nil) } // RegisterListenSocket registers the given listen socket s in channelz database // with ref as its reference name, and add it to the child list of its parent // (identified by pid). It returns the unique channelz tracking id assigned to // this listen socket. -func RegisterListenSocket(s Socket, pid int64, ref string) int64 { - if pid == 0 { - logger.Error("a ListenSocket's parent id cannot be 0") - return 0 +// +// If channelz is not turned ON, the channelz database is not mutated. +func RegisterListenSocket(s Socket, pid *Identifier, ref string) (*Identifier, error) { + if pid == nil { + return nil, errors.New("a ListenSocket's parent id cannot be 0") } id := idGen.genID() - ls := &listenSocket{refName: ref, s: s, id: id, pid: pid} - db.get().addListenSocket(id, ls, pid) - return id + if !IsOn() { + return newIdentifer(RefListenSocket, id, pid), nil + } + + ls := &listenSocket{refName: ref, s: s, id: id, pid: pid.Int()} + db.get().addListenSocket(id, ls, pid.Int()) + return newIdentifer(RefListenSocket, id, pid), nil } // RegisterNormalSocket registers the given normal socket s in channelz database -// with ref as its reference name, and add it to the child list of its parent +// with ref as its reference name, and adds it to the child list of its parent // (identified by pid). It returns the unique channelz tracking id assigned to // this normal socket. -func RegisterNormalSocket(s Socket, pid int64, ref string) int64 { - if pid == 0 { - logger.Error("a NormalSocket's parent id cannot be 0") - return 0 +// +// If channelz is not turned ON, the channelz database is not mutated. +func RegisterNormalSocket(s Socket, pid *Identifier, ref string) (*Identifier, error) { + if pid == nil { + return nil, errors.New("a NormalSocket's parent id cannot be 0") } id := idGen.genID() - ns := &normalSocket{refName: ref, s: s, id: id, pid: pid} - db.get().addNormalSocket(id, ns, pid) - return id + if !IsOn() { + return newIdentifer(RefNormalSocket, id, pid), nil + } + + ns := &normalSocket{refName: ref, s: s, id: id, pid: pid.Int()} + db.get().addNormalSocket(id, ns, pid.Int()) + return newIdentifer(RefNormalSocket, id, pid), nil } // RemoveEntry removes an entry with unique channelz tracking id to be id from // channelz database. -func RemoveEntry(id int64) { - db.get().removeEntry(id) +// +// If channelz is not turned ON, this function is a no-op. +func RemoveEntry(id *Identifier) { + if !IsOn() { + return + } + db.get().removeEntry(id.Int()) } -// TraceEventDesc is what the caller of AddTraceEvent should provide to describe the event to be added -// to the channel trace. -// The Parent field is optional. It is used for event that will be recorded in the entity's parent -// trace also. +// TraceEventDesc is what the caller of AddTraceEvent should provide to describe +// the event to be added to the channel trace. +// +// The Parent field is optional. It is used for an event that will be recorded +// in the entity's parent trace. type TraceEventDesc struct { Desc string Severity Severity Parent *TraceEventDesc } -// AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc. -func AddTraceEvent(l grpclog.DepthLoggerV2, id int64, depth int, desc *TraceEventDesc) { - for d := desc; d != nil; d = d.Parent { - switch d.Severity { - case CtUnknown, CtInfo: - l.InfoDepth(depth+1, d.Desc) - case CtWarning: - l.WarningDepth(depth+1, d.Desc) - case CtError: - l.ErrorDepth(depth+1, d.Desc) - } +// AddTraceEvent adds trace related to the entity with specified id, using the +// provided TraceEventDesc. +// +// If channelz is not turned ON, this will simply log the event descriptions. +func AddTraceEvent(l grpclog.DepthLoggerV2, id *Identifier, depth int, desc *TraceEventDesc) { + // Log only the trace description associated with the bottom most entity. + switch desc.Severity { + case CtUnknown, CtInfo: + l.InfoDepth(depth+1, withParens(id)+desc.Desc) + case CtWarning: + l.WarningDepth(depth+1, withParens(id)+desc.Desc) + case CtError: + l.ErrorDepth(depth+1, withParens(id)+desc.Desc) } + if getMaxTraceEntry() == 0 { return } - db.get().traceEvent(id, desc) + if IsOn() { + db.get().traceEvent(id.Int(), desc) + } } // channelMap is the storage data structure for channelz. diff --git a/vendor/google.golang.org/grpc/internal/channelz/id.go b/vendor/google.golang.org/grpc/internal/channelz/id.go new file mode 100644 index 00000000..c9a27acd --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/channelz/id.go @@ -0,0 +1,75 @@ +/* + * + * Copyright 2022 gRPC authors. + * + * 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 channelz + +import "fmt" + +// Identifier is an opaque identifier which uniquely identifies an entity in the +// channelz database. +type Identifier struct { + typ RefChannelType + id int64 + str string + pid *Identifier +} + +// Type returns the entity type corresponding to id. +func (id *Identifier) Type() RefChannelType { + return id.typ +} + +// Int returns the integer identifier corresponding to id. +func (id *Identifier) Int() int64 { + return id.id +} + +// String returns a string representation of the entity corresponding to id. +// +// This includes some information about the parent as well. Examples: +// Top-level channel: [Channel #channel-number] +// Nested channel: [Channel #parent-channel-number Channel #channel-number] +// Sub channel: [Channel #parent-channel SubChannel #subchannel-number] +func (id *Identifier) String() string { + return id.str +} + +// Equal returns true if other is the same as id. +func (id *Identifier) Equal(other *Identifier) bool { + if (id != nil) != (other != nil) { + return false + } + if id == nil && other == nil { + return true + } + return id.typ == other.typ && id.id == other.id && id.pid == other.pid +} + +// NewIdentifierForTesting returns a new opaque identifier to be used only for +// testing purposes. +func NewIdentifierForTesting(typ RefChannelType, id int64, pid *Identifier) *Identifier { + return newIdentifer(typ, id, pid) +} + +func newIdentifer(typ RefChannelType, id int64, pid *Identifier) *Identifier { + str := fmt.Sprintf("%s #%d", typ, id) + if pid != nil { + str = fmt.Sprintf("%s %s", pid, str) + } + return &Identifier{typ: typ, id: id, str: str, pid: pid} +} diff --git a/vendor/google.golang.org/grpc/internal/channelz/logging.go b/vendor/google.golang.org/grpc/internal/channelz/logging.go index b0013f9c..8e13a3d2 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/logging.go +++ b/vendor/google.golang.org/grpc/internal/channelz/logging.go @@ -26,77 +26,54 @@ import ( var logger = grpclog.Component("channelz") +func withParens(id *Identifier) string { + return "[" + id.String() + "] " +} + // Info logs and adds a trace event if channelz is on. -func Info(l grpclog.DepthLoggerV2, id int64, args ...interface{}) { - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: fmt.Sprint(args...), - Severity: CtInfo, - }) - } else { - l.InfoDepth(1, args...) - } +func Info(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprint(args...), + Severity: CtInfo, + }) } // Infof logs and adds a trace event if channelz is on. -func Infof(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: msg, - Severity: CtInfo, - }) - } else { - l.InfoDepth(1, msg) - } +func Infof(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprintf(format, args...), + Severity: CtInfo, + }) } // Warning logs and adds a trace event if channelz is on. -func Warning(l grpclog.DepthLoggerV2, id int64, args ...interface{}) { - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: fmt.Sprint(args...), - Severity: CtWarning, - }) - } else { - l.WarningDepth(1, args...) - } +func Warning(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprint(args...), + Severity: CtWarning, + }) } // Warningf logs and adds a trace event if channelz is on. -func Warningf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: msg, - Severity: CtWarning, - }) - } else { - l.WarningDepth(1, msg) - } +func Warningf(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprintf(format, args...), + Severity: CtWarning, + }) } // Error logs and adds a trace event if channelz is on. -func Error(l grpclog.DepthLoggerV2, id int64, args ...interface{}) { - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: fmt.Sprint(args...), - Severity: CtError, - }) - } else { - l.ErrorDepth(1, args...) - } +func Error(l grpclog.DepthLoggerV2, id *Identifier, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprint(args...), + Severity: CtError, + }) } // Errorf logs and adds a trace event if channelz is on. -func Errorf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if IsOn() { - AddTraceEvent(l, id, 1, &TraceEventDesc{ - Desc: msg, - Severity: CtError, - }) - } else { - l.ErrorDepth(1, msg) - } +func Errorf(l grpclog.DepthLoggerV2, id *Identifier, format string, args ...interface{}) { + AddTraceEvent(l, id, 1, &TraceEventDesc{ + Desc: fmt.Sprintf(format, args...), + Severity: CtError, + }) } diff --git a/vendor/google.golang.org/grpc/internal/channelz/types.go b/vendor/google.golang.org/grpc/internal/channelz/types.go index 3c595d15..ad0ce4da 100644 --- a/vendor/google.golang.org/grpc/internal/channelz/types.go +++ b/vendor/google.golang.org/grpc/internal/channelz/types.go @@ -686,12 +686,33 @@ const ( type RefChannelType int const ( + // RefUnknown indicates an unknown entity type, the zero value for this type. + RefUnknown RefChannelType = iota // RefChannel indicates the referenced entity is a Channel. - RefChannel RefChannelType = iota + RefChannel // RefSubChannel indicates the referenced entity is a SubChannel. RefSubChannel + // RefServer indicates the referenced entity is a Server. + RefServer + // RefListenSocket indicates the referenced entity is a ListenSocket. + RefListenSocket + // RefNormalSocket indicates the referenced entity is a NormalSocket. + RefNormalSocket ) +var refChannelTypeToString = map[RefChannelType]string{ + RefUnknown: "Unknown", + RefChannel: "Channel", + RefSubChannel: "SubChannel", + RefServer: "Server", + RefListenSocket: "ListenSocket", + RefNormalSocket: "NormalSocket", +} + +func (r RefChannelType) String() string { + return refChannelTypeToString[r] +} + func (c *channelTrace) dumpData() *ChannelTrace { c.mu.Lock() ct := &ChannelTrace{EventNum: c.eventCount, CreationTime: c.createdTime} diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 20fb880f..6d355b0b 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -85,3 +85,9 @@ const ( // that supports backend returned by grpclb balancer. CredsBundleModeBackendFromBalancer = "backend-from-balancer" ) + +// RLSLoadBalancingPolicyName is the name of the RLS LB policy. +// +// It currently has an experimental suffix which would be removed once +// end-to-end testing of the policy is completed. +const RLSLoadBalancingPolicyName = "rls_experimental" diff --git a/vendor/google.golang.org/grpc/internal/metadata/metadata.go b/vendor/google.golang.org/grpc/internal/metadata/metadata.go index b8733dbf..b2980f8a 100644 --- a/vendor/google.golang.org/grpc/internal/metadata/metadata.go +++ b/vendor/google.golang.org/grpc/internal/metadata/metadata.go @@ -22,6 +22,9 @@ package metadata import ( + "fmt" + "strings" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/resolver" ) @@ -72,3 +75,46 @@ func Set(addr resolver.Address, md metadata.MD) resolver.Address { addr.Attributes = addr.Attributes.WithValue(mdKey, mdValue(md)) return addr } + +// Validate returns an error if the input md contains invalid keys or values. +// +// If the header is not a pseudo-header, the following items are checked: +// - header names must contain one or more characters from this set [0-9 a-z _ - .]. +// - if the header-name ends with a "-bin" suffix, no validation of the header value is performed. +// - otherwise, the header value must contain one or more characters from the set [%x20-%x7E]. +func Validate(md metadata.MD) error { + for k, vals := range md { + // pseudo-header will be ignored + if k[0] == ':' { + continue + } + // check key, for i that saving a conversion if not using for range + for i := 0; i < len(k); i++ { + r := k[i] + if !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '.' && r != '-' && r != '_' { + return fmt.Errorf("header key %q contains illegal characters not in [0-9a-z-_.]", k) + } + } + if strings.HasSuffix(k, "-bin") { + continue + } + // check value + for _, val := range vals { + if hasNotPrintable(val) { + return fmt.Errorf("header key %q contains value with non-printable ASCII characters", k) + } + } + } + return nil +} + +// hasNotPrintable return true if msg contains any characters which are not in %x20-%x7E +func hasNotPrintable(msg string) bool { + // for i that saving a conversion if not using for range + for i := 0; i < len(msg); i++ { + if msg[i] < 0x20 || msg[i] > 0x7E { + return true + } + } + return false +} diff --git a/vendor/google.golang.org/grpc/internal/pretty/pretty.go b/vendor/google.golang.org/grpc/internal/pretty/pretty.go new file mode 100644 index 00000000..0177af4b --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/pretty/pretty.go @@ -0,0 +1,82 @@ +/* + * + * Copyright 2021 gRPC authors. + * + * 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 pretty defines helper functions to pretty-print structs for logging. +package pretty + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/golang/protobuf/jsonpb" + protov1 "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/encoding/protojson" + protov2 "google.golang.org/protobuf/proto" +) + +const jsonIndent = " " + +// ToJSON marshals the input into a json string. +// +// If marshal fails, it falls back to fmt.Sprintf("%+v"). +func ToJSON(e interface{}) string { + switch ee := e.(type) { + case protov1.Message: + mm := jsonpb.Marshaler{Indent: jsonIndent} + ret, err := mm.MarshalToString(ee) + if err != nil { + // This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2 + // messages are not imported, and this will fail because the message + // is not found. + return fmt.Sprintf("%+v", ee) + } + return ret + case protov2.Message: + mm := protojson.MarshalOptions{ + Multiline: true, + Indent: jsonIndent, + } + ret, err := mm.Marshal(ee) + if err != nil { + // This may fail for proto.Anys, e.g. for xDS v2, LDS, the v2 + // messages are not imported, and this will fail because the message + // is not found. + return fmt.Sprintf("%+v", ee) + } + return string(ret) + default: + ret, err := json.MarshalIndent(ee, "", jsonIndent) + if err != nil { + return fmt.Sprintf("%+v", ee) + } + return string(ret) + } +} + +// FormatJSON formats the input json bytes with indentation. +// +// If Indent fails, it returns the unchanged input as string. +func FormatJSON(b []byte) string { + var out bytes.Buffer + err := json.Indent(&out, b, "", jsonIndent) + if err != nil { + return string(b) + } + return out.String() +} diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index 8394d252..244f4b08 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -137,6 +137,7 @@ type earlyAbortStream struct { streamID uint32 contentSubtype string status *status.Status + rst bool } func (*earlyAbortStream) isTransportResponseFrame() bool { return false } @@ -786,6 +787,11 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error { if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil { return err } + if eas.rst { + if err := l.framer.fr.WriteRSTStream(eas.streamID, http2.ErrCodeNo); err != nil { + return err + } + } return nil } diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index f0c72d33..24ca5908 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -132,7 +132,7 @@ type http2Client struct { kpDormant bool // Fields below are for channelz metric collection. - channelzID int64 // channelz unique identification number + channelzID *channelz.Identifier czData *channelzData onGoAway func(GoAwayReason) @@ -351,8 +351,9 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts } t.statsHandler.HandleConn(t.ctx, connBegin) } - if channelz.IsOn() { - t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr)) + t.channelzID, err = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, fmt.Sprintf("%s -> %s", t.localAddr, t.remoteAddr)) + if err != nil { + return nil, err } if t.keepaliveEnabled { t.kpDormancyCond = sync.NewCond(&t.mu) @@ -630,8 +631,8 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call // the wire. However, there are two notable exceptions: // // 1. If the stream headers violate the max header list size allowed by the -// server. In this case there is no reason to retry at all, as it is -// assumed the RPC would continue to fail on subsequent attempts. +// server. It's possible this could succeed on another transport, even if +// it's unlikely, but do not transparently retry. // 2. If the credentials errored when requesting their headers. In this case, // it's possible a retry can fix the problem, but indefinitely transparently // retrying is not appropriate as it is likely the credentials, if they can @@ -639,8 +640,7 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call type NewStreamError struct { Err error - DoNotRetry bool - DoNotTransparentRetry bool + AllowTransparentRetry bool } func (e NewStreamError) Error() string { @@ -649,11 +649,11 @@ func (e NewStreamError) Error() string { // NewStream creates a stream and registers it into the transport as "active" // streams. All non-nil errors returned will be *NewStreamError. -func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) { +func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error) { ctx = peer.NewContext(ctx, t.getPeer()) headerFields, err := t.createHeaderFields(ctx, callHdr) if err != nil { - return nil, &NewStreamError{Err: err, DoNotTransparentRetry: true} + return nil, &NewStreamError{Err: err, AllowTransparentRetry: false} } s := t.newStream(ctx, callHdr) cleanup := func(err error) { @@ -753,13 +753,14 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea return true }, hdr) if err != nil { - return nil, &NewStreamError{Err: err} + // Connection closed. + return nil, &NewStreamError{Err: err, AllowTransparentRetry: true} } if success { break } if hdrListSizeErr != nil { - return nil, &NewStreamError{Err: hdrListSizeErr, DoNotRetry: true} + return nil, &NewStreamError{Err: hdrListSizeErr} } firstTry = false select { @@ -767,9 +768,9 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea case <-ctx.Done(): return nil, &NewStreamError{Err: ContextErr(ctx.Err())} case <-t.goAway: - return nil, &NewStreamError{Err: errStreamDrain} + return nil, &NewStreamError{Err: errStreamDrain, AllowTransparentRetry: true} case <-t.ctx.Done(): - return nil, &NewStreamError{Err: ErrConnClosing} + return nil, &NewStreamError{Err: ErrConnClosing, AllowTransparentRetry: true} } } if t.statsHandler != nil { @@ -898,9 +899,7 @@ func (t *http2Client) Close(err error) { t.controlBuf.finish() t.cancel() t.conn.Close() - if channelz.IsOn() { - channelz.RemoveEntry(t.channelzID) - } + channelz.RemoveEntry(t.channelzID) // Append info about previous goaways if there were any, since this may be important // for understanding the root cause for this connection to be closed. _, goAwayDebugMessage := t.GetGoAwayReason() diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 2c6eaf0e..45d7bd14 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -21,7 +21,6 @@ package transport import ( "bytes" "context" - "errors" "fmt" "io" "math" @@ -36,6 +35,7 @@ import ( "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "google.golang.org/grpc/internal/grpcutil" + "google.golang.org/grpc/internal/syscall" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -52,10 +52,10 @@ import ( var ( // ErrIllegalHeaderWrite indicates that setting header is illegal because of // the stream's state. - ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called") + ErrIllegalHeaderWrite = status.Error(codes.Internal, "transport: SendHeader called multiple times") // ErrHeaderListSizeLimitViolation indicates that the header list size is larger // than the limit set by peer. - ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer") + ErrHeaderListSizeLimitViolation = status.Error(codes.Internal, "transport: trying to send header list size larger than the limit set by peer") ) // serverConnectionCounter counts the number of connections a server has seen @@ -117,7 +117,7 @@ type http2Server struct { idle time.Time // Fields below are for channelz metric collection. - channelzID int64 // channelz unique identification number + channelzID *channelz.Identifier czData *channelzData bufferPool *bufferPool @@ -231,6 +231,11 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, if kp.Timeout == 0 { kp.Timeout = defaultServerKeepaliveTimeout } + if kp.Time != infinity { + if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil { + return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err) + } + } kep := config.KeepalivePolicy if kep.MinTime == 0 { kep.MinTime = defaultKeepalivePolicyMinTime @@ -275,12 +280,12 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, connBegin := &stats.ConnBegin{} t.stats.HandleConn(t.ctx, connBegin) } - if channelz.IsOn() { - t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr)) + t.channelzID, err = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr)) + if err != nil { + return nil, err } t.connectionID = atomic.AddUint64(&serverConnectionCounter, 1) - t.framer.writer.Flush() defer func() { @@ -443,6 +448,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( streamID: streamID, contentSubtype: s.contentSubtype, status: status.New(codes.Internal, errMsg), + rst: !frame.StreamEnded(), }) return false } @@ -516,14 +522,16 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( } if httpMethod != http.MethodPost { t.mu.Unlock() + errMsg := fmt.Sprintf("http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) if logger.V(logLevel) { - logger.Infof("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) + logger.Infof("transport: %v", errMsg) } - t.controlBuf.put(&cleanupStream{ - streamID: streamID, - rst: true, - rstCode: http2.ErrCodeProtocol, - onWrite: func() {}, + t.controlBuf.put(&earlyAbortStream{ + httpStatus: 405, + streamID: streamID, + contentSubtype: s.contentSubtype, + status: status.New(codes.Internal, errMsg), + rst: !frame.StreamEnded(), }) s.cancel() return false @@ -544,6 +552,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( streamID: s.id, contentSubtype: s.contentSubtype, status: stat, + rst: !frame.StreamEnded(), }) return false } @@ -925,11 +934,25 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool { return true } +func (t *http2Server) streamContextErr(s *Stream) error { + select { + case <-t.done: + return ErrConnClosing + default: + } + return ContextErr(s.ctx.Err()) +} + // WriteHeader sends the header metadata md back to the client. func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { - if s.updateHeaderSent() || s.getState() == streamDone { + if s.updateHeaderSent() { return ErrIllegalHeaderWrite } + + if s.getState() == streamDone { + return t.streamContextErr(s) + } + s.hdrMu.Lock() if md.Len() > 0 { if s.header.Len() > 0 { @@ -940,7 +963,7 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { } if err := t.writeHeaderLocked(s); err != nil { s.hdrMu.Unlock() - return err + return status.Convert(err).Err() } s.hdrMu.Unlock() return nil @@ -1056,23 +1079,12 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error { func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { if !s.isHeaderSent() { // Headers haven't been written yet. if err := t.WriteHeader(s, nil); err != nil { - if _, ok := err.(ConnectionError); ok { - return err - } - // TODO(mmukhi, dfawley): Make sure this is the right code to return. - return status.Errorf(codes.Internal, "transport: %v", err) + return err } } else { // Writing headers checks for this condition. if s.getState() == streamDone { - // TODO(mmukhi, dfawley): Should the server write also return io.EOF? - s.cancel() - select { - case <-t.done: - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) + return t.streamContextErr(s) } } df := &dataFrame{ @@ -1082,12 +1094,7 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e onEachWrite: t.setResetPingStrikes, } if err := s.wq.get(int32(len(hdr) + len(data))); err != nil { - select { - case <-t.done: - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) + return t.streamContextErr(s) } return t.controlBuf.put(df) } @@ -1210,9 +1217,7 @@ func (t *http2Server) Close() { if err := t.conn.Close(); err != nil && logger.V(logLevel) { logger.Infof("transport: error closing conn during Close: %v", err) } - if channelz.IsOn() { - channelz.RemoveEntry(t.channelzID) - } + channelz.RemoveEntry(t.channelzID) // Cancel all active streams. for _, s := range streams { s.cancel() @@ -1225,10 +1230,6 @@ func (t *http2Server) Close() { // deleteStream deletes the stream s from transport's active streams. func (t *http2Server) deleteStream(s *Stream, eosReceived bool) { - // In case stream sending and receiving are invoked in separate - // goroutines (e.g., bi-directional streaming), cancel needs to be - // called to interrupt the potential blocking on other goroutines. - s.cancel() t.mu.Lock() if _, ok := t.activeStreams[s.id]; ok { @@ -1250,6 +1251,11 @@ func (t *http2Server) deleteStream(s *Stream, eosReceived bool) { // finishStream closes the stream and puts the trailing headerFrame into controlbuf. func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) { + // In case stream sending and receiving are invoked in separate + // goroutines (e.g., bi-directional streaming), cancel needs to be + // called to interrupt the potential blocking on other goroutines. + s.cancel() + oldState := s.swapState(streamDone) if oldState == streamDone { // If the stream was already done, return. @@ -1269,6 +1275,11 @@ func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, h // closeStream clears the footprint of a stream when the stream is not needed any more. func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eosReceived bool) { + // In case stream sending and receiving are invoked in separate + // goroutines (e.g., bi-directional streaming), cancel needs to be + // called to interrupt the potential blocking on other goroutines. + s.cancel() + s.swapState(streamDone) t.deleteStream(s, eosReceived) diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 0c43efaa..a9ce717f 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -34,6 +34,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" "google.golang.org/grpc/resolver" @@ -529,7 +530,7 @@ type ServerConfig struct { InitialConnWindowSize int32 WriteBufferSize int ReadBufferSize int - ChannelzParentID int64 + ChannelzParentID *channelz.Identifier MaxHeaderListSize *uint32 HeaderTableSize *uint32 } @@ -563,7 +564,7 @@ type ConnectOptions struct { // ReadBufferSize sets the size of read buffer, which in turn determines how much data can be read at most for one read syscall. ReadBufferSize int // ChannelzParentID sets the addrConn id which initiate the creation of this client transport. - ChannelzParentID int64 + ChannelzParentID *channelz.Identifier // MaxHeaderListSize sets the max (uncompressed) size of header list that is prepared to be received. MaxHeaderListSize *uint32 // UseProxy specifies if a proxy should be used. diff --git a/vendor/google.golang.org/grpc/metadata/metadata.go b/vendor/google.golang.org/grpc/metadata/metadata.go index 3604c781..8e0f6abe 100644 --- a/vendor/google.golang.org/grpc/metadata/metadata.go +++ b/vendor/google.golang.org/grpc/metadata/metadata.go @@ -188,7 +188,9 @@ func FromIncomingContext(ctx context.Context) (MD, bool) { // map, and there's no guarantee that the MD attached to the context is // created using our helper functions. key := strings.ToLower(k) - out[key] = v + s := make([]string, len(v)) + copy(s, v) + out[key] = s } return out, true } @@ -226,7 +228,9 @@ func FromOutgoingContext(ctx context.Context) (MD, bool) { // map, and there's no guarantee that the MD attached to the context is // created using our helper functions. key := strings.ToLower(k) - out[key] = v + s := make([]string, len(v)) + copy(s, v) + out[key] = s } for _, added := range raw.added { if len(added)%2 == 1 { diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go index e8367cb8..843633c9 100644 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ b/vendor/google.golang.org/grpc/picker_wrapper.go @@ -131,7 +131,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. } if _, ok := status.FromError(err); ok { // Status error: end the RPC unconditionally with this status. - return nil, nil, err + return nil, nil, dropError{error: err} } // For all other errors, wait for ready RPCs should block and other // RPCs should fail with unavailable. @@ -175,3 +175,9 @@ func (pw *pickerWrapper) close() { pw.done = true close(pw.blockingCh) } + +// dropError is a wrapper error that indicates the LB policy wishes to drop the +// RPC and not retry it. +type dropError struct { + error +} diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/pickfirst.go index 5168b62b..fb7a99e0 100644 --- a/vendor/google.golang.org/grpc/pickfirst.go +++ b/vendor/google.golang.org/grpc/pickfirst.go @@ -44,79 +44,107 @@ func (*pickfirstBuilder) Name() string { } type pickfirstBalancer struct { - state connectivity.State - cc balancer.ClientConn - sc balancer.SubConn + state connectivity.State + cc balancer.ClientConn + subConn balancer.SubConn } func (b *pickfirstBalancer) ResolverError(err error) { - switch b.state { - case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting: - // Set a failing picker if we don't have a good picker. - b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure, - Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)}, - }) - } if logger.V(2) { logger.Infof("pickfirstBalancer: ResolverError called with error %v", err) } + if b.subConn == nil { + b.state = connectivity.TransientFailure + } + + if b.state != connectivity.TransientFailure { + // The picker will not change since the balancer does not currently + // report an error. + return + } + b.cc.UpdateState(balancer.State{ + ConnectivityState: connectivity.TransientFailure, + Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)}, + }) } -func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) error { - if len(cs.ResolverState.Addresses) == 0 { +func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { + if len(state.ResolverState.Addresses) == 0 { + // The resolver reported an empty address list. Treat it like an error by + // calling b.ResolverError. + if b.subConn != nil { + // Remove the old subConn. All addresses were removed, so it is no longer + // valid. + b.cc.RemoveSubConn(b.subConn) + b.subConn = nil + } b.ResolverError(errors.New("produced zero addresses")) return balancer.ErrBadResolverState } - if b.sc == nil { - var err error - b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{}) - if err != nil { - if logger.V(2) { - logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) - } - b.state = connectivity.TransientFailure - b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure, - Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)}, - }) - return balancer.ErrBadResolverState - } - b.state = connectivity.Idle - b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}}) - b.sc.Connect() - } else { - b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses) - b.sc.Connect() + + if b.subConn != nil { + b.cc.UpdateAddresses(b.subConn, state.ResolverState.Addresses) + return nil } + + subConn, err := b.cc.NewSubConn(state.ResolverState.Addresses, balancer.NewSubConnOptions{}) + if err != nil { + if logger.V(2) { + logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) + } + b.state = connectivity.TransientFailure + b.cc.UpdateState(balancer.State{ + ConnectivityState: connectivity.TransientFailure, + Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)}, + }) + return balancer.ErrBadResolverState + } + b.subConn = subConn + b.state = connectivity.Idle + b.cc.UpdateState(balancer.State{ + ConnectivityState: connectivity.Idle, + Picker: &picker{result: balancer.PickResult{SubConn: b.subConn}}, + }) + b.subConn.Connect() return nil } -func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) { +func (b *pickfirstBalancer) UpdateSubConnState(subConn balancer.SubConn, state balancer.SubConnState) { if logger.V(2) { - logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s) + logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", subConn, state) } - if b.sc != sc { + if b.subConn != subConn { if logger.V(2) { - logger.Infof("pickfirstBalancer: ignored state change because sc is not recognized") + logger.Infof("pickfirstBalancer: ignored state change because subConn is not recognized") } return } - b.state = s.ConnectivityState - if s.ConnectivityState == connectivity.Shutdown { - b.sc = nil + b.state = state.ConnectivityState + if state.ConnectivityState == connectivity.Shutdown { + b.subConn = nil return } - switch s.ConnectivityState { + switch state.ConnectivityState { case connectivity.Ready: - b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}}) + b.cc.UpdateState(balancer.State{ + ConnectivityState: state.ConnectivityState, + Picker: &picker{result: balancer.PickResult{SubConn: subConn}}, + }) case connectivity.Connecting: - b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}}) + b.cc.UpdateState(balancer.State{ + ConnectivityState: state.ConnectivityState, + Picker: &picker{err: balancer.ErrNoSubConnAvailable}, + }) case connectivity.Idle: - b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &idlePicker{sc: sc}}) + b.cc.UpdateState(balancer.State{ + ConnectivityState: state.ConnectivityState, + Picker: &idlePicker{subConn: subConn}, + }) case connectivity.TransientFailure: b.cc.UpdateState(balancer.State{ - ConnectivityState: s.ConnectivityState, - Picker: &picker{err: s.ConnectionError}, + ConnectivityState: state.ConnectivityState, + Picker: &picker{err: state.ConnectionError}, }) } } @@ -125,8 +153,8 @@ func (b *pickfirstBalancer) Close() { } func (b *pickfirstBalancer) ExitIdle() { - if b.sc != nil && b.state == connectivity.Idle { - b.sc.Connect() + if b.subConn != nil && b.state == connectivity.Idle { + b.subConn.Connect() } } @@ -135,18 +163,18 @@ type picker struct { err error } -func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { +func (p *picker) Pick(balancer.PickInfo) (balancer.PickResult, error) { return p.result, p.err } // idlePicker is used when the SubConn is IDLE and kicks the SubConn into // CONNECTING when Pick is called. type idlePicker struct { - sc balancer.SubConn + subConn balancer.SubConn } -func (i *idlePicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { - i.sc.Connect() +func (i *idlePicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { + i.subConn.Connect() return balancer.PickResult{}, balancer.ErrNoSubConnAvailable } diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go index e28b6802..ca2e35a3 100644 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/vendor/google.golang.org/grpc/resolver/resolver.go @@ -27,6 +27,7 @@ import ( "google.golang.org/grpc/attributes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/serviceconfig" ) @@ -139,13 +140,18 @@ type Address struct { // Equal returns whether a and o are identical. Metadata is compared directly, // not with any recursive introspection. -func (a *Address) Equal(o Address) bool { +func (a Address) Equal(o Address) bool { return a.Addr == o.Addr && a.ServerName == o.ServerName && a.Attributes.Equal(o.Attributes) && a.BalancerAttributes.Equal(o.BalancerAttributes) && a.Type == o.Type && a.Metadata == o.Metadata } +// String returns JSON formatted string representation of the address. +func (a Address) String() string { + return pretty.ToJSON(a) +} + // BuildOptions includes additional information for the builder to create // the resolver. type BuildOptions struct { diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go index 2c47cd54..05a9d4e0 100644 --- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go +++ b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go @@ -19,7 +19,6 @@ package grpc import ( - "fmt" "strings" "sync" @@ -27,6 +26,7 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" + "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" ) @@ -97,10 +97,7 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error { if ccr.done.HasFired() { return nil } - channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s) - if channelz.IsOn() { - ccr.addChannelzTraceEvent(s) - } + ccr.addChannelzTraceEvent(s) ccr.curState = s if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState { return balancer.ErrBadResolverState @@ -125,10 +122,7 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { if ccr.done.HasFired() { return } - channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending new addresses to cc: %v", addrs) - if channelz.IsOn() { - ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) - } + ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) ccr.curState.Addresses = addrs ccr.cc.updateResolverState(ccr.curState, nil) } @@ -141,7 +135,7 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { if ccr.done.HasFired() { return } - channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: got new service config: %v", sc) + channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: got new service config: %s", sc) if ccr.cc.dopts.disableServiceConfig { channelz.Info(logger, ccr.cc.channelzID, "Service config lookups disabled; ignoring config") return @@ -151,9 +145,7 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err) return } - if channelz.IsOn() { - ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) - } + ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr}) ccr.curState.ServiceConfig = scpr ccr.cc.updateResolverState(ccr.curState, nil) } @@ -180,8 +172,5 @@ func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) { } else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 { updates = append(updates, "resolver returned new addresses") } - channelz.AddTraceEvent(logger, ccr.cc.channelzID, 0, &channelz.TraceEventDesc{ - Desc: fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")), - Severity: channelz.CtInfo, - }) + channelz.Infof(logger, ccr.cc.channelzID, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; ")) } diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index b24b6d53..65de84b3 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -134,7 +134,7 @@ type Server struct { channelzRemoveOnce sync.Once serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop - channelzID int64 // channelz unique identification number + channelzID *channelz.Identifier czData *channelzData serverWorkerChannels []chan *serverWorkerData @@ -584,9 +584,8 @@ func NewServer(opt ...ServerOption) *Server { s.initServerWorkers() } - if channelz.IsOn() { - s.channelzID = channelz.RegisterServer(&channelzServer{s}, "") - } + s.channelzID = channelz.RegisterServer(&channelzServer{s}, "") + channelz.Info(logger, s.channelzID, "Server created") return s } @@ -712,7 +711,7 @@ var ErrServerStopped = errors.New("grpc: the server has been stopped") type listenSocket struct { net.Listener - channelzID int64 + channelzID *channelz.Identifier } func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric { @@ -724,9 +723,8 @@ func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric { func (l *listenSocket) Close() error { err := l.Listener.Close() - if channelz.IsOn() { - channelz.RemoveEntry(l.channelzID) - } + channelz.RemoveEntry(l.channelzID) + channelz.Info(logger, l.channelzID, "ListenSocket deleted") return err } @@ -759,11 +757,6 @@ func (s *Server) Serve(lis net.Listener) error { ls := &listenSocket{Listener: lis} s.lis[ls] = true - if channelz.IsOn() { - ls.channelzID = channelz.RegisterListenSocket(ls, s.channelzID, lis.Addr().String()) - } - s.mu.Unlock() - defer func() { s.mu.Lock() if s.lis != nil && s.lis[ls] { @@ -773,8 +766,16 @@ func (s *Server) Serve(lis net.Listener) error { s.mu.Unlock() }() - var tempDelay time.Duration // how long to sleep on accept failure + var err error + ls.channelzID, err = channelz.RegisterListenSocket(ls, s.channelzID, lis.Addr().String()) + if err != nil { + s.mu.Unlock() + return err + } + s.mu.Unlock() + channelz.Info(logger, ls.channelzID, "ListenSocket created") + var tempDelay time.Duration // how long to sleep on accept failure for { rawConn, err := lis.Accept() if err != nil { @@ -1709,11 +1710,7 @@ func (s *Server) Stop() { s.done.Fire() }() - s.channelzRemoveOnce.Do(func() { - if channelz.IsOn() { - channelz.RemoveEntry(s.channelzID) - } - }) + s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) }) s.mu.Lock() listeners := s.lis @@ -1751,11 +1748,7 @@ func (s *Server) GracefulStop() { s.quit.Fire() defer s.done.Fire() - s.channelzRemoveOnce.Do(func() { - if channelz.IsOn() { - channelz.RemoveEntry(s.channelzID) - } - }) + s.channelzRemoveOnce.Do(func() { channelz.RemoveEntry(s.channelzID) }) s.mu.Lock() if s.conns == nil { s.mu.Unlock() @@ -1808,12 +1801,26 @@ func (s *Server) getCodec(contentSubtype string) baseCodec { return codec } -// SetHeader sets the header metadata. -// When called multiple times, all the provided metadata will be merged. -// All the metadata will be sent out when one of the following happens: -// - grpc.SendHeader() is called; -// - The first response is sent out; -// - An RPC status is sent out (error or success). +// SetHeader sets the header metadata to be sent from the server to the client. +// The context provided must be the context passed to the server's handler. +// +// Streaming RPCs should prefer the SetHeader method of the ServerStream. +// +// When called multiple times, all the provided metadata will be merged. All +// the metadata will be sent out when one of the following happens: +// +// - grpc.SendHeader is called, or for streaming handlers, stream.SendHeader. +// - The first response message is sent. For unary handlers, this occurs when +// the handler returns; for streaming handlers, this can happen when stream's +// SendMsg method is called. +// - An RPC status is sent out (error or success). This occurs when the handler +// returns. +// +// SetHeader will fail if called after any of the events above. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SetHeader(ctx context.Context, md metadata.MD) error { if md.Len() == 0 { return nil @@ -1825,8 +1832,14 @@ func SetHeader(ctx context.Context, md metadata.MD) error { return stream.SetHeader(md) } -// SendHeader sends header metadata. It may be called at most once. -// The provided md and headers set by SetHeader() will be sent. +// SendHeader sends header metadata. It may be called at most once, and may not +// be called after any event that causes headers to be sent (see SetHeader for +// a complete list). The provided md and headers set by SetHeader() will be +// sent. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SendHeader(ctx context.Context, md metadata.MD) error { stream := ServerTransportStreamFromContext(ctx) if stream == nil { @@ -1840,6 +1853,10 @@ func SendHeader(ctx context.Context, md metadata.MD) error { // SetTrailer sets the trailer metadata that will be sent when an RPC returns. // When called more than once, all the provided metadata will be merged. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SetTrailer(ctx context.Context, md metadata.MD) error { if md.Len() == 0 { return nil diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go index 6926a06d..b01c548b 100644 --- a/vendor/google.golang.org/grpc/service_config.go +++ b/vendor/google.golang.org/grpc/service_config.go @@ -381,6 +381,9 @@ func init() { // // If any of them is NOT *ServiceConfig, return false. func equalServiceConfig(a, b serviceconfig.Config) bool { + if a == nil && b == nil { + return true + } aa, ok := a.(*ServiceConfig) if !ok { return false diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index 8cdd652e..236fc17e 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -36,6 +36,7 @@ import ( "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcutil" + imetadata "google.golang.org/grpc/internal/metadata" iresolver "google.golang.org/grpc/internal/resolver" "google.golang.org/grpc/internal/serviceconfig" "google.golang.org/grpc/internal/transport" @@ -166,6 +167,11 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth } func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { + if md, _, ok := metadata.FromOutgoingContextRaw(ctx); ok { + if err := imetadata.Validate(md); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + } if channelz.IsOn() { cc.incrCallsStarted() defer func() { @@ -297,14 +303,28 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client } cs.binlog = binarylog.GetMethodLogger(method) - if err := cs.newAttemptLocked(false /* isTransparent */); err != nil { + cs.attempt, err = cs.newAttemptLocked(false /* isTransparent */) + if err != nil { cs.finish(err) return nil, err } - op := func(a *csAttempt) error { return a.newStream() } + // Pick the transport to use and create a new stream on the transport. + // Assign cs.attempt upon success. + op := func(a *csAttempt) error { + if err := a.getTransport(); err != nil { + return err + } + if err := a.newStream(); err != nil { + return err + } + // Because this operation is always called either here (while creating + // the clientStream) or by the retry code while locked when replaying + // the operation, it is safe to access cs.attempt directly. + cs.attempt = a + return nil + } if err := cs.withRetry(op, func() { cs.bufferForRetryLocked(0, op) }); err != nil { - cs.finish(err) return nil, err } @@ -343,9 +363,15 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client return cs, nil } -// newAttemptLocked creates a new attempt with a transport. -// If it succeeds, then it replaces clientStream's attempt with this new attempt. -func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) { +// newAttemptLocked creates a new csAttempt without a transport or stream. +func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error) { + if err := cs.ctx.Err(); err != nil { + return nil, toRPCErr(err) + } + if err := cs.cc.ctx.Err(); err != nil { + return nil, ErrClientConnClosing + } + ctx := newContextWithRPCInfo(cs.ctx, cs.callInfo.failFast, cs.callInfo.codec, cs.cp, cs.comp) method := cs.callHdr.Method sh := cs.cc.dopts.copts.StatsHandler @@ -379,27 +405,6 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) { ctx = trace.NewContext(ctx, trInfo.tr) } - newAttempt := &csAttempt{ - ctx: ctx, - beginTime: beginTime, - cs: cs, - dc: cs.cc.dopts.dc, - statsHandler: sh, - trInfo: trInfo, - } - defer func() { - if retErr != nil { - // This attempt is not set in the clientStream, so it's finish won't - // be called. Call it here for stats and trace in case they are not - // nil. - newAttempt.finish(retErr) - } - }() - - if err := ctx.Err(); err != nil { - return toRPCErr(err) - } - if cs.cc.parsedTarget.Scheme == "xds" { // Add extra metadata (metadata that will be added by transport) to context // so the balancer can see them. @@ -407,16 +412,32 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (retErr error) { "content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype), )) } - t, done, err := cs.cc.getTransport(ctx, cs.callInfo.failFast, cs.callHdr.Method) + + return &csAttempt{ + ctx: ctx, + beginTime: beginTime, + cs: cs, + dc: cs.cc.dopts.dc, + statsHandler: sh, + trInfo: trInfo, + }, nil +} + +func (a *csAttempt) getTransport() error { + cs := a.cs + + var err error + a.t, a.done, err = cs.cc.getTransport(a.ctx, cs.callInfo.failFast, cs.callHdr.Method) if err != nil { + if de, ok := err.(dropError); ok { + err = de.error + a.drop = true + } return err } - if trInfo != nil { - trInfo.firstLine.SetRemoteAddr(t.RemoteAddr()) + if a.trInfo != nil { + a.trInfo.firstLine.SetRemoteAddr(a.t.RemoteAddr()) } - newAttempt.t = t - newAttempt.done = done - cs.attempt = newAttempt return nil } @@ -425,12 +446,21 @@ func (a *csAttempt) newStream() error { cs.callHdr.PreviousAttempts = cs.numRetries s, err := a.t.NewStream(a.ctx, cs.callHdr) if err != nil { - // Return without converting to an RPC error so retry code can - // inspect. - return err + nse, ok := err.(*transport.NewStreamError) + if !ok { + // Unexpected. + return err + } + + if nse.AllowTransparentRetry { + a.allowTransparentRetry = true + } + + // Unwrap and convert error. + return toRPCErr(nse.Err) } - cs.attempt.s = s - cs.attempt.p = &parser{r: s} + a.s = s + a.p = &parser{r: s} return nil } @@ -456,7 +486,7 @@ type clientStream struct { retryThrottler *retryThrottler // The throttler active when the RPC began. - binlog *binarylog.MethodLogger // Binary logger, can be nil. + binlog binarylog.MethodLogger // Binary logger, can be nil. // serverHeaderBinlogged is a boolean for whether server header has been // logged. Server header will be logged when the first time one of those // happens: stream.Header(), stream.Recv(). @@ -508,6 +538,11 @@ type csAttempt struct { statsHandler stats.Handler beginTime time.Time + + // set for newStream errors that may be transparently retried + allowTransparentRetry bool + // set for pick errors that are returned as a status + drop bool } func (cs *clientStream) commitAttemptLocked() { @@ -527,41 +562,21 @@ func (cs *clientStream) commitAttempt() { // shouldRetry returns nil if the RPC should be retried; otherwise it returns // the error that should be returned by the operation. If the RPC should be // retried, the bool indicates whether it is being retried transparently. -func (cs *clientStream) shouldRetry(err error) (bool, error) { - if cs.attempt.s == nil { - // Error from NewClientStream. - nse, ok := err.(*transport.NewStreamError) - if !ok { - // Unexpected, but assume no I/O was performed and the RPC is not - // fatal, so retry indefinitely. - return true, nil - } +func (a *csAttempt) shouldRetry(err error) (bool, error) { + cs := a.cs - // Unwrap and convert error. - err = toRPCErr(nse.Err) - - // Never retry DoNotRetry errors, which indicate the RPC should not be - // retried due to max header list size violation, etc. - if nse.DoNotRetry { - return false, err - } - - // In the event of a non-IO operation error from NewStream, we never - // attempted to write anything to the wire, so we can retry - // indefinitely. - if !nse.DoNotTransparentRetry { - return true, nil - } - } - if cs.finished || cs.committed { - // RPC is finished or committed; cannot retry. + if cs.finished || cs.committed || a.drop { + // RPC is finished or committed or was dropped by the picker; cannot retry. return false, err } + if a.s == nil && a.allowTransparentRetry { + return true, nil + } // Wait for the trailers. unprocessed := false - if cs.attempt.s != nil { - <-cs.attempt.s.Done() - unprocessed = cs.attempt.s.Unprocessed() + if a.s != nil { + <-a.s.Done() + unprocessed = a.s.Unprocessed() } if cs.firstAttempt && unprocessed { // First attempt, stream unprocessed: transparently retry. @@ -573,14 +588,14 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) { pushback := 0 hasPushback := false - if cs.attempt.s != nil { - if !cs.attempt.s.TrailersOnly() { + if a.s != nil { + if !a.s.TrailersOnly() { return false, err } // TODO(retry): Move down if the spec changes to not check server pushback // before considering this a failure for throttling. - sps := cs.attempt.s.Trailer()["grpc-retry-pushback-ms"] + sps := a.s.Trailer()["grpc-retry-pushback-ms"] if len(sps) == 1 { var e error if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 { @@ -597,10 +612,10 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) { } var code codes.Code - if cs.attempt.s != nil { - code = cs.attempt.s.Status().Code() + if a.s != nil { + code = a.s.Status().Code() } else { - code = status.Convert(err).Code() + code = status.Code(err) } rp := cs.methodConfig.RetryPolicy @@ -645,19 +660,24 @@ func (cs *clientStream) shouldRetry(err error) (bool, error) { } // Returns nil if a retry was performed and succeeded; error otherwise. -func (cs *clientStream) retryLocked(lastErr error) error { +func (cs *clientStream) retryLocked(attempt *csAttempt, lastErr error) error { for { - cs.attempt.finish(toRPCErr(lastErr)) - isTransparent, err := cs.shouldRetry(lastErr) + attempt.finish(toRPCErr(lastErr)) + isTransparent, err := attempt.shouldRetry(lastErr) if err != nil { cs.commitAttemptLocked() return err } cs.firstAttempt = false - if err := cs.newAttemptLocked(isTransparent); err != nil { + attempt, err = cs.newAttemptLocked(isTransparent) + if err != nil { + // Only returns error if the clientconn is closed or the context of + // the stream is canceled. return err } - if lastErr = cs.replayBufferLocked(); lastErr == nil { + // Note that the first op in the replay buffer always sets cs.attempt + // if it is able to pick a transport and create a stream. + if lastErr = cs.replayBufferLocked(attempt); lastErr == nil { return nil } } @@ -667,7 +687,10 @@ func (cs *clientStream) Context() context.Context { cs.commitAttempt() // No need to lock before using attempt, since we know it is committed and // cannot change. - return cs.attempt.s.Context() + if cs.attempt.s != nil { + return cs.attempt.s.Context() + } + return cs.ctx } func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func()) error { @@ -697,7 +720,7 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func()) cs.mu.Unlock() return err } - if err := cs.retryLocked(err); err != nil { + if err := cs.retryLocked(a, err); err != nil { cs.mu.Unlock() return err } @@ -728,7 +751,7 @@ func (cs *clientStream) Header() (metadata.MD, error) { cs.binlog.Log(logEntry) cs.serverHeaderBinlogged = true } - return m, err + return m, nil } func (cs *clientStream) Trailer() metadata.MD { @@ -746,10 +769,9 @@ func (cs *clientStream) Trailer() metadata.MD { return cs.attempt.s.Trailer() } -func (cs *clientStream) replayBufferLocked() error { - a := cs.attempt +func (cs *clientStream) replayBufferLocked(attempt *csAttempt) error { for _, f := range cs.buffer { - if err := f(a); err != nil { + if err := f(attempt); err != nil { return err } } @@ -797,22 +819,17 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) { if len(payload) > *cs.callInfo.maxSendMessageSize { return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(payload), *cs.callInfo.maxSendMessageSize) } - msgBytes := data // Store the pointer before setting to nil. For binary logging. op := func(a *csAttempt) error { - err := a.sendMsg(m, hdr, payload, data) - // nil out the message and uncomp when replaying; they are only needed for - // stats which is disabled for subsequent attempts. - m, data = nil, nil - return err + return a.sendMsg(m, hdr, payload, data) } err = cs.withRetry(op, func() { cs.bufferForRetryLocked(len(hdr)+len(payload), op) }) if cs.binlog != nil && err == nil { cs.binlog.Log(&binarylog.ClientMessage{ OnClientSide: true, - Message: msgBytes, + Message: data, }) } - return + return err } func (cs *clientStream) RecvMsg(m interface{}) error { @@ -1364,8 +1381,10 @@ func (as *addrConnStream) finish(err error) { // ServerStream defines the server-side behavior of a streaming RPC. // -// All errors returned from ServerStream methods are compatible with the -// status package. +// Errors returned from ServerStream methods are compatible with the status +// package. However, the status code will often not match the RPC status as +// seen by the client application, and therefore, should not be relied upon for +// this purpose. type ServerStream interface { // SetHeader sets the header metadata. It may be called multiple times. // When call multiple times, all the provided metadata will be merged. @@ -1428,7 +1447,7 @@ type serverStream struct { statsHandler stats.Handler - binlog *binarylog.MethodLogger + binlog binarylog.MethodLogger // serverHeaderBinlogged indicates whether server header has been logged. It // will happen when one of the following two happens: stream.SendHeader(), // stream.Send(). @@ -1448,11 +1467,20 @@ func (ss *serverStream) SetHeader(md metadata.MD) error { if md.Len() == 0 { return nil } + err := imetadata.Validate(md) + if err != nil { + return status.Error(codes.Internal, err.Error()) + } return ss.s.SetHeader(md) } func (ss *serverStream) SendHeader(md metadata.MD) error { - err := ss.t.WriteHeader(ss.s, md) + err := imetadata.Validate(md) + if err != nil { + return status.Error(codes.Internal, err.Error()) + } + + err = ss.t.WriteHeader(ss.s, md) if ss.binlog != nil && !ss.serverHeaderBinlogged { h, _ := ss.s.Header() ss.binlog.Log(&binarylog.ServerHeader{ @@ -1467,6 +1495,9 @@ func (ss *serverStream) SetTrailer(md metadata.MD) { if md.Len() == 0 { return } + if err := imetadata.Validate(md); err != nil { + logger.Errorf("stream: failed to validate md when setting trailer, err: %v", err) + } ss.s.SetTrailer(md) } diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 5bd4f534..5bc03f9b 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.45.0" +const Version = "1.47.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index edebf8e8..4c133860 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,6 +84,9 @@ github.com/coredns/coredns/plugin/pkg/transport github.com/coredns/coredns/plugin/pkg/uniq github.com/coredns/coredns/plugin/test github.com/coredns/coredns/request +# github.com/coreos/go-oidc/v3 v3.4.0 +## explicit; go 1.14 +github.com/coreos/go-oidc/v3/oidc # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf ## explicit github.com/coreos/go-systemd/daemon @@ -141,6 +144,7 @@ github.com/gobwas/ws/wsutil github.com/golang-collections/collections/queue # github.com/golang/protobuf v1.5.2 ## explicit; go 1.9 +github.com/golang/protobuf/jsonpb github.com/golang/protobuf/proto github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any @@ -386,11 +390,11 @@ golang.org/x/net/ipv6 golang.org/x/net/proxy golang.org/x/net/trace golang.org/x/net/websocket -# golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 -## explicit; go 1.11 +# golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 +## explicit; go 1.17 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.0.0-20210220032951-036812b2e83c +# golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f ## explicit golang.org/x/sync/errgroup # golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 @@ -434,8 +438,8 @@ golang.org/x/tools/internal/imports golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal -# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 -## explicit; go 1.11 +# golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f +## explicit; go 1.17 golang.org/x/xerrors golang.org/x/xerrors/internal # google.golang.org/appengine v1.6.7 @@ -447,12 +451,12 @@ google.golang.org/appengine/internal/log google.golang.org/appengine/internal/remote_api google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/urlfetch -# google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb -## explicit; go 1.11 +# google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 +## explicit; go 1.15 google.golang.org/genproto/googleapis/api/httpbody google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/protobuf/field_mask -# google.golang.org/grpc v1.45.0 +# google.golang.org/grpc v1.47.0 ## explicit; go 1.14 google.golang.org/grpc google.golang.org/grpc/attributes @@ -462,6 +466,7 @@ google.golang.org/grpc/balancer/base google.golang.org/grpc/balancer/grpclb/state google.golang.org/grpc/balancer/roundrobin google.golang.org/grpc/binarylog/grpc_binarylog_v1 +google.golang.org/grpc/channelz google.golang.org/grpc/codes google.golang.org/grpc/connectivity google.golang.org/grpc/credentials @@ -471,6 +476,7 @@ google.golang.org/grpc/encoding/proto google.golang.org/grpc/grpclog google.golang.org/grpc/internal google.golang.org/grpc/internal/backoff +google.golang.org/grpc/internal/balancer/gracefulswitch google.golang.org/grpc/internal/balancerload google.golang.org/grpc/internal/binarylog google.golang.org/grpc/internal/buffer @@ -482,6 +488,7 @@ google.golang.org/grpc/internal/grpcrand google.golang.org/grpc/internal/grpcsync google.golang.org/grpc/internal/grpcutil google.golang.org/grpc/internal/metadata +google.golang.org/grpc/internal/pretty google.golang.org/grpc/internal/resolver google.golang.org/grpc/internal/resolver/dns google.golang.org/grpc/internal/resolver/passthrough From 0aa21f302ef683d8678d5949054d99a4484bb599 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 22 Sep 2022 11:58:17 +0100 Subject: [PATCH 211/238] TUN-6792: Fix brew core release by not auditing the formula --- .teamcity/update-homebrew-core.sh | 2 +- cfsetup.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.teamcity/update-homebrew-core.sh b/.teamcity/update-homebrew-core.sh index 7e6f95e3..e3b982a3 100755 --- a/.teamcity/update-homebrew-core.sh +++ b/.teamcity/update-homebrew-core.sh @@ -23,4 +23,4 @@ git config --global user.name "cloudflare-warp-bot" git config --global user.email "warp-bot@cloudflare.com" # bump formula pr -brew bump-formula-pr cloudflared --version="$VERSION" --no-browse +brew bump-formula-pr cloudflared --version="$VERSION" --no-browse --no-audit diff --git a/cfsetup.yaml b/cfsetup.yaml index 585771b1..dd92bf52 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -220,6 +220,7 @@ stretch: &stretch - s3cmd - jq - build-essential + - procps post-cache: - .teamcity/update-homebrew.sh - .teamcity/update-homebrew-core.sh From 462d2f87dfcee67df34b1b73954bb5db117c7d40 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 22 Sep 2022 14:04:47 +0100 Subject: [PATCH 212/238] TUN-6774: Validate OriginRequest.Access to add Ingress.Middleware We take advantage of the JWTValidator middleware and attach it to an ingress rule based on Access configurations. We attach the Validator directly to the ingress rules because we want to take advantage of caching and token revert/handling that comes with go-oidc. --- config/configuration.go | 8 +++---- ingress/ingress.go | 35 ++++++++++++++++++++++++++++++ ingress/middleware/jwtvalidator.go | 4 ++-- ingress/rule.go | 6 ++--- ingress/rule_test.go | 8 +++---- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/config/configuration.go b/config/configuration.go index 94e6467d..34a35612 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -234,14 +234,14 @@ type OriginRequestConfig struct { } type AccessConfig struct { - // Enabled when set to true will fail every request that does not arrive through an access authenticated endpoint. - Enabled bool + // Required when set to true will fail every request that does not arrive through an access authenticated endpoint. + Required bool `yaml:"required" json:"required,omitempty"` // TeamName is the organization team name to get the public key certificates for. - TeamName string `yaml:"teamName" json:"teamName,omitempty"` + TeamName string `yaml:"teamName" json:"teamName"` // AudTag is the AudTag to verify access JWT against. - AudTag []string `yaml:"audTag" json:"audTag,omitempty"` + AudTag []string `yaml:"audTag" json:"audTag"` } type IngressIPRule struct { diff --git a/ingress/ingress.go b/ingress/ingress.go index b15eced3..b1d87878 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -13,6 +13,7 @@ import ( "github.com/urfave/cli/v2" "github.com/cloudflare/cloudflared/config" + "github.com/cloudflare/cloudflared/ingress/middleware" "github.com/cloudflare/cloudflared/ipaccess" ) @@ -168,6 +169,28 @@ func (ing Ingress) CatchAll() *Rule { return &ing.Rules[len(ing.Rules)-1] } +func validateAccessConfiguration(cfg *config.AccessConfig) error { + if !cfg.Required { + return nil + } + + // It is possible to set `required:true` and not have these two configured yet. + // But if one of them is configured, we'd validate for correctness. + if len(cfg.AudTag) == 0 && cfg.TeamName == "" { + return nil + } + + if len(cfg.AudTag) == 0 { + return errors.New("access audtag cannot be empty") + } + + if cfg.TeamName == "" { + return errors.New("access.TeamName cannot be blank") + } + + return nil +} + func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginRequestConfig) (Ingress, error) { rules := make([]Rule, len(ingress)) for i, r := range ingress { @@ -237,6 +260,17 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq } } + var handlers []middleware.Handler + if access := r.OriginRequest.Access; access != nil { + if err := validateAccessConfiguration(access); err != nil { + return Ingress{}, err + } + if access.Required { + verifier := middleware.NewJWTValidator(access.TeamName, "", access.AudTag) + handlers = append(handlers, verifier) + } + } + if err := validateHostname(r, i, len(ingress)); err != nil { return Ingress{}, err } @@ -255,6 +289,7 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq Hostname: r.Hostname, Service: service, Path: pathRegexp, + Handlers: handlers, Config: cfg, } } diff --git a/ingress/middleware/jwtvalidator.go b/ingress/middleware/jwtvalidator.go index 8bdab7e1..667bc1ce 100644 --- a/ingress/middleware/jwtvalidator.go +++ b/ingress/middleware/jwtvalidator.go @@ -42,8 +42,8 @@ func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTVal } } -func (v *JWTValidator) Handle(ctx context.Context, headers http.Header) error { - accessJWT := headers.Get(headerKeyAccessJWTAssertion) +func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) error { + accessJWT := r.Header.Get(headerKeyAccessJWTAssertion) if accessJWT == "" { return ErrNoAccessToken } diff --git a/ingress/rule.go b/ingress/rule.go index b3dd0001..c7733254 100644 --- a/ingress/rule.go +++ b/ingress/rule.go @@ -4,6 +4,8 @@ import ( "encoding/json" "regexp" "strings" + + "github.com/cloudflare/cloudflared/ingress/middleware" ) // Rule routes traffic from a hostname/path on the public internet to the @@ -21,9 +23,7 @@ type Rule struct { Service OriginService `json:"service"` // Handlers is a list of functions that acts as a middleware during ProxyHTTP - // TODO TUN-6774: Uncomment when we parse ingress to this. This serves as a demonstration on how - // we want to plug in Verifiers. - // Handlers []middleware.Handler + Handlers []middleware.Handler // Configure the request cloudflared sends to this specific origin. Config OriginRequestConfig `json:"originRequest"` diff --git a/ingress/rule_test.go b/ingress/rule_test.go index d399b1be..8ab86132 100644 --- a/ingress/rule_test.go +++ b/ingress/rule_test.go @@ -182,25 +182,25 @@ func TestMarshalJSON(t *testing.T) { { name: "Nil", path: nil, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, want: true, }, { name: "Nil regex", path: &Regexp{Regexp: nil}, - expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, + expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, want: true, }, { name: "Empty", path: &Regexp{Regexp: regexp.MustCompile("")}, - expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, + expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, want: true, }, { name: "Basic", path: &Regexp{Regexp: regexp.MustCompile("/echo")}, - expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`, + expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`, want: true, }, } From 5d6b0642db91cd8635be98fd9c52cae19bf4c6f0 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 21 Sep 2022 11:16:37 +0100 Subject: [PATCH 213/238] TUN-6772: Add a JWT Validator as an ingress verifier This adds a new verifier interface that can be attached to ingress.Rule. This would act as a middleware layer that gets executed at the start of proxy.ProxyHTTP. A jwt validator implementation for this verifier is also provided. The validator downloads the public key from the access teams endpoint and uses it to verify the JWT sent to cloudflared with the audtag (clientID) information provided in the config. --- ingress/middleware/jwtvalidator_test.go | 6 ++++++ ingress/middleware/verifier.go | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 ingress/middleware/jwtvalidator_test.go create mode 100644 ingress/middleware/verifier.go diff --git a/ingress/middleware/jwtvalidator_test.go b/ingress/middleware/jwtvalidator_test.go new file mode 100644 index 00000000..c12f6011 --- /dev/null +++ b/ingress/middleware/jwtvalidator_test.go @@ -0,0 +1,6 @@ +package middleware + +import "testing" + +func TestJWTValidatorHandle(t *testing.T) { +} diff --git a/ingress/middleware/verifier.go b/ingress/middleware/verifier.go new file mode 100644 index 00000000..7888dc31 --- /dev/null +++ b/ingress/middleware/verifier.go @@ -0,0 +1,10 @@ +package middleware + +import ( + "context" + "net/http" +) + +type Handler interface { + Handle(ctx context.Context, r *http.Request) error +} From eb36716ba4b2a190f6484ab92cee2a15bcae08a2 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 21 Sep 2022 15:27:55 +0100 Subject: [PATCH 214/238] TUN-6774: Validate OriginRequest.Access to add Ingress.Middleware We take advantage of the JWTValidator middleware and attach it to an ingress rule based on Access configurations. We attach the Validator directly to the ingress rules because we want to take advantage of caching and token revert/handling that comes with go-oidc. --- ingress/middleware/verifier.go | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ingress/middleware/verifier.go diff --git a/ingress/middleware/verifier.go b/ingress/middleware/verifier.go deleted file mode 100644 index 7888dc31..00000000 --- a/ingress/middleware/verifier.go +++ /dev/null @@ -1,10 +0,0 @@ -package middleware - -import ( - "context" - "net/http" -) - -type Handler interface { - Handle(ctx context.Context, r *http.Request) error -} From 9bb7628fbc756e70f10360539a28215c30bdd6dd Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Wed, 21 Sep 2022 11:16:37 +0100 Subject: [PATCH 215/238] TUN-6772: Add a JWT Validator as an ingress verifier This adds a new verifier interface that can be attached to ingress.Rule. This would act as a middleware layer that gets executed at the start of proxy.ProxyHTTP. A jwt validator implementation for this verifier is also provided. The validator downloads the public key from the access teams endpoint and uses it to verify the JWT sent to cloudflared with the audtag (clientID) information provided in the config. --- ingress/middleware/verifier.go | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 ingress/middleware/verifier.go diff --git a/ingress/middleware/verifier.go b/ingress/middleware/verifier.go new file mode 100644 index 00000000..7888dc31 --- /dev/null +++ b/ingress/middleware/verifier.go @@ -0,0 +1,10 @@ +package middleware + +import ( + "context" + "net/http" +) + +type Handler interface { + Handle(ctx context.Context, r *http.Request) error +} From 7f487c2651e465d6bec4075630fa5fbaac766046 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Thu, 22 Sep 2022 15:11:59 +0100 Subject: [PATCH 216/238] TUN-6775: Add middleware.Handler verification to ProxyHTTP ProxyHTTP now processes middleware Handler before executing the request. A chain of handlers is now executed and appropriate response status codes are sent. --- ingress/middleware/jwtvalidator.go | 29 ++++++++++++++++++------- ingress/middleware/jwtvalidator_test.go | 6 ----- ingress/middleware/middleware.go | 13 +++++++++-- ingress/middleware/verifier.go | 10 --------- proxy/proxy.go | 22 +++++++++++++++++++ 5 files changed, 54 insertions(+), 26 deletions(-) delete mode 100644 ingress/middleware/jwtvalidator_test.go delete mode 100644 ingress/middleware/verifier.go diff --git a/ingress/middleware/jwtvalidator.go b/ingress/middleware/jwtvalidator.go index 667bc1ce..8ee9b789 100644 --- a/ingress/middleware/jwtvalidator.go +++ b/ingress/middleware/jwtvalidator.go @@ -6,7 +6,6 @@ import ( "net/http" "github.com/coreos/go-oidc/v3/oidc" - "github.com/pkg/errors" ) const ( @@ -14,7 +13,6 @@ const ( ) var ( - ErrNoAccessToken = errors.New("no access token provided in request") cloudflareAccessCertsURL = "https://%s.cloudflareaccess.com" ) @@ -39,28 +37,43 @@ func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTVal verifier := oidc.NewVerifier(certsURL, keySet, config) return &JWTValidator{ IDTokenVerifier: verifier, + audTags: audTags, } } -func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) error { +func (v *JWTValidator) Name() string { + return "AccessJWTValidator" +} + +func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) (*HandleResult, error) { accessJWT := r.Header.Get(headerKeyAccessJWTAssertion) if accessJWT == "" { - return ErrNoAccessToken + // log the exact error message here. the message is specific to the handler implementation logic, we don't gain anything + // in passing it upstream. and each handler impl know what logging level to use for each. + return &HandleResult{ + ShouldFilterRequest: true, + StatusCode: http.StatusForbidden, + Reason: "no access token in request", + }, nil } token, err := v.IDTokenVerifier.Verify(ctx, accessJWT) if err != nil { - return fmt.Errorf("Invalid token: %w", err) + return nil, err } - // We want atleast one audTag to match + // We want at least one audTag to match for _, jwtAudTag := range token.Audience { for _, acceptedAudTag := range v.audTags { if acceptedAudTag == jwtAudTag { - return nil + return &HandleResult{ShouldFilterRequest: false}, nil } } } - return fmt.Errorf("Invalid token: %w", err) + return &HandleResult{ + ShouldFilterRequest: true, + StatusCode: http.StatusForbidden, + Reason: fmt.Sprintf("Invalid token in jwt: %v", token.Audience), + }, nil } diff --git a/ingress/middleware/jwtvalidator_test.go b/ingress/middleware/jwtvalidator_test.go deleted file mode 100644 index c12f6011..00000000 --- a/ingress/middleware/jwtvalidator_test.go +++ /dev/null @@ -1,6 +0,0 @@ -package middleware - -import "testing" - -func TestJWTValidatorHandle(t *testing.T) { -} diff --git a/ingress/middleware/middleware.go b/ingress/middleware/middleware.go index 7888dc31..c3b02612 100644 --- a/ingress/middleware/middleware.go +++ b/ingress/middleware/middleware.go @@ -5,6 +5,15 @@ import ( "net/http" ) -type Handler interface { - Handle(ctx context.Context, r *http.Request) error +type HandleResult struct { + // Tells that the request didn't passed the handler and should be filtered + ShouldFilterRequest bool + // The status code to return in case ShouldFilterRequest is true. + StatusCode int + Reason string +} + +type Handler interface { + Name() string + Handle(ctx context.Context, r *http.Request) (result *HandleResult, err error) } diff --git a/ingress/middleware/verifier.go b/ingress/middleware/verifier.go deleted file mode 100644 index 7888dc31..00000000 --- a/ingress/middleware/verifier.go +++ /dev/null @@ -1,10 +0,0 @@ -package middleware - -import ( - "context" - "net/http" -) - -type Handler interface { - Handle(ctx context.Context, r *http.Request) error -} diff --git a/proxy/proxy.go b/proxy/proxy.go index 6fe4efa4..c769e19e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -60,6 +60,21 @@ func NewOriginProxy( return proxy } +func (p *Proxy) applyIngressMiddleware(rule *ingress.Rule, r *http.Request, w connection.ResponseWriter) (error, bool) { + for _, handler := range rule.Handlers { + result, err := handler.Handle(r.Context(), r) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("error while processing middleware handler %s", handler.Name())), false + } + + if result.ShouldFilterRequest { + w.WriteRespHeaders(result.StatusCode, nil) + return fmt.Errorf("request filtered by middleware handler (%s) due to: %s", handler.Name(), result.Reason), true + } + } + return nil, true +} + // ProxyHTTP further depends on ingress rules to establish a connection with the origin service. This may be // a simple roundtrip or a tcp/websocket dial depending on ingres rule setup. func (p *Proxy) ProxyHTTP( @@ -86,6 +101,13 @@ func (p *Proxy) ProxyHTTP( p.logRequest(req, logFields) ruleSpan.SetAttributes(attribute.Int("rule-num", ruleNum)) ruleSpan.End() + if err, applied := p.applyIngressMiddleware(rule, req, w); err != nil { + if applied { + p.log.Error().Msg(err.Error()) + return nil + } + return err + } switch originProxy := rule.Service.(type) { case ingress.HTTPOriginProxy: From 3449ea35f27bf6c0734a5a4ee59e68ff8b058f20 Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 22 Sep 2022 10:49:19 +0100 Subject: [PATCH 217/238] TUN-6791: Calculate ICMPv6 checksum --- ingress/origin_icmp_proxy_test.go | 7 +-- packet/decoder_test.go | 1 + packet/packet.go | 39 ++++++++++++- packet/packet_test.go | 93 +++++++++++++++++++++++++++++++ packet/router_test.go | 1 + 5 files changed, 134 insertions(+), 7 deletions(-) diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index 6046c575..e6f37a29 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -299,12 +299,7 @@ func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) { require.Equal(t, ipv6.ICMPTypeEchoReply, decoded.Type) } require.Equal(t, 0, decoded.Code) - if echoReq.Type == ipv4.ICMPTypeEcho { - require.NotZero(t, decoded.Checksum) - } else { - // For ICMPv6, the kernel will compute the checksum during transmission unless pseudo header is not nil - require.Zero(t, decoded.Checksum) - } + require.NotZero(t, decoded.Checksum) require.Equal(t, echoReq.Body, decoded.Body) } diff --git a/packet/decoder_test.go b/packet/decoder_test.go index c07896e4..b8770d74 100644 --- a/packet/decoder_test.go +++ b/packet/decoder_test.go @@ -152,6 +152,7 @@ func TestDecodeICMP(t *testing.T) { require.Equal(t, test.packet.Type, icmpPacket.Type) require.Equal(t, test.packet.Code, icmpPacket.Code) + assertICMPChecksum(t, icmpPacket) require.Equal(t, test.packet.Body, icmpPacket.Body) expectedBody, err := test.packet.Body.Marshal(test.packet.Type.Protocol()) require.NoError(t, err) diff --git a/packet/packet.go b/packet/packet.go index b691790f..de1e1d50 100644 --- a/packet/packet.go +++ b/packet/packet.go @@ -1,6 +1,7 @@ package packet import ( + "encoding/binary" "fmt" "net/netip" @@ -21,6 +22,7 @@ const ( // 0 = ttl exceed in transit, 1 = fragment reassembly time exceeded icmpTTLExceedInTransitCode = 0 DefaultTTL uint8 = 255 + pseudoHeaderLen = 40 ) // Packet represents an IP packet or a packet that is encapsulated by IP @@ -117,7 +119,18 @@ func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) { return nil, err } - msg, err := i.Marshal(nil) + var serializedPsh []byte = nil + if i.Protocol == layers.IPProtocolICMPv6 { + psh := &PseudoHeader{ + SrcIP: i.Src.As16(), + DstIP: i.Dst.As16(), + // i.Marshal re-calculates the UpperLayerPacketLength + UpperLayerPacketLength: 0, + NextHeader: uint8(i.Protocol), + } + serializedPsh = psh.Marshal() + } + msg, err := i.Marshal(serializedPsh) if err != nil { return nil, err } @@ -125,6 +138,29 @@ func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) { return append(ipLayers, icmpLayer), nil } +// https://www.rfc-editor.org/rfc/rfc2460#section-8.1 +type PseudoHeader struct { + SrcIP [16]byte + DstIP [16]byte + UpperLayerPacketLength uint32 + zero [3]byte + NextHeader uint8 +} + +func (ph *PseudoHeader) Marshal() []byte { + buf := make([]byte, pseudoHeaderLen) + index := 0 + copy(buf, ph.SrcIP[:]) + index += 16 + copy(buf[index:], ph.DstIP[:]) + index += 16 + binary.BigEndian.PutUint32(buf[index:], ph.UpperLayerPacketLength) + index += 4 + copy(buf[index:], ph.zero[:]) + buf[pseudoHeaderLen-1] = ph.NextHeader + return buf +} + func NewICMPTTLExceedPacket(originalIP *IP, originalPacket RawPacket, routerIP netip.Addr) *ICMP { var ( protocol layers.IPProtocol @@ -137,6 +173,7 @@ func NewICMPTTLExceedPacket(originalIP *IP, originalPacket RawPacket, routerIP n protocol = layers.IPProtocolICMPv6 icmpType = ipv6.ICMPTypeTimeExceeded } + return &ICMP{ IP: &IP{ Src: routerIP, diff --git a/packet/packet_test.go b/packet/packet_test.go index 8b0b314f..e486c744 100644 --- a/packet/packet_test.go +++ b/packet/packet_test.go @@ -5,6 +5,7 @@ import ( "net/netip" "testing" + "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/stretchr/testify/require" "golang.org/x/net/icmp" @@ -101,4 +102,96 @@ func assertTTLExceedPacket(t *testing.T, pk *ICMP) { require.Len(t, rawTTLExceedPacket.Data, headerLen+icmpHeaderLen+len(rawPacket.Data)) require.True(t, bytes.Equal(rawPacket.Data, rawTTLExceedPacket.Data[headerLen+icmpHeaderLen:])) } + + decoder := NewICMPDecoder() + decodedPacket, err := decoder.Decode(rawTTLExceedPacket) + require.NoError(t, err) + assertICMPChecksum(t, decodedPacket) +} + +func assertICMPChecksum(t *testing.T, icmpPacket *ICMP) { + buf := gopacket.NewSerializeBuffer() + if icmpPacket.Protocol == layers.IPProtocolICMPv4 { + icmpv4 := layers.ICMPv4{ + TypeCode: layers.CreateICMPv4TypeCode(uint8(icmpPacket.Type.(ipv4.ICMPType)), uint8(icmpPacket.Code)), + } + switch body := icmpPacket.Body.(type) { + case *icmp.Echo: + icmpv4.Id = uint16(body.ID) + icmpv4.Seq = uint16(body.Seq) + payload := gopacket.Payload(body.Data) + require.NoError(t, payload.SerializeTo(buf, serializeOpts)) + default: + require.NoError(t, serializeICMPAsPayload(icmpPacket.Message, buf)) + } + // SerializeTo sets the checksum in icmpv4 + require.NoError(t, icmpv4.SerializeTo(buf, serializeOpts)) + require.Equal(t, icmpv4.Checksum, uint16(icmpPacket.Checksum)) + } else { + switch body := icmpPacket.Body.(type) { + case *icmp.Echo: + payload := gopacket.Payload(body.Data) + require.NoError(t, payload.SerializeTo(buf, serializeOpts)) + echo := layers.ICMPv6Echo{ + Identifier: uint16(body.ID), + SeqNumber: uint16(body.Seq), + } + require.NoError(t, echo.SerializeTo(buf, serializeOpts)) + default: + require.NoError(t, serializeICMPAsPayload(icmpPacket.Message, buf)) + } + + icmpv6 := layers.ICMPv6{ + TypeCode: layers.CreateICMPv6TypeCode(uint8(icmpPacket.Type.(ipv6.ICMPType)), uint8(icmpPacket.Code)), + } + ipLayer := layers.IPv6{ + Version: 6, + SrcIP: icmpPacket.Src.AsSlice(), + DstIP: icmpPacket.Dst.AsSlice(), + NextHeader: icmpPacket.Protocol, + HopLimit: icmpPacket.TTL, + } + require.NoError(t, icmpv6.SetNetworkLayerForChecksum(&ipLayer)) + + // SerializeTo sets the checksum in icmpv4 + require.NoError(t, icmpv6.SerializeTo(buf, serializeOpts)) + require.Equal(t, icmpv6.Checksum, uint16(icmpPacket.Checksum)) + } +} + +func serializeICMPAsPayload(message *icmp.Message, buf gopacket.SerializeBuffer) error { + serializedBody, err := message.Body.Marshal(message.Type.Protocol()) + if err != nil { + return err + } + payload := gopacket.Payload(serializedBody) + return payload.SerializeTo(buf, serializeOpts) +} + +func TestChecksum(t *testing.T) { + data := []byte{0x63, 0x2c, 0x49, 0xd6, 0x00, 0x0d, 0xc1, 0xda} + pk := ICMP{ + IP: &IP{ + Src: netip.MustParseAddr("2606:4700:110:89c1:c63a:861:e08c:b049"), + Dst: netip.MustParseAddr("fde8:b693:d420:109b::2"), + Protocol: layers.IPProtocolICMPv6, + TTL: 3, + }, + Message: &icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, + Code: 0, + Body: &icmp.Echo{ + ID: 0x20a7, + Seq: 8, + Data: data, + }, + }, + } + encoder := NewEncoder() + encoded, err := encoder.Encode(&pk) + require.NoError(t, err) + + decoder := NewICMPDecoder() + decoded, err := decoder.Decode(encoded) + require.Equal(t, 0xff96, decoded.Checksum) } diff --git a/packet/router_test.go b/packet/router_test.go index 14e49986..48afdc77 100644 --- a/packet/router_test.go +++ b/packet/router_test.go @@ -96,6 +96,7 @@ func assertTTLExceed(t *testing.T, originalPacket *ICMP, expectedSrc netip.Addr, require.Equal(t, ipv6.ICMPTypeTimeExceeded, decoded.Type) } require.Equal(t, 0, decoded.Code) + assertICMPChecksum(t, decoded) timeExceed, ok := decoded.Body.(*icmp.TimeExceeded) require.True(t, ok) require.True(t, bytes.Equal(rawPacket.Data, timeExceed.Data)) From be0305ec584104127b1bb92acc63f9f1eaebba76 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 20 Sep 2022 11:39:51 +0100 Subject: [PATCH 218/238] TUN-6741: ICMP proxy tries to listen on specific IPv4 & IPv6 when possible If it cannot determine the correct interface IP, it will fallback to all interfaces. This commit also introduces the icmpv4-src and icmpv6-src flags --- cmd/cloudflared/tunnel/configuration.go | 153 ++++++++++++++++++++++++ cmd/cloudflared/tunnel/subcommands.go | 12 ++ component-tests/constants.py | 1 + component-tests/test_logging.py | 19 ++- connection/quic.go | 19 ++- connection/quic_test.go | 8 +- ingress/icmp_darwin.go | 5 +- ingress/icmp_generic.go | 2 +- ingress/icmp_linux.go | 13 +- ingress/icmp_posix.go | 11 +- ingress/icmp_posix_test.go | 2 +- ingress/icmp_windows.go | 2 +- ingress/icmp_windows_test.go | 2 +- ingress/origin_icmp_proxy.go | 7 +- ingress/origin_icmp_proxy_test.go | 6 +- packet/router.go | 56 +++------ packet/router_test.go | 7 +- quic/datagram.go | 4 +- quic/datagram_test.go | 6 +- quic/datagramv2.go | 20 ++-- supervisor/supervisor.go | 12 +- supervisor/tunnel.go | 4 +- 22 files changed, 262 insertions(+), 109 deletions(-) diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index ef9a7e28..9f031641 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -5,6 +5,8 @@ import ( "fmt" "io/ioutil" mathRand "math/rand" + "net" + "net/netip" "os" "path/filepath" "strings" @@ -20,6 +22,7 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/edgediscovery/allregions" + "github.com/cloudflare/cloudflared/packet" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/connection" @@ -384,6 +387,12 @@ func prepareTunnelConfig( NeedPQ: needPQ, PQKexIdx: pqKexIdx, } + packetConfig, err := newPacketConfig(c, log) + if err != nil { + log.Warn().Err(err).Msg("ICMP proxy feature is disabled") + } else { + tunnelConfig.PacketConfig = packetConfig + } orchestratorConfig := &orchestration.Config{ Ingress: &ingressRules, WarpRouting: ingress.NewWarpRoutingConfig(&cfg.WarpRouting), @@ -453,3 +462,147 @@ func parseConfigIPVersion(version string) (v allregions.ConfigIPVersion, err err } return } + +func newPacketConfig(c *cli.Context, logger *zerolog.Logger) (*packet.GlobalRouterConfig, error) { + ipv4Src, err := determineICMPv4Src(c.String("icmpv4-src"), logger) + if err != nil { + return nil, errors.Wrap(err, "failed to determine IPv4 source address for ICMP proxy") + } + logger.Info().Msgf("ICMP proxy will use %s as source for IPv4", ipv4Src) + + ipv6Src, zone, err := determineICMPv6Src(c.String("icmpv6-src"), logger, ipv4Src) + if err != nil { + return nil, errors.Wrap(err, "failed to determine IPv6 source address for ICMP proxy") + } + if zone != "" { + logger.Info().Msgf("ICMP proxy will use %s in zone %s as source for IPv6", ipv6Src, zone) + } else { + logger.Info().Msgf("ICMP proxy will use %s as source for IPv6", ipv6Src) + } + + icmpRouter, err := ingress.NewICMPRouter(ipv4Src, ipv6Src, zone, logger) + if err != nil { + return nil, err + } + return &packet.GlobalRouterConfig{ + ICMPRouter: icmpRouter, + IPv4Src: ipv4Src, + IPv6Src: ipv6Src, + Zone: zone, + }, nil +} + +func determineICMPv4Src(userDefinedSrc string, logger *zerolog.Logger) (netip.Addr, error) { + if userDefinedSrc != "" { + addr, err := netip.ParseAddr(userDefinedSrc) + if err != nil { + return netip.Addr{}, err + } + if addr.Is4() { + return addr, nil + } + return netip.Addr{}, fmt.Errorf("expect IPv4, but %s is IPv6", userDefinedSrc) + } + + addr, err := findLocalAddr(net.ParseIP("192.168.0.1"), 53) + if err != nil { + addr = netip.IPv4Unspecified() + logger.Debug().Err(err).Msgf("Failed to determine the IPv4 for this machine. It will use %s to send/listen for ICMPv4 echo", addr) + } + return addr, nil +} + +type interfaceIP struct { + name string + ip net.IP +} + +func determineICMPv6Src(userDefinedSrc string, logger *zerolog.Logger, ipv4Src netip.Addr) (addr netip.Addr, zone string, err error) { + if userDefinedSrc != "" { + userDefinedIP, zone, _ := strings.Cut(userDefinedSrc, "%") + addr, err := netip.ParseAddr(userDefinedIP) + if err != nil { + return netip.Addr{}, "", err + } + if addr.Is6() { + return addr, zone, nil + } + return netip.Addr{}, "", fmt.Errorf("expect IPv6, but %s is IPv4", userDefinedSrc) + } + + // Loop through all the interfaces, the preference is + // 1. The interface where ipv4Src is in + // 2. Interface with IPv6 address + // 3. Unspecified interface + + interfaces, err := net.Interfaces() + if err != nil { + return netip.IPv6Unspecified(), "", nil + } + + interfacesWithIPv6 := make([]interfaceIP, 0) + for _, interf := range interfaces { + interfaceAddrs, err := interf.Addrs() + if err != nil { + continue + } + + foundIPv4SrcInterface := false + for _, interfaceAddr := range interfaceAddrs { + if ipnet, ok := interfaceAddr.(*net.IPNet); ok { + ip := ipnet.IP + if ip.Equal(ipv4Src.AsSlice()) { + foundIPv4SrcInterface = true + } + if ip.To4() == nil { + interfacesWithIPv6 = append(interfacesWithIPv6, interfaceIP{ + name: interf.Name, + ip: ip, + }) + } + } + } + // Found the interface of ipv4Src. Loop through the addresses to see if there is an IPv6 + if foundIPv4SrcInterface { + for _, interfaceAddr := range interfaceAddrs { + if ipnet, ok := interfaceAddr.(*net.IPNet); ok { + ip := ipnet.IP + if ip.To4() == nil { + addr, err := netip.ParseAddr(ip.String()) + if err == nil { + return addr, interf.Name, nil + } + } + } + } + } + } + + for _, interf := range interfacesWithIPv6 { + addr, err := netip.ParseAddr(interf.ip.String()) + if err == nil { + return addr, interf.name, nil + } + } + logger.Debug().Err(err).Msgf("Failed to determine the IPv6 for this machine. It will use %s to send/listen for ICMPv6 echo", netip.IPv6Unspecified()) + + return netip.IPv6Unspecified(), "", nil +} + +// FindLocalAddr tries to dial UDP and returns the local address picked by the OS +func findLocalAddr(dst net.IP, port int) (netip.Addr, error) { + udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{ + IP: dst, + Port: port, + }) + if err != nil { + return netip.Addr{}, err + } + defer udpConn.Close() + localAddrPort, err := netip.ParseAddrPort(udpConn.LocalAddr().String()) + if err != nil { + return netip.Addr{}, err + } + localAddr := localAddrPort.Addr() + return localAddr, nil +} diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 71435465..4494e761 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -176,6 +176,16 @@ var ( Usage: "Base64 encoded secret to set for the tunnel. The decoded secret must be at least 32 bytes long. If not specified, a random 32-byte secret will be generated.", EnvVars: []string{"TUNNEL_CREATE_SECRET"}, } + icmpv4SrcFlag = &cli.StringFlag{ + Name: "icmpv4-src", + Usage: "Source address to send/receive ICMPv4 messages. If not provided cloudflared will dial a local address to determine the source IP or fallback to 0.0.0.0.", + EnvVars: []string{"TUNNEL_ICMPV4_SRC"}, + } + icmpv6SrcFlag = &cli.StringFlag{ + Name: "icmpv6-src", + Usage: "Source address and the interface name to send/receive ICMPv6 messages. If not provided cloudflared will dial a local address to determine the source IP or fallback to ::.", + EnvVars: []string{"TUNNEL_ICMPV6_SRC"}, + } ) func buildCreateCommand() *cli.Command { @@ -613,6 +623,8 @@ func buildRunCommand() *cli.Command { selectProtocolFlag, featuresFlag, tunnelTokenFlag, + icmpv4SrcFlag, + icmpv6SrcFlag, } flags = append(flags, configureProxyFlags(false)...) return &cli.Command{ diff --git a/component-tests/constants.py b/component-tests/constants.py index bb4b9d22..e46bac31 100644 --- a/component-tests/constants.py +++ b/component-tests/constants.py @@ -1,6 +1,7 @@ METRICS_PORT = 51000 MAX_RETRIES = 5 BACKOFF_SECS = 7 +MAX_LOG_LINES = 50 PROXY_DNS_PORT = 9053 diff --git a/component-tests/test_logging.py b/component-tests/test_logging.py index 51282ded..e9b0bfbd 100644 --- a/component-tests/test_logging.py +++ b/component-tests/test_logging.py @@ -2,6 +2,7 @@ import json import os +from constants import MAX_LOG_LINES from util import start_cloudflared, wait_tunnel_ready, send_requests # Rolling logger rotate log files after 1 MB @@ -11,14 +12,24 @@ expect_message = "Starting Hello" def assert_log_to_terminal(cloudflared): - stderr = cloudflared.stderr.read(1500) - assert expect_message.encode() in stderr, f"{stderr} doesn't contain {expect_message}" + for _ in range(0, MAX_LOG_LINES): + line = cloudflared.stderr.readline() + if not line: + break + if expect_message.encode() in line: + return + raise Exception(f"terminal log doesn't contain {expect_message}") def assert_log_in_file(file): with open(file, "r") as f: - log = f.read(2000) - assert expect_message in log, f"{log} doesn't contain {expect_message}" + for _ in range(0, MAX_LOG_LINES): + line = f.readline() + if not line: + break + if expect_message in line: + return + raise Exception(f"log file doesn't contain {expect_message}") def assert_json_log(file): diff --git a/connection/quic.go b/connection/quic.go index 86a5228c..95829a6e 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -65,7 +65,7 @@ func NewQUICConnection( connOptions *tunnelpogs.ConnectionOptions, controlStreamHandler ControlStreamHandler, logger *zerolog.Logger, - icmpRouter packet.ICMPRouter, + packetRouterConfig *packet.GlobalRouterConfig, ) (*QUICConnection, error) { session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig) if err != nil { @@ -73,19 +73,14 @@ func NewQUICConnection( } sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) - var ( - datagramMuxer quicpogs.BaseDatagramMuxer - pr *packet.Router - ) - if icmpRouter != nil { - datagramMuxerV2 := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) - pr = packet.NewRouter(datagramMuxerV2, &returnPipe{muxer: datagramMuxerV2}, icmpRouter, logger) - datagramMuxer = datagramMuxerV2 - } else { - datagramMuxer = quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan) - } + datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) + var pr *packet.Router + if packetRouterConfig != nil { + pr = packet.NewRouter(packetRouterConfig, datagramMuxer, &returnPipe{muxer: datagramMuxer}, logger) + } + return &QUICConnection{ session: session, orchestrator: orchestrator, diff --git a/connection/quic_test.go b/connection/quic_test.go index 8904eeeb..c2990878 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -583,8 +583,12 @@ func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic. close(sessionDone) }() - // Send a message to the quic session on edge side, it should be deumx to this datagram session - muxedPayload := append(payload, sessionID[:]...) + // Send a message to the quic session on edge side, it should be deumx to this datagram v2 session + muxedPayload, err := quicpogs.SuffixSessionID(sessionID, payload) + require.NoError(t, err) + muxedPayload, err = quicpogs.SuffixType(muxedPayload, quicpogs.DatagramTypeUDP) + require.NoError(t, err) + err = edgeQUICSession.SendMessage(muxedPayload) require.NoError(t, err) diff --git a/ingress/icmp_darwin.go b/ingress/icmp_darwin.go index 352b5c54..2a57b415 100644 --- a/ingress/icmp_darwin.go +++ b/ingress/icmp_darwin.go @@ -113,11 +113,12 @@ func (snf echoFunnelID) String() string { return strconv.FormatUint(uint64(snf), 10) } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { - conn, err := newICMPConn(listenIP) +func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { + conn, err := newICMPConn(listenIP, zone) if err != nil { return nil, err } + logger.Info().Msgf("Created ICMP proxy listening on %s", conn.LocalAddr()) return &icmpProxy{ srcFunnelTracker: packet.NewFunnelTracker(), echoIDTracker: newEchoIDTracker(), diff --git a/ingress/icmp_generic.go b/ingress/icmp_generic.go index c685a2f4..e1c66e81 100644 --- a/ingress/icmp_generic.go +++ b/ingress/icmp_generic.go @@ -26,6 +26,6 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { return errICMPProxyNotImplemented } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { +func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { return nil, errICMPProxyNotImplemented } diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index 5f122e49..bc7f968d 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -24,25 +24,27 @@ import ( type icmpProxy struct { srcFunnelTracker *packet.FunnelTracker listenIP netip.Addr + ipv6Zone string logger *zerolog.Logger idleTimeout time.Duration } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { - if err := testPermission(listenIP); err != nil { +func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { + if err := testPermission(listenIP, zone); err != nil { return nil, err } return &icmpProxy{ srcFunnelTracker: packet.NewFunnelTracker(), listenIP: listenIP, + ipv6Zone: zone, logger: logger, idleTimeout: idleTimeout, }, nil } -func testPermission(listenIP netip.Addr) error { +func testPermission(listenIP netip.Addr, zone string) error { // Opens a non-privileged ICMP socket. On Linux the group ID of the process needs to be in ping_group_range - conn, err := newICMPConn(listenIP) + conn, err := newICMPConn(listenIP, zone) if err != nil { // TODO: TUN-6715 check if cloudflared is in ping_group_range if the check failed. If not log instruction to // change the group ID @@ -63,10 +65,11 @@ func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) er } newConnChan := make(chan *icmp.PacketConn, 1) newFunnelFunc := func() (packet.Funnel, error) { - conn, err := newICMPConn(ip.listenIP) + conn, err := newICMPConn(ip.listenIP, ip.ipv6Zone) if err != nil { return nil, errors.Wrap(err, "failed to open ICMP socket") } + ip.logger.Debug().Msgf("Opened ICMP socket listen on %s", conn.LocalAddr()) newConnChan <- conn localUDPAddr, ok := conn.LocalAddr().(*net.UDPAddr) if !ok { diff --git a/ingress/icmp_posix.go b/ingress/icmp_posix.go index 5c3c62b3..0badb685 100644 --- a/ingress/icmp_posix.go +++ b/ingress/icmp_posix.go @@ -16,12 +16,15 @@ import ( ) // Opens a non-privileged ICMP socket on Linux and Darwin -func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) { - network := "udp6" +func newICMPConn(listenIP netip.Addr, zone string) (*icmp.PacketConn, error) { if listenIP.Is4() { - network = "udp4" + return icmp.ListenPacket("udp4", listenIP.String()) } - return icmp.ListenPacket(network, listenIP.String()) + listenAddr := listenIP.String() + if zone != "" { + listenAddr = listenAddr + "%" + zone + } + return icmp.ListenPacket("udp6", listenAddr) } func netipAddr(addr net.Addr) (netip.Addr, bool) { diff --git a/ingress/icmp_posix_test.go b/ingress/icmp_posix_test.go index 397aa524..bbadd196 100644 --- a/ingress/icmp_posix_test.go +++ b/ingress/icmp_posix_test.go @@ -24,7 +24,7 @@ func TestFunnelIdleTimeout(t *testing.T) { startSeq = 8129 ) logger := zerolog.New(os.Stderr) - proxy, err := newICMPProxy(localhostIP, &logger, idleTimeout) + proxy, err := newICMPProxy(localhostIP, "", &logger, idleTimeout) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index 8ad313b5..bc98bcbe 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -224,7 +224,7 @@ type icmpProxy struct { encoderPool sync.Pool } -func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { +func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { var ( srcSocketAddr *sockAddrIn6 handle uintptr diff --git a/ingress/icmp_windows_test.go b/ingress/icmp_windows_test.go index 05f7e620..eefc654f 100644 --- a/ingress/icmp_windows_test.go +++ b/ingress/icmp_windows_test.go @@ -132,7 +132,7 @@ func TestSendEchoErrors(t *testing.T) { } func testSendEchoErrors(t *testing.T, listenIP netip.Addr) { - proxy, err := newICMPProxy(listenIP, &noopLogger, time.Second) + proxy, err := newICMPProxy(listenIP, "", &noopLogger, time.Second) require.NoError(t, err) echo := icmp.Echo{ diff --git a/ingress/origin_icmp_proxy.go b/ingress/origin_icmp_proxy.go index 4bd0c0fe..4b6826b4 100644 --- a/ingress/origin_icmp_proxy.go +++ b/ingress/origin_icmp_proxy.go @@ -33,10 +33,9 @@ type icmpRouter struct { // NewICMPRouter doesn't return an error if either ipv4 proxy or ipv6 proxy can be created. The machine might only // support one of them -func NewICMPRouter(logger *zerolog.Logger) (*icmpRouter, error) { - // TODO: TUN-6741: don't bind to all interface - ipv4Proxy, ipv4Err := newICMPProxy(netip.IPv4Unspecified(), logger, funnelIdleTimeout) - ipv6Proxy, ipv6Err := newICMPProxy(netip.IPv6Unspecified(), logger, funnelIdleTimeout) +func NewICMPRouter(ipv4Addr, ipv6Addr netip.Addr, ipv6Zone string, logger *zerolog.Logger) (*icmpRouter, error) { + ipv4Proxy, ipv4Err := newICMPProxy(ipv4Addr, "", logger, funnelIdleTimeout) + ipv6Proxy, ipv6Err := newICMPProxy(ipv6Addr, ipv6Zone, logger, funnelIdleTimeout) if ipv4Err != nil && ipv6Err != nil { return nil, fmt.Errorf("cannot create ICMPv4 proxy: %v nor ICMPv6 proxy: %v", ipv4Err, ipv6Err) } diff --git a/ingress/origin_icmp_proxy_test.go b/ingress/origin_icmp_proxy_test.go index e6f37a29..d080f46a 100644 --- a/ingress/origin_icmp_proxy_test.go +++ b/ingress/origin_icmp_proxy_test.go @@ -42,7 +42,7 @@ func testICMPRouterEcho(t *testing.T, sendIPv4 bool) { endSeq = 20 ) - router, err := NewICMPRouter(&noopLogger) + router, err := NewICMPRouter(localhostIP, localhostIPv6, "", &noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) @@ -106,7 +106,7 @@ func TestConcurrentRequestsToSameDst(t *testing.T) { endSeq = 5 ) - router, err := NewICMPRouter(&noopLogger) + router, err := NewICMPRouter(localhostIP, localhostIPv6, "", &noopLogger) require.NoError(t, err) proxyDone := make(chan struct{}) @@ -238,7 +238,7 @@ func TestICMPRouterRejectNotEcho(t *testing.T) { } func testICMPRouterRejectNotEcho(t *testing.T, srcDstIP netip.Addr, msgs []icmp.Message) { - router, err := NewICMPRouter(&noopLogger) + router, err := NewICMPRouter(localhostIP, localhostIPv6, "", &noopLogger) require.NoError(t, err) responder := echoFlowResponder{ diff --git a/packet/router.go b/packet/router.go index 83acc393..8e7e399d 100644 --- a/packet/router.go +++ b/packet/router.go @@ -2,18 +2,11 @@ package packet import ( "context" - "net" "net/netip" "github.com/rs/zerolog" ) -var ( - // Source IP in documentation range to return ICMP error messages if we can't determine the IP of this machine - icmpv4ErrFallbackSrc = netip.MustParseAddr("192.0.2.30") - icmpv6ErrFallbackSrc = netip.MustParseAddr("2001:db8::") -) - // ICMPRouter sends ICMP messages and listens for their responses type ICMPRouter interface { // Serve starts listening for responses to the requests until context is done @@ -28,32 +21,31 @@ type Upstream interface { ReceivePacket(ctx context.Context) (RawPacket, error) } +// Router routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets type Router struct { upstream Upstream returnPipe FunnelUniPipe - icmpProxy ICMPRouter + icmpRouter ICMPRouter ipv4Src netip.Addr ipv6Src netip.Addr logger *zerolog.Logger } -func NewRouter(upstream Upstream, returnPipe FunnelUniPipe, icmpProxy ICMPRouter, logger *zerolog.Logger) *Router { - ipv4Src, err := findLocalAddr(net.ParseIP("1.1.1.1"), 53) - if err != nil { - logger.Warn().Err(err).Msgf("Failed to determine the IPv4 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv4ErrFallbackSrc) - ipv4Src = icmpv4ErrFallbackSrc - } - ipv6Src, err := findLocalAddr(net.ParseIP("2606:4700:4700::1111"), 53) - if err != nil { - logger.Warn().Err(err).Msgf("Failed to determine the IPv6 for this machine. It will use %s as source IP for error messages such as ICMP TTL exceed", icmpv6ErrFallbackSrc) - ipv6Src = icmpv6ErrFallbackSrc - } +// GlobalRouterConfig is the configuration shared by all instance of Router. +type GlobalRouterConfig struct { + ICMPRouter ICMPRouter + IPv4Src netip.Addr + IPv6Src netip.Addr + Zone string +} + +func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger) *Router { return &Router{ upstream: upstream, returnPipe: returnPipe, - icmpProxy: icmpProxy, - ipv4Src: ipv4Src, - ipv6Src: ipv6Src, + icmpRouter: globalConfig.ICMPRouter, + ipv4Src: globalConfig.IPv4Src, + ipv6Src: globalConfig.IPv6Src, logger: logger, } } @@ -80,7 +72,7 @@ func (r *Router) Serve(ctx context.Context) error { } icmpPacket.TTL-- - if err := r.icmpProxy.Request(icmpPacket, r.returnPipe); err != nil { + if err := r.icmpRouter.Request(icmpPacket, r.returnPipe); err != nil { r.logger.Err(err). Str("src", icmpPacket.Src.String()). Str("dst", icmpPacket.Dst.String()). @@ -106,21 +98,3 @@ func (r *Router) sendTTLExceedMsg(pk *ICMP, rawPacket RawPacket, encoder *Encode } return r.returnPipe.SendPacket(pk.Src, encodedTTLExceed) } - -// findLocalAddr tries to dial UDP and returns the local address picked by the OS -func findLocalAddr(dst net.IP, port int) (netip.Addr, error) { - udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{ - IP: dst, - Port: port, - }) - if err != nil { - return netip.Addr{}, err - } - defer udpConn.Close() - localAddrPort, err := netip.ParseAddrPort(udpConn.LocalAddr().String()) - if err != nil { - return netip.Addr{}, err - } - localAddr := localAddrPort.Addr() - return localAddr, nil -} diff --git a/packet/router_test.go b/packet/router_test.go index 48afdc77..8b009c1c 100644 --- a/packet/router_test.go +++ b/packet/router_test.go @@ -26,7 +26,12 @@ func TestRouterReturnTTLExceed(t *testing.T) { returnPipe := &mockFunnelUniPipe{ uniPipe: make(chan RawPacket), } - router := NewRouter(upstream, returnPipe, &mockICMPRouter{}, &noopLogger) + packetConfig := &GlobalRouterConfig{ + ICMPRouter: &mockICMPRouter{}, + IPv4Src: netip.MustParseAddr("172.16.0.1"), + IPv6Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + } + router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger) ctx, cancel := context.WithCancel(context.Background()) routerStopped := make(chan struct{}) go func() { diff --git a/quic/datagram.go b/quic/datagram.go index 334754d4..23408cfe 100644 --- a/quic/datagram.go +++ b/quic/datagram.go @@ -49,7 +49,7 @@ func (dm *DatagramMuxer) SendToSession(session *packet.Session) error { packetTooBigDropped.Inc() return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } - payloadWithMetadata, err := suffixSessionID(session.ID, session.Payload) + payloadWithMetadata, err := SuffixSessionID(session.ID, session.Payload) if err != nil { return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") } @@ -112,7 +112,7 @@ func extractSessionID(b []byte) (uuid.UUID, []byte, error) { // SuffixSessionID appends the session ID at the end of the payload. Suffix is more performant than prefix because // the payload slice might already have enough capacity to append the session ID at the end -func suffixSessionID(sessionID uuid.UUID, b []byte) ([]byte, error) { +func SuffixSessionID(sessionID uuid.UUID, b []byte) ([]byte, error) { if len(b)+len(sessionID) > MaxDatagramFrameSize { return nil, fmt.Errorf("datagram size exceed %d", MaxDatagramFrameSize) } diff --git a/quic/datagram_test.go b/quic/datagram_test.go index bd55f425..69bb0b71 100644 --- a/quic/datagram_test.go +++ b/quic/datagram_test.go @@ -31,7 +31,7 @@ var ( func TestSuffixThenRemoveSessionID(t *testing.T) { msg := []byte(t.Name()) - msgWithID, err := suffixSessionID(testSessionID, msg) + msgWithID, err := SuffixSessionID(testSessionID, msg) require.NoError(t, err) require.Len(t, msgWithID, len(msg)+sessionIDLen) @@ -50,11 +50,11 @@ func TestRemoveSessionIDError(t *testing.T) { func TestSuffixSessionIDError(t *testing.T) { msg := make([]byte, MaxDatagramFrameSize-sessionIDLen) - _, err := suffixSessionID(testSessionID, msg) + _, err := SuffixSessionID(testSessionID, msg) require.NoError(t, err) msg = make([]byte, MaxDatagramFrameSize-sessionIDLen+1) - _, err = suffixSessionID(testSessionID, msg) + _, err = SuffixSessionID(testSessionID, msg) require.Error(t, err) } diff --git a/quic/datagramv2.go b/quic/datagramv2.go index 3f1c8f0e..373d8731 100644 --- a/quic/datagramv2.go +++ b/quic/datagramv2.go @@ -11,11 +11,11 @@ import ( "github.com/cloudflare/cloudflared/packet" ) -type datagramV2Type byte +type DatagramV2Type byte const ( - udp datagramV2Type = iota - ip + DatagramTypeUDP DatagramV2Type = iota + DatagramTypeIP ) const ( @@ -24,7 +24,7 @@ const ( packetChanCapacity = 128 ) -func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) { +func SuffixType(b []byte, datagramType DatagramV2Type) ([]byte, error) { if len(b)+typeIDLen > MaxDatagramFrameSize { return nil, fmt.Errorf("datagram size %d exceeds max frame size %d", len(b), MaxDatagramFrameSize) } @@ -65,11 +65,11 @@ func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { packetTooBigDropped.Inc() return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu()) } - msgWithID, err := suffixSessionID(session.ID, session.Payload) + msgWithID, err := SuffixSessionID(session.ID, session.Payload) if err != nil { return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped") } - msgWithIDAndType, err := suffixType(msgWithID, udp) + msgWithIDAndType, err := SuffixType(msgWithID, DatagramTypeUDP) if err != nil { return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped") } @@ -82,7 +82,7 @@ func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error { // SendPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing // the payload as IP and look at the source and destination. func (dm *DatagramMuxerV2) SendPacket(pk packet.RawPacket) error { - payloadWithVersion, err := suffixType(pk.Data, ip) + payloadWithVersion, err := SuffixType(pk.Data, DatagramTypeIP) if err != nil { return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped") } @@ -121,12 +121,12 @@ func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error if len(msgWithType) < typeIDLen { return fmt.Errorf("QUIC datagram should have at least %d byte", typeIDLen) } - msgType := datagramV2Type(msgWithType[len(msgWithType)-typeIDLen]) + msgType := DatagramV2Type(msgWithType[len(msgWithType)-typeIDLen]) msg := msgWithType[0 : len(msgWithType)-typeIDLen] switch msgType { - case udp: + case DatagramTypeUDP: return dm.handleSession(ctx, msg) - case ip: + case DatagramTypeIP: return dm.handlePacket(ctx, msg) default: return fmt.Errorf("Unexpected datagram type %d", msgType) diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 04f5536e..9f6a2fe2 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -16,7 +16,6 @@ import ( "github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery/allregions" "github.com/cloudflare/cloudflared/h2mux" - "github.com/cloudflare/cloudflared/ingress" "github.com/cloudflare/cloudflared/orchestration" "github.com/cloudflare/cloudflared/retry" "github.com/cloudflare/cloudflared/signal" @@ -117,13 +116,6 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato connAwareLogger: log, } - icmpRouter, err := ingress.NewICMPRouter(config.Log) - if err != nil { - log.Logger().Warn().Err(err).Msg("Failed to create icmp router, ICMP proxy feature is disabled") - } else { - edgeTunnelServer.icmpRouter = icmpRouter - } - useReconnectToken := false if config.ClassicTunnel != nil { useReconnectToken = config.ClassicTunnel.UseReconnectToken @@ -151,9 +143,9 @@ func (s *Supervisor) Run( ctx context.Context, connectedSignal *signal.Signal, ) error { - if s.edgeTunnelServer.icmpRouter != nil { + if s.config.PacketConfig != nil { go func() { - if err := s.edgeTunnelServer.icmpRouter.Serve(ctx); err != nil { + if err := s.config.PacketConfig.ICMPRouter.Serve(ctx); err != nil { if errors.Is(err, net.ErrClosed) { s.log.Logger().Info().Err(err).Msg("icmp router terminated") } else { diff --git a/supervisor/tunnel.go b/supervisor/tunnel.go index 22fb55e3..5e65f3d1 100644 --- a/supervisor/tunnel.go +++ b/supervisor/tunnel.go @@ -70,6 +70,7 @@ type TunnelConfig struct { MuxerConfig *connection.MuxerConfig ProtocolSelector connection.ProtocolSelector EdgeTLSConfigs map[connection.Protocol]*tls.Config + PacketConfig *packet.GlobalRouterConfig } func (c *TunnelConfig) registrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions { @@ -200,7 +201,6 @@ type EdgeTunnelServer struct { reconnectCh chan ReconnectSignal gracefulShutdownC <-chan struct{} tracker *tunnelstate.ConnTracker - icmpRouter packet.ICMPRouter connAwareLogger *ConnAwareLogger } @@ -661,7 +661,7 @@ func (e *EdgeTunnelServer) serveQUIC( connOptions, controlStreamHandler, connLogger.Logger(), - e.icmpRouter) + e.config.PacketConfig) if err != nil { if e.config.NeedPQ { handlePQTunnelError(err, e.config) From b3e26420c082e250447038a3d66301ce63ad87eb Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Thu, 22 Sep 2022 16:19:06 -0700 Subject: [PATCH 219/238] TUN-6801: Add punycode alternatives for ingress rules --- ingress/ingress.go | 22 +++++++++++---- ingress/ingress_test.go | 30 ++++++++++++++++++++ ingress/rule.go | 16 +++++++++-- ingress/rule_test.go | 62 ++++++++++++++++++++++++----------------- 4 files changed, 98 insertions(+), 32 deletions(-) diff --git a/ingress/ingress.go b/ingress/ingress.go index b1d87878..b4600453 100644 --- a/ingress/ingress.go +++ b/ingress/ingress.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/urfave/cli/v2" + "golang.org/x/net/idna" "github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/ingress/middleware" @@ -275,6 +276,16 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq return Ingress{}, err } + isCatchAllRule := (r.Hostname == "" || r.Hostname == "*") && r.Path == "" + punycodeHostname := "" + if !isCatchAllRule { + punycode, err := idna.Lookup.ToASCII(r.Hostname) + // Don't provide the punycode hostname if it is the same as the original hostname + if err == nil && punycode != r.Hostname { + punycodeHostname = punycode + } + } + var pathRegexp *Regexp if r.Path != "" { var err error @@ -286,11 +297,12 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq } rules[i] = Rule{ - Hostname: r.Hostname, - Service: service, - Path: pathRegexp, - Handlers: handlers, - Config: cfg, + Hostname: r.Hostname, + punycodeHostname: punycodeHostname, + Service: service, + Path: pathRegexp, + Handlers: handlers, + Config: cfg, } } return Ingress{Rules: rules, Defaults: defaults}, nil diff --git a/ingress/ingress_test.go b/ingress/ingress_test.go index 2f1c2850..1e09fac7 100644 --- a/ingress/ingress_test.go +++ b/ingress/ingress_test.go @@ -130,6 +130,36 @@ ingress: }, }, }, + { + name: "Unicode domain", + args: args{rawYAML: ` +ingress: + - hostname: môô.cloudflare.com + service: https://localhost:8000 + - service: https://localhost:8001 +`}, + want: []Rule{ + { + Hostname: "môô.cloudflare.com", + punycodeHostname: "xn--m-xgaa.cloudflare.com", + Service: &httpService{url: localhost8000}, + Config: defaultConfig, + }, + { + Service: &httpService{url: localhost8001}, + Config: defaultConfig, + }, + }, + }, + { + name: "Invalid unicode domain", + args: args{rawYAML: fmt.Sprintf(` +ingress: + - hostname: %s + service: https://localhost:8000 +`, string(rune(0xd8f3))+".cloudflare.com")}, + wantErr: true, + }, { name: "Invalid service", args: args{rawYAML: ` diff --git a/ingress/rule.go b/ingress/rule.go index c7733254..43c7ad5e 100644 --- a/ingress/rule.go +++ b/ingress/rule.go @@ -14,6 +14,9 @@ type Rule struct { // Requests for this hostname will be proxied to this rule's service. Hostname string `json:"hostname"` + // punycodeHostname is an additional optional hostname converted to punycode. + punycodeHostname string + // Path is an optional regex that can specify path-driven ingress rules. Path *Regexp `json:"path"` @@ -50,9 +53,18 @@ func (r Rule) MultiLineString() string { // Matches checks if the rule matches a given hostname/path combination. func (r *Rule) Matches(hostname, path string) bool { - hostMatch := r.Hostname == "" || r.Hostname == "*" || matchHost(r.Hostname, hostname) + hostMatch := false + if r.Hostname == "" || r.Hostname == "*" { + hostMatch = true + } else { + hostMatch = matchHost(r.Hostname, hostname) + } + punycodeHostMatch := false + if r.punycodeHostname != "" { + punycodeHostMatch = matchHost(r.punycodeHostname, hostname) + } pathMatch := r.Path == nil || r.Path.Regexp == nil || r.Path.Regexp.MatchString(path) - return hostMatch && pathMatch + return (hostMatch || punycodeHostMatch) && pathMatch } // Regexp adds unmarshalling from json for regexp.Regexp diff --git a/ingress/rule_test.go b/ingress/rule_test.go index 8ab86132..1c051137 100644 --- a/ingress/rule_test.go +++ b/ingress/rule_test.go @@ -14,23 +14,18 @@ import ( ) func Test_rule_matches(t *testing.T) { - type fields struct { - Hostname string - Path *Regexp - Service OriginService - } type args struct { requestURL *url.URL } tests := []struct { - name string - fields fields - args args - want bool + name string + rule Rule + args args + want bool }{ { name: "Just hostname, pass", - fields: fields{ + rule: Rule{ Hostname: "example.com", }, args: args{ @@ -38,9 +33,31 @@ func Test_rule_matches(t *testing.T) { }, want: true, }, + { + name: "Unicode hostname with unicode request, pass", + rule: Rule{ + Hostname: "môô.cloudflare.com", + punycodeHostname: "xn--m-xgaa.cloudflare.com", + }, + args: args{ + requestURL: MustParseURL(t, "https://môô.cloudflare.com"), + }, + want: true, + }, + { + name: "Unicode hostname with punycode request, pass", + rule: Rule{ + Hostname: "môô.cloudflare.com", + punycodeHostname: "xn--m-xgaa.cloudflare.com", + }, + args: args{ + requestURL: MustParseURL(t, "https://xn--m-xgaa.cloudflare.com"), + }, + want: true, + }, { name: "Entire hostname is wildcard, should match everything", - fields: fields{ + rule: Rule{ Hostname: "*", }, args: args{ @@ -50,7 +67,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Just hostname, fail", - fields: fields{ + rule: Rule{ Hostname: "example.com", }, args: args{ @@ -60,7 +77,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Just wildcard hostname, pass", - fields: fields{ + rule: Rule{ Hostname: "*.example.com", }, args: args{ @@ -70,7 +87,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Just wildcard hostname, fail", - fields: fields{ + rule: Rule{ Hostname: "*.example.com", }, args: args{ @@ -80,7 +97,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Just wildcard outside of subdomain in hostname, fail", - fields: fields{ + rule: Rule{ Hostname: "*example.com", }, args: args{ @@ -90,7 +107,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Wildcard over multiple subdomains", - fields: fields{ + rule: Rule{ Hostname: "*.example.com", }, args: args{ @@ -100,7 +117,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Hostname and path", - fields: fields{ + rule: Rule{ Hostname: "*.example.com", Path: &Regexp{Regexp: regexp.MustCompile("/static/.*\\.html")}, }, @@ -111,7 +128,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Hostname and empty Regex", - fields: fields{ + rule: Rule{ Hostname: "example.com", Path: &Regexp{}, }, @@ -122,7 +139,7 @@ func Test_rule_matches(t *testing.T) { }, { name: "Hostname and nil path", - fields: fields{ + rule: Rule{ Hostname: "example.com", Path: nil, }, @@ -134,13 +151,8 @@ func Test_rule_matches(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := Rule{ - Hostname: tt.fields.Hostname, - Path: tt.fields.Path, - Service: tt.fields.Service, - } u := tt.args.requestURL - if got := r.Matches(u.Hostname(), u.Path); got != tt.want { + if got := tt.rule.Matches(u.Hostname(), u.Path); got != tt.want { t.Errorf("rule.matches() = %v, want %v", got, tt.want) } }) From fdddd8638052f0e72fb977a8eefa3e72ee3e4ff5 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 27 Sep 2022 15:56:42 +0100 Subject: [PATCH 220/238] TUN-6715: Provide suggestion to add cloudflared to ping_group_range if it failed to open ICMP socket --- ingress/icmp_linux.go | 50 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index bc7f968d..de6829a0 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -12,6 +12,9 @@ import ( "fmt" "net" "net/netip" + "os" + "regexp" + "strconv" "time" "github.com/pkg/errors" @@ -21,6 +24,15 @@ import ( "github.com/cloudflare/cloudflared/packet" ) +const ( + // https://lwn.net/Articles/550551/ IPv4 and IPv6 share the same path + pingGroupPath = "/proc/sys/net/ipv4/ping_group_range" +) + +var ( + findGroupIDRegex = regexp.MustCompile(`\d+`) +) + type icmpProxy struct { srcFunnelTracker *packet.FunnelTracker listenIP netip.Addr @@ -30,7 +42,7 @@ type icmpProxy struct { } func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idleTimeout time.Duration) (*icmpProxy, error) { - if err := testPermission(listenIP, zone); err != nil { + if err := testPermission(listenIP, zone, logger); err != nil { return nil, err } return &icmpProxy{ @@ -42,12 +54,17 @@ func newICMPProxy(listenIP netip.Addr, zone string, logger *zerolog.Logger, idle }, nil } -func testPermission(listenIP netip.Addr, zone string) error { +func testPermission(listenIP netip.Addr, zone string, logger *zerolog.Logger) error { // Opens a non-privileged ICMP socket. On Linux the group ID of the process needs to be in ping_group_range + // Only check ping_group_range once for IPv4 + if listenIP.Is4() { + if err := checkInPingGroup(); err != nil { + logger.Warn().Err(err).Msgf("The user running cloudflared process has a GID (group ID) that is not within ping_group_range. You might need to add that user to a group within that range, or instead update the range to encompass a group the user is already in by modifying %s. Otherwise cloudflared will not be able to ping this network", pingGroupPath) + return err + } + } conn, err := newICMPConn(listenIP, zone) if err != nil { - // TODO: TUN-6715 check if cloudflared is in ping_group_range if the check failed. If not log instruction to - // change the group ID return err } // This conn is only to test if cloudflared has permission to open this type of socket @@ -55,6 +72,31 @@ func testPermission(listenIP netip.Addr, zone string) error { return nil } +func checkInPingGroup() error { + file, err := os.ReadFile(pingGroupPath) + if err != nil { + return err + } + groupID := os.Getgid() + // Example content: 999 59999 + found := findGroupIDRegex.FindAll(file, 2) + if len(found) == 2 { + groupMin, err := strconv.ParseUint(string(found[0]), 10, 16) + if err != nil { + return errors.Wrapf(err, "failed to determine minimum ping group ID") + } + groupMax, err := strconv.ParseUint(string(found[1]), 10, 16) + if err != nil { + return errors.Wrapf(err, "failed to determine minimum ping group ID") + } + if groupID < int(groupMin) || groupID > int(groupMax) { + return fmt.Errorf("Group ID %d is not between ping group %d to %d", groupID, groupMin, groupMax) + } + return nil + } + return fmt.Errorf("did not find group range in %s", pingGroupPath) +} + func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { if pk == nil { return errPacketNil From 870193c064986838e9c488af7ede7e7fa3955ab8 Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 29 Sep 2022 12:59:38 +0100 Subject: [PATCH 221/238] TUN-6811: Ping group range should be parsed as int32 --- ingress/icmp_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ingress/icmp_linux.go b/ingress/icmp_linux.go index de6829a0..10ef3e76 100644 --- a/ingress/icmp_linux.go +++ b/ingress/icmp_linux.go @@ -81,11 +81,11 @@ func checkInPingGroup() error { // Example content: 999 59999 found := findGroupIDRegex.FindAll(file, 2) if len(found) == 2 { - groupMin, err := strconv.ParseUint(string(found[0]), 10, 16) + groupMin, err := strconv.ParseInt(string(found[0]), 10, 32) if err != nil { return errors.Wrapf(err, "failed to determine minimum ping group ID") } - groupMax, err := strconv.ParseUint(string(found[1]), 10, 16) + groupMax, err := strconv.ParseInt(string(found[1]), 10, 32) if err != nil { return errors.Wrapf(err, "failed to determine minimum ping group ID") } From cbf8c71fab2dca00e464c6232656d186a06fe001 Mon Sep 17 00:00:00 2001 From: cthuang Date: Wed, 28 Sep 2022 19:02:46 +0100 Subject: [PATCH 222/238] TUN-6716: Document limitation of Windows ICMP proxy --- ingress/icmp_windows.go | 3 +++ packet/router.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ingress/icmp_windows.go b/ingress/icmp_windows.go index bc98bcbe..7d9a46e0 100644 --- a/ingress/icmp_windows.go +++ b/ingress/icmp_windows.go @@ -262,6 +262,9 @@ func (ip *icmpProxy) Serve(ctx context.Context) error { return ctx.Err() } +// Request sends an ICMP echo request and wait for a reply or timeout. +// The async version of Win32 APIs take a callback whose memory is not garbage collected, so we use the synchronous version. +// It's possible that a slow request will block other requests, so we set the timeout to only 1s. func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FunnelUniPipe) error { if pk == nil { return errPacketNil diff --git a/packet/router.go b/packet/router.go index 8e7e399d..2f0fa37b 100644 --- a/packet/router.go +++ b/packet/router.go @@ -11,7 +11,7 @@ import ( type ICMPRouter interface { // Serve starts listening for responses to the requests until context is done Serve(ctx context.Context) error - // Request sends an ICMP message + // Request sends an ICMP message. Implementations should not modify pk after the function returns. Request(pk *ICMP, responder FunnelUniPipe) error } From d7fb18be22c46d95bf781ed530208132295bad73 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 28 Sep 2022 12:25:54 -0700 Subject: [PATCH 223/238] TUN-6810: Add component test for post-quantum --- cfsetup.yaml | 30 ++++++++++++++++++++++-------- component-tests/test_pq.py | 17 +++++++++++++++++ component-tests/util.py | 6 ++++++ 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 component-tests/test_pq.py diff --git a/cfsetup.yaml b/cfsetup.yaml index dd92bf52..6af0f2de 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -195,6 +195,26 @@ stretch: &stretch - ./fmt-check.sh - make test | gotest-to-teamcity component-test: + build_dir: *build_dir + builddeps: + - *pinned_go + - python3.7 + - python3-pip + - python3-setuptools + # procps installs the ps command which is needed in test_sysv_service because the init script + # uses ps pid to determine if the agent is running + - procps + pre-cache-copy-paths: + - component-tests/requirements.txt + pre-cache: &component_test_pre_cache + - sudo pip3 install --upgrade -r component-tests/requirements.txt + post-cache: &component_test_post_cache + # Creates and routes a Named Tunnel for this build. Also constructs config file from env vars. + - python3 component-tests/setup.py --type create + - pytest component-tests -o log_cli=true --log-cli-level=INFO + # The Named Tunnel is deleted and its route unprovisioned here. + - python3 component-tests/setup.py --type cleanup + component-test-fips: build_dir: *build_dir builddeps: - *pinned_go_fips @@ -206,14 +226,8 @@ stretch: &stretch - procps pre-cache-copy-paths: - component-tests/requirements.txt - pre-cache: - - sudo pip3 install --upgrade -r component-tests/requirements.txt - post-cache: - # Creates and routes a Named Tunnel for this build. Also constructs config file from env vars. - - python3 component-tests/setup.py --type create - - pytest component-tests -o log_cli=true --log-cli-level=INFO - # The Named Tunnel is deleted and its route unprovisioned here. - - python3 component-tests/setup.py --type cleanup + pre-cache: *component_test_pre_cache + post-cache: *component_test_post_cache update-homebrew: builddeps: - openssh-client diff --git a/component-tests/test_pq.py b/component-tests/test_pq.py new file mode 100644 index 00000000..e897f293 --- /dev/null +++ b/component-tests/test_pq.py @@ -0,0 +1,17 @@ +from util import LOGGER, nofips, start_cloudflared, wait_tunnel_ready + + +@nofips +class TestPostQuantum: + def _extra_config(self): + config = { + "protocol": "quic", + } + return config + + def test_post_quantum(self, tmp_path, component_tests_config): + config = component_tests_config(self._extra_config()) + LOGGER.debug(config) + with start_cloudflared(tmp_path, config, cfd_args=["run", "--post-quantum"], new_process=True): + wait_tunnel_ready(tunnel_url=config.get_url(), + require_min_connections=4) diff --git a/component-tests/util.py b/component-tests/util.py index 34f5faf7..02a9f1fd 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -20,6 +20,12 @@ def select_platform(plat): return pytest.mark.skipif( platform.system() != plat, reason=f"Only runs on {plat}") +def fips_enabled(): + env_fips = os.getenv("COMPONENT_TESTS_FIPS") + return env_fips is not None and env_fips != "0" + +nofips = pytest.mark.skipif( + fips_enabled(), reason=f"Only runs without FIPS (COMPONENT_TESTS_FIPS=0)") def write_config(directory, config): config_path = directory / "config.yml" From 5b309257732a7386bb01a2cffbb7cb91b6fe6f9e Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Fri, 30 Sep 2022 10:26:55 +0100 Subject: [PATCH 224/238] TUN-6755: Remove unused publish functions We no longer need the functions that publish deb and rpm to the old pkg.cloudflare.com backed since we now send them to R2. --- Makefile | 16 ---------------- cfsetup.yaml | 27 --------------------------- 2 files changed, 43 deletions(-) diff --git a/Makefile b/Makefile index 555d57e2..691da9bb 100644 --- a/Makefile +++ b/Makefile @@ -145,22 +145,6 @@ endif test-ssh-server: docker-compose -f ssh_server_tests/docker-compose.yml up -define publish_package - chmod 664 $(BINARY_NAME)*.$(1); \ - for HOST in $(CF_PKG_HOSTS); do \ - ssh-keyscan -t ecdsa $$HOST >> ~/.ssh/known_hosts; \ - scp -p -4 $(BINARY_NAME)*.$(1) cfsync@$$HOST:/state/cf-pkg/staging/$(2)/$(TARGET_PUBLIC_REPO)/$(BINARY_NAME)/; \ - done -endef - -.PHONY: publish-deb -publish-deb: cloudflared-deb - $(call publish_package,deb,apt) - -.PHONY: publish-rpm -publish-rpm: cloudflared-rpm - $(call publish_package,rpm,yum) - cloudflared.1: cloudflared_man_template cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > cloudflared.1 diff --git a/cfsetup.yaml b/cfsetup.yaml index 6af0f2de..9b8981de 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -119,18 +119,6 @@ stretch: &stretch - export GOOS=linux - export GOARCH=arm64 - make cloudflared-deb - publish-deb: - build_dir: *build_dir - builddeps: - - *pinned_go - - build-essential - - fakeroot - - rubygem-fpm - - openssh-client - post-cache: - - export GOOS=linux - - export GOARCH=amd64 - - make publish-deb github-release-macos-amd64: build_dir: *build_dir builddeps: &build_pygithub @@ -277,18 +265,3 @@ stretch: &stretch buster: *stretch bullseye: *stretch bookworm: *stretch -centos-7: - publish-rpm: - build_dir: *build_dir - builddeps: &el7_builddeps - - https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm - pre-cache: - - yum install -y fakeroot - - yum upgrade -y binutils-2.27-44.base.el7.x86_64 - - wget https://go.dev/dl/go1.18.6.linux-amd64.tar.gz -P /tmp/ - - tar -C /usr/local -xzf /tmp/go1.18.6.linux-amd64.tar.gz - post-cache: - - export PATH=$PATH:/usr/local/go/bin - - export GOOS=linux - - export GOARCH=amd64 - - make publish-rpm From eacc8c648d39c0b0422c02f66d1eb355a4c6c738 Mon Sep 17 00:00:00 2001 From: cthuang Date: Thu, 29 Sep 2022 15:42:30 +0100 Subject: [PATCH 225/238] TUN-6812: Drop IP packets if ICMP proxy is not initialized --- connection/quic.go | 21 +++++++-------------- packet/router.go | 30 +++++++++++++++--------------- packet/router_test.go | 4 ++-- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 95829a6e..3004b929 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -50,7 +50,7 @@ type QUICConnection struct { // sessionManager tracks active sessions. It receives datagrams from quic connection via datagramMuxer sessionManager datagramsession.Manager // datagramMuxer mux/demux datagrams from quic connection - datagramMuxer quicpogs.BaseDatagramMuxer + datagramMuxer *quicpogs.DatagramMuxerV2 packetRouter *packet.Router controlStreamHandler ControlStreamHandler connOptions *tunnelpogs.ConnectionOptions @@ -75,11 +75,7 @@ func NewQUICConnection( sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) - - var pr *packet.Router - if packetRouterConfig != nil { - pr = packet.NewRouter(packetRouterConfig, datagramMuxer, &returnPipe{muxer: datagramMuxer}, logger) - } + packetRouter := packet.NewRouter(packetRouterConfig, datagramMuxer, &returnPipe{muxer: datagramMuxer}, logger) return &QUICConnection{ session: session, @@ -87,7 +83,7 @@ func NewQUICConnection( logger: logger, sessionManager: sessionManager, datagramMuxer: datagramMuxer, - packetRouter: pr, + packetRouter: packetRouter, controlStreamHandler: controlStreamHandler, connOptions: connOptions, }, nil @@ -123,17 +119,14 @@ func (q *QUICConnection) Serve(ctx context.Context) error { defer cancel() return q.sessionManager.Serve(ctx) }) - errGroup.Go(func() error { defer cancel() return q.datagramMuxer.ServeReceive(ctx) }) - if q.packetRouter != nil { - errGroup.Go(func() error { - defer cancel() - return q.packetRouter.Serve(ctx) - }) - } + errGroup.Go(func() error { + defer cancel() + return q.packetRouter.Serve(ctx) + }) return errGroup.Wait() } diff --git a/packet/router.go b/packet/router.go index 2f0fa37b..3fc451fc 100644 --- a/packet/router.go +++ b/packet/router.go @@ -23,12 +23,10 @@ type Upstream interface { // Router routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets type Router struct { - upstream Upstream - returnPipe FunnelUniPipe - icmpRouter ICMPRouter - ipv4Src netip.Addr - ipv6Src netip.Addr - logger *zerolog.Logger + upstream Upstream + returnPipe FunnelUniPipe + globalConfig *GlobalRouterConfig + logger *zerolog.Logger } // GlobalRouterConfig is the configuration shared by all instance of Router. @@ -41,12 +39,10 @@ type GlobalRouterConfig struct { func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger) *Router { return &Router{ - upstream: upstream, - returnPipe: returnPipe, - icmpRouter: globalConfig.ICMPRouter, - ipv4Src: globalConfig.IPv4Src, - ipv6Src: globalConfig.IPv6Src, - logger: logger, + upstream: upstream, + returnPipe: returnPipe, + globalConfig: globalConfig, + logger: logger, } } @@ -58,6 +54,10 @@ func (r *Router) Serve(ctx context.Context) error { if err != nil { return err } + // Drop packets if ICMPRouter wasn't created + if r.globalConfig == nil { + continue + } icmpPacket, err := icmpDecoder.Decode(rawPacket) if err != nil { r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram") @@ -72,7 +72,7 @@ func (r *Router) Serve(ctx context.Context) error { } icmpPacket.TTL-- - if err := r.icmpRouter.Request(icmpPacket, r.returnPipe); err != nil { + if err := r.globalConfig.ICMPRouter.Request(icmpPacket, r.returnPipe); err != nil { r.logger.Err(err). Str("src", icmpPacket.Src.String()). Str("dst", icmpPacket.Dst.String()). @@ -86,9 +86,9 @@ func (r *Router) Serve(ctx context.Context) error { func (r *Router) sendTTLExceedMsg(pk *ICMP, rawPacket RawPacket, encoder *Encoder) error { var srcIP netip.Addr if pk.Dst.Is4() { - srcIP = r.ipv4Src + srcIP = r.globalConfig.IPv4Src } else { - srcIP = r.ipv6Src + srcIP = r.globalConfig.IPv6Src } ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP) diff --git a/packet/router_test.go b/packet/router_test.go index 8b009c1c..c1056450 100644 --- a/packet/router_test.go +++ b/packet/router_test.go @@ -56,7 +56,7 @@ func TestRouterReturnTTLExceed(t *testing.T) { }, }, } - assertTTLExceed(t, &pk, router.ipv4Src, upstream, returnPipe) + assertTTLExceed(t, &pk, router.globalConfig.IPv4Src, upstream, returnPipe) pk = ICMP{ IP: &IP{ Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), @@ -74,7 +74,7 @@ func TestRouterReturnTTLExceed(t *testing.T) { }, }, } - assertTTLExceed(t, &pk, router.ipv6Src, upstream, returnPipe) + assertTTLExceed(t, &pk, router.globalConfig.IPv6Src, upstream, returnPipe) cancel() <-routerStopped From 49438f30f507574e997657fcdde11ddea7be2935 Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 30 Sep 2022 10:43:39 +0100 Subject: [PATCH 226/238] TUN-6813: Only proxy ICMP packets when warp-routing is enabled --- connection/connection.go | 1 + connection/connection_test.go | 4 ++ connection/quic.go | 2 +- orchestration/orchestrator.go | 23 ++++-- orchestration/orchestrator_test.go | 3 + packet/router.go | 26 ++++--- packet/router_test.go | 108 ++++++++++++++++++++++++++--- 7 files changed, 145 insertions(+), 22 deletions(-) diff --git a/connection/connection.go b/connection/connection.go index 5d2db19c..cf17d506 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -39,6 +39,7 @@ type Orchestrator interface { UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse GetConfigJSON() ([]byte, error) GetOriginProxy() (OriginProxy, error) + WarpRoutingEnabled() (enabled bool) } type NamedTunnelProperties struct { diff --git a/connection/connection_test.go b/connection/connection_test.go index 3708e16a..14c20c0d 100644 --- a/connection/connection_test.go +++ b/connection/connection_test.go @@ -56,6 +56,10 @@ func (mcr *mockOrchestrator) GetOriginProxy() (OriginProxy, error) { return mcr.originProxy, nil } +func (mcr *mockOrchestrator) WarpRoutingEnabled() (enabled bool) { + return true +} + type mockOriginProxy struct{} func (moc *mockOriginProxy) ProxyHTTP( diff --git a/connection/quic.go b/connection/quic.go index 3004b929..a6d6f6f0 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -75,7 +75,7 @@ func NewQUICConnection( sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity) datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan) sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan) - packetRouter := packet.NewRouter(packetRouterConfig, datagramMuxer, &returnPipe{muxer: datagramMuxer}, logger) + packetRouter := packet.NewRouter(packetRouterConfig, datagramMuxer, &returnPipe{muxer: datagramMuxer}, logger, orchestrator.WarpRoutingEnabled) return &QUICConnection{ session: session, diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index 0659411a..6bf62a5d 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -27,10 +27,12 @@ type Orchestrator struct { // Used by UpdateConfig to make sure one update at a time lock sync.RWMutex // Underlying value is proxy.Proxy, can be read without the lock, but still needs the lock to update - proxy atomic.Value - config *Config - tags []tunnelpogs.Tag - log *zerolog.Logger + proxy atomic.Value + // TODO: TUN-6815 Use atomic.Bool once we upgrade to go 1.19. 1 Means enabled and 0 means disabled + warpRoutingEnabled uint32 + config *Config + tags []tunnelpogs.Tag + log *zerolog.Logger // orchestrator must not handle any more updates after shutdownC is closed shutdownC <-chan struct{} @@ -122,6 +124,11 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting i o.proxy.Store(newProxy) o.config.Ingress = &ingressRules o.config.WarpRouting = warpRouting + if warpRouting.Enabled { + atomic.StoreUint32(&o.warpRoutingEnabled, 1) + } else { + atomic.StoreUint32(&o.warpRoutingEnabled, 0) + } // If proxyShutdownC is nil, there is no previous running proxy if o.proxyShutdownC != nil { @@ -190,6 +197,14 @@ func (o *Orchestrator) GetOriginProxy() (connection.OriginProxy, error) { return proxy, nil } +// TODO: TUN-6815 consider storing WarpRouting.Enabled as atomic.Bool once we upgrade to go 1.19 +func (o *Orchestrator) WarpRoutingEnabled() (enabled bool) { + if atomic.LoadUint32(&o.warpRoutingEnabled) == 0 { + return false + } + return true +} + func (o *Orchestrator) waitToCloseLastProxy() { <-o.shutdownC o.lock.Lock() diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 5c18b3a7..d3e1ee62 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -55,6 +55,7 @@ func TestUpdateConfiguration(t *testing.T) { initOriginProxy, err := orchestrator.GetOriginProxy() require.NoError(t, err) require.IsType(t, &proxy.Proxy{}, initOriginProxy) + require.False(t, orchestrator.WarpRoutingEnabled()) configJSONV2 := []byte(` { @@ -122,6 +123,7 @@ func TestUpdateConfiguration(t *testing.T) { require.Equal(t, false, configV2.Ingress.Rules[2].Config.NoTLSVerify) require.Equal(t, true, configV2.Ingress.Rules[2].Config.NoHappyEyeballs) require.True(t, configV2.WarpRouting.Enabled) + require.Equal(t, configV2.WarpRouting.Enabled, orchestrator.WarpRoutingEnabled()) require.Equal(t, configV2.WarpRouting.ConnectTimeout.Duration, 10*time.Second) originProxyV2, err := orchestrator.GetOriginProxy() @@ -166,6 +168,7 @@ func TestUpdateConfiguration(t *testing.T) { require.True(t, configV10.Ingress.Rules[0].Matches("blogs.tunnel.io", "/2022/02/10")) require.Equal(t, ingress.HelloWorldService, configV10.Ingress.Rules[0].Service.String()) require.False(t, configV10.WarpRouting.Enabled) + require.Equal(t, configV10.WarpRouting.Enabled, orchestrator.WarpRoutingEnabled()) originProxyV10, err := orchestrator.GetOriginProxy() require.NoError(t, err) diff --git a/packet/router.go b/packet/router.go index 3fc451fc..29a5d839 100644 --- a/packet/router.go +++ b/packet/router.go @@ -23,10 +23,11 @@ type Upstream interface { // Router routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets type Router struct { - upstream Upstream - returnPipe FunnelUniPipe - globalConfig *GlobalRouterConfig - logger *zerolog.Logger + upstream Upstream + returnPipe FunnelUniPipe + globalConfig *GlobalRouterConfig + logger *zerolog.Logger + checkRouterEnabledFunc func() bool } // GlobalRouterConfig is the configuration shared by all instance of Router. @@ -37,12 +38,13 @@ type GlobalRouterConfig struct { Zone string } -func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger) *Router { +func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger, checkRouterEnabledFunc func() bool) *Router { return &Router{ - upstream: upstream, - returnPipe: returnPipe, - globalConfig: globalConfig, - logger: logger, + upstream: upstream, + returnPipe: returnPipe, + globalConfig: globalConfig, + logger: logger, + checkRouterEnabledFunc: checkRouterEnabledFunc, } } @@ -54,10 +56,16 @@ func (r *Router) Serve(ctx context.Context) error { if err != nil { return err } + // Drop packets if ICMPRouter wasn't created if r.globalConfig == nil { continue } + + if enabled := r.checkRouterEnabledFunc(); !enabled { + continue + } + icmpPacket, err := icmpDecoder.Decode(rawPacket) if err != nil { r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram") diff --git a/packet/router_test.go b/packet/router_test.go index c1056450..4998456e 100644 --- a/packet/router_test.go +++ b/packet/router_test.go @@ -5,7 +5,9 @@ import ( "context" "fmt" "net/netip" + "sync/atomic" "testing" + "time" "github.com/google/gopacket/layers" "github.com/rs/zerolog" @@ -16,7 +18,12 @@ import ( ) var ( - noopLogger = zerolog.Nop() + noopLogger = zerolog.Nop() + packetConfig = &GlobalRouterConfig{ + ICMPRouter: &mockICMPRouter{}, + IPv4Src: netip.MustParseAddr("172.16.0.1"), + IPv6Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), + } ) func TestRouterReturnTTLExceed(t *testing.T) { @@ -26,12 +33,9 @@ func TestRouterReturnTTLExceed(t *testing.T) { returnPipe := &mockFunnelUniPipe{ uniPipe: make(chan RawPacket), } - packetConfig := &GlobalRouterConfig{ - ICMPRouter: &mockICMPRouter{}, - IPv4Src: netip.MustParseAddr("172.16.0.1"), - IPv6Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"), - } - router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger) + routerEnabled := &routerEnabledChecker{} + routerEnabled.set(true) + router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger, routerEnabled.isEnabled) ctx, cancel := context.WithCancel(context.Background()) routerStopped := make(chan struct{}) go func() { @@ -80,12 +84,71 @@ func TestRouterReturnTTLExceed(t *testing.T) { <-routerStopped } +func TestRouterCheckEnabled(t *testing.T) { + upstream := &mockUpstream{ + source: make(chan RawPacket), + } + returnPipe := &mockFunnelUniPipe{ + uniPipe: make(chan RawPacket), + } + routerEnabled := &routerEnabledChecker{} + router := NewRouter(packetConfig, upstream, returnPipe, &noopLogger, routerEnabled.isEnabled) + ctx, cancel := context.WithCancel(context.Background()) + routerStopped := make(chan struct{}) + go func() { + router.Serve(ctx) + close(routerStopped) + }() + + pk := ICMP{ + IP: &IP{ + Src: netip.MustParseAddr("192.168.1.1"), + Dst: netip.MustParseAddr("10.0.0.1"), + Protocol: layers.IPProtocolICMPv4, + TTL: 1, + }, + Message: &icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: 12481, + Seq: 8036, + Data: []byte(t.Name()), + }, + }, + } + + // router is disabled + require.NoError(t, upstream.send(&pk)) + select { + case <-time.After(time.Millisecond * 10): + case <-returnPipe.uniPipe: + t.Error("Unexpected reply when router is disabled") + } + routerEnabled.set(true) + // router is enabled, expects reply + require.NoError(t, upstream.send(&pk)) + <-returnPipe.uniPipe + + routerEnabled.set(false) + // router is disabled + require.NoError(t, upstream.send(&pk)) + select { + case <-time.After(time.Millisecond * 10): + case <-returnPipe.uniPipe: + t.Error("Unexpected reply when router is disabled") + } + + cancel() + <-routerStopped +} + func assertTTLExceed(t *testing.T, originalPacket *ICMP, expectedSrc netip.Addr, upstream *mockUpstream, returnPipe *mockFunnelUniPipe) { encoder := NewEncoder() rawPacket, err := encoder.Encode(originalPacket) require.NoError(t, err) - upstream.source <- rawPacket + resp := <-returnPipe.uniPipe decoder := NewICMPDecoder() decoded, err := decoder.Decode(resp) @@ -111,6 +174,16 @@ type mockUpstream struct { source chan RawPacket } +func (ms *mockUpstream) send(pk Packet) error { + encoder := NewEncoder() + rawPacket, err := encoder.Encode(pk) + if err != nil { + return err + } + ms.source <- rawPacket + return nil +} + func (ms *mockUpstream) ReceivePacket(ctx context.Context) (RawPacket, error) { select { case <-ctx.Done(): @@ -129,3 +202,22 @@ func (mir mockICMPRouter) Serve(ctx context.Context) error { func (mir mockICMPRouter) Request(pk *ICMP, responder FunnelUniPipe) error { return fmt.Errorf("Request not implemented by mockICMPRouter") } + +type routerEnabledChecker struct { + enabled uint32 +} + +func (rec *routerEnabledChecker) isEnabled() bool { + if atomic.LoadUint32(&rec.enabled) == 0 { + return false + } + return true +} + +func (rec *routerEnabledChecker) set(enabled bool) { + if enabled { + atomic.StoreUint32(&rec.enabled, 1) + } else { + atomic.StoreUint32(&rec.enabled, 0) + } +} From f81d35447eaa8b988ff135d6fd5fbc9d262a2dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 3 Oct 2022 09:55:15 +0100 Subject: [PATCH 227/238] Release 2022.10.0 --- RELEASE_NOTES | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 190cee88..e222fa9d 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,21 @@ +2022.10.0 +- 2022-09-30 TUN-6755: Remove unused publish functions +- 2022-09-30 TUN-6813: Only proxy ICMP packets when warp-routing is enabled +- 2022-09-29 TUN-6811: Ping group range should be parsed as int32 +- 2022-09-29 TUN-6812: Drop IP packets if ICMP proxy is not initialized +- 2022-09-28 TUN-6716: Document limitation of Windows ICMP proxy +- 2022-09-28 TUN-6810: Add component test for post-quantum +- 2022-09-27 TUN-6715: Provide suggestion to add cloudflared to ping_group_range if it failed to open ICMP socket +- 2022-09-22 TUN-6792: Fix brew core release by not auditing the formula +- 2022-09-22 TUN-6774: Validate OriginRequest.Access to add Ingress.Middleware +- 2022-09-22 TUN-6775: Add middleware.Handler verification to ProxyHTTP +- 2022-09-22 TUN-6791: Calculate ICMPv6 checksum +- 2022-09-22 TUN-6801: Add punycode alternatives for ingress rules +- 2022-09-21 TUN-6772: Add a JWT Validator as an ingress verifier +- 2022-09-21 TUN-6772: Add a JWT Validator as an ingress verifier +- 2022-09-21 TUN-6774: Validate OriginRequest.Access to add Ingress.Middleware +- 2022-09-21 TUN-6772: Add a JWT Validator as an ingress verifier +- 2022-09-20 TUN-6741: ICMP proxy tries to listen on specific IPv4 & IPv6 when possible 2022.9.1 - 2022-09-20 TUN-6777: Fix race condition in TestFunnelIdleTimeout - 2022-09-20 TUN-6595: Enable datagramv2 and icmp proxy by default From 6be36fa2c52634a2f4a3223f8f7cd1ac331102f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Mon, 3 Oct 2022 10:17:11 +0100 Subject: [PATCH 228/238] TUN-6806: Add ingress rule number to log when filtering due to middlware handler --- proxy/proxy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index c769e19e..71990cf9 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -103,7 +103,8 @@ func (p *Proxy) ProxyHTTP( ruleSpan.End() if err, applied := p.applyIngressMiddleware(rule, req, w); err != nil { if applied { - p.log.Error().Msg(err.Error()) + rule, srv := ruleField(p.ingressRules, ruleNum) + p.logRequestError(err, cfRay, "", rule, srv) return nil } return err From e89bceca5ecefd2963fcb50ba22d8d77f16408b9 Mon Sep 17 00:00:00 2001 From: Sudarsan Reddy Date: Tue, 4 Oct 2022 14:14:17 +0100 Subject: [PATCH 229/238] TUN-6825: Fix cloudflared:version images require arch hyphens Once we introduced multi arch docker images, pinning cloudflared versions required suffixing -(arm64/amd64) to the cloudflared:version image tag. This change should fix that by adding specific versions to the cloudflare docker build cycle --- .docker-images | 3 +-- Makefile | 4 ++++ cfsetup.yaml | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.docker-images b/.docker-images index 984a3183..4ceaf139 100644 --- a/.docker-images +++ b/.docker-images @@ -2,8 +2,7 @@ images: - name: cloudflared dockerfile: Dockerfile.$ARCH context: . - versions: - - latest + version_file: versions registries: - name: docker.io/cloudflare user: env:DOCKER_USER diff --git a/Makefile b/Makefile index 691da9bb..7652ff21 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,10 @@ endif container: docker build --build-arg=TARGET_ARCH=$(TARGET_ARCH) --build-arg=TARGET_OS=$(TARGET_OS) -t cloudflare/cloudflared-$(TARGET_OS)-$(TARGET_ARCH):"$(VERSION)" . +.PHONY: generate-docker-version +generate-docker-version: + echo latest $(VERSION) > versions + .PHONY: test test: vet ifndef CI diff --git a/cfsetup.yaml b/cfsetup.yaml index 9b8981de..7b7b9cd4 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -78,6 +78,12 @@ stretch: &stretch # same logic as above, but for FIPS packages only - ./build-packages-fips.sh - make github-release-built-pkgs + generate-versions-file: + build_dir: *build_dir + builddeps: + - build-essential + post-cache: + - make generate-docker-version build-deb: build_dir: *build_dir builddeps: &build_deb_deps From f4ae8d1446a465a7c3b50d26d295927f75dc7398 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Oct 2022 01:33:17 +0200 Subject: [PATCH 230/238] Add post-quantum flag to quick tunnel Github #773 --- cmd/cloudflared/tunnel/cmd.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index a45e6e0c..6acafe4b 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -665,6 +665,13 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"TUNNEL_MAX_FETCH_SIZE"}, Hidden: true, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "post-quantum", + Usage: "When given creates an experimental post-quantum secure tunnel", + Aliases: []string{"pq"}, + EnvVars: []string{"TUNNEL_POST_QUANTUM"}, + Hidden: FipsEnabled, + }), selectProtocolFlag, overwriteDNSFlag, }...) From d0c10b34dd6799e19a6614e7fe4785592381f93f Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Mon, 10 Oct 2022 14:21:11 +0200 Subject: [PATCH 231/238] RTG-2276 Update qtls and go mod tidy --- go.mod | 6 +++--- go.sum | 9 +++++---- .../marten-seemann/qtls-go1-18/handshake_server.go | 9 ++++++++- .../marten-seemann/qtls-go1-19/handshake_server.go | 9 ++++++++- vendor/modules.txt | 8 ++++---- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index bdd0d711..d55b4b2a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 + github.com/coreos/go-oidc/v3 v3.4.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 github.com/fsnotify/fsnotify v1.4.9 @@ -56,7 +57,6 @@ require ( github.com/cheekybits/genny v1.0.0 // indirect github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 // indirect github.com/coredns/caddy v1.1.1 // indirect - github.com/coreos/go-oidc/v3 v3.4.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -113,8 +113,8 @@ replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 // Post-quantum tunnel RTG-1339 replace ( // branch go1.18 - github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af + github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20221010110824-0053225e48b2 // branch go1.19 - github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af + github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20221010110800-4f3769902fe0 ) diff --git a/go.sum b/go.sum index 1c5d35d9..68f4b8d5 100644 --- a/go.sum +++ b/go.sum @@ -132,10 +132,10 @@ github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 h1:YzpECHxZ9TzO github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47/go.mod h1:qhx8gBILsYlbam7h09SvHDSkjpe3TfLA7b/z4rxJvkE= github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc h1:Dvk3ySBsOm5EviLx6VCyILnafPcQinXGP5jbTdHUJgE= github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc/go.mod h1:HlgKKR8V5a1wroIDDIz3/A+T+9Janfq+7n1P5sEFdi0= -github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af h1:JMpOQAaXjRRBkUX73fTNe9mConJLFl6FsIp9fHdLm7Y= -github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af/go.mod h1:aIsWqC0WXyUiUxBl/RfxAjDyWE9CCLqvSMnCMTd/+bc= -github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af h1:bhCmedjwrOSyzLtHVeQ+KhimcNTSfs0P5T7kbRQS+gA= -github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af/go.mod h1:mW0BgKFFDAiSmOdUwoORtjo0V2vqw5QzVYRtKQqw/Jg= +github.com/cloudflare/qtls-pq v0.0.0-20221010110800-4f3769902fe0 h1:LEsjEHfKnIJEJU9QEIPVRuslxpBu+2kG2DXhxpkGT+o= +github.com/cloudflare/qtls-pq v0.0.0-20221010110800-4f3769902fe0/go.mod h1:aIsWqC0WXyUiUxBl/RfxAjDyWE9CCLqvSMnCMTd/+bc= +github.com/cloudflare/qtls-pq v0.0.0-20221010110824-0053225e48b2 h1:ErNoeVNqFXV+emlf4gY7Ms7/0DbQ8PT2UFxNyWBc51Q= +github.com/cloudflare/qtls-pq v0.0.0-20221010110824-0053225e48b2/go.mod h1:mW0BgKFFDAiSmOdUwoORtjo0V2vqw5QzVYRtKQqw/Jg= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -741,6 +741,7 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server.go b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server.go index 2fe82848..8ee42837 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server.go +++ b/vendor/github.com/marten-seemann/qtls-go1-18/handshake_server.go @@ -270,7 +270,7 @@ func (hs *serverHandshakeState) processClientHello() error { hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints) - if hs.ecdheOk { + if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 { // Although omitting the ec_point_formats extension is permitted, some // old OpenSSL version will refuse to handshake if not present. // @@ -351,6 +351,13 @@ func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8 break } } + // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is + // missing, uncompressed points are supported. If supportedPoints is empty, + // the extension must be missing, as an empty extension body is rejected by + // the parser. See https://go.dev/issue/49126. + if len(supportedPoints) == 0 { + supportsPointFormat = true + } return supportsCurve && supportsPointFormat } diff --git a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go index e93874af..31981c6b 100644 --- a/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go +++ b/vendor/github.com/marten-seemann/qtls-go1-19/handshake_server.go @@ -270,7 +270,7 @@ func (hs *serverHandshakeState) processClientHello() error { hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints) - if hs.ecdheOk { + if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 { // Although omitting the ec_point_formats extension is permitted, some // old OpenSSL version will refuse to handshake if not present. // @@ -351,6 +351,13 @@ func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8 break } } + // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is + // missing, uncompressed points are supported. If supportedPoints is empty, + // the extension must be missing, as an empty extension body is rejected by + // the parser. See https://go.dev/issue/49126. + if len(supportedPoints) == 0 { + supportsPointFormat = true + } return supportsCurve && supportsPointFormat } diff --git a/vendor/modules.txt b/vendor/modules.txt index 4c133860..abdd20f9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -197,10 +197,10 @@ github.com/marten-seemann/qtls-go1-16 # github.com/marten-seemann/qtls-go1-17 v0.1.2 ## explicit; go 1.17 github.com/marten-seemann/qtls-go1-17 -# github.com/marten-seemann/qtls-go1-18 v0.1.2 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af +# github.com/marten-seemann/qtls-go1-18 v0.1.2 => github.com/cloudflare/qtls-pq v0.0.0-20221010110824-0053225e48b2 ## explicit; go 1.18 github.com/marten-seemann/qtls-go1-18 -# github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af +# github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 => github.com/cloudflare/qtls-pq v0.0.0-20221010110800-4f3769902fe0 ## explicit; go 1.19 github.com/marten-seemann/qtls-go1-19 # github.com/mattn/go-colorable v0.1.8 @@ -583,5 +583,5 @@ zombiezen.com/go/capnproto2/std/capnp/rpc # github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f # github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1 # gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 -# github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20220824105406-fb955667e0af -# github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20220824104809-96561a41e0af +# github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20221010110824-0053225e48b2 +# github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20221010110800-4f3769902fe0 From 4642316167b84871d0536b8c9766665dd07d0033 Mon Sep 17 00:00:00 2001 From: Devin Carr Date: Wed, 5 Oct 2022 11:44:59 -0700 Subject: [PATCH 232/238] TUN-6823: Update github release message to pull from KV By running the github release message step after all of the binaries are built, the KV will be populated with all of the binary checksums to inject into the release message. --- github_message.py | 95 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 21 deletions(-) mode change 100644 => 100755 github_message.py diff --git a/github_message.py b/github_message.py old mode 100644 new mode 100755 index fa69a5b3..4b1509d5 --- a/github_message.py +++ b/github_message.py @@ -1,29 +1,56 @@ #!/usr/bin/python3 """ -Creates Github Releases Notes with content hashes +Create Github Releases Notes with binary checksums from Workers KV """ import argparse import logging import os -import hashlib -import glob +import requests -from github import Github, GithubException, UnknownObjectException +from github import Github, UnknownObjectException FORMAT = "%(levelname)s - %(asctime)s: %(message)s" -logging.basicConfig(format=FORMAT) +logging.basicConfig(format=FORMAT, level=logging.INFO) CLOUDFLARED_REPO = os.environ.get("GITHUB_REPO", "cloudflare/cloudflared") GITHUB_CONFLICT_CODE = "already_exists" +BASE_KV_URL = 'https://api.cloudflare.com/client/v4/accounts/' -def get_sha256(filename): - """ get the sha256 of a file """ - sha256_hash = hashlib.sha256() - with open(filename,"rb") as f: - for byte_block in iter(lambda: f.read(4096),b""): - sha256_hash.update(byte_block) - return sha256_hash.hexdigest() + +def kv_get_keys(prefix, account, namespace, api_token): + """ get the KV keys for a given prefix """ + response = requests.get( + BASE_KV_URL + account + "/storage/kv/namespaces/" + + namespace + "/keys" + "?prefix=" + prefix, + headers={ + "Content-Type": "application/json", + "Authorization": "Bearer " + api_token, + }, + ) + if response.status_code != 200: + jsonResponse = response.json() + errors = jsonResponse["errors"] + if len(errors) > 0: + raise Exception("failed to get checksums: {0}", errors[0]) + return response.json()["result"] + + +def kv_get_value(key, account, namespace, api_token): + """ get the KV value for a provided key """ + response = requests.get( + BASE_KV_URL + account + "/storage/kv/namespaces/" + namespace + "/values/" + key, + headers={ + "Content-Type": "application/json", + "Authorization": "Bearer " + api_token, + }, + ) + if response.status_code != 200: + jsonResponse = response.json() + errors = jsonResponse["errors"] + if len(errors) > 0: + raise Exception("failed to get checksums: {0}", errors[0]) + return response.text def update_or_add_message(msg, name, sha): @@ -42,6 +69,7 @@ def update_or_add_message(msg, name, sha): return '{0}{1}```'.format(msg[:back], new_text) return '{0} \n### SHA256 Checksums:\n```\n{1}```'.format(msg, new_text) + def get_release(repo, version): """ Get a Github Release matching the version tag. """ try: @@ -55,11 +83,20 @@ def get_release(repo, version): def parse_args(): """ Parse and validate args """ parser = argparse.ArgumentParser( - description="Creates Github Releases and uploads assets." + description="Updates a Github Release with checksums from KV" ) parser.add_argument( "--api-key", default=os.environ.get("API_KEY"), help="Github API key" ) + parser.add_argument( + "--kv-namespace-id", default=os.environ.get("KV_NAMESPACE"), help="workers KV namespace id" + ) + parser.add_argument( + "--kv-account-id", default=os.environ.get("KV_ACCOUNT"), help="workers KV account id" + ) + parser.add_argument( + "--kv-api-token", default=os.environ.get("KV_API_TOKEN"), help="workers KV API Token" + ) parser.add_argument( "--release-version", metavar="version", @@ -80,6 +117,18 @@ def parse_args(): logging.error("Missing API key") is_valid = False + if not args.kv_namespace_id: + logging.error("Missing KV namespace id") + is_valid = False + + if not args.kv_account_id: + logging.error("Missing KV account id") + is_valid = False + + if not args.kv_api_token: + logging.error("Missing KV API token") + is_valid = False + if is_valid: return args @@ -95,16 +144,20 @@ def main(): repo = client.get_repo(CLOUDFLARED_REPO) release = get_release(repo, args.release_version) - msg = release.body + msg = "" - for filepath in glob.glob("artifacts/*"): - pkg_hash = get_sha256(filepath) - # add the sha256 of the new artifact to the release message body - name = os.path.basename(filepath) - msg = update_or_add_message(msg, name, pkg_hash) + prefix = f"update_{args.release_version}_" + keys = kv_get_keys(prefix, args.kv_account_id, + args.kv_namespace_id, args.kv_api_token) + for key in [k["name"] for k in keys]: + checksum = kv_get_value( + key, args.kv_account_id, args.kv_namespace_id, args.kv_api_token) + binary_name = key[len(prefix):] + msg = update_or_add_message(msg, binary_name, checksum) if args.dry_run: - logging.info("Skipping asset upload because of dry-run") + logging.info("Skipping release message update because of dry-run") + logging.info(f"Github message:\n{msg}") return # update the release body text @@ -115,4 +168,4 @@ def main(): exit(1) -main() \ No newline at end of file +main() From 40ea6a5080c583ddaefdef840a296cc64297e566 Mon Sep 17 00:00:00 2001 From: cthuang Date: Tue, 11 Oct 2022 16:48:36 +0100 Subject: [PATCH 233/238] TUN-6829: Allow user of datagramsession to control logging level of errors --- datagramsession/session.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/datagramsession/session.go b/datagramsession/session.go index 94562673..7e0131da 100644 --- a/datagramsession/session.go +++ b/datagramsession/session.go @@ -24,6 +24,13 @@ func SessionIdleErr(timeout time.Duration) error { type transportSender func(session *packet.Session) error +// ErrVithVariableSeverity are errors that have variable severity +type ErrVithVariableSeverity interface { + error + // LogLevel return the severity of this error + LogLevel() zerolog.Level +} + // Session is a bidirectional pipe of datagrams between transport and dstConn // Destination can be a connection with origin or with eyeball // When the destination is origin: @@ -53,7 +60,11 @@ func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (clos if errors.Is(err, net.ErrClosed) { s.log.Debug().Msg("Destination connection closed") } else { - s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport") + level := zerolog.ErrorLevel + if variableErr, ok := err.(ErrVithVariableSeverity); ok { + level = variableErr.LogLevel() + } + s.log.WithLevel(level).Err(err).Msg("Failed to send session payload from destination to transport") } if closeSession { s.closeChan <- err From c280d62fe506df2f90fa4343259e08a979124ddd Mon Sep 17 00:00:00 2001 From: Nigel Armstrong Date: Wed, 17 Aug 2022 22:08:01 -0700 Subject: [PATCH 234/238] Label correct container Previous PR added label to the build container, not the final container. --- Dockerfile | 4 ++-- Dockerfile.amd64 | 4 ++-- Dockerfile.arm64 | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8ca108e6..870bbbeb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,6 @@ ENV GO111MODULE=on \ TARGET_GOOS=${TARGET_GOOS} \ TARGET_GOARCH=${TARGET_GOARCH} -LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" - WORKDIR /go/src/github.com/cloudflare/cloudflared/ # copy our sources into the builder image @@ -20,6 +18,8 @@ RUN make cloudflared # use a distroless base image with glibc FROM gcr.io/distroless/base-debian11:nonroot +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" + # copy our compiled binary COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index cadfebfc..b5eefc21 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -3,8 +3,6 @@ FROM golang:1.19 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 -LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" - WORKDIR /go/src/github.com/cloudflare/cloudflared/ # copy our sources into the builder image @@ -16,6 +14,8 @@ RUN GOOS=linux GOARCH=amd64 make cloudflared # use a distroless base image with glibc FROM gcr.io/distroless/base-debian11:nonroot +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" + # copy our compiled binary COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 3ca14ce8..7995b6c3 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -3,8 +3,6 @@ FROM golang:1.19 as builder ENV GO111MODULE=on \ CGO_ENABLED=0 -LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" - WORKDIR /go/src/github.com/cloudflare/cloudflared/ # copy our sources into the builder image @@ -16,6 +14,8 @@ RUN GOOS=linux GOARCH=arm64 make cloudflared # use a distroless base image with glibc FROM gcr.io/distroless/base-debian11:nonroot-arm64 +LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared" + # copy our compiled binary COPY --from=builder --chown=nonroot /go/src/github.com/cloudflare/cloudflared/cloudflared /usr/local/bin/ From 9904929b8371191a2722e9c5dd2f2e23bec5a92c Mon Sep 17 00:00:00 2001 From: Jamie Nguyen Date: Tue, 16 Aug 2022 14:54:13 +0100 Subject: [PATCH 235/238] Fix typo in help text for `cloudflared tunnel route lb` --- cmd/cloudflared/tunnel/subcommands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 4494e761..9adc684c 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -821,7 +821,7 @@ Further information about managing Cloudflare WARP traffic to your tunnel is ava Name: "lb", Action: cliutil.ConfiguredAction(routeLbCommand), Usage: "Use this tunnel as a load balancer origin, creating pool and load balancer if necessary", - UsageText: "cloudflared tunnel route dns [TUNNEL] [HOSTNAME] [LB-POOL]", + UsageText: "cloudflared tunnel route lb [TUNNEL] [HOSTNAME] [LB-POOL]", Description: `Creates Load Balancer with an origin pool that points to the tunnel.`, }, buildRouteIPSubcommand(), From 4aead129edd4f7017b86913555523aa0f15a3c61 Mon Sep 17 00:00:00 2001 From: Samuel Rhea Date: Mon, 15 Mar 2021 08:36:32 +0000 Subject: [PATCH 236/238] update-build-readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 353a5663..d4f5a230 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,13 @@ routing), but for legacy reasons this requirement is still necessary: ## Installing `cloudflared` -Downloads are available as standalone binaries, a Docker image, and Debian, RPM, and Homebrew packages. You can also find releases here on the `cloudflared` GitHub repository. +Downloads are available as standalone binaries, a Docker image, and Debian, RPM, and Homebrew packages. You can also find releases [here](https://github.com/cloudflare/cloudflared/releases) on the `cloudflared` GitHub repository. * You can [install on macOS](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#macos) via Homebrew or by downloading the [latest Darwin amd64 release](https://github.com/cloudflare/cloudflared/releases) * Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#linux) * A Docker image of `cloudflared` is [available on DockerHub](https://hub.docker.com/r/cloudflare/cloudflared) * You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#windows) +* Build from source with the [instructions here](npm ERR! cb() never called!) User documentation for Cloudflare Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps From 90e5255a0d39cc3a1873e8b21e62d2fb9d900060 Mon Sep 17 00:00:00 2001 From: Samuel Rhea Date: Mon, 15 Mar 2021 08:51:08 +0000 Subject: [PATCH 237/238] fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4f5a230..c617dd4c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Downloads are available as standalone binaries, a Docker image, and Debian, RPM, * Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#linux) * A Docker image of `cloudflared` is [available on DockerHub](https://hub.docker.com/r/cloudflare/cloudflared) * You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#windows) -* Build from source with the [instructions here](npm ERR! cb() never called!) +* Build from source with the [instructions here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#build-from-source) User documentation for Cloudflare Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps From 2aca8445702571bab93ffe7bf0360952fcd98a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20H=C3=B6xter?= Date: Mon, 18 Jul 2022 08:36:57 +0200 Subject: [PATCH 238/238] drop usage of cat when sed is invoked to generate the manpage --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7652ff21..39ed94f0 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ test-ssh-server: docker-compose -f ssh_server_tests/docker-compose.yml up cloudflared.1: cloudflared_man_template - cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > cloudflared.1 + sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' cloudflared_man_template > cloudflared.1 install: cloudflared cloudflared.1 mkdir -p $(DESTDIR)$(INSTALL_BINDIR) $(DESTDIR)$(INSTALL_MANDIR)

      #VBW_{5$#Up)b}Ig*}DiqurNa^ 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%0AHGXPxdb@gt{vr5%NssK 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